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