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 <node.hxx>
25 #include <UndoTable.hxx>
26 #include <pam.hxx>
27 #include <frmfmt.hxx>
28 #include <frmatr.hxx>
29 #include <cellfrm.hxx>
30 #include <fmtfsize.hxx>
31 #include <doc.hxx>
32 #include <IDocumentUndoRedo.hxx>
35 #include <cstdlib>
36 #include <vector>
37 #include <set>
38 #include <list>
39 #include <memory>
40 #include <editeng/boxitem.hxx>
41 #include <editeng/protitem.hxx>
42 #include <swtblfmt.hxx>
43 #include <calbck.hxx>
44 #include <sal/log.hxx>
45 
46 #ifdef DBG_UTIL
47 #define CHECK_TABLE(t) (t).CheckConsistency();
48 #else
49 #define CHECK_TABLE(t)
50 #endif
51 
62 {
63 public:
64  std::vector<SwSelBoxes> maBoxes;
66  SwBoxSelection() : mnMergeWidth(0) {}
67  bool isEmpty() const { return maBoxes.empty(); }
68  void push_back(const SwSelBoxes& rNew) { maBoxes.push_back(rNew); }
69 };
70 
87 bool SwTable::NewMerge( SwDoc* pDoc, const SwSelBoxes& rBoxes,
88  const SwSelBoxes& rMerged, SwUndoTableMerge* pUndo )
89 {
90  if( pUndo )
91  pUndo->SetSelBoxes( rBoxes );
92  DeleteSel( pDoc, rBoxes, &rMerged, nullptr, true, true );
93 
94  CHECK_TABLE( *this )
95  return true;
96 }
97 
121 static void lcl_CheckMinMax( long& rMin, long& rMax, const SwTableLine& rLine, size_t nCheck, bool bSet )
122 {
123  ++nCheck;
124  if( rLine.GetTabBoxes().size() < nCheck )
125  { // robust
126  OSL_FAIL( "Box out of table line" );
127  nCheck = rLine.GetTabBoxes().size();
128  }
129 
130  long nNew = 0; // will be the right border of the current box
131  long nWidth = 0; // the width of the current box
132  for( size_t nCurrBox = 0; nCurrBox < nCheck; ++nCurrBox )
133  {
134  SwTableBox* pBox = rLine.GetTabBoxes()[nCurrBox];
135  OSL_ENSURE( pBox, "Missing table box" );
136  nWidth = pBox->GetFrameFormat()->GetFrameSize().GetWidth();
137  nNew += nWidth;
138  }
139  // nNew is the right border of the wished box
140  if( bSet || nNew > rMax )
141  rMax = nNew;
142  nNew -= nWidth; // nNew becomes the left border of the wished box
143  if( bSet || nNew < rMin )
144  rMin = nNew;
145 }
146 
159 static long lcl_Box2LeftBorder( const SwTableBox& rBox )
160 {
161  if( !rBox.GetUpper() )
162  return 0;
163  long nLeft = 0;
164  const SwTableLine &rLine = *rBox.GetUpper();
165  const size_t nCount = rLine.GetTabBoxes().size();
166  for( size_t nCurrBox = 0; nCurrBox < nCount; ++nCurrBox )
167  {
168  SwTableBox* pBox = rLine.GetTabBoxes()[nCurrBox];
169  OSL_ENSURE( pBox, "Missing table box" );
170  if( pBox == &rBox )
171  return nLeft;
172  nLeft += pBox->GetFrameFormat()->GetFrameSize().GetWidth();
173  }
174  OSL_FAIL( "Box not found in own upper?" );
175  return nLeft;
176 }
177 
194 static SwTableBox* lcl_LeftBorder2Box( long nLeft, const SwTableLine* pLine )
195 {
196  if( !pLine )
197  return nullptr;
198  long nCurrLeft = 0;
199  const size_t nCount = pLine->GetTabBoxes().size();
200  for( size_t nCurrBox = 0; nCurrBox < nCount; ++nCurrBox )
201  {
202  SwTableBox* pBox = pLine->GetTabBoxes()[nCurrBox];
203  OSL_ENSURE( pBox, "Missing table box" );
204  if( pBox->GetFrameFormat()->GetFrameSize().GetWidth() )
205  {
206  if( nCurrLeft == nLeft )
207  return pBox;
208  // HACK: It appears that rounding errors may result in positions not matching
209  // exactly, so allow a little tolerance. This happens at least with merged cells
210  // in the doc from fdo#38414 .
211  if( std::abs( nCurrLeft - nLeft ) <= ( nLeft / 1000 ))
212  return pBox;
213  if( nCurrLeft >= nLeft )
214  {
215  SAL_WARN( "sw.core", "Possibly wrong box found" );
216  return pBox;
217  }
218  }
219  nCurrLeft += pBox->GetFrameFormat()->GetFrameSize().GetWidth();
220  }
221  OSL_FAIL( "Didn't find wished box" );
222  return nullptr;
223 }
224 
254 static void lcl_ChangeRowSpan( const SwTable& rTable, const long nDiff,
255  sal_uInt16 nRowIdx, const bool bSingle )
256 {
257  if( !nDiff || nRowIdx >= rTable.GetTabLines().size() )
258  return;
259  OSL_ENSURE( !bSingle || nDiff > 0, "Don't set bSingle when deleting lines!" );
260  bool bGoOn;
261  // nDistance is the distance between the current row and the critical row,
262  // e.g. the deleted rows or the inserted rows.
263  // If the row span is lower than the distance there is nothing to do
264  // because the row span ends before the critical area.
265  // When the inserted rows should not be overlapped by row spans which ends
266  // exactly in the row above, the trick is to start with a distance of 1.
267  long nDistance = bSingle ? 1 : 0;
268  do
269  {
270  bGoOn = false; // will be set to true if we found a non-master cell
271  // which has to be manipulated => we have to check the previous row, too.
272  const SwTableLine* pLine = rTable.GetTabLines()[ nRowIdx ];
273  const size_t nBoxCount = pLine->GetTabBoxes().size();
274  for( size_t nCurrBox = 0; nCurrBox < nBoxCount; ++nCurrBox )
275  {
276  long nRowSpan = pLine->GetTabBoxes()[nCurrBox]->getRowSpan();
277  long nAbsSpan = nRowSpan > 0 ? nRowSpan : -nRowSpan;
278  // Check if the last overlapped cell is above or below
279  // the critical area
280  if( nAbsSpan > nDistance )
281  {
282  if( nDiff > 0 )
283  {
284  if( nRowSpan > 0 )
285  nRowSpan += nDiff; // increment row span of master cell
286  else
287  {
288  nRowSpan -= nDiff; // increment row span of non-master cell
289  bGoOn = true;
290  }
291  }
292  else
293  {
294  if( nRowSpan > 0 )
295  { // A master cell
296  // end of row span behind the deleted area ..
297  if( nRowSpan - nDistance > -nDiff )
298  nRowSpan += nDiff;
299  else // .. or inside the deleted area
300  nRowSpan = nDistance + 1;
301  }
302  else
303  { // Same for a non-master cell
304  if( nRowSpan + nDistance < nDiff )
305  nRowSpan -= nDiff;
306  else
307  nRowSpan = -nDistance - 1;
308  bGoOn = true; // We have to continue
309  }
310  }
311  pLine->GetTabBoxes()[ nCurrBox ]->setRowSpan( nRowSpan );
312  }
313  }
314  ++nDistance;
315  if( nRowIdx )
316  --nRowIdx;
317  else
318  bGoOn = false; //robust
319  } while( bGoOn );
320 }
321 
326 std::unique_ptr<SwBoxSelection> SwTable::CollectBoxSelection( const SwPaM& rPam ) const
327 {
328  OSL_ENSURE( m_bNewModel, "Don't call me for old tables" );
329  if( m_aLines.empty() )
330  return nullptr;
331  const SwNode* pStartNd = rPam.Start()->nNode.GetNode().FindTableBoxStartNode();
332  const SwNode* pEndNd = rPam.End()->nNode.GetNode().FindTableBoxStartNode();
333  if( !pStartNd || !pEndNd || pStartNd == pEndNd )
334  return nullptr;
335 
336  const size_t nLines = m_aLines.size();
337  size_t nTop = 0;
338  size_t nBottom = 0;
339  long nMin = 0, nMax = 0;
340  int nFound = 0;
341  for( size_t nRow = 0; nFound < 2 && nRow < nLines; ++nRow )
342  {
343  SwTableLine* pLine = m_aLines[nRow];
344  OSL_ENSURE( pLine, "Missing table line" );
345  const size_t nCols = pLine->GetTabBoxes().size();
346  for( size_t nCol = 0; nCol < nCols; ++nCol )
347  {
348  SwTableBox* pBox = pLine->GetTabBoxes()[nCol];
349  OSL_ENSURE( pBox, "Missing table box" );
350  if( nFound )
351  {
352  if( pBox->GetSttNd() == pEndNd )
353  {
354  nBottom = nRow;
355  lcl_CheckMinMax( nMin, nMax, *pLine, nCol, false );
356  ++nFound;
357  break;
358  }
359  }
360  else if( pBox->GetSttNd() == pStartNd )
361  {
362  nTop = nRow;
363  lcl_CheckMinMax( nMin, nMax, *pLine, nCol, true );
364  ++nFound;
365  }
366  }
367  }
368  if( nFound < 2 )
369  return nullptr;
370 
371  bool bOkay = true;
372  long nMid = ( nMin + nMax ) / 2;
373 
374  auto pRet(std::make_unique<SwBoxSelection>());
375  std::vector< std::pair< SwTableBox*, long > > aNewWidthVector;
376  size_t nCheckBottom = nBottom;
377  long nLeftSpan = 0;
378  long nRightSpan = 0;
379  long nLeftSpanCnt = 0;
380  long nRightSpanCnt = 0;
381  for( size_t nRow = nTop; nRow <= nBottom && bOkay && nRow < nLines; ++nRow )
382  {
383  SwTableLine* pLine = m_aLines[nRow];
384  OSL_ENSURE( pLine, "Missing table line" );
385  SwSelBoxes aBoxes;
386  long nRight = 0;
387  const size_t nCount = pLine->GetTabBoxes().size();
388  for( size_t nCurrBox = 0; nCurrBox < nCount; ++nCurrBox )
389  {
390  SwTableBox* pBox = pLine->GetTabBoxes()[nCurrBox];
391  OSL_ENSURE( pBox, "Missing table box" );
392  long nLeft = nRight;
393  nRight += pBox->GetFrameFormat()->GetFrameSize().GetWidth();
394  long nRowSpan = pBox->getRowSpan();
395  if( nRight <= nMin )
396  {
397  if( nRight == nMin && nLeftSpanCnt )
398  bOkay = false;
399  continue;
400  }
401  SwTableBox* pInnerBox = nullptr;
402  SwTableBox* pLeftBox = nullptr;
403  SwTableBox* pRightBox = nullptr;
404  long nDiff = 0;
405  long nDiff2 = 0;
406  if( nLeft < nMin )
407  {
408  if( nRight >= nMid || nRight + nLeft >= nMin + nMin )
409  {
410  if( nCurrBox )
411  {
412  aBoxes.insert(pBox);
413  pInnerBox = pBox;
414  pLeftBox = pLine->GetTabBoxes()[nCurrBox-1];
415  nDiff = nMin - nLeft;
416  if( nRight > nMax )
417  {
418  if( nCurrBox+1 < nCount )
419  {
420  pRightBox = pLine->GetTabBoxes()[nCurrBox+1];
421  nDiff2 = nRight - nMax;
422  }
423  else
424  bOkay = false;
425  }
426  else if( nRightSpanCnt && nRight == nMax )
427  bOkay = false;
428  }
429  else
430  bOkay = false;
431  }
432  else if( nCurrBox+1 < nCount )
433  {
434  pLeftBox = pBox;
435  pInnerBox = pLine->GetTabBoxes()[nCurrBox+1];
436  nDiff = nMin - nRight;
437  }
438  else
439  bOkay = false;
440  }
441  else if( nRight <= nMax )
442  {
443  aBoxes.insert(pBox);
444  if( nRow == nTop && nRowSpan < 0 )
445  {
446  bOkay = false;
447  break;
448  }
449  if( nRowSpan > 1 && nRow + nRowSpan - 1 > nBottom )
450  nBottom = nRow + nRowSpan - 1;
451  if( nRowSpan < -1 && nRow - nRowSpan - 1 > nBottom )
452  nBottom = nRow - nRowSpan - 1;
453  if( nRightSpanCnt && nRight == nMax )
454  bOkay = false;
455  }
456  else if( nLeft < nMax )
457  {
458  if( nLeft <= nMid || nRight + nLeft <= nMax )
459  {
460  if( nCurrBox+1 < nCount )
461  {
462  aBoxes.insert(pBox);
463  pInnerBox = pBox;
464  pRightBox = pLine->GetTabBoxes()[nCurrBox+1];
465  nDiff = nRight - nMax;
466  }
467  else
468  bOkay = false;
469  }
470  else if( nCurrBox )
471  {
472  pRightBox = pBox;
473  pInnerBox = pLine->GetTabBoxes()[nCurrBox-1];
474  nDiff = nLeft - nMax;
475  }
476  else
477  bOkay = false;
478  }
479  else
480  break;
481  if( pInnerBox )
482  {
483  if( nRow == nBottom )
484  {
485  long nTmpSpan = pInnerBox->getRowSpan();
486  if( nTmpSpan > 1 )
487  nBottom += nTmpSpan - 1;
488  else if( nTmpSpan < -1 )
489  nBottom -= nTmpSpan + 1;
490  }
491  SwTableBox* pOuterBox = pLeftBox;
492  do
493  {
494  if( pOuterBox )
495  {
496  long nOutSpan = pOuterBox->getRowSpan();
497  if( nOutSpan != 1 )
498  {
499  size_t nCheck = nRow;
500  if( nOutSpan < 0 )
501  {
502  const SwTableBox& rBox =
503  pOuterBox->FindStartOfRowSpan( *this );
504  nOutSpan = rBox.getRowSpan();
505  const SwTableLine* pTmpL = rBox.GetUpper();
506  nCheck = GetTabLines().GetPos( pTmpL );
507  if( nCheck < nTop )
508  bOkay = false;
509  if( pOuterBox == pLeftBox )
510  {
511  if( !nLeftSpanCnt || nMin - nDiff != nLeftSpan )
512  bOkay = false;
513  }
514  else
515  {
516  if( !nRightSpanCnt || nMax + nDiff != nRightSpan )
517  bOkay = false;
518  }
519  }
520  else
521  {
522  if( pOuterBox == pLeftBox )
523  {
524  if( nLeftSpanCnt )
525  bOkay = false;
526  nLeftSpan = nMin - nDiff;
527  nLeftSpanCnt = nOutSpan;
528  }
529  else
530  {
531  if( nRightSpanCnt )
532  bOkay = false;
533  nRightSpan = nMax + nDiff;
534  nRightSpanCnt = nOutSpan;
535  }
536  }
537  nCheck += nOutSpan - 1;
538  if( nCheck > nCheckBottom )
539  nCheckBottom = nCheck;
540  }
541  else if( ( nLeftSpanCnt && pLeftBox == pOuterBox ) ||
542  ( nRightSpanCnt && pRightBox == pOuterBox ) )
543  bOkay = false;
544  std::pair< SwTableBox*, long > aTmp;
545  aTmp.first = pInnerBox;
546  aTmp.second = -nDiff;
547  aNewWidthVector.push_back(aTmp);
548  aTmp.first = pOuterBox;
549  aTmp.second = nDiff;
550  aNewWidthVector.push_back(aTmp);
551  }
552  pOuterBox = pOuterBox == pRightBox ? nullptr : pRightBox;
553  if( nDiff2 )
554  nDiff = nDiff2;
555  } while( pOuterBox );
556  }
557  }
558  if( nLeftSpanCnt )
559  --nLeftSpanCnt;
560  if( nRightSpanCnt )
561  --nRightSpanCnt;
562  pRet->push_back(aBoxes);
563  }
564  if( nCheckBottom > nBottom )
565  bOkay = false;
566  if( bOkay )
567  {
568  pRet->mnMergeWidth = nMax - nMin;
569  for (auto const& newWidth : aNewWidthVector)
570  {
571  SwFrameFormat* pFormat = newWidth.first->ClaimFrameFormat();
572  long nNewWidth = pFormat->GetFrameSize().GetWidth() + newWidth.second;
573  pFormat->SetFormatAttr( SwFormatFrameSize( ATT_VAR_SIZE, nNewWidth, 0 ) );
574  }
575  }
576  else
577  pRet.reset();
578 
579  return pRet;
580 }
581 
586 static void lcl_InvalidateCellFrame( const SwTableBox& rBox )
587 {
589  for( SwCellFrame* pCell = aIter.First(); pCell; pCell = aIter.Next() )
590  {
591  if( pCell->GetTabBox() == &rBox )
592  {
593  pCell->InvalidateSize();
594  SwFrame* pLower = pCell->GetLower();
595  if( pLower )
596  pLower->InvalidateSize_();
597  }
598  }
599 }
600 
605 static long lcl_InsertPosition( SwTable &rTable, std::vector<sal_uInt16>& rInsPos,
606  const SwSelBoxes& rBoxes, bool bBehind )
607 {
608  sal_Int32 nAddWidth = 0;
609  long nCount = 0;
610  for (size_t j = 0; j < rBoxes.size(); ++j)
611  {
612  SwTableBox *pBox = rBoxes[j];
613  SwTableLine* pLine = pBox->GetUpper();
614  long nWidth = rBoxes[j]->GetFrameFormat()->GetFrameSize().GetWidth();
615  nAddWidth += nWidth;
616  sal_uInt16 nCurrBox = pLine->GetBoxPos( pBox );
617  sal_uInt16 nCurrLine = rTable.GetTabLines().GetPos( pLine );
618  OSL_ENSURE( nCurrLine != USHRT_MAX, "Time to say Good-Bye.." );
619  if( rInsPos[ nCurrLine ] == USHRT_MAX )
620  {
621  rInsPos[ nCurrLine ] = nCurrBox;
622  ++nCount;
623  }
624  else if( ( rInsPos[ nCurrLine ] > nCurrBox ) == !bBehind )
625  rInsPos[ nCurrLine ] = nCurrBox;
626  }
627  if( nCount )
628  nAddWidth /= nCount;
629  return nAddWidth;
630 }
631 
650 bool SwTable::NewInsertCol( SwDoc* pDoc, const SwSelBoxes& rBoxes,
651  sal_uInt16 nCnt, bool bBehind )
652 {
653  if( m_aLines.empty() || !nCnt )
654  return false;
655 
656  CHECK_TABLE( *this )
657  long nNewBoxWidth = 0;
658  std::vector< sal_uInt16 > aInsPos( m_aLines.size(), USHRT_MAX );
659  { // Calculation of the insert positions and the width of the new boxes
660  sal_uInt64 nTableWidth = 0;
661  for( size_t i = 0; i < m_aLines[0]->GetTabBoxes().size(); ++i )
662  nTableWidth += m_aLines[0]->GetTabBoxes()[i]->GetFrameFormat()->GetFrameSize().GetWidth();
663 
664  // Fill the vector of insert positions and the (average) width to insert
665  sal_uInt64 nAddWidth = lcl_InsertPosition( *this, aInsPos, rBoxes, bBehind );
666 
667  // Given is the (average) width of the selected boxes, if we would
668  // insert nCnt of columns the table would grow
669  // So we will shrink the table first, then insert the new boxes and
670  // get a table with the same width than before.
671  // But we will not shrink the table by the full already calculated value,
672  // we will reduce this value proportional to the old table width
673  nAddWidth *= nCnt; // we have to insert nCnt boxes per line
674  sal_uInt64 nResultingWidth = nAddWidth + nTableWidth;
675  if( !nResultingWidth )
676  return false;
677  nAddWidth = (nAddWidth * nTableWidth) / nResultingWidth;
678  nNewBoxWidth = long( nAddWidth / nCnt ); // Rounding
679  nAddWidth = nNewBoxWidth * nCnt; // Rounding
680  if( !nAddWidth || nAddWidth >= nTableWidth )
681  return false;
682  AdjustWidths( static_cast< long >(nTableWidth), static_cast< long >(nTableWidth - nAddWidth) );
683  }
684 
685  FndBox_ aFndBox( nullptr, nullptr );
686  aFndBox.SetTableLines( rBoxes, *this );
687  aFndBox.DelFrames( *this );
688 
689  SwTableNode* pTableNd = GetTableNode();
690  std::vector<SwTableBoxFormat*> aInsFormat( nCnt, nullptr );
691  size_t nLastLine = SAL_MAX_SIZE;
692  long nLastRowSpan = 1;
693 
694  for( size_t i = 0; i < m_aLines.size(); ++i )
695  {
696  SwTableLine* pLine = m_aLines[ i ];
697  sal_uInt16 nInsPos = aInsPos[i];
698  assert(nInsPos != USHRT_MAX); // didn't find insert position
699  SwTableBox* pBox = pLine->GetTabBoxes()[ nInsPos ];
700  if( bBehind )
701  ++nInsPos;
702  SwTableBoxFormat* pBoxFrameFormat = static_cast<SwTableBoxFormat*>(pBox->GetFrameFormat());
703  ::InsTableBox( pDoc, pTableNd, pLine, pBoxFrameFormat, pBox, nInsPos, nCnt );
704  long nRowSpan = pBox->getRowSpan();
705  long nDiff = i - nLastLine;
706  bool bNewSpan = false;
707  if( nLastLine != SAL_MAX_SIZE && nDiff <= nLastRowSpan &&
708  nRowSpan != nDiff - nLastRowSpan )
709  {
710  bNewSpan = true;
711  while( nLastLine < i )
712  {
713  SwTableLine* pTmpLine = m_aLines[ nLastLine ];
714  sal_uInt16 nTmpPos = aInsPos[nLastLine];
715  if( bBehind )
716  ++nTmpPos;
717  for( sal_uInt16 j = 0; j < nCnt; ++j )
718  pTmpLine->GetTabBoxes()[nTmpPos+j]->setRowSpan( nDiff );
719  if( nDiff > 0 )
720  nDiff = -nDiff;
721  ++nDiff;
722  ++nLastLine;
723  }
724  }
725  if( nRowSpan > 0 )
726  bNewSpan = true;
727  if( bNewSpan )
728  {
729  nLastLine = i;
730  if( nRowSpan < 0 )
731  nLastRowSpan = -nRowSpan;
732  else
733  nLastRowSpan = nRowSpan;
734  }
735  const SvxBoxItem& aSelBoxItem = pBoxFrameFormat->GetBox();
736  std::unique_ptr<SvxBoxItem> pNoRightBorder;
737  if( aSelBoxItem.GetRight() )
738  {
739  pNoRightBorder.reset( new SvxBoxItem( aSelBoxItem ));
740  pNoRightBorder->SetLine( nullptr, SvxBoxItemLine::RIGHT );
741  }
742  for( sal_uInt16 j = 0; j < nCnt; ++j )
743  {
744  SwTableBox *pCurrBox = pLine->GetTabBoxes()[nInsPos+j];
745  if( bNewSpan )
746  {
747  pCurrBox->setRowSpan( nLastRowSpan );
748  SwFrameFormat* pFrameFormat = pCurrBox->ClaimFrameFormat();
749  SwFormatFrameSize aFrameSz( pFrameFormat->GetFrameSize() );
750  aFrameSz.SetWidth( nNewBoxWidth );
751  pFrameFormat->SetFormatAttr( aFrameSz );
752  if( pNoRightBorder && ( !bBehind || j+1 < nCnt ) )
753  pFrameFormat->SetFormatAttr( *pNoRightBorder );
754  aInsFormat[j] = static_cast<SwTableBoxFormat*>(pFrameFormat);
755  }
756  else
757  pCurrBox->ChgFrameFormat( aInsFormat[j] );
758  }
759  if( bBehind && pNoRightBorder )
760  {
761  SwFrameFormat* pFrameFormat = pBox->ClaimFrameFormat();
762  pFrameFormat->SetFormatAttr( *pNoRightBorder );
763  }
764  }
765 
766  aFndBox.MakeFrames( *this );
767 #if OSL_DEBUG_LEVEL > 0
768  {
769  const SwTableBoxes &rTabBoxes = m_aLines[0]->GetTabBoxes();
770  long nNewWidth = 0;
771  for( size_t i = 0; i < rTabBoxes.size(); ++i )
772  nNewWidth += rTabBoxes[i]->GetFrameFormat()->GetFrameSize().GetWidth();
773  OSL_ENSURE( nNewWidth > 0, "Very small" );
774  }
775 #endif
776  CHECK_TABLE( *this )
777 
778  return true;
779 }
780 
806 bool SwTable::PrepareMerge( const SwPaM& rPam, SwSelBoxes& rBoxes,
807  SwSelBoxes& rMerged, SwTableBox** ppMergeBox, SwUndoTableMerge* pUndo )
808 {
809  if( !m_bNewModel )
810  {
811  ::GetMergeSel( rPam, rBoxes, ppMergeBox, pUndo );
812  return rBoxes.size() > 1;
813  }
814  CHECK_TABLE( *this )
815  // We have to assert a "rectangular" box selection before we start to merge
816  std::unique_ptr< SwBoxSelection > pSel( CollectBoxSelection( rPam ) );
817  if (!pSel || pSel->isEmpty())
818  return false;
819  // Now we should have a rectangle of boxes,
820  // i.e. contiguous cells in contiguous rows
821  bool bMerge = false; // will be set if any content is transferred from
822  // a "not already overlapped" cell into the new master cell.
823  const SwSelBoxes& rFirstBoxes = pSel->maBoxes[0];
824  if (rFirstBoxes.empty())
825  return false;
826  SwTableBox *pMergeBox = rFirstBoxes[0]; // the master cell box
827  if( !pMergeBox )
828  return false;
829  (*ppMergeBox) = pMergeBox;
830  // The new master box will get the left and the top border of the top-left
831  // box of the selection and because the new master cell _is_ the top-left
832  // box, the left and right border does not need to be changed.
833  // The right and bottom border instead has to be derived from the right-
834  // bottom box of the selection. If this is a overlapped cell,
835  // the appropriate master box.
836  SwTableBox* pLastBox = nullptr; // the right-bottom (master) cell
837  SwDoc* pDoc = GetFrameFormat()->GetDoc();
838  SwPosition aInsPos( *pMergeBox->GetSttNd()->EndOfSectionNode() );
839  SwPaM aChkPam( aInsPos );
840  // The number of lines in the selection rectangle: nLineCount
841  const size_t nLineCount = pSel->maBoxes.size();
842  // BTW: nLineCount is the rowspan of the new master cell
843  long nRowSpan = static_cast<long>(nLineCount);
844  // We will need the first and last line of the selection
845  // to check if there any superfluous row after merging
846  SwTableLine* pFirstLn = nullptr;
847  SwTableLine* pLastLn = nullptr;
848  // Iteration over the lines of the selection...
849  for( size_t nCurrLine = 0; nCurrLine < nLineCount; ++nCurrLine )
850  {
851  // The selected boxes in the current line
852  const SwSelBoxes& rLineBoxes = pSel->maBoxes[nCurrLine];
853  size_t nColCount = rLineBoxes.size();
854  // Iteration over the selected cell in the current row
855  for (size_t nCurrCol = 0; nCurrCol < nColCount; ++nCurrCol)
856  {
857  SwTableBox* pBox = rLineBoxes[nCurrCol];
858  rMerged.insert( pBox );
859  // Only the first selected cell in every row will be alive,
860  // the other will be deleted => put into rBoxes
861  if( nCurrCol )
862  rBoxes.insert( pBox );
863  else
864  {
865  if( nCurrLine == 1 )
866  pFirstLn = pBox->GetUpper(); // we need this line later on
867  if( nCurrLine + 1 == nLineCount )
868  pLastLn = pBox->GetUpper(); // and this one, too.
869  }
870  // A box has to be merged if it's not the master box itself,
871  // but an already overlapped cell must not be merged as well.
872  bool bDoMerge = pBox != pMergeBox && pBox->getRowSpan() > 0;
873  // The last box has to be in the last "column" of the selection
874  // and it has to be a master cell
875  if( nCurrCol+1 == nColCount && pBox->getRowSpan() > 0 )
876  pLastBox = pBox;
877  if( bDoMerge )
878  {
879  bMerge = true;
880  // If the cell to merge contains only one empty paragraph,
881  // we do not transfer this paragraph.
882  if( !IsEmptyBox( *pBox, aChkPam ) )
883  {
884  SwNodeIndex& rInsPosNd = aInsPos.nNode;
885  SwPaM aPam( aInsPos );
886  aPam.GetPoint()->nNode.Assign( *pBox->GetSttNd()->EndOfSectionNode(), -1 );
887  SwContentNode* pCNd = aPam.GetContentNode();
888  aPam.GetPoint()->nContent.Assign( pCNd, pCNd ? pCNd->Len() : 0 );
889  SwNodeIndex aSttNdIdx( *pBox->GetSttNd(), 1 );
890  bool const bUndo = pDoc->GetIDocumentUndoRedo().DoesUndo();
891  if( pUndo )
892  {
893  pDoc->GetIDocumentUndoRedo().DoUndo(false);
894  }
896  if( pUndo )
897  {
898  pDoc->GetIDocumentUndoRedo().DoUndo(bUndo);
899  }
900  SwNodeRange aRg( aSttNdIdx, aPam.GetPoint()->nNode );
901  if( pUndo )
902  pUndo->MoveBoxContent( pDoc, aRg, rInsPosNd );
903  else
904  {
905  pDoc->getIDocumentContentOperations().MoveNodeRange( aRg, rInsPosNd,
907  }
908  }
909  }
910  // Only the cell of the first selected column will stay alive
911  // and got a new row span
912  if( !nCurrCol )
913  pBox->setRowSpan( nRowSpan );
914  }
915  if( nRowSpan > 0 ) // the master cell is done, from now on we set
916  nRowSpan = -nRowSpan; // negative row spans
917  ++nRowSpan; // ... -3, -2, -1
918  }
919  if( bMerge )
920  {
921  // A row containing overlapped cells is superfluous,
922  // these cells can be put into rBoxes for deletion
923  FindSuperfluousRows_( rBoxes, pFirstLn, pLastLn );
924  // pNewFormat will be set to the new master box and the overlapped cells
925  SwFrameFormat* pNewFormat = pMergeBox->ClaimFrameFormat();
926  pNewFormat->SetFormatAttr( SwFormatFrameSize( ATT_VAR_SIZE, pSel->mnMergeWidth, 0 ) );
927  for( size_t nCurrLine = 0; nCurrLine < nLineCount; ++nCurrLine )
928  {
929  const SwSelBoxes& rLineBoxes = pSel->maBoxes[nCurrLine];
930  size_t nColCount = rLineBoxes.size();
931  for (size_t nCurrCol = 0; nCurrCol < nColCount; ++nCurrCol)
932  {
933  SwTableBox* pBox = rLineBoxes[nCurrCol];
934  if( nCurrCol )
935  {
936  // Even this box will be deleted soon,
937  // we have to correct the width to avoid side effects
938  SwFrameFormat* pFormat = pBox->ClaimFrameFormat();
939  pFormat->SetFormatAttr( SwFormatFrameSize( ATT_VAR_SIZE, 0, 0 ) );
940  }
941  else
942  {
943  pBox->ChgFrameFormat( static_cast<SwTableBoxFormat*>(pNewFormat) );
944  // remove numbering from cells that will be disabled in the merge
945  if( nCurrLine )
946  {
947  SwPaM aPam( *pBox->GetSttNd(), 0 );
948  aPam.GetPoint()->nNode++;
949  SwTextNode* pNd = aPam.GetNode().GetTextNode();
950  while( pNd )
951  {
952  pNd->SetCountedInList( false );
953 
954  aPam.GetPoint()->nNode++;
955  pNd = aPam.GetNode().GetTextNode();
956  }
957  }
958  }
959  }
960  }
961  if( pLastBox ) // Robust
962  {
963  // The new borders of the master cell...
964  SvxBoxItem aBox( pMergeBox->GetFrameFormat()->GetBox() );
965  bool bOld = aBox.GetRight() || aBox.GetBottom();
966  const SvxBoxItem& rBox = pLastBox->GetFrameFormat()->GetBox();
967  aBox.SetLine( rBox.GetRight(), SvxBoxItemLine::RIGHT );
968  aBox.SetLine( rBox.GetBottom(), SvxBoxItemLine::BOTTOM );
969  if( bOld || aBox.GetLeft() || aBox.GetTop() || aBox.GetRight() || aBox.GetBottom() )
970  (*ppMergeBox)->GetFrameFormat()->SetFormatAttr( aBox );
971  }
972 
973  if( pUndo )
974  pUndo->AddNewBox( pMergeBox->GetSttIdx() );
975  }
976  return bMerge;
977 }
978 
984  SwTableLine* pFirstLn, SwTableLine* pLastLn )
985 {
986  if( !pFirstLn || !pLastLn )
987  {
988  if( rBoxes.empty() )
989  return;
990  pFirstLn = rBoxes[0]->GetUpper();
991  pLastLn = rBoxes.back()->GetUpper();
992  }
993  sal_uInt16 nFirstLn = GetTabLines().GetPos( pFirstLn );
994  sal_uInt16 nLastLn = GetTabLines().GetPos( pLastLn );
995  for( sal_uInt16 nRow = nFirstLn; nRow <= nLastLn; ++nRow )
996  {
997  SwTableLine* pLine = m_aLines[nRow];
998  OSL_ENSURE( pLine, "Missing table line" );
999  const size_t nCols = pLine->GetTabBoxes().size();
1000  bool bSuperfl = true;
1001  for( size_t nCol = 0; nCol < nCols; ++nCol )
1002  {
1003  SwTableBox *pBox = pLine->GetTabBoxes()[nCol];
1004  if( pBox->getRowSpan() > 0 &&
1005  rBoxes.end() == rBoxes.find( pBox ) )
1006  {
1007  bSuperfl = false;
1008  break;
1009  }
1010  }
1011  if( bSuperfl )
1012  {
1013  for( size_t nCol = 0; nCol < nCols; ++nCol )
1014  {
1015  SwTableBox* pBox = pLine->GetTabBoxes()[nCol];
1016  rBoxes.insert( pBox );
1017  }
1018  }
1019  }
1020 }
1021 
1026 SwTableBox& SwTableBox::FindStartOfRowSpan( const SwTable& rTable, sal_uInt16 nMaxStep )
1027 {
1028  if( getRowSpan() > 0 || !nMaxStep )
1029  return *this;
1030 
1031  long nLeftBorder = lcl_Box2LeftBorder( *this );
1032  SwTableBox* pBox = this;
1033  const SwTableLine* pMyUpper = GetUpper();
1034  sal_uInt16 nLine = rTable.GetTabLines().GetPos( pMyUpper );
1035  if( nLine && nLine < rTable.GetTabLines().size() )
1036  {
1037  SwTableBox* pNext;
1038  do
1039  {
1040  pNext = lcl_LeftBorder2Box( nLeftBorder, rTable.GetTabLines()[--nLine] );
1041  if( pNext )
1042  pBox = pNext;
1043  } while( nLine && --nMaxStep && pNext && pBox->getRowSpan() < 1 );
1044  }
1045 
1046  return *pBox;
1047 }
1048 
1053 SwTableBox& SwTableBox::FindEndOfRowSpan( const SwTable& rTable, sal_uInt16 nMaxStep )
1054 {
1055  long nAbsSpan = getRowSpan();
1056  if( nAbsSpan < 0 )
1057  nAbsSpan = -nAbsSpan;
1058  if( nAbsSpan == 1 || !nMaxStep )
1059  return *this;
1060 
1061  if( nMaxStep > --nAbsSpan )
1062  nMaxStep = static_cast<sal_uInt16>(nAbsSpan);
1063  const SwTableLine* pMyUpper = GetUpper();
1064  sal_uInt16 nLine = rTable.GetTabLines().GetPos( pMyUpper );
1065  nMaxStep = nLine + nMaxStep;
1066  if( nMaxStep >= rTable.GetTabLines().size() )
1067  nMaxStep = rTable.GetTabLines().size() - 1;
1068  long nLeftBorder = lcl_Box2LeftBorder( *this );
1069  SwTableBox* pBox =
1070  lcl_LeftBorder2Box( nLeftBorder, rTable.GetTabLines()[ nMaxStep ] );
1071  if ( !pBox )
1072  pBox = this;
1073 
1074  return *pBox;
1075 }
1076 
1080 static void lcl_getAllMergedBoxes( const SwTable& rTable, SwSelBoxes& rBoxes, SwTableBox& rBox )
1081 {
1082  SwTableBox* pBox = &rBox;
1083  OSL_ENSURE( pBox == &rBox.FindStartOfRowSpan( rTable ), "Not a master box" );
1084  rBoxes.insert( pBox );
1085  if( pBox->getRowSpan() == 1 )
1086  return;
1087  const SwTableLine* pMyUpper = pBox->GetUpper();
1088  sal_uInt16 nLine = rTable.GetTabLines().GetPos( pMyUpper );
1089  long nLeftBorder = lcl_Box2LeftBorder( *pBox );
1090  sal_uInt16 nCount = rTable.GetTabLines().size();
1091  while( ++nLine < nCount && pBox && pBox->getRowSpan() != -1 )
1092  {
1093  pBox = lcl_LeftBorder2Box( nLeftBorder, rTable.GetTabLines()[nLine] );
1094  if( pBox )
1095  rBoxes.insert( pBox );
1096  }
1097 }
1098 
1103 static void lcl_UnMerge( const SwTable& rTable, SwTableBox& rBox, size_t nCnt,
1104  bool bSameHeight )
1105 {
1106  SwSelBoxes aBoxes;
1107  lcl_getAllMergedBoxes( rTable, aBoxes, rBox );
1108  size_t const nCount = aBoxes.size();
1109  if( nCount < 2 )
1110  return;
1111  if( nCnt > nCount )
1112  nCnt = nCount;
1113  std::unique_ptr<size_t[]> const pSplitIdx(new size_t[nCnt]);
1114  if( bSameHeight )
1115  {
1116  std::unique_ptr<SwTwips[]> const pHeights(new SwTwips[nCount]);
1117  SwTwips nHeight = 0;
1118  for (size_t i = 0; i < nCount; ++i)
1119  {
1120  SwTableLine* pLine = aBoxes[ i ]->GetUpper();
1121  SwFrameFormat *pRowFormat = pLine->GetFrameFormat();
1122  pHeights[ i ] = pRowFormat->GetFrameSize().GetHeight();
1123  nHeight += pHeights[ i ];
1124  }
1125  SwTwips nSumH = 0;
1126  size_t nIdx = 0;
1127  for (size_t i = 1; i <= nCnt; ++i)
1128  {
1129  SwTwips nSplit = ( i * nHeight ) / nCnt;
1130  while( nSumH < nSplit && nIdx < nCount )
1131  nSumH += pHeights[ nIdx++ ];
1132  pSplitIdx[ i - 1 ] = nIdx;
1133  }
1134  }
1135  else
1136  {
1137  for (size_t i = 1; i <= nCnt; ++i)
1138  {
1139  pSplitIdx[ i - 1 ] = ( i * nCount ) / nCnt;
1140  }
1141  }
1142  size_t nIdx = 0;
1143  for (size_t i = 0; i < nCnt; ++i)
1144  {
1145  size_t nNextIdx = pSplitIdx[ i ];
1146  aBoxes[ nIdx ]->setRowSpan( nNextIdx - nIdx );
1147  lcl_InvalidateCellFrame( *aBoxes[ nIdx ] );
1148  while( ++nIdx < nNextIdx )
1149  aBoxes[ nIdx ]->setRowSpan( nIdx - nNextIdx );
1150  }
1151 }
1152 
1156 static void lcl_FillSelBoxes( SwSelBoxes &rBoxes, SwTableLine &rLine )
1157 {
1158  const size_t nBoxCount = rLine.GetTabBoxes().size();
1159  for( size_t i = 0; i < nBoxCount; ++i )
1160  rBoxes.insert( rLine.GetTabBoxes()[i] );
1161 }
1162 
1167 void SwTable::InsertSpannedRow( SwDoc* pDoc, sal_uInt16 nRowIdx, sal_uInt16 nCnt )
1168 {
1169  CHECK_TABLE( *this )
1170  OSL_ENSURE( nCnt && nRowIdx < GetTabLines().size(), "Wrong call of InsertSpannedRow" );
1171  SwSelBoxes aBoxes;
1172  SwTableLine& rLine = *GetTabLines()[ nRowIdx ];
1173  lcl_FillSelBoxes( aBoxes, rLine );
1174  SwFormatFrameSize aFSz( rLine.GetFrameFormat()->GetFrameSize() );
1175  if( ATT_VAR_SIZE != aFSz.GetHeightSizeType() )
1176  {
1177  SwFrameFormat* pFrameFormat = rLine.ClaimFrameFormat();
1178  long nNewHeight = aFSz.GetHeight() / ( nCnt + 1 );
1179  if( !nNewHeight )
1180  ++nNewHeight;
1181  aFSz.SetHeight( nNewHeight );
1182  pFrameFormat->SetFormatAttr( aFSz );
1183  }
1184  InsertRow_( pDoc, aBoxes, nCnt, true );
1185  const size_t nBoxCount = rLine.GetTabBoxes().size();
1186  for( sal_uInt16 n = 0; n < nCnt; ++n )
1187  {
1188  SwTableLine *pNewLine = GetTabLines()[ nRowIdx + nCnt - n ];
1189  for( size_t nCurrBox = 0; nCurrBox < nBoxCount; ++nCurrBox )
1190  {
1191  long nRowSpan = rLine.GetTabBoxes()[nCurrBox]->getRowSpan();
1192  if( nRowSpan > 0 )
1193  nRowSpan = - nRowSpan;
1194  pNewLine->GetTabBoxes()[ nCurrBox ]->setRowSpan( nRowSpan - n );
1195  }
1196  }
1197  lcl_ChangeRowSpan( *this, nCnt, nRowIdx, false );
1198  CHECK_TABLE( *this )
1199 }
1200 
1201 typedef std::pair< sal_uInt16, sal_uInt16 > SwLineOffset;
1202 typedef std::vector< SwLineOffset > SwLineOffsetArray;
1203 
1204 /*
1205 * When a couple of table boxes has to be split,
1206 * lcl_SophisticatedFillLineIndices delivers the information where and how many
1207 * rows have to be inserted.
1208 * Input
1209 * rTable: the table to manipulate
1210 * rBoxes: an array of boxes to split
1211 * nCnt: how many parts are wanted
1212 * Output
1213 * rArr: a list of pairs ( line index, number of lines to insert )
1214 */
1216  const SwTable& rTable, const SwSelBoxes& rBoxes, sal_uInt16 nCnt )
1217 {
1218  std::list< SwLineOffset > aBoxes;
1219  SwLineOffset aLnOfs( USHRT_MAX, USHRT_MAX );
1220  for (size_t i = 0; i < rBoxes.size(); ++i)
1221  { // Collect all end line indices and the row spans
1222  const SwTableBox &rBox = rBoxes[ i ]->FindStartOfRowSpan( rTable );
1223  OSL_ENSURE( rBox.getRowSpan() > 0, "Didn't I say 'StartOfRowSpan' ??" );
1224  if( nCnt > rBox.getRowSpan() )
1225  {
1226  const SwTableLine *pLine = rBox.GetUpper();
1227  const sal_uInt16 nEnd = sal_uInt16( rBox.getRowSpan() +
1228  rTable.GetTabLines().GetPos( pLine ) );
1229  // The next if statement is a small optimization
1230  if( aLnOfs.first != nEnd || aLnOfs.second != rBox.getRowSpan() )
1231  {
1232  aLnOfs.first = nEnd; // ok, this is the line behind the box
1233  aLnOfs.second = sal_uInt16( rBox.getRowSpan() ); // the row span
1234  aBoxes.insert( aBoxes.end(), aLnOfs );
1235  }
1236  }
1237  }
1238  // As I said, I noted the line index _behind_ the last line of the boxes
1239  // in the resulting array the index has to be _on_ the line
1240  // nSum is to evaluate the wished value
1241  sal_uInt16 nSum = 1;
1242  while( !aBoxes.empty() )
1243  {
1244  // I. step:
1245  // Looking for the "smallest" line end with the smallest row span
1246  std::list< SwLineOffset >::iterator pCurr = aBoxes.begin();
1247  aLnOfs = *pCurr; // the line end and row span of the first box
1248  while( ++pCurr != aBoxes.end() )
1249  {
1250  if( aLnOfs.first > pCurr->first )
1251  { // Found a smaller line end
1252  aLnOfs.first = pCurr->first;
1253  aLnOfs.second = pCurr->second; // row span
1254  }
1255  else if( aLnOfs.first == pCurr->first &&
1256  aLnOfs.second < pCurr->second )
1257  aLnOfs.second = pCurr->second; // Found a smaller row span
1258  }
1259  OSL_ENSURE( aLnOfs.second < nCnt, "Clean-up failed" );
1260  aLnOfs.second = nCnt - aLnOfs.second; // the number of rows to insert
1261  rArr.emplace_back( aLnOfs.first - nSum, aLnOfs.second );
1262  // the correction has to be incremented because in the following
1263  // loops the line ends were manipulated
1264  nSum = nSum + aLnOfs.second;
1265 
1266  pCurr = aBoxes.begin();
1267  while( pCurr != aBoxes.end() )
1268  {
1269  if( pCurr->first == aLnOfs.first )
1270  { // These boxes can be removed because the last insertion
1271  // of rows will expand their row span above the needed value
1272  pCurr = aBoxes.erase(pCurr);
1273  }
1274  else
1275  {
1276  bool bBefore = ( pCurr->first - pCurr->second < aLnOfs.first );
1277  // Manipulation of the end line indices as if the rows are
1278  // already inserted
1279  pCurr->first = pCurr->first + aLnOfs.second;
1280  if( bBefore )
1281  { // If the insertion is inside the box,
1282  // its row span has to be incremented
1283  pCurr->second = pCurr->second + aLnOfs.second;
1284  if( pCurr->second >= nCnt )
1285  { // if the row span is bigger than the split factor
1286  // this box is done
1287  pCurr = aBoxes.erase(pCurr);
1288  }
1289  else
1290  ++pCurr;
1291  }
1292  else
1293  ++pCurr;
1294  }
1295  }
1296  }
1297 }
1298 
1299 typedef std::set< SwTwips > SwSplitLines;
1300 
1306  const SwTable& rTable, const SwSelBoxes& rBoxes, sal_uInt16 nCnt )
1307 {
1308  if( nCnt < 2 )
1309  return 0;
1310  std::vector< SwLineOffset > aBoxes;
1311  SwLineOffset aLnOfs( USHRT_MAX, USHRT_MAX );
1312  sal_uInt16 nFirst = USHRT_MAX; // becomes the index of the first line
1313  sal_uInt16 nLast = 0; // becomes the index of the last line of the splitting
1314  for (size_t i = 0; i < rBoxes.size(); ++i)
1315  { // Collect all pairs (start+end) of line indices to split
1316  const SwTableBox &rBox = rBoxes[ i ]->FindStartOfRowSpan( rTable );
1317  OSL_ENSURE( rBox.getRowSpan() > 0, "Didn't I say 'StartOfRowSpan' ??" );
1318  const SwTableLine *pLine = rBox.GetUpper();
1319  const sal_uInt16 nStart = rTable.GetTabLines().GetPos( pLine );
1320  const sal_uInt16 nEnd = sal_uInt16( rBox.getRowSpan() + nStart - 1 );
1321  // The next if statement is a small optimization
1322  if( aLnOfs.first != nStart || aLnOfs.second != nEnd )
1323  {
1324  aLnOfs.first = nStart;
1325  aLnOfs.second = nEnd;
1326  aBoxes.push_back( aLnOfs );
1327  if( nStart < nFirst )
1328  nFirst = nStart;
1329  if( nEnd > nLast )
1330  nLast = nEnd;
1331  }
1332  }
1333 
1334  if (nFirst == USHRT_MAX)
1335  {
1336  assert(aBoxes.empty());
1337  return 0;
1338  }
1339 
1340  SwTwips nHeight = 0;
1341  std::unique_ptr<SwTwips[]> pLines(new SwTwips[ nLast + 1 - nFirst ]);
1342  for( sal_uInt16 i = nFirst; i <= nLast; ++i )
1343  {
1344  bool bLayoutAvailable = false;
1345  nHeight += rTable.GetTabLines()[ i ]->GetTableLineHeight( bLayoutAvailable );
1346  rCurr.insert( rCurr.end(), nHeight );
1347  pLines[ i - nFirst ] = nHeight;
1348  }
1349  for( const auto& rSplit : aBoxes )
1350  {
1351  SwTwips nBase = rSplit.first <= nFirst ? 0 :
1352  pLines[ rSplit.first - nFirst - 1 ];
1353  SwTwips nDiff = pLines[ rSplit.second - nFirst ] - nBase;
1354  for( sal_uInt16 i = 1; i < nCnt; ++i )
1355  {
1356  SwTwips nSplit = nBase + ( i * nDiff ) / nCnt;
1357  rNew.insert( nSplit );
1358  }
1359  }
1360  return nFirst;
1361 }
1362 
1367 static sal_uInt16 lcl_LineIndex( const SwTable& rTable, const SwSelBoxes& rBoxes,
1368  bool bBehind )
1369 {
1370  sal_uInt16 nDirect = USHRT_MAX;
1371  sal_uInt16 nSpan = USHRT_MAX;
1372  for (size_t i = 0; i < rBoxes.size(); ++i)
1373  {
1374  SwTableBox *pBox = rBoxes[i];
1375  const SwTableLine* pLine = rBoxes[i]->GetUpper();
1376  sal_uInt16 nPos = rTable.GetTabLines().GetPos( pLine );
1377  if( USHRT_MAX != nPos )
1378  {
1379  if( bBehind )
1380  {
1381  if( nPos > nDirect || nDirect == USHRT_MAX )
1382  nDirect = nPos;
1383  long nRowSpan = pBox->getRowSpan();
1384  if( nRowSpan < 2 )
1385  nSpan = 0;
1386  else if( nSpan )
1387  {
1388  sal_uInt16 nEndOfRowSpan = static_cast<sal_uInt16>(nPos + nRowSpan - 1);
1389  if( nEndOfRowSpan > nSpan || nSpan == USHRT_MAX )
1390  nSpan = nEndOfRowSpan;
1391  }
1392  }
1393  else if( nPos < nDirect )
1394  nDirect = nPos;
1395  }
1396  }
1397  if( nSpan && nSpan < USHRT_MAX )
1398  return nSpan;
1399  return nDirect;
1400 }
1401 
1405 bool SwTable::NewSplitRow( SwDoc* pDoc, const SwSelBoxes& rBoxes, sal_uInt16 nCnt,
1406  bool bSameHeight )
1407 {
1408  CHECK_TABLE( *this )
1409  ++nCnt;
1410  FndBox_ aFndBox( nullptr, nullptr );
1411  aFndBox.SetTableLines( rBoxes, *this );
1412 
1413  if( bSameHeight && pDoc->getIDocumentLayoutAccess().GetCurrentViewShell() )
1414  {
1415  SwSplitLines aRowLines;
1416  SwSplitLines aSplitLines;
1417  sal_uInt16 nFirst = lcl_CalculateSplitLineHeights( aRowLines, aSplitLines,
1418  *this, rBoxes, nCnt );
1419  aFndBox.DelFrames( *this );
1420  SwTwips nLast = 0;
1421  SwSplitLines::iterator pSplit = aSplitLines.begin();
1422  for( const auto& rCurr : aRowLines )
1423  {
1424  while( pSplit != aSplitLines.end() && *pSplit < rCurr )
1425  {
1426  InsertSpannedRow( pDoc, nFirst, 1 );
1427  SwTableLine* pRow = GetTabLines()[ nFirst ];
1428  SwFrameFormat* pRowFormat = pRow->ClaimFrameFormat();
1429  SwFormatFrameSize aFSz( pRowFormat->GetFrameSize() );
1431  aFSz.SetHeight( *pSplit - nLast );
1432  pRowFormat->SetFormatAttr( aFSz );
1433  nLast = *pSplit;
1434  ++pSplit;
1435  ++nFirst;
1436  }
1437  if( pSplit != aSplitLines.end() && rCurr == *pSplit )
1438  ++pSplit;
1439  SwTableLine* pRow = GetTabLines()[ nFirst ];
1440  SwFrameFormat* pRowFormat = pRow->ClaimFrameFormat();
1441  SwFormatFrameSize aFSz( pRowFormat->GetFrameSize() );
1443  aFSz.SetHeight( rCurr - nLast );
1444  pRowFormat->SetFormatAttr( aFSz );
1445  nLast = rCurr;
1446  ++nFirst;
1447  }
1448  }
1449  else
1450  {
1451  aFndBox.DelFrames( *this );
1452  bSameHeight = false;
1453  }
1454  if( !bSameHeight )
1455  {
1456  SwLineOffsetArray aLineOffs;
1457  lcl_SophisticatedFillLineIndices( aLineOffs, *this, rBoxes, nCnt );
1458  SwLineOffsetArray::reverse_iterator pCurr( aLineOffs.rbegin() );
1459  while( pCurr != aLineOffs.rend() )
1460  {
1461  InsertSpannedRow( pDoc, pCurr->first, pCurr->second );
1462  ++pCurr;
1463  }
1464  }
1465 
1466  std::set<size_t> aIndices;
1467  for (size_t i = 0; i < rBoxes.size(); ++i)
1468  {
1469  OSL_ENSURE( rBoxes[i]->getRowSpan() != 1, "Forgot to split?" );
1470  if( rBoxes[i]->getRowSpan() > 1 )
1471  aIndices.insert( i );
1472  }
1473 
1474  for( const auto& rCurrBox : aIndices )
1475  lcl_UnMerge( *this, *rBoxes[rCurrBox], nCnt, bSameHeight );
1476 
1477  CHECK_TABLE( *this )
1478  // update the layout
1479  aFndBox.MakeFrames( *this );
1480 
1481  return true;
1482 }
1483 
1488 bool SwTable::InsertRow( SwDoc* pDoc, const SwSelBoxes& rBoxes,
1489  sal_uInt16 nCnt, bool bBehind )
1490 {
1491  bool bRet = false;
1492  if( IsNewModel() )
1493  {
1494  CHECK_TABLE( *this )
1495  sal_uInt16 nRowIdx = lcl_LineIndex( *this, rBoxes, bBehind );
1496  if( nRowIdx < USHRT_MAX )
1497  {
1498  FndBox_ aFndBox( nullptr, nullptr );
1499  aFndBox.SetTableLines( rBoxes, *this );
1500  aFndBox.DelFrames( *this );
1501 
1502  bRet = true;
1503  SwTableLine *pLine = GetTabLines()[ nRowIdx ];
1504  SwSelBoxes aLineBoxes;
1505  lcl_FillSelBoxes( aLineBoxes, *pLine );
1506  InsertRow_( pDoc, aLineBoxes, nCnt, bBehind );
1507  const size_t nBoxCount = pLine->GetTabBoxes().size();
1508  sal_uInt16 nOfs = bBehind ? 0 : 1;
1509  for( sal_uInt16 n = 0; n < nCnt; ++n )
1510  {
1511  SwTableLine *pNewLine = GetTabLines()[ nRowIdx+nCnt-n-nOfs];
1512  for( size_t nCurrBox = 0; nCurrBox < nBoxCount; ++nCurrBox )
1513  {
1514  long nRowSpan = pLine->GetTabBoxes()[nCurrBox]->getRowSpan();
1515  if( bBehind )
1516  {
1517  if( nRowSpan == 1 || nRowSpan == -1 )
1518  nRowSpan = n + 1;
1519  else if( nRowSpan > 1 )
1520  nRowSpan = - nRowSpan;
1521  }
1522  else
1523  {
1524  if( nRowSpan > 0 )
1525  nRowSpan = n + 1;
1526  else
1527  --nRowSpan;
1528  }
1529  pNewLine->GetTabBoxes()[ nCurrBox ]->setRowSpan( nRowSpan - n );
1530  }
1531  }
1532  if( bBehind )
1533  ++nRowIdx;
1534  if( nRowIdx )
1535  lcl_ChangeRowSpan( *this, nCnt, --nRowIdx, true );
1536  // update the layout
1537  aFndBox.MakeFrames( *this );
1538  }
1539  CHECK_TABLE( *this )
1540  }
1541  else
1542  bRet = InsertRow_( pDoc, rBoxes, nCnt, bBehind );
1543  return bRet;
1544 }
1545 
1551 {
1552  if( IsNewModel() )
1553  {
1554  for (size_t i = 0; i < rBoxes.size(); ++i)
1555  {
1556  SwTableBox* pBox = rBoxes[i];
1557  long nRowSpan = pBox->getRowSpan();
1558  if( nRowSpan != 1 && pBox->GetFrameFormat()->GetFrameSize().GetWidth() )
1559  {
1560  long nLeft = lcl_Box2LeftBorder( *pBox );
1561  SwTableLine *pLine = pBox->GetUpper();
1562  sal_uInt16 nLinePos = GetTabLines().GetPos( pLine);
1563  OSL_ENSURE( nLinePos < USHRT_MAX, "Box/table mismatch" );
1564  if( nRowSpan > 1 )
1565  {
1566  if( ++nLinePos < GetTabLines().size() )
1567  {
1568  pLine = GetTabLines()[ nLinePos ];
1569  pBox = lcl_LeftBorder2Box( nLeft, pLine );
1570  OSL_ENSURE( pBox, "RowSpan irritation I" );
1571  if( pBox )
1572  pBox->setRowSpan( --nRowSpan );
1573  }
1574  }
1575  else if( nLinePos > 0 )
1576  {
1577  do
1578  {
1579  pLine = GetTabLines()[ --nLinePos ];
1580  pBox = lcl_LeftBorder2Box( nLeft, pLine );
1581  OSL_ENSURE( pBox, "RowSpan irritation II" );
1582  if( pBox )
1583  {
1584  nRowSpan = pBox->getRowSpan();
1585  if( nRowSpan > 1 )
1586  {
1587  lcl_InvalidateCellFrame( *pBox );
1588  --nRowSpan;
1589  }
1590  else
1591  ++nRowSpan;
1592  pBox->setRowSpan( nRowSpan );
1593  }
1594  else
1595  nRowSpan = 1;
1596  }
1597  while( nRowSpan < 0 && nLinePos > 0 );
1598  }
1599  }
1600  }
1601  }
1602 }
1603 
1608 static void lcl_SearchSelBox( const SwTable &rTable, SwSelBoxes& rBoxes, long nMin, long nMax,
1609  SwTableLine& rLine, bool bChkProtected, bool bColumn )
1610 {
1611  long nLeft = 0;
1612  long nRight = 0;
1613  long nMid = ( nMax + nMin )/ 2;
1614  const size_t nCount = rLine.GetTabBoxes().size();
1615  for( size_t nCurrBox = 0; nCurrBox < nCount; ++nCurrBox )
1616  {
1617  SwTableBox* pBox = rLine.GetTabBoxes()[nCurrBox];
1618  OSL_ENSURE( pBox, "Missing table box" );
1619  long nWidth = pBox->GetFrameFormat()->GetFrameSize().GetWidth();
1620  nRight += nWidth;
1621  if( nRight > nMin )
1622  {
1623  bool bAdd = false;
1624  if( nRight <= nMax )
1625  bAdd = nLeft >= nMin || nRight >= nMid ||
1626  nRight - nMin > nMin - nLeft;
1627  else
1628  bAdd = nLeft <= nMid || nRight - nMax < nMax - nLeft;
1629  long nRowSpan = pBox->getRowSpan();
1630  if( bAdd &&
1631  ( !bChkProtected ||
1632  !pBox->GetFrameFormat()->GetProtect().IsContentProtected() ) )
1633  {
1634  size_t const nOldCnt = rBoxes.size();
1635  rBoxes.insert( pBox );
1636  if( bColumn && nRowSpan != 1 && nOldCnt < rBoxes.size() )
1637  {
1638  SwTableBox *pMasterBox = pBox->getRowSpan() > 0 ? pBox
1639  : &pBox->FindStartOfRowSpan( rTable );
1640  lcl_getAllMergedBoxes( rTable, rBoxes, *pMasterBox );
1641  }
1642  }
1643  }
1644  if( nRight >= nMax )
1645  break;
1646  nLeft = nRight;
1647  }
1648 }
1649 
1654 void SwTable::CreateSelection( const SwPaM& rPam, SwSelBoxes& rBoxes,
1655  const SearchType eSearch, bool bChkProtected ) const
1656 {
1657  OSL_ENSURE( m_bNewModel, "Don't call me for old tables" );
1658  if( m_aLines.empty() )
1659  return;
1660  const SwNode* pStartNd = rPam.GetPoint()->nNode.GetNode().FindTableBoxStartNode();
1661  const SwNode* pEndNd = rPam.GetMark()->nNode.GetNode().FindTableBoxStartNode();
1662  if( !pStartNd || !pEndNd )
1663  return;
1664  CreateSelection( pStartNd, pEndNd, rBoxes, eSearch, bChkProtected );
1665 }
1666 
1670 void SwTable::CreateSelection( const SwNode* pStartNd, const SwNode* pEndNd,
1671  SwSelBoxes& rBoxes, const SearchType eSearch, bool bChkProtected ) const
1672 {
1673  rBoxes.clear();
1674  // Looking for start and end of the selection given by SwNode-pointer
1675  const size_t nLines = m_aLines.size();
1676  // nTop becomes the line number of the upper box
1677  // nBottom becomes the line number of the lower box
1678  size_t nTop = 0;
1679  size_t nBottom = 0;
1680  // nUpperMin becomes the left border value of the upper box
1681  // nUpperMax becomes the right border of the upper box
1682  // nLowerMin and nLowerMax the borders of the lower box
1683  long nUpperMin = 0, nUpperMax = 0;
1684  long nLowerMin = 0, nLowerMax = 0;
1685  // nFound will incremented if a box is found
1686  // 0 => no box found; 1 => the upper box has been found; 2 => both found
1687  int nFound = 0;
1688  for( size_t nRow = 0; nFound < 2 && nRow < nLines; ++nRow )
1689  {
1690  SwTableLine* pLine = m_aLines[nRow];
1691  OSL_ENSURE( pLine, "Missing table line" );
1692  const size_t nCols = pLine->GetTabBoxes().size();
1693  for( size_t nCol = 0; nCol < nCols; ++nCol )
1694  {
1695  SwTableBox* pBox = pLine->GetTabBoxes()[nCol];
1696  OSL_ENSURE( pBox, "Missing table box" );
1697  if( pBox->GetSttNd() == pEndNd || pBox->GetSttNd() == pStartNd )
1698  {
1699  if( !bChkProtected ||
1701  rBoxes.insert( pBox );
1702  if( nFound )
1703  {
1704  nBottom = nRow;
1705  lcl_CheckMinMax( nLowerMin, nLowerMax, *pLine, nCol, true );
1706  ++nFound;
1707  break;
1708  }
1709  else
1710  {
1711  nTop = nRow;
1712  lcl_CheckMinMax( nUpperMin, nUpperMax, *pLine, nCol, true );
1713  ++nFound;
1714  // If start and end node are identical, we're nearly done..
1715  if( pEndNd == pStartNd )
1716  {
1717  nBottom = nTop;
1718  nLowerMin = nUpperMin;
1719  nLowerMax = nUpperMax;
1720  ++nFound;
1721  }
1722  }
1723  }
1724  }
1725  }
1726  if( nFound < 2 )
1727  return; // At least one node was not a part of the given table
1728  if( eSearch == SEARCH_ROW )
1729  {
1730  // Selection of a row is quiet easy:
1731  // every (unprotected) box between start and end line
1732  // with a positive row span will be collected
1733  for( size_t nRow = nTop; nRow <= nBottom; ++nRow )
1734  {
1735  SwTableLine* pLine = m_aLines[nRow];
1736  OSL_ENSURE( pLine, "Missing table line" );
1737  const size_t nCount = pLine->GetTabBoxes().size();
1738  for( size_t nCurrBox = 0; nCurrBox < nCount; ++nCurrBox )
1739  {
1740  SwTableBox* pBox = pLine->GetTabBoxes()[nCurrBox];
1741  OSL_ENSURE( pBox, "Missing table box" );
1742  if( pBox->getRowSpan() > 0 && ( !bChkProtected ||
1743  !pBox->GetFrameFormat()->GetProtect().IsContentProtected() ) )
1744  rBoxes.insert( pBox );
1745  }
1746  }
1747  return;
1748  }
1749  bool bCombine = nTop == nBottom;
1750  if( !bCombine )
1751  {
1752  long nMinWidth = nUpperMax - nUpperMin;
1753  long nTmp = nLowerMax - nLowerMin;
1754  if( nMinWidth > nTmp )
1755  nMinWidth = nTmp;
1756  nTmp = std::min(nLowerMax, nUpperMax);
1757  nTmp -= ( nLowerMin < nUpperMin ) ? nUpperMin : nLowerMin;
1758  // If the overlapping between upper and lower box is less than half
1759  // of the width (of the smaller cell), bCombine is set,
1760  // e.g. if upper and lower cell are in different columns
1761  bCombine = ( nTmp + nTmp < nMinWidth );
1762  }
1763  if( bCombine )
1764  {
1765  if( nUpperMin < nLowerMin )
1766  nLowerMin = nUpperMin;
1767  else
1768  nUpperMin = nLowerMin;
1769  if( nUpperMax > nLowerMax )
1770  nLowerMax = nUpperMax;
1771  else
1772  nUpperMax = nLowerMax;
1773  }
1774  const bool bColumn = eSearch == SEARCH_COL;
1775  if( bColumn )
1776  {
1777  for( size_t i = 0; i < nTop; ++i )
1778  lcl_SearchSelBox( *this, rBoxes, nUpperMin, nUpperMax,
1779  *m_aLines[i], bChkProtected, bColumn );
1780  }
1781 
1782  {
1783  long nMin = std::min(nUpperMin, nLowerMin);
1784  long nMax = nUpperMax < nLowerMax ? nLowerMax : nUpperMax;
1785  for( size_t i = nTop; i <= nBottom; ++i )
1786  lcl_SearchSelBox( *this, rBoxes, nMin, nMax, *m_aLines[i],
1787  bChkProtected, bColumn );
1788  }
1789  if( bColumn )
1790  {
1791  for( size_t i = nBottom + 1; i < nLines; ++i )
1792  lcl_SearchSelBox( *this, rBoxes, nLowerMin, nLowerMax, *m_aLines[i],
1793  bChkProtected, true );
1794  }
1795 }
1796 
1801 void SwTable::ExpandColumnSelection( SwSelBoxes& rBoxes, long &rMin, long &rMax ) const
1802 {
1803  OSL_ENSURE( m_bNewModel, "Don't call me for old tables" );
1804  rMin = 0;
1805  rMax = 0;
1806  if( m_aLines.empty() || rBoxes.empty() )
1807  return;
1808 
1809  const size_t nLineCnt = m_aLines.size();
1810  const size_t nBoxCnt = rBoxes.size();
1811  size_t nBox = 0;
1812  for( size_t nRow = 0; nRow < nLineCnt && nBox < nBoxCnt; ++nRow )
1813  {
1814  SwTableLine* pLine = m_aLines[nRow];
1815  OSL_ENSURE( pLine, "Missing table line" );
1816  const size_t nCols = pLine->GetTabBoxes().size();
1817  for( size_t nCol = 0; nCol < nCols; ++nCol )
1818  {
1819  SwTableBox* pBox = pLine->GetTabBoxes()[nCol];
1820  OSL_ENSURE( pBox, "Missing table box" );
1821  if( pBox == rBoxes[nBox] )
1822  {
1823  lcl_CheckMinMax( rMin, rMax, *pLine, nCol, nBox == 0 );
1824  if( ++nBox >= nBoxCnt )
1825  break;
1826  }
1827  }
1828  }
1829  for( size_t nRow = 0; nRow < nLineCnt; ++nRow )
1830  {
1831  SwTableLine* pLine = m_aLines[nRow];
1832  const size_t nCols = pLine->GetTabBoxes().size();
1833  long nRight = 0;
1834  for( size_t nCurrBox = 0; nCurrBox < nCols; ++nCurrBox )
1835  {
1836  long nLeft = nRight;
1837  SwTableBox* pBox = pLine->GetTabBoxes()[nCurrBox];
1838  nRight += pBox->GetFrameFormat()->GetFrameSize().GetWidth();
1839  if( nLeft >= rMin && nRight <= rMax )
1840  rBoxes.insert( pBox );
1841  }
1842  }
1843 }
1844 
1848 void SwTable::PrepareDeleteCol( long nMin, long nMax )
1849 {
1850  OSL_ENSURE( m_bNewModel, "Don't call me for old tables" );
1851  if( m_aLines.empty() || nMax < nMin )
1852  return;
1853  long nMid = nMin ? ( nMin + nMax ) / 2 : 0;
1854  const SwTwips nTabSize = GetFrameFormat()->GetFrameSize().GetWidth();
1855  if( nTabSize == nMax )
1856  nMid = nMax;
1857  const size_t nLineCnt = m_aLines.size();
1858  for( size_t nRow = 0; nRow < nLineCnt; ++nRow )
1859  {
1860  SwTableLine* pLine = m_aLines[nRow];
1861  const size_t nCols = pLine->GetTabBoxes().size();
1862  long nRight = 0;
1863  for( size_t nCurrBox = 0; nCurrBox < nCols; ++nCurrBox )
1864  {
1865  long nLeft = nRight;
1866  SwTableBox* pBox = pLine->GetTabBoxes()[nCurrBox];
1867  nRight += pBox->GetFrameFormat()->GetFrameSize().GetWidth();
1868  if( nRight < nMin )
1869  continue;
1870  if( nLeft > nMax )
1871  break;
1872  long nNewWidth = -1;
1873  if( nLeft < nMin )
1874  {
1875  if( nRight <= nMax )
1876  nNewWidth = nMid - nLeft;
1877  }
1878  else if( nRight > nMax )
1879  nNewWidth = nRight - nMid;
1880  else
1881  nNewWidth = 0;
1882  if( nNewWidth >= 0 )
1883  {
1884  SwFrameFormat* pFrameFormat = pBox->ClaimFrameFormat();
1885  SwFormatFrameSize aFrameSz( pFrameFormat->GetFrameSize() );
1886  aFrameSz.SetWidth( nNewWidth );
1887  pFrameFormat->SetFormatAttr( aFrameSz );
1888  }
1889  }
1890  }
1891 }
1892 
1898 {
1899  for (size_t i = 0; i < rBoxes.size(); ++i)
1900  {
1901  SwTableBox *pBox = rBoxes[i];
1902  long nRowSpan = pBox->getRowSpan();
1903  if( nRowSpan != 1 )
1904  {
1905  SwTableBox *pMasterBox = nRowSpan > 0 ? pBox
1906  : &pBox->FindStartOfRowSpan( *this );
1907  lcl_getAllMergedBoxes( *this, rBoxes, *pMasterBox );
1908  }
1909  }
1910 }
1911 
1916 void SwTable::CheckRowSpan( SwTableLine* &rpLine, bool bUp ) const
1917 {
1918  OSL_ENSURE( IsNewModel(), "Don't call me for old tables" );
1919  sal_uInt16 nLineIdx = GetTabLines().GetPos( rpLine );
1920  OSL_ENSURE( nLineIdx < GetTabLines().size(), "Start line out of range" );
1921  bool bChange = true;
1922  if( bUp )
1923  {
1924  while( bChange )
1925  {
1926  bChange = false;
1927  rpLine = GetTabLines()[ nLineIdx ];
1928  const size_t nCols = rpLine->GetTabBoxes().size();
1929  for( size_t nCol = 0; !bChange && nCol < nCols; ++nCol )
1930  {
1931  SwTableBox* pBox = rpLine->GetTabBoxes()[nCol];
1932  if( pBox->getRowSpan() > 1 || pBox->getRowSpan() < -1 )
1933  bChange = true;
1934  }
1935  if( bChange )
1936  {
1937  if( nLineIdx )
1938  --nLineIdx;
1939  else
1940  {
1941  bChange = false;
1942  rpLine = nullptr;
1943  }
1944  }
1945  }
1946  }
1947  else
1948  {
1949  const size_t nMaxLine = GetTabLines().size();
1950  while( bChange )
1951  {
1952  bChange = false;
1953  rpLine = GetTabLines()[ nLineIdx ];
1954  const size_t nCols = rpLine->GetTabBoxes().size();
1955  for( size_t nCol = 0; !bChange && nCol < nCols; ++nCol )
1956  {
1957  SwTableBox* pBox = rpLine->GetTabBoxes()[nCol];
1958  if( pBox->getRowSpan() < 0 )
1959  bChange = true;
1960  }
1961  if( bChange )
1962  {
1963  ++nLineIdx;
1964  if( nLineIdx >= nMaxLine )
1965  {
1966  bChange = false;
1967  rpLine = nullptr;
1968  }
1969  }
1970  }
1971  }
1972 }
1973 
1974 // This structure corrects the row span attributes for a top line of a table
1975 // In a top line no negative row span is allowed, so these have to be corrected.
1976 // If there has been at least one correction, all values are stored
1977 // and can be used by undo of table split
1978 SwSaveRowSpan::SwSaveRowSpan( SwTableBoxes& rBoxes, sal_uInt16 nSplitLn )
1979  : mnSplitLine( nSplitLn )
1980 {
1981  bool bDontSave = true; // nothing changed, nothing to save
1982  const size_t nColCount = rBoxes.size();
1983  OSL_ENSURE( nColCount, "Empty Table Line" );
1984  mnRowSpans.resize( nColCount );
1985  for( size_t nCurrCol = 0; nCurrCol < nColCount; ++nCurrCol )
1986  {
1987  SwTableBox* pBox = rBoxes[nCurrCol];
1988  OSL_ENSURE( pBox, "Missing Table Box" );
1989  long nRowSp = pBox->getRowSpan();
1990  mnRowSpans[ nCurrCol ] = nRowSp;
1991  if( nRowSp < 0 )
1992  {
1993  bDontSave = false;
1994  nRowSp = -nRowSp;
1995  pBox->setRowSpan( nRowSp ); // correction needed
1996  }
1997  }
1998  if( bDontSave )
1999  mnRowSpans.clear();
2000 }
2001 
2002 // This function is called by undo of table split to restore the old row span
2003 // values at the split line
2005 {
2006  if( !IsNewModel() ) // for new model only
2007  return;
2008  sal_uInt16 nLineCount = GetTabLines().size();
2009  OSL_ENSURE( rSave.mnSplitLine < nLineCount, "Restore behind last line?" );
2010  if( rSave.mnSplitLine < nLineCount )
2011  {
2012  SwTableLine* pLine = GetTabLines()[rSave.mnSplitLine];
2013  const size_t nColCount = pLine->GetTabBoxes().size();
2014  OSL_ENSURE( nColCount, "Empty Table Line" );
2015  OSL_ENSURE( nColCount == rSave.mnRowSpans.size(), "Wrong row span store" );
2016  if( nColCount == rSave.mnRowSpans.size() )
2017  {
2018  for( size_t nCurrCol = 0; nCurrCol < nColCount; ++nCurrCol )
2019  {
2020  SwTableBox* pBox = pLine->GetTabBoxes()[nCurrCol];
2021  OSL_ENSURE( pBox, "Missing Table Box" );
2022  long nRowSp = pBox->getRowSpan();
2023  if( nRowSp != rSave.mnRowSpans[ nCurrCol ] )
2024  {
2025  OSL_ENSURE( -nRowSp == rSave.mnRowSpans[ nCurrCol ], "Pardon me?!" );
2026  OSL_ENSURE( rSave.mnRowSpans[ nCurrCol ] < 0, "Pardon me?!" );
2027  pBox->setRowSpan( -nRowSp );
2028 
2029  sal_uInt16 nLine = rSave.mnSplitLine;
2030  if( nLine )
2031  {
2032  long nLeftBorder = lcl_Box2LeftBorder( *pBox );
2033  SwTableBox* pNext;
2034  do
2035  {
2036  pNext = lcl_LeftBorder2Box( nLeftBorder, GetTabLines()[--nLine] );
2037  if( pNext )
2038  {
2039  pBox = pNext;
2040  long nNewSpan = pBox->getRowSpan();
2041  if( pBox->getRowSpan() < 1 )
2042  nNewSpan -= nRowSp;
2043  else
2044  {
2045  nNewSpan += nRowSp;
2046  pNext = nullptr;
2047  }
2048  pBox->setRowSpan( nNewSpan );
2049  }
2050  } while( nLine && pNext );
2051  }
2052  }
2053  }
2054  }
2055  }
2056 }
2057 
2058 std::unique_ptr<SwSaveRowSpan> SwTable::CleanUpTopRowSpan( sal_uInt16 nSplitLine )
2059 {
2060  if( !IsNewModel() )
2061  return nullptr;
2062  std::unique_ptr<SwSaveRowSpan> pRet(new SwSaveRowSpan( GetTabLines()[0]->GetTabBoxes(), nSplitLine ));
2063  if( pRet->mnRowSpans.empty() )
2064  return nullptr;
2065  return pRet;
2066 }
2067 
2068 void SwTable::CleanUpBottomRowSpan( sal_uInt16 nDelLines )
2069 {
2070  if( !IsNewModel() )
2071  return;
2072  const size_t nLastLine = GetTabLines().size()-1;
2073  SwTableLine* pLine = GetTabLines()[nLastLine];
2074  const size_t nColCount = pLine->GetTabBoxes().size();
2075  OSL_ENSURE( nColCount, "Empty Table Line" );
2076  for( size_t nCurrCol = 0; nCurrCol < nColCount; ++nCurrCol )
2077  {
2078  SwTableBox* pBox = pLine->GetTabBoxes()[nCurrCol];
2079  OSL_ENSURE( pBox, "Missing Table Box" );
2080  long nRowSp = pBox->getRowSpan();
2081  if( nRowSp < 0 )
2082  nRowSp = -nRowSp;
2083  if( nRowSp > 1 )
2084  {
2085  lcl_ChangeRowSpan( *this, -static_cast<long>(nDelLines),
2086  static_cast<sal_uInt16>(nLastLine), false );
2087  break;
2088  }
2089  }
2090 }
2091 
2092 #ifdef DBG_UTIL
2093 
2095 {
2096  long nRowSpan;
2099 };
2100 
2102 {
2103  if( !IsNewModel() )
2104  return;
2105  const size_t nLineCount = GetTabLines().size();
2106  const SwTwips nTabSize = GetFrameFormat()->GetFrameSize().GetWidth();
2107  SwTwips nLineWidth = 0;
2108  std::list< RowSpanCheck > aRowSpanCells;
2109  std::list< RowSpanCheck >::iterator aIter = aRowSpanCells.end();
2110  for( size_t nCurrLine = 0; nCurrLine < nLineCount; ++nCurrLine )
2111  {
2112  SwTwips nWidth = 0;
2113  SwTableLine* pLine = GetTabLines()[nCurrLine];
2114  SAL_WARN_IF( !pLine, "sw.core", "Missing Table Line" );
2115  const size_t nColCount = pLine->GetTabBoxes().size();
2116  SAL_WARN_IF( !nColCount, "sw.core", "Empty Table Line" );
2117  for( size_t nCurrCol = 0; nCurrCol < nColCount; ++nCurrCol )
2118  {
2119  SwTableBox* pBox = pLine->GetTabBoxes()[nCurrCol];
2120  SAL_WARN_IF( !pBox, "sw.core", "Missing Table Box" );
2121  SwTwips nNewWidth = pBox->GetFrameFormat()->GetFrameSize().GetWidth() + nWidth;
2122  long nRowSp = pBox->getRowSpan();
2123  if( nRowSp < 0 )
2124  {
2125  SAL_WARN_IF( aIter == aRowSpanCells.end(),
2126  "sw.core", "Missing master box");
2127  if (aIter != aRowSpanCells.end())
2128  {
2129  SAL_WARN_IF( aIter->nLeft != nWidth || aIter->nRight != nNewWidth,
2130  "sw.core", "Wrong position/size of overlapped table box");
2131  --(aIter->nRowSpan);
2132  SAL_WARN_IF( aIter->nRowSpan != -nRowSp, "sw.core",
2133  "Wrong row span value" );
2134  if( nRowSp == -1 )
2135  {
2136  aIter = aRowSpanCells.erase(aIter);
2137  }
2138  else
2139  ++aIter;
2140  }
2141  }
2142  else if( nRowSp != 1 )
2143  {
2144  SAL_WARN_IF( !nRowSp, "sw.core", "Zero row span?!" );
2145  RowSpanCheck aEntry;
2146  aEntry.nLeft = nWidth;
2147  aEntry.nRight = nNewWidth;
2148  aEntry.nRowSpan = nRowSp;
2149  aRowSpanCells.insert( aIter, aEntry );
2150  }
2151  nWidth = nNewWidth;
2152  }
2153  if( !nCurrLine )
2154  nLineWidth = nWidth;
2155  SAL_WARN_IF( nWidth != nLineWidth, "sw.core",
2156  "Different Line Widths: first: " << nLineWidth
2157  << " current [" << nCurrLine << "]: " << nWidth);
2158  SAL_WARN_IF( std::abs(nWidth - nTabSize) > 1 /* how tolerant? */, "sw.core",
2159  "Line width differs from table width: " << nTabSize
2160  << " current [" << nCurrLine << "]: " << nWidth);
2161  SAL_WARN_IF( nWidth < 0 || nWidth > USHRT_MAX, "sw.core",
2162  "Width out of range [" << nCurrLine << "]: " << nWidth);
2163  SAL_WARN_IF( aIter != aRowSpanCells.end(), "sw.core",
2164  "Missing overlapped box" );
2165  aIter = aRowSpanCells.begin();
2166  }
2167  bool bEmpty = aRowSpanCells.empty();
2168  SAL_WARN_IF( !bEmpty, "sw.core", "Open row span detected" );
2169 }
2170 
2171 #endif
2172 
2173 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
#define CHECK_TABLE(t)
Definition: swnewtable.cxx:47
const SwEndNode * EndOfSectionNode() const
Definition: node.hxx:682
const int nColCount
Base class of the Writer layout elements.
Definition: frame.hxx:295
void ExpandColumnSelection(SwSelBoxes &rBoxes, long &rMin, long &rMax) const
void SwTable::ExpandColumnSelection(..) adds cell to the give selection to assure that at least one c...
virtual sal_Int32 Len() const
Definition: node.cxx:1183
std::vector< SwSelBoxes > maBoxes
Definition: swnewtable.cxx:64
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...
bool NewSplitRow(SwDoc *, const SwSelBoxes &, sal_uInt16, bool)
SwTable::NewSplitRow(..) splits all selected boxes horizontally.
const Value & back() const
static void lcl_SearchSelBox(const SwTable &rTable, SwSelBoxes &rBoxes, long nMin, 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...
Marks a position in the document model.
Definition: pam.hxx:35
sal_uLong GetSttIdx() const
Definition: swtable.cxx:1880
SwNodeIndex nNode
Definition: pam.hxx:37
SwSaveRowSpan(SwTableBoxes &rBoxes, sal_uInt16 nSplitLn)
long GetWidth() const
std::pair< sal_uInt16, sal_uInt16 > SwLineOffset
const SwPosition * GetMark() const
Definition: pam.hxx:209
std::vector< SwLineOffset > SwLineOffsetArray
SwTwips nRight
void InvalidateSize_()
Definition: frame.hxx:749
Definition: doc.hxx:185
SwFrameFormat * ClaimFrameFormat()
Definition: swtable.cxx:1482
void DelFrames(SwTable &rTable)
Definition: tblsel.cxx:2160
void GetMergeSel(const SwPaM &rPam, SwSelBoxes &rBoxes, SwTableBox **ppMergeBox, SwUndoTableMerge *pUndo)
Definition: tblsel.cxx:917
const_iterator find(const Value &x) const
SwTableLine is one table row in the document model.
Definition: swtable.hxx:344
SwNode & GetNode() const
Definition: ndindex.hxx:118
long SwTwips
Definition: swtypes.hxx:49
static long lcl_Box2LeftBorder(const SwTableBox &rBox)
lcl_Box2LeftBorder(..) delivers the left (logical) border of a table box
Definition: swnewtable.cxx:159
IDocumentUndoRedo & GetIDocumentUndoRedo()
Definition: doc.cxx:176
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:347
void CheckRowSpan(SwTableLine *&rpLine, bool bUp) const
SwTable::CheckRowSpan(..) looks for the next line without an overlapping to the previous line...
void setRowSpan(long nNewRowSpan)
Definition: swtable.cxx:107
SwTableFormat * GetFrameFormat()
Definition: swtable.hxx:201
bool PrepareMerge(const SwPaM &rPam, SwSelBoxes &rBoxes, SwSelBoxes &rMerged, SwTableBox **ppMergeBox, SwUndoTableMerge *pUndo)
SwTable::PrepareMerge(..) some preparation for the coming Merge(..)
Definition: swnewtable.cxx:806
size_type size() const
Definition: swtable.hxx:74
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
SwContentNode * GetContentNode(bool bPoint=true) const
Definition: pam.hxx:229
SwTableLines m_aLines
Definition: swtable.hxx:114
bool IsEmptyBox(const SwTableBox &rBox, SwPaM &rPam)
Definition: tblsel.cxx:881
Value in Var-direction gives minimum (can be exceeded but not be less).
Definition: fmtfsize.hxx:39
SwIndex nContent
Definition: pam.hxx:38
static void lcl_FillSelBoxes(SwSelBoxes &rBoxes, SwTableLine &rLine)
lcl_FillSelBoxes(..) puts all boxes of a given line into the selection structure
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.
iterator insert(iterator aIt, SwTableLine *pLine)
Definition: swtable.hxx:84
void SetCountedInList(bool bCounted)
Definition: ndtxt.cxx:4223
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:2321
void SetWidth(long n)
bool NewMerge(SwDoc *, const SwSelBoxes &, const SwSelBoxes &rMerged, SwUndoTableMerge *)
NewMerge(..) removes the superfluous cells after cell merge.
Definition: swnewtable.cxx:87
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:2098
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:928
SwIndex & Assign(SwIndexReg *, sal_Int32)
Definition: index.cxx:198
void CheckConsistency() const
int i
bool isEmpty() const
Definition: swnewtable.cxx:67
void InsTableBox(SwDoc *pDoc, SwTableNode *pTableNd, SwTableLine *pLine, SwTableBoxFormat *pBoxFrameFormat, SwTableBox *pBox, sal_uInt16 nInsPos, sal_uInt16 nCnt=1)
Definition: swtable.cxx:157
void SetSelBoxes(const SwSelBoxes &rBoxes)
Definition: untbl.cxx:2018
void InsertSpannedRow(SwDoc *pDoc, sal_uInt16 nIdx, sal_uInt16 nCnt)
SwTable::InsertSpannedRow(..) inserts "superfluous" rows, i.e.
SwFrameFormat * GetFrameFormat()
Definition: swtable.hxx:366
void push_back(const SwSelBoxes &rNew)
Definition: swnewtable.cxx:68
bool IsContentProtected() const
size
Marks a node in the document model.
Definition: ndindex.hxx:31
SwFrameFormat * GetFrameFormat()
Definition: swtable.hxx:425
static void lcl_CheckMinMax(long &rMin, long &rMax, const SwTableLine &rLine, size_t nCheck, bool bSet)
lcl_CheckMinMax helps evaluating (horizontal) min/max of boxes
Definition: swnewtable.cxx:121
void FindSuperfluousRows_(SwSelBoxes &rBoxes, SwTableLine *, SwTableLine *)
SwTable::FindSuperfluousRows_(..) is looking for superfluous rows, i.e.
Definition: swnewtable.cxx:983
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
void MoveBoxContent(SwDoc *pDoc, SwNodeRange &rRg, SwNodeIndex &rPos)
Definition: untbl.cxx:2003
bool empty() const
const SwDoc * GetDoc() const
The document is set in SwAttrPool now, therefore you always can access it.
Definition: format.hxx:119
sal_uInt16 GetBoxPos(const SwTableBox *pBox) const
Definition: swtable.hxx:356
virtual bool MoveNodeRange(SwNodeRange &, SwNodeIndex &, SwMoveFlags)=0
SwTableLines & GetTabLines()
Definition: swtable.hxx:198
IDocumentLayoutAccess const & getIDocumentLayoutAccess() const
Definition: doc.cxx:437
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
bool NewInsertCol(SwDoc *, const SwSelBoxes &rBoxes, sal_uInt16 nCnt, bool)
SwTable::NewInsertCol(..) insert new column(s) into a table.
Definition: swnewtable.cxx:650
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:326
SwTextNode is a paragraph in the document model.
Definition: ndtxt.hxx:79
SwTableBoxes & GetTabBoxes()
Definition: swtable.hxx:354
std::vector< SwTableBox * > SwTableBoxes
Definition: swtable.hxx:103
const SwStartNode * GetSttNd() const
Definition: swtable.hxx:439
void ChgFrameFormat(SwTableBoxFormat *pNewFormat, bool bNeedToReregister=true)
Definition: swtable.cxx:1742
void AdjustWidths(const long nOld, const long nNew)
Definition: swtable.cxx:369
static void lcl_ChangeRowSpan(const SwTable &rTable, const long nDiff, sal_uInt16 nRowIdx, const bool bSingle)
lcl_ChangeRowSpan corrects row span after insertion/deletion of rows
Definition: swnewtable.cxx:254
SwTableBox & FindStartOfRowSpan(const SwTable &, sal_uInt16 nMaxStep=USHRT_MAX)
SwTableBox::FindStartOfRowSpan(..) returns the "master" cell, the cell which overlaps the given cell...
long getRowSpan() const
Definition: swtable.cxx:102
#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:261
void CleanUpBottomRowSpan(sal_uInt16 nDelLines)
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:386
bool m_bNewModel
Definition: swtable.hxx:137
SwFrame * GetLower()
Definition: findfrm.cxx:169
static SwTableBox * lcl_LeftBorder2Box(long nLeft, const SwTableLine *pLine)
lcl_LeftBorder2Box delivers the box to a given left border
Definition: swnewtable.cxx:194
static 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:605
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:1706
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 PrepareDeleteCol(long nMin, long nMax)
SwTable::PrepareDeleteCol(..) adjusts the widths of the neighbour cells of a cell selection for an up...
sal_uInt16 const mnSplitLine
Definition: tblrwcl.hxx:113
#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
void AddNewBox(sal_uLong nSttNdIdx)
Definition: UndoTable.hxx:231
std::pair< const_iterator, bool > insert(Value &&x)
long GetHeight() const
bool IsNewModel() const
Definition: swtable.hxx:185
const int nLineCount
SwTableLine * GetUpper()
Definition: swtable.hxx:421
SwBoxSelection is a small helperclass (structure) to handle selections of cells (boxes) between table...
Definition: swnewtable.cxx:61
sal_Int32 nPos
void SetHeightSizeType(SwFrameSize eSize)
Definition: fmtfsize.hxx:81
Frame is variable in Var-direction.
Definition: fmtfsize.hxx:37
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:507
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:586
SwCellFrame is one table cell in the document layout.
Definition: cellfrm.hxx:30
SwTextNode * GetTextNode()
Inline methods from Node.hxx.
Definition: ndtxt.hxx:843
SwNodeIndex & Assign(SwNodes const &rNds, sal_uLong)
Definition: ndindex.hxx:271
SwTableNode * GetTableNode() const
Definition: swtable.cxx:1927
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