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