LibreOffice Module sw (master)  1
swnewtable.cxx
Go to the documentation of this file.
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  * Licensed to the Apache Software Foundation (ASF) under one or more
12  * contributor license agreements. See the NOTICE file distributed
13  * with this work for additional information regarding copyright
14  * ownership. The ASF licenses this file to you under the Apache
15  * License, Version 2.0 (the "License"); you may not use this file
16  * except in compliance with the License. You may obtain a copy of
17  * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <swtable.hxx>
21 #include <tblsel.hxx>
22 #include <tblrwcl.hxx>
23 #include <ndtxt.hxx>
24 #include <ndole.hxx>
25 #include <node.hxx>
26 #include <UndoTable.hxx>
27 #include <pam.hxx>
28 #include <frmfmt.hxx>
29 #include <frmatr.hxx>
30 #include <cellfrm.hxx>
31 #include <fmtfsize.hxx>
32 #include <doc.hxx>
33 #include <IDocumentUndoRedo.hxx>
37 #include <cstdlib>
38 #include <vector>
39 #include <set>
40 #include <list>
41 #include <memory>
42 #include <editeng/boxitem.hxx>
43 #include <editeng/protitem.hxx>
44 #include <swtblfmt.hxx>
45 #include <calbck.hxx>
46 #include <sal/log.hxx>
47 #include <osl/diagnose.h>
48 
49 #ifdef DBG_UTIL
50 #define CHECK_TABLE(t) (t).CheckConsistency();
51 #else
52 #define CHECK_TABLE(t)
53 #endif
54 
65 {
66 public:
67  std::vector<SwSelBoxes> maBoxes;
69  SwBoxSelection() : mnMergeWidth(0) {}
70  bool isEmpty() const { return maBoxes.empty(); }
71  void push_back(const SwSelBoxes& rNew) { maBoxes.push_back(rNew); }
72 };
73 
90 bool SwTable::NewMerge( SwDoc* pDoc, const SwSelBoxes& rBoxes,
91  const SwSelBoxes& rMerged, SwUndoTableMerge* pUndo )
92 {
93  if( pUndo )
94  pUndo->SetSelBoxes( rBoxes );
95  DeleteSel( pDoc, rBoxes, &rMerged, nullptr, true, true );
96 
97  CHECK_TABLE( *this )
98  return true;
99 }
100 
124 static void lcl_CheckMinMax( tools::Long& rMin, tools::Long& rMax, const SwTableLine& rLine, size_t nCheck, bool bSet )
125 {
126  ++nCheck;
127  if( rLine.GetTabBoxes().size() < nCheck )
128  { // robust
129  OSL_FAIL( "Box out of table line" );
130  nCheck = rLine.GetTabBoxes().size();
131  }
132 
133  tools::Long nNew = 0; // will be the right border of the current box
134  tools::Long nWidth = 0; // the width of the current box
135  for( size_t nCurrBox = 0; nCurrBox < nCheck; ++nCurrBox )
136  {
137  SwTableBox* pBox = rLine.GetTabBoxes()[nCurrBox];
138  OSL_ENSURE( pBox, "Missing table box" );
139  nWidth = pBox->GetFrameFormat()->GetFrameSize().GetWidth();
140  nNew += nWidth;
141  }
142  // nNew is the right border of the wished box
143  if( bSet || nNew > rMax )
144  rMax = nNew;
145  nNew -= nWidth; // nNew becomes the left border of the wished box
146  if( bSet || nNew < rMin )
147  rMin = nNew;
148 }
149 
163 {
164  if( !rBox.GetUpper() )
165  return 0;
166  tools::Long nLeft = 0;
167  const SwTableLine &rLine = *rBox.GetUpper();
168  const size_t nCount = rLine.GetTabBoxes().size();
169  for( size_t nCurrBox = 0; nCurrBox < nCount; ++nCurrBox )
170  {
171  SwTableBox* pBox = rLine.GetTabBoxes()[nCurrBox];
172  OSL_ENSURE( pBox, "Missing table box" );
173  if( pBox == &rBox )
174  return nLeft;
175  nLeft += pBox->GetFrameFormat()->GetFrameSize().GetWidth();
176  }
177  OSL_FAIL( "Box not found in own upper?" );
178  return nLeft;
179 }
180 
197 static SwTableBox* lcl_LeftBorder2Box( tools::Long nLeft, const SwTableLine* pLine )
198 {
199  if( !pLine )
200  return nullptr;
201  tools::Long nCurrLeft = 0;
202  const size_t nCount = pLine->GetTabBoxes().size();
203  for( size_t nCurrBox = 0; nCurrBox < nCount; ++nCurrBox )
204  {
205  SwTableBox* pBox = pLine->GetTabBoxes()[nCurrBox];
206  OSL_ENSURE( pBox, "Missing table box" );
207  if( pBox->GetFrameFormat()->GetFrameSize().GetWidth() )
208  {
209  if( nCurrLeft == nLeft )
210  return pBox;
211  // HACK: It appears that rounding errors may result in positions not matching
212  // exactly, so allow a little tolerance. This happens at least with merged cells
213  // in the doc from fdo#38414 .
214  if( std::abs( nCurrLeft - nLeft ) <= ( nLeft / 1000 ))
215  return pBox;
216  if( nCurrLeft >= nLeft )
217  {
218  SAL_WARN( "sw.core", "Possibly wrong box found" );
219  return pBox;
220  }
221  }
222  nCurrLeft += pBox->GetFrameFormat()->GetFrameSize().GetWidth();
223  }
224  OSL_FAIL( "Didn't find wished box" );
225  return nullptr;
226 }
227 
257 static void lcl_ChangeRowSpan( const SwTable& rTable, const tools::Long nDiff,
258  sal_uInt16 nRowIdx, const bool bSingle )
259 {
260  if( !nDiff || nRowIdx >= rTable.GetTabLines().size() )
261  return;
262  OSL_ENSURE( !bSingle || nDiff > 0, "Don't set bSingle when deleting lines!" );
263  bool bGoOn;
264  // nDistance is the distance between the current row and the critical row,
265  // e.g. the deleted rows or the inserted rows.
266  // If the row span is lower than the distance there is nothing to do
267  // because the row span ends before the critical area.
268  // When the inserted rows should not be overlapped by row spans which ends
269  // exactly in the row above, the trick is to start with a distance of 1.
270  tools::Long nDistance = bSingle ? 1 : 0;
271  do
272  {
273  bGoOn = false; // will be set to true if we found a non-master cell
274  // which has to be manipulated => we have to check the previous row, too.
275  const SwTableLine* pLine = rTable.GetTabLines()[ nRowIdx ];
276  const size_t nBoxCount = pLine->GetTabBoxes().size();
277  for( size_t nCurrBox = 0; nCurrBox < nBoxCount; ++nCurrBox )
278  {
279  sal_Int32 nRowSpan = pLine->GetTabBoxes()[nCurrBox]->getRowSpan();
280  sal_Int32 nAbsSpan = nRowSpan > 0 ? nRowSpan : -nRowSpan;
281  // Check if the last overlapped cell is above or below
282  // the critical area
283  if( nAbsSpan > nDistance )
284  {
285  if( nDiff > 0 )
286  {
287  if( nRowSpan > 0 )
288  nRowSpan += nDiff; // increment row span of master cell
289  else
290  {
291  nRowSpan -= nDiff; // increment row span of non-master cell
292  bGoOn = true;
293  }
294  }
295  else
296  {
297  if( nRowSpan > 0 )
298  { // A master cell
299  // end of row span behind the deleted area ..
300  if( nRowSpan - nDistance > -nDiff )
301  nRowSpan += nDiff;
302  else // .. or inside the deleted area
303  nRowSpan = nDistance + 1;
304  }
305  else
306  { // Same for a non-master cell
307  if( nRowSpan + nDistance < nDiff )
308  nRowSpan -= nDiff;
309  else
310  nRowSpan = -nDistance - 1;
311  bGoOn = true; // We have to continue
312  }
313  }
314  pLine->GetTabBoxes()[ nCurrBox ]->setRowSpan( nRowSpan );
315  }
316  }
317  ++nDistance;
318  if( nRowIdx )
319  --nRowIdx;
320  else
321  bGoOn = false; //robust
322  } while( bGoOn );
323 }
324 
329 std::unique_ptr<SwBoxSelection> SwTable::CollectBoxSelection( const SwPaM& rPam ) const
330 {
331  OSL_ENSURE( m_bNewModel, "Don't call me for old tables" );
332  if( m_aLines.empty() )
333  return nullptr;
334  const SwNode* pStartNd = rPam.Start()->nNode.GetNode().FindTableBoxStartNode();
335  const SwNode* pEndNd = rPam.End()->nNode.GetNode().FindTableBoxStartNode();
336  if( !pStartNd || !pEndNd || pStartNd == pEndNd )
337  return nullptr;
338 
339  const size_t nLines = m_aLines.size();
340  size_t nTop = 0;
341  size_t nBottom = 0;
342  tools::Long nMin = 0, nMax = 0;
343  int nFound = 0;
344  for( size_t nRow = 0; nFound < 2 && nRow < nLines; ++nRow )
345  {
346  SwTableLine* pLine = m_aLines[nRow];
347  OSL_ENSURE( pLine, "Missing table line" );
348  const size_t nCols = pLine->GetTabBoxes().size();
349  for( size_t nCol = 0; nCol < nCols; ++nCol )
350  {
351  SwTableBox* pBox = pLine->GetTabBoxes()[nCol];
352  OSL_ENSURE( pBox, "Missing table box" );
353  if( nFound )
354  {
355  if( pBox->GetSttNd() == pEndNd )
356  {
357  nBottom = nRow;
358  lcl_CheckMinMax( nMin, nMax, *pLine, nCol, false );
359  ++nFound;
360  break;
361  }
362  }
363  else if( pBox->GetSttNd() == pStartNd )
364  {
365  nTop = nRow;
366  lcl_CheckMinMax( nMin, nMax, *pLine, nCol, true );
367  ++nFound;
368  }
369  }
370  }
371  if( nFound < 2 )
372  return nullptr;
373 
374  bool bOkay = true;
375  tools::Long nMid = ( nMin + nMax ) / 2;
376 
377  auto pRet(std::make_unique<SwBoxSelection>());
378  std::vector< std::pair< SwTableBox*, tools::Long > > aNewWidthVector;
379  size_t nCheckBottom = nBottom;
380  tools::Long nLeftSpan = 0;
381  tools::Long nRightSpan = 0;
382  tools::Long nLeftSpanCnt = 0;
383  tools::Long nRightSpanCnt = 0;
384  for( size_t nRow = nTop; nRow <= nBottom && bOkay && nRow < nLines; ++nRow )
385  {
386  SwTableLine* pLine = m_aLines[nRow];
387  OSL_ENSURE( pLine, "Missing table line" );
388  SwSelBoxes aBoxes;
389  tools::Long nRight = 0;
390  const size_t nCount = pLine->GetTabBoxes().size();
391  for( size_t nCurrBox = 0; nCurrBox < nCount; ++nCurrBox )
392  {
393  SwTableBox* pBox = pLine->GetTabBoxes()[nCurrBox];
394  OSL_ENSURE( pBox, "Missing table box" );
395  tools::Long nLeft = nRight;
396  nRight += pBox->GetFrameFormat()->GetFrameSize().GetWidth();
397  sal_Int32 nRowSpan = pBox->getRowSpan();
398  if( nRight <= nMin )
399  {
400  if( nRight == nMin && nLeftSpanCnt )
401  bOkay = false;
402  continue;
403  }
404  SwTableBox* pInnerBox = nullptr;
405  SwTableBox* pLeftBox = nullptr;
406  SwTableBox* pRightBox = nullptr;
407  tools::Long nDiff = 0;
408  tools::Long nDiff2 = 0;
409  if( nLeft < nMin )
410  {
411  if( nRight >= nMid || nRight + nLeft >= nMin + nMin )
412  {
413  if( nCurrBox )
414  {
415  aBoxes.insert(pBox);
416  pInnerBox = pBox;
417  pLeftBox = pLine->GetTabBoxes()[nCurrBox-1];
418  nDiff = nMin - nLeft;
419  if( nRight > nMax )
420  {
421  if( nCurrBox+1 < nCount )
422  {
423  pRightBox = pLine->GetTabBoxes()[nCurrBox+1];
424  nDiff2 = nRight - nMax;
425  }
426  else
427  bOkay = false;
428  }
429  else if( nRightSpanCnt && nRight == nMax )
430  bOkay = false;
431  }
432  else
433  bOkay = false;
434  }
435  else if( nCurrBox+1 < nCount )
436  {
437  pLeftBox = pBox;
438  pInnerBox = pLine->GetTabBoxes()[nCurrBox+1];
439  nDiff = nMin - nRight;
440  }
441  else
442  bOkay = false;
443  }
444  else if( nRight <= nMax )
445  {
446  aBoxes.insert(pBox);
447  if( nRow == nTop && nRowSpan < 0 )
448  {
449  bOkay = false;
450  break;
451  }
452  if( nRowSpan > 1 && nRow + nRowSpan - 1 > nBottom )
453  nBottom = nRow + nRowSpan - 1;
454  if( nRowSpan < -1 && nRow - nRowSpan - 1 > nBottom )
455  nBottom = nRow - nRowSpan - 1;
456  if( nRightSpanCnt && nRight == nMax )
457  bOkay = false;
458  }
459  else if( nLeft < nMax )
460  {
461  if( nLeft <= nMid || nRight + nLeft <= nMax )
462  {
463  if( nCurrBox+1 < nCount )
464  {
465  aBoxes.insert(pBox);
466  pInnerBox = pBox;
467  pRightBox = pLine->GetTabBoxes()[nCurrBox+1];
468  nDiff = nRight - nMax;
469  }
470  else
471  bOkay = false;
472  }
473  else if( nCurrBox )
474  {
475  pRightBox = pBox;
476  pInnerBox = pLine->GetTabBoxes()[nCurrBox-1];
477  nDiff = nLeft - nMax;
478  }
479  else
480  bOkay = false;
481  }
482  else
483  break;
484  if( pInnerBox )
485  {
486  if( nRow == nBottom )
487  {
488  tools::Long nTmpSpan = pInnerBox->getRowSpan();
489  if( nTmpSpan > 1 )
490  nBottom += nTmpSpan - 1;
491  else if( nTmpSpan < -1 )
492  nBottom -= nTmpSpan + 1;
493  }
494  SwTableBox* pOuterBox = pLeftBox;
495  do
496  {
497  if( pOuterBox )
498  {
499  tools::Long nOutSpan = pOuterBox->getRowSpan();
500  if( nOutSpan != 1 )
501  {
502  size_t nCheck = nRow;
503  if( nOutSpan < 0 )
504  {
505  const SwTableBox& rBox =
506  pOuterBox->FindStartOfRowSpan( *this );
507  nOutSpan = rBox.getRowSpan();
508  const SwTableLine* pTmpL = rBox.GetUpper();
509  nCheck = GetTabLines().GetPos( pTmpL );
510  if( nCheck < nTop )
511  bOkay = false;
512  if( pOuterBox == pLeftBox )
513  {
514  if( !nLeftSpanCnt || nMin - nDiff != nLeftSpan )
515  bOkay = false;
516  }
517  else
518  {
519  if( !nRightSpanCnt || nMax + nDiff != nRightSpan )
520  bOkay = false;
521  }
522  }
523  else
524  {
525  if( pOuterBox == pLeftBox )
526  {
527  if( nLeftSpanCnt )
528  bOkay = false;
529  nLeftSpan = nMin - nDiff;
530  nLeftSpanCnt = nOutSpan;
531  }
532  else
533  {
534  if( nRightSpanCnt )
535  bOkay = false;
536  nRightSpan = nMax + nDiff;
537  nRightSpanCnt = nOutSpan;
538  }
539  }
540  nCheck += nOutSpan - 1;
541  if( nCheck > nCheckBottom )
542  nCheckBottom = nCheck;
543  }
544  else if( ( nLeftSpanCnt && pLeftBox == pOuterBox ) ||
545  ( nRightSpanCnt && pRightBox == pOuterBox ) )
546  bOkay = false;
547  std::pair< SwTableBox*, long > aTmp;
548  aTmp.first = pInnerBox;
549  aTmp.second = -nDiff;
550  aNewWidthVector.push_back(aTmp);
551  aTmp.first = pOuterBox;
552  aTmp.second = nDiff;
553  aNewWidthVector.push_back(aTmp);
554  }
555  pOuterBox = pOuterBox == pRightBox ? nullptr : pRightBox;
556  if( nDiff2 )
557  nDiff = nDiff2;
558  } while( pOuterBox );
559  }
560  }
561  if( nLeftSpanCnt )
562  --nLeftSpanCnt;
563  if( nRightSpanCnt )
564  --nRightSpanCnt;
565  pRet->push_back(aBoxes);
566  }
567  if( nCheckBottom > nBottom )
568  bOkay = false;
569  if( bOkay )
570  {
571  pRet->mnMergeWidth = nMax - nMin;
572  for (auto const& newWidth : aNewWidthVector)
573  {
574  SwFrameFormat* pFormat = newWidth.first->ClaimFrameFormat();
575  tools::Long nNewWidth = pFormat->GetFrameSize().GetWidth() + newWidth.second;
576  pFormat->SetFormatAttr( SwFormatFrameSize( SwFrameSize::Variable, nNewWidth, 0 ) );
577  }
578  }
579  else
580  pRet.reset();
581 
582  return pRet;
583 }
584 
589 static void lcl_InvalidateCellFrame( const SwTableBox& rBox )
590 {
592  for( SwCellFrame* pCell = aIter.First(); pCell; pCell = aIter.Next() )
593  {
594  if( pCell->GetTabBox() == &rBox )
595  {
596  pCell->InvalidateSize();
597  SwFrame* pLower = pCell->GetLower();
598  if( pLower )
599  pLower->InvalidateSize_();
600  }
601  }
602 }
603 
608 static tools::Long lcl_InsertPosition( SwTable &rTable, std::vector<sal_uInt16>& rInsPos,
609  const SwSelBoxes& rBoxes, bool bBehind )
610 {
611  sal_Int32 nAddWidth = 0;
612  tools::Long nCount = 0;
613  for (size_t j = 0; j < rBoxes.size(); ++j)
614  {
615  SwTableBox *pBox = rBoxes[j];
616  SwTableLine* pLine = pBox->GetUpper();
617  tools::Long nWidth = rBoxes[j]->GetFrameFormat()->GetFrameSize().GetWidth();
618  nAddWidth += nWidth;
619  sal_uInt16 nCurrBox = pLine->GetBoxPos( pBox );
620  sal_uInt16 nCurrLine = rTable.GetTabLines().GetPos( pLine );
621  OSL_ENSURE( nCurrLine != USHRT_MAX, "Time to say Good-Bye.." );
622  if( rInsPos[ nCurrLine ] == USHRT_MAX )
623  {
624  rInsPos[ nCurrLine ] = nCurrBox;
625  ++nCount;
626  }
627  else if( ( rInsPos[ nCurrLine ] > nCurrBox ) == !bBehind )
628  rInsPos[ nCurrLine ] = nCurrBox;
629  }
630  if( nCount )
631  nAddWidth /= nCount;
632  return nAddWidth;
633 }
634 
653 bool SwTable::NewInsertCol( SwDoc& rDoc, const SwSelBoxes& rBoxes,
654  sal_uInt16 nCnt, bool bBehind )
655 {
656  if( m_aLines.empty() || !nCnt )
657  return false;
658 
659  CHECK_TABLE( *this )
660  tools::Long nNewBoxWidth = 0;
661  std::vector< sal_uInt16 > aInsPos( m_aLines.size(), USHRT_MAX );
662  { // Calculation of the insert positions and the width of the new boxes
663  sal_uInt64 nTableWidth = 0;
664  for( size_t i = 0; i < m_aLines[0]->GetTabBoxes().size(); ++i )
665  nTableWidth += m_aLines[0]->GetTabBoxes()[i]->GetFrameFormat()->GetFrameSize().GetWidth();
666 
667  // Fill the vector of insert positions and the (average) width to insert
668  sal_uInt64 nAddWidth = lcl_InsertPosition( *this, aInsPos, rBoxes, bBehind );
669 
670  // Given is the (average) width of the selected boxes, if we would
671  // insert nCnt of columns the table would grow
672  // So we will shrink the table first, then insert the new boxes and
673  // get a table with the same width than before.
674  // But we will not shrink the table by the full already calculated value,
675  // we will reduce this value proportional to the old table width
676  nAddWidth *= nCnt; // we have to insert nCnt boxes per line
677  sal_uInt64 nResultingWidth = nAddWidth + nTableWidth;
678  if( !nResultingWidth )
679  return false;
680  nAddWidth = (nAddWidth * nTableWidth) / nResultingWidth;
681  nNewBoxWidth = tools::Long( nAddWidth / nCnt ); // Rounding
682  nAddWidth = nNewBoxWidth * nCnt; // Rounding
683  if( !nAddWidth || nAddWidth >= nTableWidth )
684  return false;
685  AdjustWidths( static_cast< tools::Long >(nTableWidth), static_cast< tools::Long >(nTableWidth - nAddWidth) );
686  }
687 
688  FndBox_ aFndBox( nullptr, nullptr );
689  aFndBox.SetTableLines( rBoxes, *this );
690  aFndBox.DelFrames( *this );
691 
692  SwTableNode* pTableNd = GetTableNode();
693  std::vector<SwTableBoxFormat*> aInsFormat( nCnt, nullptr );
694  size_t nLastLine = SAL_MAX_SIZE;
695  sal_Int32 nLastRowSpan = 1;
696 
697  for( size_t i = 0; i < m_aLines.size(); ++i )
698  {
699  SwTableLine* pLine = m_aLines[ i ];
700  sal_uInt16 nInsPos = aInsPos[i];
701  assert(nInsPos != USHRT_MAX); // didn't find insert position
702  SwTableBox* pBox = pLine->GetTabBoxes()[ nInsPos ];
703  if( bBehind )
704  ++nInsPos;
705  SwTableBoxFormat* pBoxFrameFormat = static_cast<SwTableBoxFormat*>(pBox->GetFrameFormat());
706  ::InsTableBox( rDoc, pTableNd, pLine, pBoxFrameFormat, pBox, nInsPos, nCnt );
707  sal_Int32 nRowSpan = pBox->getRowSpan();
708  tools::Long nDiff = i - nLastLine;
709  bool bNewSpan = false;
710  if( nLastLine != SAL_MAX_SIZE && nDiff <= nLastRowSpan &&
711  nRowSpan != nDiff - nLastRowSpan )
712  {
713  bNewSpan = true;
714  while( nLastLine < i )
715  {
716  SwTableLine* pTmpLine = m_aLines[ nLastLine ];
717  sal_uInt16 nTmpPos = aInsPos[nLastLine];
718  if( bBehind )
719  ++nTmpPos;
720  for( sal_uInt16 j = 0; j < nCnt; ++j )
721  pTmpLine->GetTabBoxes()[nTmpPos+j]->setRowSpan( nDiff );
722  if( nDiff > 0 )
723  nDiff = -nDiff;
724  ++nDiff;
725  ++nLastLine;
726  }
727  }
728  if( nRowSpan > 0 )
729  bNewSpan = true;
730  if( bNewSpan )
731  {
732  nLastLine = i;
733  if( nRowSpan < 0 )
734  nLastRowSpan = -nRowSpan;
735  else
736  nLastRowSpan = nRowSpan;
737  }
738  const SvxBoxItem& aSelBoxItem = pBoxFrameFormat->GetBox();
739  std::unique_ptr<SvxBoxItem> pNoRightBorder;
740  if( aSelBoxItem.GetRight() )
741  {
742  pNoRightBorder.reset( new SvxBoxItem( aSelBoxItem ));
743  pNoRightBorder->SetLine( nullptr, SvxBoxItemLine::RIGHT );
744  }
745  for( sal_uInt16 j = 0; j < nCnt; ++j )
746  {
747  SwTableBox *pCurrBox = pLine->GetTabBoxes()[nInsPos+j];
748  if( bNewSpan )
749  {
750  pCurrBox->setRowSpan( nLastRowSpan );
751  SwFrameFormat* pFrameFormat = pCurrBox->ClaimFrameFormat();
752  SwFormatFrameSize aFrameSz( pFrameFormat->GetFrameSize() );
753  aFrameSz.SetWidth( nNewBoxWidth );
754  pFrameFormat->SetFormatAttr( aFrameSz );
755  if( pNoRightBorder && ( !bBehind || j+1 < nCnt ) )
756  pFrameFormat->SetFormatAttr( *pNoRightBorder );
757  aInsFormat[j] = static_cast<SwTableBoxFormat*>(pFrameFormat);
758  }
759  else
760  pCurrBox->ChgFrameFormat( aInsFormat[j] );
761  }
762  if( bBehind && pNoRightBorder )
763  {
764  SwFrameFormat* pFrameFormat = pBox->ClaimFrameFormat();
765  pFrameFormat->SetFormatAttr( *pNoRightBorder );
766  }
767  }
768 
769  aFndBox.MakeFrames( *this );
770 #if OSL_DEBUG_LEVEL > 0
771  {
772  const SwTableBoxes &rTabBoxes = m_aLines[0]->GetTabBoxes();
773  tools::Long nNewWidth = 0;
774  for( size_t i = 0; i < rTabBoxes.size(); ++i )
775  nNewWidth += rTabBoxes[i]->GetFrameFormat()->GetFrameSize().GetWidth();
776  OSL_ENSURE( nNewWidth > 0, "Very small" );
777  }
778 #endif
779  CHECK_TABLE( *this )
780 
781  return true;
782 }
783 
809 bool SwTable::PrepareMerge( const SwPaM& rPam, SwSelBoxes& rBoxes,
810  SwSelBoxes& rMerged, SwTableBox** ppMergeBox, SwUndoTableMerge* pUndo )
811 {
812  if( !m_bNewModel )
813  {
814  ::GetMergeSel( rPam, rBoxes, ppMergeBox, pUndo );
815  return rBoxes.size() > 1;
816  }
817  CHECK_TABLE( *this )
818  // We have to assert a "rectangular" box selection before we start to merge
819  std::unique_ptr< SwBoxSelection > pSel( CollectBoxSelection( rPam ) );
820  if (!pSel || pSel->isEmpty())
821  return false;
822  // Now we should have a rectangle of boxes,
823  // i.e. contiguous cells in contiguous rows
824  bool bMerge = false; // will be set if any content is transferred from
825  // a "not already overlapped" cell into the new master cell.
826  const SwSelBoxes& rFirstBoxes = pSel->maBoxes[0];
827  if (rFirstBoxes.empty())
828  return false;
829  SwTableBox *pMergeBox = rFirstBoxes[0]; // the master cell box
830  if( !pMergeBox )
831  return false;
832  (*ppMergeBox) = pMergeBox;
833  // The new master box will get the left and the top border of the top-left
834  // box of the selection and because the new master cell _is_ the top-left
835  // box, the left and right border does not need to be changed.
836  // The right and bottom border instead has to be derived from the right-
837  // bottom box of the selection. If this is an overlapped cell,
838  // the appropriate master box.
839  SwTableBox* pLastBox = nullptr; // the right-bottom (master) cell
840  SwDoc* pDoc = GetFrameFormat()->GetDoc();
841  SwPosition aInsPos( *pMergeBox->GetSttNd()->EndOfSectionNode() );
842  SwPaM aChkPam( aInsPos );
843  // The number of lines in the selection rectangle: nLineCount
844  const size_t nLineCount = pSel->maBoxes.size();
845  // BTW: nLineCount is the rowspan of the new master cell
846  sal_Int32 nRowSpan = static_cast<tools::Long>(nLineCount);
847  // We will need the first and last line of the selection
848  // to check if there any superfluous row after merging
849  SwTableLine* pFirstLn = nullptr;
850  SwTableLine* pLastLn = nullptr;
851  // Iteration over the lines of the selection...
852  for( size_t nCurrLine = 0; nCurrLine < nLineCount; ++nCurrLine )
853  {
854  // The selected boxes in the current line
855  const SwSelBoxes& rLineBoxes = pSel->maBoxes[nCurrLine];
856  size_t nColCount = rLineBoxes.size();
857  // Iteration over the selected cell in the current row
858  for (size_t nCurrCol = 0; nCurrCol < nColCount; ++nCurrCol)
859  {
860  SwTableBox* pBox = rLineBoxes[nCurrCol];
861  rMerged.insert( pBox );
862  // Only the first selected cell in every row will be alive,
863  // the other will be deleted => put into rBoxes
864  if( nCurrCol )
865  rBoxes.insert( pBox );
866  else
867  {
868  if( nCurrLine == 1 )
869  pFirstLn = pBox->GetUpper(); // we need this line later on
870  if( nCurrLine + 1 == nLineCount )
871  pLastLn = pBox->GetUpper(); // and this one, too.
872  }
873  // A box has to be merged if it's not the master box itself,
874  // but an already overlapped cell must not be merged as well.
875  bool bDoMerge = pBox != pMergeBox && pBox->getRowSpan() > 0;
876  // The last box has to be in the last "column" of the selection
877  // and it has to be a master cell
878  if( nCurrCol+1 == nColCount && pBox->getRowSpan() > 0 )
879  pLastBox = pBox;
880  if( bDoMerge )
881  {
882  bMerge = true;
883  // If the cell to merge contains only one empty paragraph,
884  // we do not transfer this paragraph.
885  if( !IsEmptyBox( *pBox, aChkPam ) )
886  {
887  SwNodeIndex& rInsPosNd = aInsPos.nNode;
888  SwPaM aPam( aInsPos );
889  aPam.GetPoint()->nNode.Assign( *pBox->GetSttNd()->EndOfSectionNode(), -1 );
890  SwContentNode* pCNd = aPam.GetContentNode();
891  aPam.GetPoint()->nContent.Assign( pCNd, pCNd ? pCNd->Len() : 0 );
892  SwNodeIndex aSttNdIdx( *pBox->GetSttNd(), 1 );
893  bool const bUndo = pDoc->GetIDocumentUndoRedo().DoesUndo();
894  if( pUndo )
895  {
896  pDoc->GetIDocumentUndoRedo().DoUndo(false);
897  }
899  if( pUndo )
900  {
901  pDoc->GetIDocumentUndoRedo().DoUndo(bUndo);
902  }
903  SwNodeRange aRg( aSttNdIdx, aPam.GetPoint()->nNode );
904  if( pUndo )
905  pUndo->MoveBoxContent( *pDoc, aRg, rInsPosNd );
906  else
907  {
908  pDoc->getIDocumentContentOperations().MoveNodeRange( aRg, rInsPosNd,
910  }
911  }
912  }
913  // Only the cell of the first selected column will stay alive
914  // and got a new row span
915  if( !nCurrCol )
916  pBox->setRowSpan( nRowSpan );
917  }
918  if( nRowSpan > 0 ) // the master cell is done, from now on we set
919  nRowSpan = -nRowSpan; // negative row spans
920  ++nRowSpan; // ... -3, -2, -1
921  }
922  if( bMerge )
923  {
924  // A row containing overlapped cells is superfluous,
925  // these cells can be put into rBoxes for deletion
926  FindSuperfluousRows_( rBoxes, pFirstLn, pLastLn );
927  // pNewFormat will be set to the new master box and the overlapped cells
928  SwFrameFormat* pNewFormat = pMergeBox->ClaimFrameFormat();
929  pNewFormat->SetFormatAttr( SwFormatFrameSize( SwFrameSize::Variable, pSel->mnMergeWidth, 0 ) );
930  for( size_t nCurrLine = 0; nCurrLine < nLineCount; ++nCurrLine )
931  {
932  const SwSelBoxes& rLineBoxes = pSel->maBoxes[nCurrLine];
933  size_t nColCount = rLineBoxes.size();
934  for (size_t nCurrCol = 0; nCurrCol < nColCount; ++nCurrCol)
935  {
936  SwTableBox* pBox = rLineBoxes[nCurrCol];
937  if( nCurrCol )
938  {
939  // Even this box will be deleted soon,
940  // we have to correct the width to avoid side effects
941  SwFrameFormat* pFormat = pBox->ClaimFrameFormat();
943  }
944  else
945  {
946  pBox->ChgFrameFormat( static_cast<SwTableBoxFormat*>(pNewFormat) );
947  // remove numbering from cells that will be disabled in the merge
948  if( nCurrLine )
949  {
950  SwPaM aPam( *pBox->GetSttNd(), 0 );
951  aPam.GetPoint()->nNode++;
952  SwTextNode* pNd = aPam.GetNode().GetTextNode();
953  while( pNd )
954  {
955  pNd->SetCountedInList( false );
956 
957  aPam.GetPoint()->nNode++;
958  pNd = aPam.GetNode().GetTextNode();
959  }
960  }
961  }
962  }
963  }
964  if( pLastBox ) // Robust
965  {
966  // The new borders of the master cell...
967  SvxBoxItem aBox( pMergeBox->GetFrameFormat()->GetBox() );
968  bool bOld = aBox.GetRight() || aBox.GetBottom();
969  const SvxBoxItem& rBox = pLastBox->GetFrameFormat()->GetBox();
970  aBox.SetLine( rBox.GetRight(), SvxBoxItemLine::RIGHT );
971  aBox.SetLine( rBox.GetBottom(), SvxBoxItemLine::BOTTOM );
972  if( bOld || aBox.GetLeft() || aBox.GetTop() || aBox.GetRight() || aBox.GetBottom() )
973  (*ppMergeBox)->GetFrameFormat()->SetFormatAttr( aBox );
974  }
975 
976  if( pUndo )
977  pUndo->AddNewBox( pMergeBox->GetSttIdx() );
978  }
979  return bMerge;
980 }
981 
987  SwTableLine* pFirstLn, SwTableLine* pLastLn )
988 {
989  if( !pFirstLn || !pLastLn )
990  {
991  if( rBoxes.empty() )
992  return;
993  pFirstLn = rBoxes[0]->GetUpper();
994  pLastLn = rBoxes.back()->GetUpper();
995  }
996  sal_uInt16 nFirstLn = GetTabLines().GetPos( pFirstLn );
997  sal_uInt16 nLastLn = GetTabLines().GetPos( pLastLn );
998  for( sal_uInt16 nRow = nFirstLn; nRow <= nLastLn; ++nRow )
999  {
1000  SwTableLine* pLine = m_aLines[nRow];
1001  OSL_ENSURE( pLine, "Missing table line" );
1002  const size_t nCols = pLine->GetTabBoxes().size();
1003  bool bSuperfl = true;
1004  for( size_t nCol = 0; nCol < nCols; ++nCol )
1005  {
1006  SwTableBox *pBox = pLine->GetTabBoxes()[nCol];
1007  if( pBox->getRowSpan() > 0 &&
1008  rBoxes.end() == rBoxes.find( pBox ) )
1009  {
1010  bSuperfl = false;
1011  break;
1012  }
1013  }
1014  if( bSuperfl )
1015  {
1016  for( size_t nCol = 0; nCol < nCols; ++nCol )
1017  {
1018  SwTableBox* pBox = pLine->GetTabBoxes()[nCol];
1019  rBoxes.insert( pBox );
1020  }
1021  }
1022  }
1023 }
1024 
1029 SwTableBox& SwTableBox::FindStartOfRowSpan( const SwTable& rTable, sal_uInt16 nMaxStep )
1030 {
1031  if( getRowSpan() > 0 || !nMaxStep )
1032  return *this;
1033 
1034  tools::Long nLeftBorder = lcl_Box2LeftBorder( *this );
1035  SwTableBox* pBox = this;
1036  const SwTableLine* pMyUpper = GetUpper();
1037  sal_uInt16 nLine = rTable.GetTabLines().GetPos( pMyUpper );
1038  if( nLine && nLine < rTable.GetTabLines().size() )
1039  {
1040  SwTableBox* pNext;
1041  do
1042  {
1043  pNext = lcl_LeftBorder2Box( nLeftBorder, rTable.GetTabLines()[--nLine] );
1044  if( pNext )
1045  pBox = pNext;
1046  } while( nLine && --nMaxStep && pNext && pBox->getRowSpan() < 1 );
1047  }
1048 
1049  return *pBox;
1050 }
1051 
1056 SwTableBox& SwTableBox::FindEndOfRowSpan( const SwTable& rTable, sal_uInt16 nMaxStep )
1057 {
1058  tools::Long nAbsSpan = getRowSpan();
1059  if( nAbsSpan < 0 )
1060  nAbsSpan = -nAbsSpan;
1061  if( nAbsSpan == 1 || !nMaxStep )
1062  return *this;
1063 
1064  if( nMaxStep > --nAbsSpan )
1065  nMaxStep = static_cast<sal_uInt16>(nAbsSpan);
1066  const SwTableLine* pMyUpper = GetUpper();
1067  sal_uInt16 nLine = rTable.GetTabLines().GetPos( pMyUpper );
1068  nMaxStep = nLine + nMaxStep;
1069  if( nMaxStep >= rTable.GetTabLines().size() )
1070  nMaxStep = rTable.GetTabLines().size() - 1;
1071  tools::Long nLeftBorder = lcl_Box2LeftBorder( *this );
1072  SwTableBox* pBox =
1073  lcl_LeftBorder2Box( nLeftBorder, rTable.GetTabLines()[ nMaxStep ] );
1074  if ( !pBox )
1075  pBox = this;
1076 
1077  return *pBox;
1078 }
1079 
1083 static void lcl_getAllMergedBoxes( const SwTable& rTable, SwSelBoxes& rBoxes, SwTableBox& rBox )
1084 {
1085  SwTableBox* pBox = &rBox;
1086  OSL_ENSURE( pBox == &rBox.FindStartOfRowSpan( rTable ), "Not a master box" );
1087  rBoxes.insert( pBox );
1088  if( pBox->getRowSpan() == 1 )
1089  return;
1090  const SwTableLine* pMyUpper = pBox->GetUpper();
1091  sal_uInt16 nLine = rTable.GetTabLines().GetPos( pMyUpper );
1092  tools::Long nLeftBorder = lcl_Box2LeftBorder( *pBox );
1093  sal_uInt16 nCount = rTable.GetTabLines().size();
1094  while( ++nLine < nCount && pBox && pBox->getRowSpan() != -1 )
1095  {
1096  pBox = lcl_LeftBorder2Box( nLeftBorder, rTable.GetTabLines()[nLine] );
1097  if( pBox )
1098  rBoxes.insert( pBox );
1099  }
1100 }
1101 
1106 static void lcl_UnMerge( const SwTable& rTable, SwTableBox& rBox, size_t nCnt,
1107  bool bSameHeight )
1108 {
1109  SwSelBoxes aBoxes;
1110  lcl_getAllMergedBoxes( rTable, aBoxes, rBox );
1111  size_t const nCount = aBoxes.size();
1112  if( nCount < 2 )
1113  return;
1114  if( nCnt > nCount )
1115  nCnt = nCount;
1116  std::unique_ptr<size_t[]> const pSplitIdx(new size_t[nCnt]);
1117  if( bSameHeight )
1118  {
1119  std::unique_ptr<SwTwips[]> const pHeights(new SwTwips[nCount]);
1120  SwTwips nHeight = 0;
1121  for (size_t i = 0; i < nCount; ++i)
1122  {
1123  SwTableLine* pLine = aBoxes[ i ]->GetUpper();
1124  SwFrameFormat *pRowFormat = pLine->GetFrameFormat();
1125  pHeights[ i ] = pRowFormat->GetFrameSize().GetHeight();
1126  nHeight += pHeights[ i ];
1127  }
1128  SwTwips nSumH = 0;
1129  size_t nIdx = 0;
1130  for (size_t i = 1; i <= nCnt; ++i)
1131  {
1132  SwTwips nSplit = ( i * nHeight ) / nCnt;
1133  while( nSumH < nSplit && nIdx < nCount )
1134  nSumH += pHeights[ nIdx++ ];
1135  pSplitIdx[ i - 1 ] = nIdx;
1136  }
1137  }
1138  else
1139  {
1140  for (size_t i = 1; i <= nCnt; ++i)
1141  {
1142  pSplitIdx[ i - 1 ] = ( i * nCount ) / nCnt;
1143  }
1144  }
1145  size_t nIdx = 0;
1146  for (size_t i = 0; i < nCnt; ++i)
1147  {
1148  size_t nNextIdx = pSplitIdx[ i ];
1149  aBoxes[ nIdx ]->setRowSpan( nNextIdx - nIdx );
1150  lcl_InvalidateCellFrame( *aBoxes[ nIdx ] );
1151  while( ++nIdx < nNextIdx )
1152  aBoxes[ nIdx ]->setRowSpan( nIdx - nNextIdx );
1153  }
1154 }
1155 
1159 static void lcl_FillSelBoxes( SwSelBoxes &rBoxes, SwTableLine &rLine )
1160 {
1161  const size_t nBoxCount = rLine.GetTabBoxes().size();
1162  for( size_t i = 0; i < nBoxCount; ++i )
1163  rBoxes.insert( rLine.GetTabBoxes()[i] );
1164 }
1165 
1170 void SwTable::InsertSpannedRow( SwDoc& rDoc, sal_uInt16 nRowIdx, sal_uInt16 nCnt )
1171 {
1172  CHECK_TABLE( *this )
1173  OSL_ENSURE( nCnt && nRowIdx < GetTabLines().size(), "Wrong call of InsertSpannedRow" );
1174  SwSelBoxes aBoxes;
1175  SwTableLine& rLine = *GetTabLines()[ nRowIdx ];
1176  lcl_FillSelBoxes( aBoxes, rLine );
1177  SwFormatFrameSize aFSz( rLine.GetFrameFormat()->GetFrameSize() );
1178  if( SwFrameSize::Variable != aFSz.GetHeightSizeType() )
1179  {
1180  SwFrameFormat* pFrameFormat = rLine.ClaimFrameFormat();
1181  tools::Long nNewHeight = aFSz.GetHeight() / ( nCnt + 1 );
1182  if( !nNewHeight )
1183  ++nNewHeight;
1184  aFSz.SetHeight( nNewHeight );
1185  pFrameFormat->SetFormatAttr( aFSz );
1186  }
1187  InsertRow_( &rDoc, aBoxes, nCnt, true );
1188  const size_t nBoxCount = rLine.GetTabBoxes().size();
1189  for( sal_uInt16 n = 0; n < nCnt; ++n )
1190  {
1191  SwTableLine *pNewLine = GetTabLines()[ nRowIdx + nCnt - n ];
1192  for( size_t nCurrBox = 0; nCurrBox < nBoxCount; ++nCurrBox )
1193  {
1194  sal_Int32 nRowSpan = rLine.GetTabBoxes()[nCurrBox]->getRowSpan();
1195  if( nRowSpan > 0 )
1196  nRowSpan = - nRowSpan;
1197  pNewLine->GetTabBoxes()[ nCurrBox ]->setRowSpan( nRowSpan - n );
1198  }
1199  }
1200  lcl_ChangeRowSpan( *this, nCnt, nRowIdx, false );
1201  CHECK_TABLE( *this )
1202 }
1203 
1204 typedef std::pair< sal_uInt16, sal_uInt16 > SwLineOffset;
1205 typedef std::vector< SwLineOffset > SwLineOffsetArray;
1206 
1207 /*
1208 * When a couple of table boxes has to be split,
1209 * lcl_SophisticatedFillLineIndices delivers the information where and how many
1210 * rows have to be inserted.
1211 * Input
1212 * rTable: the table to manipulate
1213 * rBoxes: an array of boxes to split
1214 * nCnt: how many parts are wanted
1215 * Output
1216 * rArr: a list of pairs ( line index, number of lines to insert )
1217 */
1219  const SwTable& rTable, const SwSelBoxes& rBoxes, sal_uInt16 nCnt )
1220 {
1221  std::list< SwLineOffset > aBoxes;
1222  SwLineOffset aLnOfs( USHRT_MAX, USHRT_MAX );
1223  for (size_t i = 0; i < rBoxes.size(); ++i)
1224  { // Collect all end line indices and the row spans
1225  const SwTableBox &rBox = rBoxes[ i ]->FindStartOfRowSpan( rTable );
1226  OSL_ENSURE( rBox.getRowSpan() > 0, "Didn't I say 'StartOfRowSpan' ??" );
1227  if( nCnt > rBox.getRowSpan() )
1228  {
1229  const SwTableLine *pLine = rBox.GetUpper();
1230  const sal_uInt16 nEnd = sal_uInt16( rBox.getRowSpan() +
1231  rTable.GetTabLines().GetPos( pLine ) );
1232  // The next if statement is a small optimization
1233  if( aLnOfs.first != nEnd || aLnOfs.second != rBox.getRowSpan() )
1234  {
1235  aLnOfs.first = nEnd; // ok, this is the line behind the box
1236  aLnOfs.second = sal_uInt16( rBox.getRowSpan() ); // the row span
1237  aBoxes.insert( aBoxes.end(), aLnOfs );
1238  }
1239  }
1240  }
1241  // As I said, I noted the line index _behind_ the last line of the boxes
1242  // in the resulting array the index has to be _on_ the line
1243  // nSum is to evaluate the wished value
1244  sal_uInt16 nSum = 1;
1245  while( !aBoxes.empty() )
1246  {
1247  // I. step:
1248  // Looking for the "smallest" line end with the smallest row span
1249  std::list< SwLineOffset >::iterator pCurr = aBoxes.begin();
1250  aLnOfs = *pCurr; // the line end and row span of the first box
1251  while( ++pCurr != aBoxes.end() )
1252  {
1253  if( aLnOfs.first > pCurr->first )
1254  { // Found a smaller line end
1255  aLnOfs.first = pCurr->first;
1256  aLnOfs.second = pCurr->second; // row span
1257  }
1258  else if( aLnOfs.first == pCurr->first &&
1259  aLnOfs.second < pCurr->second )
1260  aLnOfs.second = pCurr->second; // Found a smaller row span
1261  }
1262  OSL_ENSURE( aLnOfs.second < nCnt, "Clean-up failed" );
1263  aLnOfs.second = nCnt - aLnOfs.second; // the number of rows to insert
1264  rArr.emplace_back( aLnOfs.first - nSum, aLnOfs.second );
1265  // the correction has to be incremented because in the following
1266  // loops the line ends were manipulated
1267  nSum = nSum + aLnOfs.second;
1268 
1269  pCurr = aBoxes.begin();
1270  while( pCurr != aBoxes.end() )
1271  {
1272  if( pCurr->first == aLnOfs.first )
1273  { // These boxes can be removed because the last insertion
1274  // of rows will expand their row span above the needed value
1275  pCurr = aBoxes.erase(pCurr);
1276  }
1277  else
1278  {
1279  bool bBefore = ( pCurr->first - pCurr->second < aLnOfs.first );
1280  // Manipulation of the end line indices as if the rows are
1281  // already inserted
1282  pCurr->first = pCurr->first + aLnOfs.second;
1283  if( bBefore )
1284  { // If the insertion is inside the box,
1285  // its row span has to be incremented
1286  pCurr->second = pCurr->second + aLnOfs.second;
1287  if( pCurr->second >= nCnt )
1288  { // if the row span is bigger than the split factor
1289  // this box is done
1290  pCurr = aBoxes.erase(pCurr);
1291  }
1292  else
1293  ++pCurr;
1294  }
1295  else
1296  ++pCurr;
1297  }
1298  }
1299  }
1300 }
1301 
1302 typedef std::set< SwTwips > SwSplitLines;
1303 
1309  const SwTable& rTable, const SwSelBoxes& rBoxes, sal_uInt16 nCnt )
1310 {
1311  if( nCnt < 2 )
1312  return 0;
1313  std::vector< SwLineOffset > aBoxes;
1314  SwLineOffset aLnOfs( USHRT_MAX, USHRT_MAX );
1315  sal_uInt16 nFirst = USHRT_MAX; // becomes the index of the first line
1316  sal_uInt16 nLast = 0; // becomes the index of the last line of the splitting
1317  for (size_t i = 0; i < rBoxes.size(); ++i)
1318  { // Collect all pairs (start+end) of line indices to split
1319  const SwTableBox &rBox = rBoxes[ i ]->FindStartOfRowSpan( rTable );
1320  OSL_ENSURE( rBox.getRowSpan() > 0, "Didn't I say 'StartOfRowSpan' ??" );
1321  const SwTableLine *pLine = rBox.GetUpper();
1322  const sal_uInt16 nStart = rTable.GetTabLines().GetPos( pLine );
1323  const sal_uInt16 nEnd = sal_uInt16( rBox.getRowSpan() + nStart - 1 );
1324  // The next if statement is a small optimization
1325  if( aLnOfs.first != nStart || aLnOfs.second != nEnd )
1326  {
1327  aLnOfs.first = nStart;
1328  aLnOfs.second = nEnd;
1329  aBoxes.push_back( aLnOfs );
1330  if( nStart < nFirst )
1331  nFirst = nStart;
1332  if( nEnd > nLast )
1333  nLast = nEnd;
1334  }
1335  }
1336 
1337  if (nFirst == USHRT_MAX)
1338  {
1339  assert(aBoxes.empty());
1340  return 0;
1341  }
1342 
1343  SwTwips nHeight = 0;
1344  std::unique_ptr<SwTwips[]> pLines(new SwTwips[ nLast + 1 - nFirst ]);
1345  for( sal_uInt16 i = nFirst; i <= nLast; ++i )
1346  {
1347  bool bLayoutAvailable = false;
1348  nHeight += rTable.GetTabLines()[ i ]->GetTableLineHeight( bLayoutAvailable );
1349  rCurr.insert( rCurr.end(), nHeight );
1350  pLines[ i - nFirst ] = nHeight;
1351  }
1352  for( const auto& rSplit : aBoxes )
1353  {
1354  SwTwips nBase = rSplit.first <= nFirst ? 0 :
1355  pLines[ rSplit.first - nFirst - 1 ];
1356  SwTwips nDiff = pLines[ rSplit.second - nFirst ] - nBase;
1357  for( sal_uInt16 i = 1; i < nCnt; ++i )
1358  {
1359  SwTwips nSplit = nBase + ( i * nDiff ) / nCnt;
1360  rNew.insert( nSplit );
1361  }
1362  }
1363  return nFirst;
1364 }
1365 
1370 static sal_uInt16 lcl_LineIndex( const SwTable& rTable, const SwSelBoxes& rBoxes,
1371  bool bBehind )
1372 {
1373  sal_uInt16 nDirect = USHRT_MAX;
1374  sal_uInt16 nSpan = USHRT_MAX;
1375  for (size_t i = 0; i < rBoxes.size(); ++i)
1376  {
1377  SwTableBox *pBox = rBoxes[i];
1378  const SwTableLine* pLine = rBoxes[i]->GetUpper();
1379  sal_uInt16 nPos = rTable.GetTabLines().GetPos( pLine );
1380  if( USHRT_MAX != nPos )
1381  {
1382  if( bBehind )
1383  {
1384  if( nPos > nDirect || nDirect == USHRT_MAX )
1385  nDirect = nPos;
1386  sal_Int32 nRowSpan = pBox->getRowSpan();
1387  if( nRowSpan < 2 )
1388  nSpan = 0;
1389  else if( nSpan )
1390  {
1391  sal_uInt16 nEndOfRowSpan = static_cast<sal_uInt16>(nPos + nRowSpan - 1);
1392  if( nEndOfRowSpan > nSpan || nSpan == USHRT_MAX )
1393  nSpan = nEndOfRowSpan;
1394  }
1395  }
1396  else if( nPos < nDirect )
1397  nDirect = nPos;
1398  }
1399  }
1400  if( nSpan && nSpan < USHRT_MAX )
1401  return nSpan;
1402  return nDirect;
1403 }
1404 
1408 bool SwTable::NewSplitRow( SwDoc& rDoc, const SwSelBoxes& rBoxes, sal_uInt16 nCnt,
1409  bool bSameHeight )
1410 {
1411  CHECK_TABLE( *this )
1412  ++nCnt;
1413  FndBox_ aFndBox( nullptr, nullptr );
1414  aFndBox.SetTableLines( rBoxes, *this );
1415 
1416  if( bSameHeight && rDoc.getIDocumentLayoutAccess().GetCurrentViewShell() )
1417  {
1418  SwSplitLines aRowLines;
1419  SwSplitLines aSplitLines;
1420  sal_uInt16 nFirst = lcl_CalculateSplitLineHeights( aRowLines, aSplitLines,
1421  *this, rBoxes, nCnt );
1422  aFndBox.DelFrames( *this );
1423  SwTwips nLast = 0;
1424  SwSplitLines::iterator pSplit = aSplitLines.begin();
1425  for( const auto& rCurr : aRowLines )
1426  {
1427  while( pSplit != aSplitLines.end() && *pSplit < rCurr )
1428  {
1429  InsertSpannedRow( rDoc, nFirst, 1 );
1430  SwTableLine* pRow = GetTabLines()[ nFirst ];
1431  SwFrameFormat* pRowFormat = pRow->ClaimFrameFormat();
1432  SwFormatFrameSize aFSz( pRowFormat->GetFrameSize() );
1434  aFSz.SetHeight( *pSplit - nLast );
1435  pRowFormat->SetFormatAttr( aFSz );
1436  nLast = *pSplit;
1437  ++pSplit;
1438  ++nFirst;
1439  }
1440  if( pSplit != aSplitLines.end() && rCurr == *pSplit )
1441  ++pSplit;
1442  SwTableLine* pRow = GetTabLines()[ nFirst ];
1443  SwFrameFormat* pRowFormat = pRow->ClaimFrameFormat();
1444  SwFormatFrameSize aFSz( pRowFormat->GetFrameSize() );
1446  aFSz.SetHeight( rCurr - nLast );
1447  pRowFormat->SetFormatAttr( aFSz );
1448  nLast = rCurr;
1449  ++nFirst;
1450  }
1451  }
1452  else
1453  {
1454  aFndBox.DelFrames( *this );
1455  bSameHeight = false;
1456  }
1457  if( !bSameHeight )
1458  {
1459  SwLineOffsetArray aLineOffs;
1460  lcl_SophisticatedFillLineIndices( aLineOffs, *this, rBoxes, nCnt );
1461  SwLineOffsetArray::reverse_iterator pCurr( aLineOffs.rbegin() );
1462  while( pCurr != aLineOffs.rend() )
1463  {
1464  InsertSpannedRow( rDoc, pCurr->first, pCurr->second );
1465  ++pCurr;
1466  }
1467  }
1468 
1469  std::set<size_t> aIndices;
1470  for (size_t i = 0; i < rBoxes.size(); ++i)
1471  {
1472  OSL_ENSURE( rBoxes[i]->getRowSpan() != 1, "Forgot to split?" );
1473  if( rBoxes[i]->getRowSpan() > 1 )
1474  aIndices.insert( i );
1475  }
1476 
1477  for( const auto& rCurrBox : aIndices )
1478  lcl_UnMerge( *this, *rBoxes[rCurrBox], nCnt, bSameHeight );
1479 
1480  CHECK_TABLE( *this )
1481  // update the layout
1482  aFndBox.MakeFrames( *this );
1483 
1484  return true;
1485 }
1486 
1491 bool SwTable::InsertRow( SwDoc* pDoc, const SwSelBoxes& rBoxes,
1492  sal_uInt16 nCnt, bool bBehind )
1493 {
1494  bool bRet = false;
1495  if( IsNewModel() )
1496  {
1497  CHECK_TABLE( *this )
1498  sal_uInt16 nRowIdx = lcl_LineIndex( *this, rBoxes, bBehind );
1499  if( nRowIdx < USHRT_MAX )
1500  {
1501  FndBox_ aFndBox( nullptr, nullptr );
1502  aFndBox.SetTableLines( rBoxes, *this );
1503  aFndBox.DelFrames( *this );
1504 
1505  bRet = true;
1506  SwTableLine *pLine = GetTabLines()[ nRowIdx ];
1507  SwSelBoxes aLineBoxes;
1508  lcl_FillSelBoxes( aLineBoxes, *pLine );
1509  InsertRow_( pDoc, aLineBoxes, nCnt, bBehind );
1510  const size_t nBoxCount = pLine->GetTabBoxes().size();
1511  sal_uInt16 nOfs = bBehind ? 0 : 1;
1512  for( sal_uInt16 n = 0; n < nCnt; ++n )
1513  {
1514  SwTableLine *pNewLine = GetTabLines()[ nRowIdx+nCnt-n-nOfs];
1515  for( size_t nCurrBox = 0; nCurrBox < nBoxCount; ++nCurrBox )
1516  {
1517  sal_Int32 nRowSpan = pLine->GetTabBoxes()[nCurrBox]->getRowSpan();
1518  if( bBehind )
1519  {
1520  if( nRowSpan == 1 || nRowSpan == -1 )
1521  nRowSpan = n + 1;
1522  else if( nRowSpan > 1 )
1523  {
1524  nRowSpan = - nRowSpan;
1525 
1526  // tdf#123102 disable numbering of the new hidden
1527  // paragraph in merged cells to avoid of bad
1528  // renumbering of next list elements
1529  SwTableBox* pBox = pNewLine->GetTabBoxes()[nCurrBox];
1530  SwNodeIndex aIdx( *pBox->GetSttNd(), +1 );
1531  SwContentNode* pCNd = aIdx.GetNode().GetContentNode();
1532  if( pCNd && pCNd->IsTextNode() && pCNd->GetTextNode()->GetNumRule() )
1533  {
1534  SwPosition aPos( *pCNd->GetTextNode() );
1535  SwPaM aPam( aPos, aPos );
1536  pDoc->DelNumRules( aPam );
1537  }
1538  }
1539  }
1540  else
1541  {
1542  if( nRowSpan > 0 )
1543  nRowSpan = n + 1;
1544  else
1545  --nRowSpan;
1546  }
1547  pNewLine->GetTabBoxes()[ nCurrBox ]->setRowSpan( nRowSpan - n );
1548  }
1549  }
1550  if( bBehind )
1551  ++nRowIdx;
1552  if( nRowIdx )
1553  lcl_ChangeRowSpan( *this, nCnt, --nRowIdx, true );
1554  // update the layout
1555  aFndBox.MakeFrames( *this );
1556  }
1557  CHECK_TABLE( *this )
1558  }
1559  else
1560  bRet = InsertRow_( pDoc, rBoxes, nCnt, bBehind );
1561  return bRet;
1562 }
1563 
1569 {
1570  if( !IsNewModel() )
1571  return;
1572 
1573  for (size_t i = 0; i < rBoxes.size(); ++i)
1574  {
1575  SwTableBox* pBox = rBoxes[i];
1576  sal_Int32 nRowSpan = pBox->getRowSpan();
1577  if( nRowSpan != 1 && pBox->GetFrameFormat()->GetFrameSize().GetWidth() )
1578  {
1579  tools::Long nLeft = lcl_Box2LeftBorder( *pBox );
1580  SwTableLine *pLine = pBox->GetUpper();
1581  sal_uInt16 nLinePos = GetTabLines().GetPos( pLine);
1582  OSL_ENSURE( nLinePos < USHRT_MAX, "Box/table mismatch" );
1583  if( nRowSpan > 1 )
1584  {
1585  if( ++nLinePos < GetTabLines().size() )
1586  {
1587  pLine = GetTabLines()[ nLinePos ];
1588  pBox = lcl_LeftBorder2Box( nLeft, pLine );
1589  OSL_ENSURE( pBox, "RowSpan irritation I" );
1590  if( pBox )
1591  pBox->setRowSpan( --nRowSpan );
1592  }
1593  }
1594  else if( nLinePos > 0 )
1595  {
1596  do
1597  {
1598  pLine = GetTabLines()[ --nLinePos ];
1599  pBox = lcl_LeftBorder2Box( nLeft, pLine );
1600  OSL_ENSURE( pBox, "RowSpan irritation II" );
1601  if( pBox )
1602  {
1603  nRowSpan = pBox->getRowSpan();
1604  if( nRowSpan > 1 )
1605  {
1606  lcl_InvalidateCellFrame( *pBox );
1607  --nRowSpan;
1608  }
1609  else
1610  ++nRowSpan;
1611  pBox->setRowSpan( nRowSpan );
1612  }
1613  else
1614  nRowSpan = 1;
1615  }
1616  while( nRowSpan < 0 && nLinePos > 0 );
1617  }
1618  }
1619  }
1620 }
1621 
1626 static void lcl_SearchSelBox( const SwTable &rTable, SwSelBoxes& rBoxes, tools::Long nMin, tools::Long nMax,
1627  SwTableLine& rLine, bool bChkProtected, bool bColumn )
1628 {
1629  tools::Long nLeft = 0;
1630  tools::Long nRight = 0;
1631  tools::Long nMid = ( nMax + nMin )/ 2;
1632  const size_t nCount = rLine.GetTabBoxes().size();
1633  for( size_t nCurrBox = 0; nCurrBox < nCount; ++nCurrBox )
1634  {
1635  SwTableBox* pBox = rLine.GetTabBoxes()[nCurrBox];
1636  OSL_ENSURE( pBox, "Missing table box" );
1637  tools::Long nWidth = pBox->GetFrameFormat()->GetFrameSize().GetWidth();
1638  nRight += nWidth;
1639  if( nRight > nMin )
1640  {
1641  bool bAdd = false;
1642  if( nRight <= nMax )
1643  bAdd = nLeft >= nMin || nRight >= nMid ||
1644  nRight - nMin > nMin - nLeft;
1645  else
1646  bAdd = nLeft <= nMid || nRight - nMax < nMax - nLeft;
1647  sal_Int32 nRowSpan = pBox->getRowSpan();
1648  if( bAdd &&
1649  ( !bChkProtected ||
1650  !pBox->GetFrameFormat()->GetProtect().IsContentProtected() ) )
1651  {
1652  size_t const nOldCnt = rBoxes.size();
1653  rBoxes.insert( pBox );
1654  if( bColumn && nRowSpan != 1 && nOldCnt < rBoxes.size() )
1655  {
1656  SwTableBox *pMasterBox = pBox->getRowSpan() > 0 ? pBox
1657  : &pBox->FindStartOfRowSpan( rTable );
1658  lcl_getAllMergedBoxes( rTable, rBoxes, *pMasterBox );
1659  }
1660  }
1661  }
1662  if( nRight >= nMax )
1663  break;
1664  nLeft = nRight;
1665  }
1666 }
1667 
1672 void SwTable::CreateSelection( const SwPaM& rPam, SwSelBoxes& rBoxes,
1673  const SearchType eSearch, bool bChkProtected ) const
1674 {
1675  OSL_ENSURE( m_bNewModel, "Don't call me for old tables" );
1676  if( m_aLines.empty() )
1677  return;
1678  const SwNode* pStartNd = rPam.GetPoint()->nNode.GetNode().FindTableBoxStartNode();
1679  const SwNode* pEndNd = rPam.GetMark()->nNode.GetNode().FindTableBoxStartNode();
1680  if( !pStartNd || !pEndNd )
1681  return;
1682  CreateSelection( pStartNd, pEndNd, rBoxes, eSearch, bChkProtected );
1683 }
1684 
1688 void SwTable::CreateSelection( const SwNode* pStartNd, const SwNode* pEndNd,
1689  SwSelBoxes& rBoxes, const SearchType eSearch, bool bChkProtected ) const
1690 {
1691  rBoxes.clear();
1692  // Looking for start and end of the selection given by SwNode-pointer
1693  const size_t nLines = m_aLines.size();
1694  // nTop becomes the line number of the upper box
1695  // nBottom becomes the line number of the lower box
1696  size_t nTop = 0;
1697  size_t nBottom = 0;
1698  // nUpperMin becomes the left border value of the upper box
1699  // nUpperMax becomes the right border of the upper box
1700  // nLowerMin and nLowerMax the borders of the lower box
1701  tools::Long nUpperMin = 0, nUpperMax = 0;
1702  tools::Long nLowerMin = 0, nLowerMax = 0;
1703  // nFound will incremented if a box is found
1704  // 0 => no box found; 1 => the upper box has been found; 2 => both found
1705  int nFound = 0;
1706  for( size_t nRow = 0; nFound < 2 && nRow < nLines; ++nRow )
1707  {
1708  SwTableLine* pLine = m_aLines[nRow];
1709  OSL_ENSURE( pLine, "Missing table line" );
1710  const size_t nCols = pLine->GetTabBoxes().size();
1711  for( size_t nCol = 0; nCol < nCols; ++nCol )
1712  {
1713  SwTableBox* pBox = pLine->GetTabBoxes()[nCol];
1714  OSL_ENSURE( pBox, "Missing table box" );
1715  if( pBox->GetSttNd() == pEndNd || pBox->GetSttNd() == pStartNd )
1716  {
1717  if( !bChkProtected ||
1719  rBoxes.insert( pBox );
1720  if( nFound )
1721  {
1722  nBottom = nRow;
1723  lcl_CheckMinMax( nLowerMin, nLowerMax, *pLine, nCol, true );
1724  ++nFound;
1725  break;
1726  }
1727  else
1728  {
1729  nTop = nRow;
1730  lcl_CheckMinMax( nUpperMin, nUpperMax, *pLine, nCol, true );
1731  ++nFound;
1732  // If start and end node are identical, we're nearly done...
1733  if( pEndNd == pStartNd )
1734  {
1735  nBottom = nTop;
1736  nLowerMin = nUpperMin;
1737  nLowerMax = nUpperMax;
1738  ++nFound;
1739  }
1740  }
1741  }
1742  }
1743  }
1744  if( nFound < 2 )
1745  return; // At least one node was not a part of the given table
1746  if( eSearch == SEARCH_ROW )
1747  {
1748  // Selection of a row is quiet easy:
1749  // every (unprotected) box between start and end line
1750  // with a positive row span will be collected
1751  for( size_t nRow = nTop; nRow <= nBottom; ++nRow )
1752  {
1753  SwTableLine* pLine = m_aLines[nRow];
1754  OSL_ENSURE( pLine, "Missing table line" );
1755  const size_t nCount = pLine->GetTabBoxes().size();
1756  for( size_t nCurrBox = 0; nCurrBox < nCount; ++nCurrBox )
1757  {
1758  SwTableBox* pBox = pLine->GetTabBoxes()[nCurrBox];
1759  OSL_ENSURE( pBox, "Missing table box" );
1760  if( pBox->getRowSpan() > 0 && ( !bChkProtected ||
1761  !pBox->GetFrameFormat()->GetProtect().IsContentProtected() ) )
1762  rBoxes.insert( pBox );
1763  }
1764  }
1765  return;
1766  }
1767  bool bCombine = nTop == nBottom;
1768  if( !bCombine )
1769  {
1770  tools::Long nMinWidth = nUpperMax - nUpperMin;
1771  tools::Long nTmp = nLowerMax - nLowerMin;
1772  if( nMinWidth > nTmp )
1773  nMinWidth = nTmp;
1774  nTmp = std::min(nLowerMax, nUpperMax);
1775  nTmp -= ( nLowerMin < nUpperMin ) ? nUpperMin : nLowerMin;
1776  // If the overlapping between upper and lower box is less than half
1777  // of the width (of the smaller cell), bCombine is set,
1778  // e.g. if upper and lower cell are in different columns
1779  bCombine = ( nTmp + nTmp < nMinWidth );
1780  }
1781  if( bCombine )
1782  {
1783  if( nUpperMin < nLowerMin )
1784  nLowerMin = nUpperMin;
1785  else
1786  nUpperMin = nLowerMin;
1787  if( nUpperMax > nLowerMax )
1788  nLowerMax = nUpperMax;
1789  else
1790  nUpperMax = nLowerMax;
1791  }
1792  const bool bColumn = eSearch == SEARCH_COL;
1793  if( bColumn )
1794  {
1795  for( size_t i = 0; i < nTop; ++i )
1796  lcl_SearchSelBox( *this, rBoxes, nUpperMin, nUpperMax,
1797  *m_aLines[i], bChkProtected, bColumn );
1798  }
1799 
1800  {
1801  tools::Long nMin = std::min(nUpperMin, nLowerMin);
1802  tools::Long nMax = nUpperMax < nLowerMax ? nLowerMax : nUpperMax;
1803  for( size_t i = nTop; i <= nBottom; ++i )
1804  lcl_SearchSelBox( *this, rBoxes, nMin, nMax, *m_aLines[i],
1805  bChkProtected, bColumn );
1806  }
1807  if( bColumn )
1808  {
1809  for( size_t i = nBottom + 1; i < nLines; ++i )
1810  lcl_SearchSelBox( *this, rBoxes, nLowerMin, nLowerMax, *m_aLines[i],
1811  bChkProtected, true );
1812  }
1813 }
1814 
1820 {
1821  OSL_ENSURE( m_bNewModel, "Don't call me for old tables" );
1822  rMin = 0;
1823  rMax = 0;
1824  if( m_aLines.empty() || rBoxes.empty() )
1825  return;
1826 
1827  const size_t nLineCnt = m_aLines.size();
1828  const size_t nBoxCnt = rBoxes.size();
1829  size_t nBox = 0;
1830  for( size_t nRow = 0; nRow < nLineCnt && nBox < nBoxCnt; ++nRow )
1831  {
1832  SwTableLine* pLine = m_aLines[nRow];
1833  OSL_ENSURE( pLine, "Missing table line" );
1834  const size_t nCols = pLine->GetTabBoxes().size();
1835  for( size_t nCol = 0; nCol < nCols; ++nCol )
1836  {
1837  SwTableBox* pBox = pLine->GetTabBoxes()[nCol];
1838  OSL_ENSURE( pBox, "Missing table box" );
1839  if( pBox == rBoxes[nBox] )
1840  {
1841  lcl_CheckMinMax( rMin, rMax, *pLine, nCol, nBox == 0 );
1842  if( ++nBox >= nBoxCnt )
1843  break;
1844  }
1845  }
1846  }
1847  for( size_t nRow = 0; nRow < nLineCnt; ++nRow )
1848  {
1849  SwTableLine* pLine = m_aLines[nRow];
1850  const size_t nCols = pLine->GetTabBoxes().size();
1851  tools::Long nRight = 0;
1852  for( size_t nCurrBox = 0; nCurrBox < nCols; ++nCurrBox )
1853  {
1854  tools::Long nLeft = nRight;
1855  SwTableBox* pBox = pLine->GetTabBoxes()[nCurrBox];
1856  nRight += pBox->GetFrameFormat()->GetFrameSize().GetWidth();
1857  if( nLeft >= rMin && nRight <= rMax )
1858  rBoxes.insert( pBox );
1859  }
1860  }
1861 }
1862 
1867 {
1868  OSL_ENSURE( m_bNewModel, "Don't call me for old tables" );
1869  if( m_aLines.empty() || nMax < nMin )
1870  return;
1871  tools::Long nMid = nMin ? ( nMin + nMax ) / 2 : 0;
1873  if( nTabSize == nMax )
1874  nMid = nMax;
1875  const size_t nLineCnt = m_aLines.size();
1876  for( size_t nRow = 0; nRow < nLineCnt; ++nRow )
1877  {
1878  SwTableLine* pLine = m_aLines[nRow];
1879  const size_t nCols = pLine->GetTabBoxes().size();
1880  tools::Long nRight = 0;
1881  for( size_t nCurrBox = 0; nCurrBox < nCols; ++nCurrBox )
1882  {
1883  tools::Long nLeft = nRight;
1884  SwTableBox* pBox = pLine->GetTabBoxes()[nCurrBox];
1885  nRight += pBox->GetFrameFormat()->GetFrameSize().GetWidth();
1886  if( nRight < nMin )
1887  continue;
1888  if( nLeft > nMax )
1889  break;
1890  tools::Long nNewWidth = -1;
1891  if( nLeft < nMin )
1892  {
1893  if( nRight <= nMax )
1894  nNewWidth = nMid - nLeft;
1895  }
1896  else if( nRight > nMax )
1897  nNewWidth = nRight - nMid;
1898  else
1899  nNewWidth = 0;
1900  if( nNewWidth >= 0 )
1901  {
1902  SwFrameFormat* pFrameFormat = pBox->ClaimFrameFormat();
1903  SwFormatFrameSize aFrameSz( pFrameFormat->GetFrameSize() );
1904  aFrameSz.SetWidth( nNewWidth );
1905  pFrameFormat->SetFormatAttr( aFrameSz );
1906  }
1907  }
1908  }
1909 }
1910 
1916 {
1917  for (size_t i = 0; i < rBoxes.size(); ++i)
1918  {
1919  SwTableBox *pBox = rBoxes[i];
1920  sal_Int32 nRowSpan = pBox->getRowSpan();
1921  if( nRowSpan != 1 )
1922  {
1923  SwTableBox *pMasterBox = nRowSpan > 0 ? pBox
1924  : &pBox->FindStartOfRowSpan( *this );
1925  lcl_getAllMergedBoxes( *this, rBoxes, *pMasterBox );
1926  }
1927  }
1928 }
1929 
1934 void SwTable::CheckRowSpan( SwTableLine* &rpLine, bool bUp ) const
1935 {
1936  OSL_ENSURE( IsNewModel(), "Don't call me for old tables" );
1937  sal_uInt16 nLineIdx = GetTabLines().GetPos( rpLine );
1938  OSL_ENSURE( nLineIdx < GetTabLines().size(), "Start line out of range" );
1939  bool bChange = true;
1940  if( bUp )
1941  {
1942  while( bChange )
1943  {
1944  bChange = false;
1945  rpLine = GetTabLines()[ nLineIdx ];
1946  const size_t nCols = rpLine->GetTabBoxes().size();
1947  for( size_t nCol = 0; !bChange && nCol < nCols; ++nCol )
1948  {
1949  SwTableBox* pBox = rpLine->GetTabBoxes()[nCol];
1950  if( pBox->getRowSpan() > 1 || pBox->getRowSpan() < -1 )
1951  bChange = true;
1952  }
1953  if( bChange )
1954  {
1955  if( nLineIdx )
1956  --nLineIdx;
1957  else
1958  {
1959  bChange = false;
1960  rpLine = nullptr;
1961  }
1962  }
1963  }
1964  }
1965  else
1966  {
1967  const size_t nMaxLine = GetTabLines().size();
1968  while( bChange )
1969  {
1970  bChange = false;
1971  rpLine = GetTabLines()[ nLineIdx ];
1972  const size_t nCols = rpLine->GetTabBoxes().size();
1973  for( size_t nCol = 0; !bChange && nCol < nCols; ++nCol )
1974  {
1975  SwTableBox* pBox = rpLine->GetTabBoxes()[nCol];
1976  if( pBox->getRowSpan() < 0 )
1977  bChange = true;
1978  }
1979  if( bChange )
1980  {
1981  ++nLineIdx;
1982  if( nLineIdx >= nMaxLine )
1983  {
1984  bChange = false;
1985  rpLine = nullptr;
1986  }
1987  }
1988  }
1989  }
1990 }
1991 
1992 // This structure corrects the row span attributes for a top line of a table
1993 // In a top line no negative row span is allowed, so these have to be corrected.
1994 // If there has been at least one correction, all values are stored
1995 // and can be used by undo of table split
1996 SwSaveRowSpan::SwSaveRowSpan( SwTableBoxes& rBoxes, sal_uInt16 nSplitLn )
1997  : mnSplitLine( nSplitLn )
1998 {
1999  bool bDontSave = true; // nothing changed, nothing to save
2000  const size_t nColCount = rBoxes.size();
2001  OSL_ENSURE( nColCount, "Empty Table Line" );
2002  mnRowSpans.resize( nColCount );
2003  for( size_t nCurrCol = 0; nCurrCol < nColCount; ++nCurrCol )
2004  {
2005  SwTableBox* pBox = rBoxes[nCurrCol];
2006  OSL_ENSURE( pBox, "Missing Table Box" );
2007  sal_Int32 nRowSp = pBox->getRowSpan();
2008  mnRowSpans[ nCurrCol ] = nRowSp;
2009  if( nRowSp < 0 )
2010  {
2011  bDontSave = false;
2012  nRowSp = -nRowSp;
2013  pBox->setRowSpan( nRowSp ); // correction needed
2014  }
2015  }
2016  if( bDontSave )
2017  mnRowSpans.clear();
2018 }
2019 
2020 // This function is called by undo of table split to restore the old row span
2021 // values at the split line
2023 {
2024  if( !IsNewModel() ) // for new model only
2025  return;
2026  sal_uInt16 nLineCount = GetTabLines().size();
2027  OSL_ENSURE( rSave.mnSplitLine < nLineCount, "Restore behind last line?" );
2028  if( rSave.mnSplitLine >= nLineCount )
2029  return;
2030 
2031  SwTableLine* pLine = GetTabLines()[rSave.mnSplitLine];
2032  const size_t nColCount = pLine->GetTabBoxes().size();
2033  OSL_ENSURE( nColCount, "Empty Table Line" );
2034  OSL_ENSURE( nColCount == rSave.mnRowSpans.size(), "Wrong row span store" );
2035  if( nColCount != rSave.mnRowSpans.size() )
2036  return;
2037 
2038  for( size_t nCurrCol = 0; nCurrCol < nColCount; ++nCurrCol )
2039  {
2040  SwTableBox* pBox = pLine->GetTabBoxes()[nCurrCol];
2041  OSL_ENSURE( pBox, "Missing Table Box" );
2042  sal_Int32 nRowSp = pBox->getRowSpan();
2043  if( nRowSp != rSave.mnRowSpans[ nCurrCol ] )
2044  {
2045  OSL_ENSURE( -nRowSp == rSave.mnRowSpans[ nCurrCol ], "Pardon me?!" );
2046  OSL_ENSURE( rSave.mnRowSpans[ nCurrCol ] < 0, "Pardon me?!" );
2047  pBox->setRowSpan( -nRowSp );
2048 
2049  sal_uInt16 nLine = rSave.mnSplitLine;
2050  if( nLine )
2051  {
2052  tools::Long nLeftBorder = lcl_Box2LeftBorder( *pBox );
2053  SwTableBox* pNext;
2054  do
2055  {
2056  pNext = lcl_LeftBorder2Box( nLeftBorder, GetTabLines()[--nLine] );
2057  if( pNext )
2058  {
2059  pBox = pNext;
2060  tools::Long nNewSpan = pBox->getRowSpan();
2061  if( pBox->getRowSpan() < 1 )
2062  nNewSpan -= nRowSp;
2063  else
2064  {
2065  nNewSpan += nRowSp;
2066  pNext = nullptr;
2067  }
2068  pBox->setRowSpan( nNewSpan );
2069  }
2070  } while( nLine && pNext );
2071  }
2072  }
2073  }
2074 }
2075 
2076 std::unique_ptr<SwSaveRowSpan> SwTable::CleanUpTopRowSpan( sal_uInt16 nSplitLine )
2077 {
2078  if( !IsNewModel() )
2079  return nullptr;
2080  std::unique_ptr<SwSaveRowSpan> pRet(new SwSaveRowSpan( GetTabLines()[0]->GetTabBoxes(), nSplitLine ));
2081  if( pRet->mnRowSpans.empty() )
2082  return nullptr;
2083  return pRet;
2084 }
2085 
2086 void SwTable::CleanUpBottomRowSpan( sal_uInt16 nDelLines )
2087 {
2088  if( !IsNewModel() )
2089  return;
2090  const size_t nLastLine = GetTabLines().size()-1;
2091  SwTableLine* pLine = GetTabLines()[nLastLine];
2092  const size_t nColCount = pLine->GetTabBoxes().size();
2093  OSL_ENSURE( nColCount, "Empty Table Line" );
2094  for( size_t nCurrCol = 0; nCurrCol < nColCount; ++nCurrCol )
2095  {
2096  SwTableBox* pBox = pLine->GetTabBoxes()[nCurrCol];
2097  OSL_ENSURE( pBox, "Missing Table Box" );
2098  sal_Int32 nRowSp = pBox->getRowSpan();
2099  if( nRowSp < 0 )
2100  nRowSp = -nRowSp;
2101  if( nRowSp > 1 )
2102  {
2103  lcl_ChangeRowSpan( *this, -static_cast<tools::Long>(nDelLines),
2104  static_cast<sal_uInt16>(nLastLine), false );
2105  break;
2106  }
2107  }
2108 }
2109 
2128 void SwTable::ConvertSubtableBox(sal_uInt16 const nRow, sal_uInt16 const nBox)
2129 {
2130  SwDoc *const pDoc(GetFrameFormat()->GetDoc());
2131  SwTableLine *const pSourceLine(GetTabLines()[nRow]);
2132  SwTableBox *const pSubTableBox(pSourceLine->GetTabBoxes()[nBox]);
2133  assert(!pSubTableBox->GetTabLines().empty());
2134  // are relative (%) heights possible? apparently not
2135  SwFormatFrameSize const outerSize(pSourceLine->GetFrameFormat()->GetFrameSize());
2136  tools::Long minHeights(0);
2137  {
2138  SwFormatFrameSize const* pSize(nullptr);
2139  SwFrameFormat const& rSubLineFormat(*pSubTableBox->GetTabLines()[0]->GetFrameFormat());
2140  if (rSubLineFormat.GetItemState(RES_FRM_SIZE, true,
2141  reinterpret_cast<SfxPoolItem const**>(&pSize)) == SfxItemState::SET)
2142  { // for first row, apply height from inner row to outer row.
2143  // in case the existing outer row height was larger than the entire
2144  // subtable, the last inserted row needs to be tweaked (below)
2145  pSourceLine->GetFrameFormat()->SetFormatAttr(*pSize);
2146  if (pSize->GetHeightSizeType() != SwFrameSize::Variable)
2147  {
2148  minHeights += pSize->GetHeight();
2149  }
2150  }
2151  }
2152  for (size_t i = 1; i < pSubTableBox->GetTabLines().size(); ++i)
2153  {
2154  SwTableLine *const pSubLine(pSubTableBox->GetTabLines()[i]);
2155  SwTableLine *const pNewLine = new SwTableLine(
2156  static_cast<SwTableLineFormat*>(pSourceLine->GetFrameFormat()),
2157  pSourceLine->GetTabBoxes().size() - 1 + pSubLine->GetTabBoxes().size(),
2158  nullptr);
2159  SwFrameFormat const& rSubLineFormat(*pSubLine->GetFrameFormat());
2160  SwFormatFrameSize const* pSize(nullptr);
2161  if (rSubLineFormat.GetItemState(RES_FRM_SIZE, true,
2162  reinterpret_cast<SfxPoolItem const**>(&pSize)) == SfxItemState::SET)
2163  { // for rows 2..N, copy inner row height to outer row
2164  pNewLine->ClaimFrameFormat();
2165  pNewLine->GetFrameFormat()->SetFormatAttr(*pSize);
2166  if (pSize->GetHeightSizeType() != SwFrameSize::Variable)
2167  {
2168  minHeights += pSize->GetHeight();
2169  }
2170  }
2171  // ensure the sum of the lines is at least as high as the outer line was
2172  if (i == pSubTableBox->GetTabLines().size() - 1
2173  && outerSize.GetHeightSizeType() != SwFrameSize::Variable
2174  && minHeights < outerSize.GetHeight())
2175  {
2176  SwFormatFrameSize lastSize(pNewLine->GetFrameFormat()->GetFrameSize());
2177  lastSize.SetHeight(lastSize.GetHeight() + outerSize.GetHeight() - minHeights);
2178  if (lastSize.GetHeightSizeType() == SwFrameSize::Variable)
2179  {
2180  lastSize.SetHeightSizeType(SwFrameSize::Minimum);
2181  }
2182  pNewLine->GetFrameFormat()->SetFormatAttr(lastSize);
2183  }
2184  SfxPoolItem const* pRowBrush(nullptr);
2185  (void)rSubLineFormat.GetItemState(RES_BACKGROUND, true, &pRowBrush);
2186  GetTabLines().insert(GetTabLines().begin() + nRow + i, pNewLine);
2187  for (size_t j = 0; j < pSourceLine->GetTabBoxes().size(); ++j)
2188  {
2189  if (j == nBox)
2190  {
2191  for (size_t k = 0; k < pSubLine->GetTabBoxes().size(); ++k)
2192  {
2193  // move box k to new outer row
2194  SwTableBox *const pSourceBox(pSubLine->GetTabBoxes()[k]);
2195  assert(pSourceBox->getRowSpan() == 1);
2196  // import filter (xmltbli.cxx) converts all box widths to absolute
2197  assert(pSourceBox->GetFrameFormat()->GetFrameSize().GetWidthPercent() == 0);
2198  ::InsTableBox(*pDoc, GetTableNode(), pNewLine,
2199  static_cast<SwTableBoxFormat*>(pSourceBox->GetFrameFormat()),
2200  pSourceBox, j+k, 1);
2201  // insert dummy text node...
2202  pDoc->GetNodes().MakeTextNode(
2203  SwNodeIndex(*pSourceBox->GetSttNd(), +1),
2204  pDoc->GetDfltTextFormatColl());
2205  SwNodeRange content(*pSourceBox->GetSttNd(), +2,
2206  *pSourceBox->GetSttNd()->EndOfSectionNode());
2207  SwTableBox *const pNewBox(pNewLine->GetTabBoxes()[j+k]);
2208  SwNodeIndex insPos(*pNewBox->GetSttNd(), 1);
2209  // MoveNodes would delete the box SwStartNode/SwEndNode
2210  // without the dummy node
2211 #if 0
2212  pDoc->GetNodes().MoveNodes(content, pDoc->GetNodes(), insPos, false);
2213 #else
2215 #endif
2216  // delete the empty node that was bundled in the new box
2217  pDoc->GetNodes().Delete(insPos);
2218  if (pRowBrush)
2219  {
2220  SfxPoolItem const* pCellBrush(nullptr);
2221  if (pNewBox->GetFrameFormat()->GetItemState(RES_BACKGROUND, true, &pCellBrush) != SfxItemState::SET)
2222  { // set inner row background on inner cell
2223  pNewBox->ClaimFrameFormat();
2224  pNewBox->GetFrameFormat()->SetFormatAttr(*pRowBrush);
2225  }
2226  }
2227  // assume that the borders can be left as they are, because
2228  // lines don't have borders, only boxes do
2229  }
2230  }
2231  else
2232  {
2233  // insert covered cell for box j
2234  SwTableBox *const pSourceBox(pSourceLine->GetTabBoxes()[j]);
2235  assert(pSourceBox->GetTabLines().empty()); // checked for that
2236  sal_uInt16 const nInsPos(j < nBox ? j : j + pSubLine->GetTabBoxes().size() - 1);
2237  ::InsTableBox(*pDoc, GetTableNode(), pNewLine,
2238  static_cast<SwTableBoxFormat*>(pSourceBox->GetFrameFormat()),
2239  pSourceBox, nInsPos, 1);
2240  // adjust row span:
2241  // N rows in subtable, N-1 rows inserted:
2242  // -1 -> -N ; -(N-1) ... -1
2243  // -2 -> -(N+1) ; -N .. -2
2244  // 1 -> N ; -(N-1) .. -1
2245  // 2 -> N+1 ; -N .. -2
2246  sal_Int32 newSourceRowSpan(pSourceBox->getRowSpan());
2247  sal_Int32 newBoxRowSpan;
2248  if (newSourceRowSpan < 0)
2249  {
2250  newSourceRowSpan -= pSubTableBox->GetTabLines().size() - 1;
2251  newBoxRowSpan = newSourceRowSpan + i;
2252  }
2253  else
2254  {
2255  newSourceRowSpan += pSubTableBox->GetTabLines().size() - 1;
2256  newBoxRowSpan = -(newSourceRowSpan - sal::static_int_cast<tools::Long>(i));
2257  }
2258  pNewLine->GetTabBoxes()[nInsPos]->setRowSpan(newBoxRowSpan);
2259  if (i == pSubTableBox->GetTabLines().size() - 1)
2260  { // only last iteration
2261  pSourceBox->setRowSpan(newSourceRowSpan);
2262  }
2263  }
2264  }
2265  }
2266  // delete inner rows 2..N
2267  while (1 < pSubTableBox->GetTabLines().size())
2268  {
2269  // careful: the last box deletes pSubLine!
2270  SwTableLine *const pSubLine(pSubTableBox->GetTabLines()[1]);
2271  for (size_t j = pSubLine->GetTabBoxes().size(); 0 < j; --j)
2272  {
2273  SwTableBox *const pBox(pSubLine->GetTabBoxes()[0]);
2274  DeleteBox_(*this, pBox, nullptr, false, false, nullptr);
2275  }
2276  }
2277  // fix row spans in lines preceding nRow
2278  lcl_ChangeRowSpan(*this, pSubTableBox->GetTabLines().size() - 1, nRow - 1, false);
2279  // note: the first line of the inner table remains; caller will call
2280  // GCLines() to remove it
2281 }
2282 
2284 {
2285  for (SwTableLine const*const pLine : GetTabLines())
2286  {
2287  bool haveSubtable(false);
2288  for (SwTableBox const*const pBox : pLine->GetTabBoxes())
2289  {
2290  if (pBox->IsFormulaOrValueBox() == RES_BOXATR_FORMULA)
2291  {
2292  return false; // no table box formulas yet
2293  }
2294  if (!pBox->GetTabLines().empty())
2295  {
2296  if (haveSubtable)
2297  { // can't handle 2 subtable in a row yet
2298  return false;
2299  }
2300  haveSubtable = true;
2301  for (SwTableLine const*const pInnerLine : pBox->GetTabLines())
2302  {
2303  // bitmap row background will look different
2304  SwFrameFormat const& rRowFormat(*pInnerLine->GetFrameFormat());
2305  std::unique_ptr<SvxBrushItem> pBrush(rRowFormat.makeBackgroundBrushItem());
2306  assert(pBrush);
2307  if (pBrush->GetGraphicObject() != nullptr)
2308  {
2309  /* TODO: all cells could override this?
2310  for (SwTableBox & rInnerBox : rInnerLine.GetTabBoxes())
2311  */
2312  if (1 < pInnerLine->GetTabBoxes().size()) // except if only 1 cell?
2313  {
2314  return false;
2315  }
2316  }
2317  for (SwTableBox const*const pInnerBox : pInnerLine->GetTabBoxes())
2318  {
2319  if (!pInnerBox->GetTabLines().empty())
2320  {
2321  return false; // nested subtable :(
2322  }
2323  }
2324  }
2325  }
2326  }
2327  }
2328  // note: fields that refer to table cells may be *outside* the table,
2329  // so the entire document needs to be imported before checking here
2330  // (same for table box formulas and charts)
2331  SwDoc *const pDoc(GetFrameFormat()->GetDoc());
2332  SwFieldType const*const pTableFields(
2334  std::vector<SwFormatField*> vFields;
2335  pTableFields->GatherFields(vFields);
2336  if (!vFields.empty())
2337  {
2338  return false; // no formulas in fields yet
2339  }
2340  if (pDoc->GetAttrPool().GetItemCount2(RES_BOXATR_FORMULA) != 0)
2341  {
2342  return false; // no table box formulas yet
2343  }
2344  OUString const tableName(GetFrameFormat()->GetName());
2346  while (SwStartNode const*const pStartNode = temp.GetNode().GetStartNode())
2347  {
2348  ++temp;
2349  SwOLENode const*const pOLENode(temp.GetNode().GetOLENode());
2350  if (pOLENode && tableName == pOLENode->GetChartTableName())
2351  { // there are charts that refer to this table
2352  // presumably such charts would need to be adapted somehow?
2353  return false;
2354  }
2355  temp.Assign(*pStartNode->EndOfSectionNode(), +1);
2356  }
2357  return true;
2358 }
2359 
2361 {
2362  for (size_t i = 0; i < GetTabLines().size(); ++i)
2363  {
2364  SwTableLine *const pLine(GetTabLines()[i]);
2365  for (size_t j = 0; j < pLine->GetTabBoxes().size(); ++j)
2366  {
2367  SwTableBox *const pBox(pLine->GetTabBoxes()[j]);
2368  SwTableLines & rInnerLines(pBox->GetTabLines());
2369  if (!rInnerLines.empty())
2370  {
2371  ConvertSubtableBox(i, j);
2372  }
2373  }
2374  }
2375  GCLines();
2376  m_bNewModel = true;
2377 #if 0
2378  // note: outline nodes (and ordinary lists) are sorted by MoveNodes() itself
2379  // (this could change order inside table of contents, but that's a
2380  // really esoteric use-case)
2381  // nodes were moved - sort marks, redlines, footnotes
2382  SwDoc *const pDoc(GetFrameFormat()->GetDoc());
2386 #endif
2387  // assume that there aren't any node indexes to the deleted box start/end nodes
2388  CHECK_TABLE( *this )
2389 }
2390 
2391 #ifdef DBG_UTIL
2392 
2393 namespace {
2394 
2395 struct RowSpanCheck
2396 {
2397  sal_Int32 nRowSpan;
2398  SwTwips nLeft;
2399  SwTwips nRight;
2400 };
2401 
2402 }
2403 
2405 {
2406  if( !IsNewModel() )
2407  return;
2408  const size_t nLineCount = GetTabLines().size();
2410  SwTwips nLineWidth = 0;
2411  std::list< RowSpanCheck > aRowSpanCells;
2412  std::list< RowSpanCheck >::iterator aIter = aRowSpanCells.end();
2414  ++index;
2415  for( size_t nCurrLine = 0; nCurrLine < nLineCount; ++nCurrLine )
2416  {
2417  SwTwips nWidth = 0;
2418  SwTableLine* pLine = GetTabLines()[nCurrLine];
2419  SAL_WARN_IF( !pLine, "sw.core", "Missing Table Line" );
2420  const size_t nColCount = pLine->GetTabBoxes().size();
2421  SAL_WARN_IF( !nColCount, "sw.core", "Empty Table Line" );
2422  for( size_t nCurrCol = 0; nCurrCol < nColCount; ++nCurrCol )
2423  {
2424  SwTableBox* pBox = pLine->GetTabBoxes()[nCurrCol];
2425  assert(pBox);
2426  SAL_WARN_IF(GetTableNode()->EndOfSectionIndex() <= index.GetIndex(), "sw.core", "Box not in table nodes");
2427  SAL_WARN_IF(!index.GetNode().IsStartNode(), "sw.core", "No box start node");
2428  index = *index.GetNode().EndOfSectionNode();
2429  ++index;
2430  SwTwips nNewWidth = pBox->GetFrameFormat()->GetFrameSize().GetWidth() + nWidth;
2431  sal_Int32 nRowSp = pBox->getRowSpan();
2432  if( nRowSp < 0 )
2433  {
2434  SAL_WARN_IF( aIter == aRowSpanCells.end(),
2435  "sw.core", "Missing master box");
2436  if (aIter != aRowSpanCells.end())
2437  {
2438  SAL_WARN_IF( aIter->nLeft != nWidth || aIter->nRight != nNewWidth,
2439  "sw.core", "Wrong position/size of overlapped table box");
2440  --(aIter->nRowSpan);
2441  SAL_WARN_IF( aIter->nRowSpan != -nRowSp, "sw.core",
2442  "Wrong row span value" );
2443  if( nRowSp == -1 )
2444  {
2445  aIter = aRowSpanCells.erase(aIter);
2446  }
2447  else
2448  ++aIter;
2449  }
2450  }
2451  else if( nRowSp != 1 )
2452  {
2453  SAL_WARN_IF( !nRowSp, "sw.core", "Zero row span?!" );
2454  RowSpanCheck aEntry;
2455  aEntry.nLeft = nWidth;
2456  aEntry.nRight = nNewWidth;
2457  aEntry.nRowSpan = nRowSp;
2458  aRowSpanCells.insert( aIter, aEntry );
2459  }
2460  nWidth = nNewWidth;
2461  }
2462  if( !nCurrLine )
2463  nLineWidth = nWidth;
2464  SAL_WARN_IF( nWidth != nLineWidth, "sw.core",
2465  "Different Line Widths: first: " << nLineWidth
2466  << " current [" << nCurrLine << "]: " << nWidth);
2467  SAL_WARN_IF( std::abs(nWidth - nTabSize) > 1 /* how tolerant? */, "sw.core",
2468  "Line width differs from table width: " << nTabSize
2469  << " current [" << nCurrLine << "]: " << nWidth);
2470  SAL_WARN_IF( nWidth < 0 || nWidth > USHRT_MAX, "sw.core",
2471  "Width out of range [" << nCurrLine << "]: " << nWidth);
2472  SAL_WARN_IF( aIter != aRowSpanCells.end(), "sw.core",
2473  "Missing overlapped box" );
2474  aIter = aRowSpanCells.begin();
2475  }
2476  bool bEmpty = aRowSpanCells.empty();
2477  SAL_WARN_IF( !bEmpty, "sw.core", "Open row span detected" );
2478  SAL_WARN_IF(GetTableNode()->EndOfSectionNode() != &index.GetNode(), "sw.core", "table end node not found");
2479 }
2480 
2481 #endif
2482 
2483 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
void Resort()
Definition: docary.hxx:270
#define CHECK_TABLE(t)
Definition: swnewtable.cxx:50
Instances of SwFields and those derived from it occur 0 to n times.
Definition: fldbas.hxx:240
const SwEndNode * EndOfSectionNode() const
Definition: node.hxx:686
Starts a section of nodes in the document model.
Definition: node.hxx:312
Base class of the Writer layout elements.
Definition: frame.hxx:298
virtual sal_Int32 Len() const
Definition: node.cxx:1245
tools::Long GetWidth() const
std::vector< SwSelBoxes > maBoxes
Definition: swnewtable.cxx:67
void CreateSelection(const SwPaM &rPam, SwSelBoxes &rBoxes, const SearchType eSearchType, bool bProtect) const
void SwTable::CreateSelection(..) fills the selection structure with table cells for a given SwPaM...
SwNode & GetEndOfAutotext() const
Section for all Flys/Header/Footers.
Definition: ndarr.hxx:156
const Value & back() const
Marks a position in the document model.
Definition: pam.hxx:35
void SetHeight(tools::Long n)
sal_uLong GetSttIdx() const
Definition: swtable.cxx:1866
virtual SwFieldType * GetFieldType(SwFieldIds nResId, const OUString &rName, bool bDbFieldMatching) const =0
SwNodeIndex nNode
Definition: pam.hxx:37
SwSaveRowSpan(SwTableBoxes &rBoxes, sal_uInt16 nSplitLn)
constexpr TypedWhichId< SwFormatFrameSize > RES_FRM_SIZE(89)
std::pair< sal_uInt16, sal_uInt16 > SwLineOffset
long Long
const SwPosition * GetMark() const
Definition: pam.hxx:209
void ConvertSubtableBox(sal_uInt16 const nRow, sal_uInt16 const nBox)
This is kind of similar to InsertSpannedRow()/InsertRow() but that one would recursively copy subtabl...
std::vector< SwLineOffset > SwLineOffsetArray
sal_Int64 n
void InvalidateSize_()
Definition: frame.hxx:754
Definition: doc.hxx:187
SwFrameFormat * ClaimFrameFormat()
Definition: swtable.cxx:1466
void DelFrames(SwTable &rTable)
Definition: tblsel.cxx:2172
void GetMergeSel(const SwPaM &rPam, SwSelBoxes &rBoxes, SwTableBox **ppMergeBox, SwUndoTableMerge *pUndo)
Definition: tblsel.cxx:929
const_iterator find(const Value &x) const
SwTableLine is one table row in the document model.
Definition: swtable.hxx:351
void PrepareDeleteCol(tools::Long nMin, tools::Long nMax)
SwTable::PrepareDeleteCol(..) adjusts the widths of the neighbour cells of a cell selection for an up...
SwNode & GetNode() const
Definition: ndindex.hxx:119
virtual void assureSortedMarkContainers() const =0
static SwTableBox * lcl_LeftBorder2Box(tools::Long nLeft, const SwTableLine *pLine)
lcl_LeftBorder2Box delivers the box to a given left border
Definition: swnewtable.cxx:197
IDocumentMarkAccess * getIDocumentMarkAccess()
Definition: docbm.cxx:1794
IDocumentUndoRedo & GetIDocumentUndoRedo()
Definition: doc.cxx:144
bool InsertRow(SwDoc *, const SwSelBoxes &rBoxes, sal_uInt16 nCnt, bool bBehind)
SwTable::InsertRow(..) inserts one or more rows before or behind the selected boxes.
const editeng::SvxBorderLine * GetRight() const
IDocumentContentOperations const & getIDocumentContentOperations() const
Definition: doc.cxx:315
void CheckRowSpan(SwTableLine *&rpLine, bool bUp) const
SwTable::CheckRowSpan(..) looks for the next line without an overlapping to the previous line...
SwTableFormat * GetFrameFormat()
Definition: swtable.hxx:203
Value in Var-direction gives minimum (can be exceeded but not be less).
bool PrepareMerge(const SwPaM &rPam, SwSelBoxes &rBoxes, SwSelBoxes &rMerged, SwTableBox **ppMergeBox, SwUndoTableMerge *pUndo)
SwTable::PrepareMerge(..) some preparation for the coming Merge(..)
Definition: swnewtable.cxx:809
size_type size() const
Definition: swtable.hxx:75
static void lcl_ChangeRowSpan(const SwTable &rTable, const tools::Long nDiff, sal_uInt16 nRowIdx, const bool bSingle)
lcl_ChangeRowSpan corrects row span after insertion/deletion of rows
Definition: swnewtable.cxx:257
static void lcl_SophisticatedFillLineIndices(SwLineOffsetArray &rArr, const SwTable &rTable, const SwSelBoxes &rBoxes, sal_uInt16 nCnt)
std::set< SwTwips > SwSplitLines
const SvxBoxItem & GetBox(bool=true) const
Definition: frmatr.hxx:84
void GatherFields(std::vector< SwFormatField * > &rvFormatFields, bool bCollectOnlyInDocNodes=true) const
Definition: fldbas.cxx:205
sal_Int32 getRowSpan() const
Definition: swtable.cxx:75
SwContentNode * GetContentNode(bool bPoint=true) const
Definition: pam.hxx:229
void ConvertSubtables()
IDocumentFieldsAccess const & getIDocumentFieldsAccess() const
Definition: doc.cxx:357
void Delete(const SwNodeIndex &rPos, sal_uLong nNodes=1)
delete nodes
Definition: nodes.cxx:1078
SwTableLines m_aLines
Definition: swtable.hxx:115
sal_uInt32 GetItemCount2(sal_uInt16 nWhich) const
sal_uInt16 mnSplitLine
Definition: tblrwcl.hxx:113
bool IsEmptyBox(const SwTableBox &rBox, SwPaM &rPam)
Definition: tblsel.cxx:893
std::vector< tools::Long > mnRowSpans
Definition: tblrwcl.hxx:114
SwIndex nContent
Definition: pam.hxx:38
const char * tableName
enumrange< T >::Iterator begin(enumrange< T >)
const BorderLinePrimitive2D *pCandidateB assert(pCandidateA)
const SwTextFormatColl * GetDfltTextFormatColl() const
Definition: doc.hxx:777
static void lcl_FillSelBoxes(SwSelBoxes &rBoxes, SwTableLine &rLine)
lcl_FillSelBoxes(..) puts all boxes of a given line into the selection structure
void AdjustWidths(const tools::Long nOld, const tools::Long nNew)
Definition: swtable.cxx:351
int nCount
sal_uLong GetIndex() const
Definition: ndindex.hxx:152
This structure is needed by Undo to restore row span attributes when a table has been split into two ...
Definition: tblrwcl.hxx:111
static sal_uInt16 lcl_CalculateSplitLineHeights(SwSplitLines &rCurr, SwSplitLines &rNew, const SwTable &rTable, const SwSelBoxes &rBoxes, sal_uInt16 nCnt)
lcl_CalculateSplitLineHeights(..) delivers all y-positions where table rows have to be split to fulfi...
virtual void DoUndo(bool const bDoUndo)=0
Enable/Disable Undo.
bool IsStartNode() const
Definition: node.hxx:628
iterator insert(iterator aIt, SwTableLine *pLine)
Definition: swtable.hxx:85
void InsertSpannedRow(SwDoc &rDoc, sal_uInt16 nIdx, sal_uInt16 nCnt)
SwTable::InsertSpannedRow(..) inserts "superfluous" rows, i.e.
void SetCountedInList(bool bCounted)
Definition: ndtxt.cxx:4229
size_type size() const
virtual bool DoesUndo() const =0
Is Undo enabled?
static void lcl_UnMerge(const SwTable &rTable, SwTableBox &rBox, size_t nCnt, bool bSameHeight)
lcl_UnMerge(..) manipulates the row span attribute of a given master cell and its overlapped cells to...
void MakeFrames(SwTable &rTable)
Definition: tblsel.cxx:2333
void InsTableBox(SwDoc &rDoc, SwTableNode *pTableNd, SwTableLine *pLine, SwTableBoxFormat *pBoxFrameFormat, SwTableBox *pBox, sal_uInt16 nInsPos, sal_uInt16 nCnt=1)
Definition: swtable.cxx:130
void DelNumRules(const SwPaM &, SwRootFrame const *pLayout=nullptr)
Definition: docnum.cxx:1324
bool NewMerge(SwDoc *, const SwSelBoxes &, const SwSelBoxes &rMerged, SwUndoTableMerge *)
NewMerge(..) removes the superfluous cells after cell merge.
Definition: swnewtable.cxx:90
PaM is Point and Mark: a selection of the document model.
Definition: pam.hxx:136
Style of a layout element.
Definition: frmfmt.hxx:58
void RestoreRowSpan(const SwSaveRowSpan &)
void SetTableLines(const SwSelBoxes &rBoxes, const SwTable &rTable)
Definition: tblsel.cxx:2110
int i
const SwStartNode * StartOfSectionNode() const
Definition: node.hxx:132
const SwPosition * GetPoint() const
Definition: pam.hxx:207
bool empty() const
Definition: swtable.hxx:74
bool DeleteSel(SwDoc *, const SwSelBoxes &rBoxes, const SwSelBoxes *pMerged, SwUndo *pUndo, const bool bDelMakeFrames, const bool bCorrBorder)
Definition: tblrwcl.cxx:934
SwIndex & Assign(SwIndexReg *, sal_Int32)
Definition: index.cxx:206
void CheckConsistency() const
void MoveBoxContent(SwDoc &rDoc, SwNodeRange &rRg, SwNodeIndex &rPos)
Definition: untbl.cxx:1984
bool isEmpty() const
Definition: swnewtable.cxx:70
SwContentNode * GetContentNode()
Definition: node.hxx:619
void SetSelBoxes(const SwSelBoxes &rBoxes)
Definition: untbl.cxx:1999
bool CanConvertSubtables() const
void ExpandColumnSelection(SwSelBoxes &rBoxes, tools::Long &rMin, tools::Long &rMax) const
void SwTable::ExpandColumnSelection(..) adds cell to the give selection to assure that at least one c...
SwFrameFormat * GetFrameFormat()
Definition: swtable.hxx:373
void push_back(const SwSelBoxes &rNew)
Definition: swnewtable.cxx:71
bool IsContentProtected() const
size
Marks a node in the document model.
Definition: ndindex.hxx:31
static void lcl_CheckMinMax(tools::Long &rMin, tools::Long &rMax, const SwTableLine &rLine, size_t nCheck, bool bSet)
lcl_CheckMinMax helps evaluating (horizontal) min/max of boxes
Definition: swnewtable.cxx:124
SwFrameFormat * GetFrameFormat()
Definition: swtable.hxx:431
void FindSuperfluousRows_(SwSelBoxes &rBoxes, SwTableLine *, SwTableLine *)
SwTable::FindSuperfluousRows_(..) is looking for superfluous rows, i.e.
Definition: swnewtable.cxx:986
static void lcl_getAllMergedBoxes(const SwTable &rTable, SwSelBoxes &rBoxes, SwTableBox &rBox)
lcl_getAllMergedBoxes(..) collects all overlapped boxes to a given (master) box
const_iterator end() const
tools::Long mnMergeWidth
Definition: swnewtable.cxx:68
tuple index
bool empty() const
const SwDoc * GetDoc() const
The document is set in SwAttrPool now, therefore you always can access it.
Definition: format.hxx:123
tools::Long GetHeight() const
SwStartNode * GetStartNode()
Definition: node.hxx:595
sal_uInt16 GetBoxPos(const SwTableBox *pBox) const
Definition: swtable.hxx:363
virtual bool MoveNodeRange(SwNodeRange &, SwNodeIndex &, SwMoveFlags)=0
sal_Int32 nLineWidth
SwTableLines & GetTabLines()
Definition: swtable.hxx:200
tools::Long SwTwips
Definition: swtypes.hxx:49
IDocumentLayoutAccess const & getIDocumentLayoutAccess() const
Definition: doc.cxx:405
SwTable is one table in the document model, containing rows (which contain cells).
Definition: swtable.hxx:111
const SwPosition * Start() const
Definition: pam.hxx:212
void SetWidth(tools::Long n)
static tools::Long lcl_InsertPosition(SwTable &rTable, std::vector< sal_uInt16 > &rInsPos, const SwSelBoxes &rBoxes, bool bBehind)
lcl_InsertPosition(..) evaluates the insert positions in every table line, when a selection of cells ...
Definition: swnewtable.cxx:608
virtual bool SetFormatAttr(const SfxPoolItem &rAttr)
Definition: format.cxx:450
std::unique_ptr< SwBoxSelection > CollectBoxSelection(const SwPaM &rPam) const
CollectBoxSelection(..) create a rectangulare selection based on the given SwPaM and prepares the sel...
Definition: swnewtable.cxx:329
SwTextNode is a paragraph in the document model.
Definition: ndtxt.hxx:79
SwTableBoxes & GetTabBoxes()
Definition: swtable.hxx:361
const size_t nTabSize
IDocumentRedlineAccess const & getIDocumentRedlineAccess() const
Definition: doc.cxx:335
std::vector< SwTableBox * > SwTableBoxes
Definition: swtable.hxx:104
const SwStartNode * GetSttNd() const
Definition: swtable.hxx:445
static void lcl_SearchSelBox(const SwTable &rTable, SwSelBoxes &rBoxes, tools::Long nMin, tools::Long nMax, SwTableLine &rLine, bool bChkProtected, bool bColumn)
lcl_SearchSelBox(..) adds cells of a given table row to the selection structure if it overlaps with t...
constexpr TypedWhichId< SvxBrushItem > RES_BACKGROUND(105)
void ChgFrameFormat(SwTableBoxFormat *pNewFormat, bool bNeedToReregister=true)
Definition: swtable.cxx:1726
void GCLines()
Definition: gctable.cxx:454
SwTableBox & FindStartOfRowSpan(const SwTable &, sal_uInt16 nMaxStep=USHRT_MAX)
SwTableBox::FindStartOfRowSpan(..) returns the "master" cell, the cell which overlaps the given cell...
#define SAL_WARN_IF(condition, area, stream)
virtual const SwViewShell * GetCurrentViewShell() const =0
Returns the layout set at the document.
std::unique_ptr< SwSaveRowSpan > CleanUpTopRowSpan(sal_uInt16 nSplitLine)
const o3tl::enumarray< SvxAdjust, unsigned short > aSvxToUnoAdjust USHRT_MAX
Definition: unosett.cxx:254
void CleanUpBottomRowSpan(sal_uInt16 nDelLines)
SwNodes & GetNodes()
Definition: doc.hxx:408
constexpr TypedWhichId< SwTableBoxFormula > RES_BOXATR_FORMULA(151)
const SwPosition * End() const
Definition: pam.hxx:217
SwTableBox & FindEndOfRowSpan(const SwTable &, sal_uInt16 nMaxStep)
SwTableBox::FindEndOfRowSpan(..) returns the last overlapped cell if there is any.
SwTableBox is one table cell in the document model.
Definition: swtable.hxx:392
bool m_bNewModel
Definition: swtable.hxx:138
SwFrame * GetLower()
Definition: findfrm.cxx:170
static sal_uInt16 lcl_LineIndex(const SwTable &rTable, const SwSelBoxes &rBoxes, bool bBehind)
lcl_LineIndex(..) delivers the line index of the line behind or above the box selection.
SwFrameFormat * ClaimFrameFormat()
Definition: swtable.cxx:1690
virtual bool AppendTextNode(SwPosition &rPos)=0
const SwFormatFrameSize & GetFrameSize(bool=true) const
Definition: fmtfsize.hxx:104
const SwStartNode * FindTableBoxStartNode() const
Definition: node.hxx:197
void DeleteBox_(SwTable &rTable, SwTableBox *pBox, SwUndo *pUndo, bool bCalcNewSize, const bool bCorrBorder, SwShareBoxFormats *pShareFormats)
Definition: tblrwcl.cxx:652
SwFootnoteIdxs & GetFootnoteIdxs()
Definition: doc.hxx:633
#define SAL_WARN(area, stream)
const SvxProtectItem & GetProtect(bool=true) const
Definition: frmatr.hxx:82
void setRowSpan(sal_Int32 nNewRowSpan)
Definition: swtable.cxx:80
sal_uInt16 GetPos(const SwTableLine *pBox) const
Definition: swtable.hxx:97
bool NewSplitRow(SwDoc &, const SwSelBoxes &, sal_uInt16, bool)
SwTable::NewSplitRow(..) splits all selected boxes horizontally.
static tools::Long lcl_Box2LeftBorder(const SwTableBox &rBox)
lcl_Box2LeftBorder(..) delivers the left (logical) border of a table box
Definition: swnewtable.cxx:162
void AddNewBox(sal_uLong nSttNdIdx)
Definition: UndoTable.hxx:232
Frame is variable in Var-direction.
virtual const SwRedlineTable & GetRedlineTable() const =0
std::pair< const_iterator, bool > insert(Value &&x)
void UpdateAllFootnote()
Definition: ftnidx.cxx:267
bool IsNewModel() const
Definition: swtable.hxx:187
SwTableLine * GetUpper()
Definition: swtable.hxx:427
SwBoxSelection is a small helperclass (structure) to handle selections of cells (boxes) between table...
Definition: swnewtable.cxx:64
void SetHeightSizeType(SwFrameSize eSize)
Definition: fmtfsize.hxx:81
void PrepareDelBoxes(const SwSelBoxes &rBoxes)
SwTable::PrepareDelBoxes(..) adjusts the row span attributes for an upcoming deletion of table cells ...
bool InsertRow_(SwDoc *, const SwSelBoxes &, sal_uInt16 nCnt, bool bBehind)
Definition: tblrwcl.cxx:511
const editeng::SvxBorderLine * GetBottom() const
static void lcl_InvalidateCellFrame(const SwTableBox &rBox)
lcl_InvalidateCellFrame(..) invalidates all layout representations of a given cell to initiate a refo...
Definition: swnewtable.cxx:589
bool NewInsertCol(SwDoc &, const SwSelBoxes &rBoxes, sal_uInt16 nCnt, bool)
SwTable::NewInsertCol(..) insert new column(s) into a table.
Definition: swnewtable.cxx:653
SwCellFrame is one table cell in the document layout.
Definition: cellfrm.hxx:30
sal_uInt16 nPos
const SwAttrPool & GetAttrPool() const
Definition: doc.hxx:1320
SwTextNode * GetTextNode()
Inline methods from Node.hxx.
Definition: ndtxt.hxx:845
SwTextNode * MakeTextNode(const SwNodeIndex &rWhere, SwTextFormatColl *pColl, bool bNewFrames=true)
Implementations of "Make...Node" are in the given .cxx-files.
Definition: ndtxt.cxx:105
bool MoveNodes(const SwNodeRange &, SwNodes &rNodes, const SwNodeIndex &, bool bNewFrames=true)
move the node pointer
Definition: nodes.cxx:400
SwNodeIndex & Assign(SwNodes const &rNds, sal_uLong)
Definition: ndindex.hxx:272
SwFrameSize GetHeightSizeType() const
Definition: fmtfsize.hxx:80
SwTableNode * GetTableNode() const
Definition: swtable.cxx:1913
void SetLine(const editeng::SvxBorderLine *pNew, SvxBoxItemLine nLine)
void ExpandSelection(SwSelBoxes &rBoxes) const
SwTable::ExpandSelection(..) adds all boxes to the box selections which are overlapped by it...
Base class of the Writer document model elements.
Definition: node.hxx:80
typedef void(CALLTYPE *GetFuncDataPtr)(sal_uInt16 &nNo