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