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 SwNodeIndex& rInsPosNd = aInsPos.nNode;
888 SwPaM aPam( aInsPos );
889 aPam.GetPoint()->nNode.Assign( *pBox->GetSttNd()->EndOfSectionNode(), -1 );
890 SwContentNode* pCNd = aPam.GetPointContentNode();
891 aPam.GetPoint()->nContent.Assign( pCNd, pCNd ? pCNd->Len() : 0 );
892 SwNodeIndex aSttNdIdx( *pBox->GetSttNd(), 1 );
893 bool const bUndo = pDoc->GetIDocumentUndoRedo().DoesUndo();
894 if( pUndo )
895 {
896 pDoc->GetIDocumentUndoRedo().DoUndo(false);
897 }
899 if( pUndo )
900 {
901 pDoc->GetIDocumentUndoRedo().DoUndo(bUndo);
902 }
903 SwNodeRange aRg( aSttNdIdx.GetNode(), aPam.GetPoint()->GetNode() );
904 if( pUndo )
905 pUndo->MoveBoxContent( *pDoc, aRg, rInsPosNd.GetNode() );
906 else
907 {
908 pDoc->getIDocumentContentOperations().MoveNodeRange( aRg, rInsPosNd.GetNode(),
910 }
911 }
912 }
913 // Only the cell of the first selected column will stay alive
914 // and got a new row span
915 if( !nCurrCol )
916 pBox->setRowSpan( nRowSpan );
917 }
918 if( nRowSpan > 0 ) // the master cell is done, from now on we set
919 nRowSpan = -nRowSpan; // negative row spans
920 ++nRowSpan; // ... -3, -2, -1
921 }
922 if( bMerge )
923 {
924 // A row containing overlapped cells is superfluous,
925 // these cells can be put into rBoxes for deletion
926 FindSuperfluousRows_( rBoxes, pFirstLn, pLastLn );
927 // pNewFormat will be set to the new master box and the overlapped cells
928 SwFrameFormat* pNewFormat = pMergeBox->ClaimFrameFormat();
929 pNewFormat->SetFormatAttr( SwFormatFrameSize( SwFrameSize::Variable, pSel->mnMergeWidth, 0 ) );
930 for( size_t nCurrLine = 0; nCurrLine < nLineCount; ++nCurrLine )
931 {
932 const SwSelBoxes& rLineBoxes = pSel->maBoxes[nCurrLine];
933 size_t nColCount = rLineBoxes.size();
934 for (size_t nCurrCol = 0; nCurrCol < nColCount; ++nCurrCol)
935 {
936 SwTableBox* pBox = rLineBoxes[nCurrCol];
937 if( nCurrCol )
938 {
939 // Even this box will be deleted soon,
940 // we have to correct the width to avoid side effects
941 SwFrameFormat* pFormat = pBox->ClaimFrameFormat();
943 }
944 else
945 {
946 pBox->ChgFrameFormat( static_cast<SwTableBoxFormat*>(pNewFormat) );
947 // remove numbering from cells that will be disabled in the merge
948 if( nCurrLine )
949 {
950 SwPaM aPam( *pBox->GetSttNd(), 0 );
951 aPam.GetPoint()->nNode++;
952 SwTextNode* pNd = aPam.GetPointNode().GetTextNode();
953 while( pNd )
954 {
955 pNd->SetCountedInList( false );
956
957 aPam.GetPoint()->nNode++;
958 pNd = aPam.GetPointNode().GetTextNode();
959 }
960 }
961 }
962 }
963 }
964 if( pLastBox ) // Robust
965 {
966 // The new borders of the master cell...
967 SvxBoxItem aBox( pMergeBox->GetFrameFormat()->GetBox() );
968 bool bOld = aBox.GetRight() || aBox.GetBottom();
969 const SvxBoxItem& rBox = pLastBox->GetFrameFormat()->GetBox();
970 aBox.SetLine( rBox.GetRight(), SvxBoxItemLine::RIGHT );
971 aBox.SetLine( rBox.GetBottom(), SvxBoxItemLine::BOTTOM );
972 if( bOld || aBox.GetLeft() || aBox.GetTop() || aBox.GetRight() || aBox.GetBottom() )
973 (*ppMergeBox)->GetFrameFormat()->SetFormatAttr( aBox );
974 }
975
976 if( pUndo )
977 pUndo->AddNewBox( pMergeBox->GetSttIdx() );
978 }
979 return bMerge;
980}
981
987 SwTableLine* pFirstLn, SwTableLine* pLastLn )
988{
989 if( !pFirstLn || !pLastLn )
990 {
991 if( rBoxes.empty() )
992 return;
993 pFirstLn = rBoxes[0]->GetUpper();
994 pLastLn = rBoxes.back()->GetUpper();
995 }
996 sal_uInt16 nFirstLn = GetTabLines().GetPos( pFirstLn );
997 sal_uInt16 nLastLn = GetTabLines().GetPos( pLastLn );
998 for( sal_uInt16 nRow = nFirstLn; nRow <= nLastLn; ++nRow )
999 {
1000 SwTableLine* pLine = m_aLines[nRow];
1001 OSL_ENSURE( pLine, "Missing table line" );
1002 const size_t nCols = pLine->GetTabBoxes().size();
1003 bool bSuperfl = true;
1004 for( size_t nCol = 0; nCol < nCols; ++nCol )
1005 {
1006 SwTableBox *pBox = pLine->GetTabBoxes()[nCol];
1007 if( pBox->getRowSpan() > 0 &&
1008 rBoxes.end() == rBoxes.find( pBox ) )
1009 {
1010 bSuperfl = false;
1011 break;
1012 }
1013 }
1014 if( bSuperfl )
1015 {
1016 for( size_t nCol = 0; nCol < nCols; ++nCol )
1017 {
1018 SwTableBox* pBox = pLine->GetTabBoxes()[nCol];
1019 rBoxes.insert( pBox );
1020 }
1021 }
1022 }
1023}
1024
1029SwTableBox& SwTableBox::FindStartOfRowSpan( const SwTable& rTable, sal_uInt16 nMaxStep )
1030{
1031 if( getRowSpan() > 0 || !nMaxStep )
1032 return *this;
1033
1034 tools::Long nLeftBorder = lcl_Box2LeftBorder( *this );
1035 SwTableBox* pBox = this;
1036 const SwTableLine* pMyUpper = GetUpper();
1037 sal_uInt16 nLine = rTable.GetTabLines().GetPos( pMyUpper );
1038 if( nLine && nLine < rTable.GetTabLines().size() )
1039 {
1040 SwTableBox* pNext;
1041 do
1042 {
1043 pNext = lcl_LeftBorder2Box( nLeftBorder, rTable.GetTabLines()[--nLine] );
1044 if( pNext )
1045 pBox = pNext;
1046 } while( nLine && --nMaxStep && pNext && pBox->getRowSpan() < 1 );
1047 }
1048
1049 return *pBox;
1050}
1051
1056SwTableBox& SwTableBox::FindEndOfRowSpan( const SwTable& rTable, sal_uInt16 nMaxStep )
1057{
1058 tools::Long nAbsSpan = getRowSpan();
1059 if( nAbsSpan < 0 )
1060 nAbsSpan = -nAbsSpan;
1061 if( nAbsSpan == 1 || !nMaxStep )
1062 return *this;
1063
1064 if( nMaxStep > --nAbsSpan )
1065 nMaxStep = o3tl::narrowing<sal_uInt16>(nAbsSpan);
1066 const SwTableLine* pMyUpper = GetUpper();
1067 sal_uInt16 nLine = rTable.GetTabLines().GetPos( pMyUpper );
1068 nMaxStep = nLine + nMaxStep;
1069 if( nMaxStep >= rTable.GetTabLines().size() )
1070 nMaxStep = rTable.GetTabLines().size() - 1;
1071 tools::Long nLeftBorder = lcl_Box2LeftBorder( *this );
1072 SwTableBox* pBox =
1073 lcl_LeftBorder2Box( nLeftBorder, rTable.GetTabLines()[ nMaxStep ] );
1074 if ( !pBox )
1075 pBox = this;
1076
1077 return *pBox;
1078}
1079
1083static void lcl_getAllMergedBoxes( const SwTable& rTable, SwSelBoxes& rBoxes, SwTableBox& rBox )
1084{
1085 SwTableBox* pBox = &rBox;
1086 OSL_ENSURE( pBox == &rBox.FindStartOfRowSpan( rTable ), "Not a master box" );
1087 rBoxes.insert( pBox );
1088 if( pBox->getRowSpan() == 1 )
1089 return;
1090 const SwTableLine* pMyUpper = pBox->GetUpper();
1091 sal_uInt16 nLine = rTable.GetTabLines().GetPos( pMyUpper );
1092 tools::Long nLeftBorder = lcl_Box2LeftBorder( *pBox );
1093 sal_uInt16 nCount = rTable.GetTabLines().size();
1094 while( ++nLine < nCount && pBox && pBox->getRowSpan() != -1 )
1095 {
1096 pBox = lcl_LeftBorder2Box( nLeftBorder, rTable.GetTabLines()[nLine] );
1097 if( pBox )
1098 rBoxes.insert( pBox );
1099 }
1100}
1101
1106static void lcl_UnMerge( const SwTable& rTable, SwTableBox& rBox, size_t nCnt,
1107 bool bSameHeight )
1108{
1109 SwSelBoxes aBoxes;
1110 lcl_getAllMergedBoxes( rTable, aBoxes, rBox );
1111 size_t const nCount = aBoxes.size();
1112 if( nCount < 2 )
1113 return;
1114 if( nCnt > nCount )
1115 nCnt = nCount;
1116 std::unique_ptr<size_t[]> const pSplitIdx(new size_t[nCnt]);
1117 if( bSameHeight )
1118 {
1119 std::unique_ptr<SwTwips[]> const pHeights(new SwTwips[nCount]);
1120 SwTwips nHeight = 0;
1121 for (size_t i = 0; i < nCount; ++i)
1122 {
1123 SwTableLine* pLine = aBoxes[ i ]->GetUpper();
1124 SwFrameFormat *pRowFormat = pLine->GetFrameFormat();
1125 pHeights[ i ] = pRowFormat->GetFrameSize().GetHeight();
1126 nHeight += pHeights[ i ];
1127 }
1128 SwTwips nSumH = 0;
1129 size_t nIdx = 0;
1130 for (size_t i = 1; i <= nCnt; ++i)
1131 {
1132 SwTwips nSplit = ( i * nHeight ) / nCnt;
1133 while( nSumH < nSplit && nIdx < nCount )
1134 nSumH += pHeights[ nIdx++ ];
1135 pSplitIdx[ i - 1 ] = nIdx;
1136 }
1137 }
1138 else
1139 {
1140 for (size_t i = 1; i <= nCnt; ++i)
1141 {
1142 pSplitIdx[ i - 1 ] = ( i * nCount ) / nCnt;
1143 }
1144 }
1145 size_t nIdx = 0;
1146 for (size_t i = 0; i < nCnt; ++i)
1147 {
1148 size_t nNextIdx = pSplitIdx[ i ];
1149 aBoxes[ nIdx ]->setRowSpan( nNextIdx - nIdx );
1150 lcl_InvalidateCellFrame( *aBoxes[ nIdx ] );
1151 while( ++nIdx < nNextIdx )
1152 aBoxes[ nIdx ]->setRowSpan( nIdx - nNextIdx );
1153 }
1154}
1155
1159static void lcl_FillSelBoxes( SwSelBoxes &rBoxes, SwTableLine &rLine )
1160{
1161 const size_t nBoxCount = rLine.GetTabBoxes().size();
1162 for( size_t i = 0; i < nBoxCount; ++i )
1163 rBoxes.insert( rLine.GetTabBoxes()[i] );
1164}
1165
1170void SwTable::InsertSpannedRow( SwDoc& rDoc, sal_uInt16 nRowIdx, sal_uInt16 nCnt )
1171{
1172 CHECK_TABLE( *this )
1173 OSL_ENSURE( nCnt && nRowIdx < GetTabLines().size(), "Wrong call of InsertSpannedRow" );
1174 SwSelBoxes aBoxes;
1175 SwTableLine& rLine = *GetTabLines()[ nRowIdx ];
1176 lcl_FillSelBoxes( aBoxes, rLine );
1179 {
1180 SwFrameFormat* pFrameFormat = rLine.ClaimFrameFormat();
1181 tools::Long nNewHeight = aFSz.GetHeight() / ( nCnt + 1 );
1182 if( !nNewHeight )
1183 ++nNewHeight;
1184 aFSz.SetHeight( nNewHeight );
1185 pFrameFormat->SetFormatAttr( aFSz );
1186 }
1187 InsertRow_( &rDoc, aBoxes, nCnt, true );
1188 const size_t nBoxCount = rLine.GetTabBoxes().size();
1189 for( sal_uInt16 n = 0; n < nCnt; ++n )
1190 {
1191 SwTableLine *pNewLine = GetTabLines()[ nRowIdx + nCnt - n ];
1192 for( size_t nCurrBox = 0; nCurrBox < nBoxCount; ++nCurrBox )
1193 {
1194 sal_Int32 nRowSpan = rLine.GetTabBoxes()[nCurrBox]->getRowSpan();
1195 if( nRowSpan > 0 )
1196 nRowSpan = - nRowSpan;
1197 pNewLine->GetTabBoxes()[ nCurrBox ]->setRowSpan( nRowSpan - n );
1198 }
1199 }
1200 lcl_ChangeRowSpan( *this, nCnt, nRowIdx, false );
1201 CHECK_TABLE( *this )
1202}
1203
1204typedef std::pair< sal_uInt16, sal_uInt16 > SwLineOffset;
1205typedef std::vector< SwLineOffset > SwLineOffsetArray;
1206
1207/*
1208* When a couple of table boxes has to be split,
1209* lcl_SophisticatedFillLineIndices delivers the information where and how many
1210* rows have to be inserted.
1211* Input
1212* rTable: the table to manipulate
1213* rBoxes: an array of boxes to split
1214* nCnt: how many parts are wanted
1215* Output
1216* rArr: a list of pairs ( line index, number of lines to insert )
1217*/
1219 const SwTable& rTable, const SwSelBoxes& rBoxes, sal_uInt16 nCnt )
1220{
1221 std::list< SwLineOffset > aBoxes;
1222 SwLineOffset aLnOfs( USHRT_MAX, USHRT_MAX );
1223 for (size_t i = 0; i < rBoxes.size(); ++i)
1224 { // Collect all end line indices and the row spans
1225 const SwTableBox &rBox = rBoxes[ i ]->FindStartOfRowSpan( rTable );
1226 OSL_ENSURE( rBox.getRowSpan() > 0, "Didn't I say 'StartOfRowSpan' ??" );
1227 if( nCnt > rBox.getRowSpan() )
1228 {
1229 const SwTableLine *pLine = rBox.GetUpper();
1230 const sal_uInt16 nEnd = sal_uInt16( rBox.getRowSpan() +
1231 rTable.GetTabLines().GetPos( pLine ) );
1232 // The next if statement is a small optimization
1233 if( aLnOfs.first != nEnd || aLnOfs.second != rBox.getRowSpan() )
1234 {
1235 aLnOfs.first = nEnd; // ok, this is the line behind the box
1236 aLnOfs.second = sal_uInt16( rBox.getRowSpan() ); // the row span
1237 aBoxes.insert( aBoxes.end(), aLnOfs );
1238 }
1239 }
1240 }
1241 // As I said, I noted the line index _behind_ the last line of the boxes
1242 // in the resulting array the index has to be _on_ the line
1243 // nSum is to evaluate the wished value
1244 sal_uInt16 nSum = 1;
1245 while( !aBoxes.empty() )
1246 {
1247 // I. step:
1248 // Looking for the "smallest" line end with the smallest row span
1249 std::list< SwLineOffset >::iterator pCurr = aBoxes.begin();
1250 aLnOfs = *pCurr; // the line end and row span of the first box
1251 while( ++pCurr != aBoxes.end() )
1252 {
1253 if( aLnOfs.first > pCurr->first )
1254 { // Found a smaller line end
1255 aLnOfs.first = pCurr->first;
1256 aLnOfs.second = pCurr->second; // row span
1257 }
1258 else if( aLnOfs.first == pCurr->first &&
1259 aLnOfs.second < pCurr->second )
1260 aLnOfs.second = pCurr->second; // Found a smaller row span
1261 }
1262 OSL_ENSURE( aLnOfs.second < nCnt, "Clean-up failed" );
1263 aLnOfs.second = nCnt - aLnOfs.second; // the number of rows to insert
1264 rArr.emplace_back( aLnOfs.first - nSum, aLnOfs.second );
1265 // the correction has to be incremented because in the following
1266 // loops the line ends were manipulated
1267 nSum = nSum + aLnOfs.second;
1268
1269 pCurr = aBoxes.begin();
1270 while( pCurr != aBoxes.end() )
1271 {
1272 if( pCurr->first == aLnOfs.first )
1273 { // These boxes can be removed because the last insertion
1274 // of rows will expand their row span above the needed value
1275 pCurr = aBoxes.erase(pCurr);
1276 }
1277 else
1278 {
1279 bool bBefore = ( pCurr->first - pCurr->second < aLnOfs.first );
1280 // Manipulation of the end line indices as if the rows are
1281 // already inserted
1282 pCurr->first = pCurr->first + aLnOfs.second;
1283 if( bBefore )
1284 { // If the insertion is inside the box,
1285 // its row span has to be incremented
1286 pCurr->second = pCurr->second + aLnOfs.second;
1287 if( pCurr->second >= nCnt )
1288 { // if the row span is bigger than the split factor
1289 // this box is done
1290 pCurr = aBoxes.erase(pCurr);
1291 }
1292 else
1293 ++pCurr;
1294 }
1295 else
1296 ++pCurr;
1297 }
1298 }
1299 }
1300}
1301
1302typedef std::set< SwTwips > SwSplitLines;
1303
1309 const SwTable& rTable, const SwSelBoxes& rBoxes, sal_uInt16 nCnt )
1310{
1311 if( nCnt < 2 )
1312 return 0;
1313 std::vector< SwLineOffset > aBoxes;
1314 SwLineOffset aLnOfs( USHRT_MAX, USHRT_MAX );
1315 sal_uInt16 nFirst = USHRT_MAX; // becomes the index of the first line
1316 sal_uInt16 nLast = 0; // becomes the index of the last line of the splitting
1317 for (size_t i = 0; i < rBoxes.size(); ++i)
1318 { // Collect all pairs (start+end) of line indices to split
1319 const SwTableBox &rBox = rBoxes[ i ]->FindStartOfRowSpan( rTable );
1320 OSL_ENSURE( rBox.getRowSpan() > 0, "Didn't I say 'StartOfRowSpan' ??" );
1321 const SwTableLine *pLine = rBox.GetUpper();
1322 const sal_uInt16 nStart = rTable.GetTabLines().GetPos( pLine );
1323 const sal_uInt16 nEnd = sal_uInt16( rBox.getRowSpan() + nStart - 1 );
1324 // The next if statement is a small optimization
1325 if( aLnOfs.first != nStart || aLnOfs.second != nEnd )
1326 {
1327 aLnOfs.first = nStart;
1328 aLnOfs.second = nEnd;
1329 aBoxes.push_back( aLnOfs );
1330 if( nStart < nFirst )
1331 nFirst = nStart;
1332 if( nEnd > nLast )
1333 nLast = nEnd;
1334 }
1335 }
1336
1337 if (nFirst == USHRT_MAX)
1338 {
1339 assert(aBoxes.empty());
1340 return 0;
1341 }
1342
1343 SwTwips nHeight = 0;
1344 std::unique_ptr<SwTwips[]> pLines(new SwTwips[ nLast + 1 - nFirst ]);
1345 for( sal_uInt16 i = nFirst; i <= nLast; ++i )
1346 {
1347 bool bLayoutAvailable = false;
1348 nHeight += rTable.GetTabLines()[ i ]->GetTableLineHeight( bLayoutAvailable );
1349 rCurr.insert( rCurr.end(), nHeight );
1350 pLines[ i - nFirst ] = nHeight;
1351 }
1352 for( const auto& rSplit : aBoxes )
1353 {
1354 SwTwips nBase = rSplit.first <= nFirst ? 0 :
1355 pLines[ rSplit.first - nFirst - 1 ];
1356 SwTwips nDiff = pLines[ rSplit.second - nFirst ] - nBase;
1357 for( sal_uInt16 i = 1; i < nCnt; ++i )
1358 {
1359 SwTwips nSplit = nBase + ( i * nDiff ) / nCnt;
1360 rNew.insert( nSplit );
1361 }
1362 }
1363 return nFirst;
1364}
1365
1370static sal_uInt16 lcl_LineIndex( const SwTable& rTable, const SwSelBoxes& rBoxes,
1371 bool bBehind )
1372{
1373 sal_uInt16 nDirect = USHRT_MAX;
1374 sal_uInt16 nSpan = USHRT_MAX;
1375 for (size_t i = 0; i < rBoxes.size(); ++i)
1376 {
1377 SwTableBox *pBox = rBoxes[i];
1378 const SwTableLine* pLine = rBoxes[i]->GetUpper();
1379 sal_uInt16 nPos = rTable.GetTabLines().GetPos( pLine );
1380 if( USHRT_MAX != nPos )
1381 {
1382 if( bBehind )
1383 {
1384 if( nPos > nDirect || nDirect == USHRT_MAX )
1385 nDirect = nPos;
1386 sal_Int32 nRowSpan = pBox->getRowSpan();
1387 if( nRowSpan < 2 )
1388 nSpan = 0;
1389 else if( nSpan )
1390 {
1391 sal_uInt16 nEndOfRowSpan = o3tl::narrowing<sal_uInt16>(nPos + nRowSpan - 1);
1392 if( nEndOfRowSpan > nSpan || nSpan == USHRT_MAX )
1393 nSpan = nEndOfRowSpan;
1394 }
1395 }
1396 else if( nPos < nDirect )
1397 nDirect = nPos;
1398 }
1399 }
1400 if( nSpan && nSpan < USHRT_MAX )
1401 return nSpan;
1402 return nDirect;
1403}
1404
1408bool SwTable::NewSplitRow( SwDoc& rDoc, const SwSelBoxes& rBoxes, sal_uInt16 nCnt,
1409 bool bSameHeight )
1410{
1411 CHECK_TABLE( *this )
1412 ++nCnt;
1413 FndBox_ aFndBox( nullptr, nullptr );
1414 aFndBox.SetTableLines( rBoxes, *this );
1415
1416 if( bSameHeight && rDoc.getIDocumentLayoutAccess().GetCurrentViewShell() )
1417 {
1418 SwSplitLines aRowLines;
1419 SwSplitLines aSplitLines;
1420 sal_uInt16 nFirst = lcl_CalculateSplitLineHeights( aRowLines, aSplitLines,
1421 *this, rBoxes, nCnt );
1422 aFndBox.DelFrames( *this );
1423 SwTwips nLast = 0;
1424 SwSplitLines::iterator pSplit = aSplitLines.begin();
1425 for( const auto& rCurr : aRowLines )
1426 {
1427 while( pSplit != aSplitLines.end() && *pSplit < rCurr )
1428 {
1429 InsertSpannedRow( rDoc, nFirst, 1 );
1430 SwTableLine* pRow = GetTabLines()[ nFirst ];
1431 SwFrameFormat* pRowFormat = pRow->ClaimFrameFormat();
1432 SwFormatFrameSize aFSz( pRowFormat->GetFrameSize() );
1434 aFSz.SetHeight( *pSplit - nLast );
1435 pRowFormat->SetFormatAttr( aFSz );
1436 nLast = *pSplit;
1437 ++pSplit;
1438 ++nFirst;
1439 }
1440 if( pSplit != aSplitLines.end() && rCurr == *pSplit )
1441 ++pSplit;
1442 SwTableLine* pRow = GetTabLines()[ nFirst ];
1443 SwFrameFormat* pRowFormat = pRow->ClaimFrameFormat();
1444 SwFormatFrameSize aFSz( pRowFormat->GetFrameSize() );
1446 aFSz.SetHeight( rCurr - nLast );
1447 pRowFormat->SetFormatAttr( aFSz );
1448 nLast = rCurr;
1449 ++nFirst;
1450 }
1451 }
1452 else
1453 {
1454 aFndBox.DelFrames( *this );
1455 bSameHeight = false;
1456 }
1457 if( !bSameHeight )
1458 {
1459 SwLineOffsetArray aLineOffs;
1460 lcl_SophisticatedFillLineIndices( aLineOffs, *this, rBoxes, nCnt );
1461 SwLineOffsetArray::reverse_iterator pCurr( aLineOffs.rbegin() );
1462 while( pCurr != aLineOffs.rend() )
1463 {
1464 InsertSpannedRow( rDoc, pCurr->first, pCurr->second );
1465 ++pCurr;
1466 }
1467 }
1468
1469 std::set<size_t> aIndices;
1470 for (size_t i = 0; i < rBoxes.size(); ++i)
1471 {
1472 OSL_ENSURE( rBoxes[i]->getRowSpan() != 1, "Forgot to split?" );
1473 if( rBoxes[i]->getRowSpan() > 1 )
1474 aIndices.insert( i );
1475 }
1476
1477 for( const auto& rCurrBox : aIndices )
1478 lcl_UnMerge( *this, *rBoxes[rCurrBox], nCnt, bSameHeight );
1479
1480 CHECK_TABLE( *this )
1481 // update the layout
1482 aFndBox.MakeFrames( *this );
1483
1484 return true;
1485}
1486
1491bool SwTable::InsertRow( SwDoc* pDoc, const SwSelBoxes& rBoxes,
1492 sal_uInt16 nCnt, bool bBehind )
1493{
1494 bool bRet = false;
1495 if( IsNewModel() )
1496 {
1497 CHECK_TABLE( *this )
1498 sal_uInt16 nRowIdx = lcl_LineIndex( *this, rBoxes, bBehind );
1499 if( nRowIdx < USHRT_MAX )
1500 {
1501 FndBox_ aFndBox( nullptr, nullptr );
1502 aFndBox.SetTableLines( rBoxes, *this );
1503 aFndBox.DelFrames( *this );
1504
1505 bRet = true;
1506 SwTableLine *pLine = GetTabLines()[ nRowIdx ];
1507 SwSelBoxes aLineBoxes;
1508 lcl_FillSelBoxes( aLineBoxes, *pLine );
1509 InsertRow_( pDoc, aLineBoxes, nCnt, bBehind );
1510 const size_t nBoxCount = pLine->GetTabBoxes().size();
1511 sal_uInt16 nOfs = bBehind ? 0 : 1;
1512 for( sal_uInt16 n = 0; n < nCnt; ++n )
1513 {
1514 SwTableLine *pNewLine = GetTabLines()[ nRowIdx+nCnt-n-nOfs];
1515 for( size_t nCurrBox = 0; nCurrBox < nBoxCount; ++nCurrBox )
1516 {
1517 sal_Int32 nRowSpan = pLine->GetTabBoxes()[nCurrBox]->getRowSpan();
1518 if( bBehind )
1519 {
1520 if( nRowSpan == 1 || nRowSpan == -1 )
1521 nRowSpan = n + 1;
1522 else if( nRowSpan > 1 )
1523 {
1524 nRowSpan = - nRowSpan;
1525
1526 // tdf#123102 disable numbering of the new hidden
1527 // paragraph in merged cells to avoid of bad
1528 // renumbering of next list elements
1529 SwTableBox* pBox = pNewLine->GetTabBoxes()[nCurrBox];
1530 SwNodeIndex aIdx( *pBox->GetSttNd(), +1 );
1531 SwContentNode* pCNd = aIdx.GetNode().GetContentNode();
1532 if( pCNd && pCNd->IsTextNode() && pCNd->GetTextNode()->GetNumRule() )
1533 {
1534 SwPaM aPam( *pCNd->GetTextNode(), *pCNd->GetTextNode() );
1535 pDoc->DelNumRules( aPam );
1536 }
1537 }
1538 }
1539 else
1540 {
1541 if( nRowSpan > 0 )
1542 nRowSpan = n + 1;
1543 else
1544 --nRowSpan;
1545 }
1546 pNewLine->GetTabBoxes()[ nCurrBox ]->setRowSpan( nRowSpan - n );
1547 }
1548 }
1549 if( bBehind )
1550 ++nRowIdx;
1551 if( nRowIdx )
1552 lcl_ChangeRowSpan( *this, nCnt, --nRowIdx, true );
1553 // update the layout
1554 aFndBox.MakeFrames( *this );
1555 }
1556 CHECK_TABLE( *this )
1557 }
1558 else
1559 bRet = InsertRow_( pDoc, rBoxes, nCnt, bBehind );
1560 return bRet;
1561}
1562
1568{
1569 if( !IsNewModel() )
1570 return;
1571
1572 for (size_t i = 0; i < rBoxes.size(); ++i)
1573 {
1574 SwTableBox* pBox = rBoxes[i];
1575 sal_Int32 nRowSpan = pBox->getRowSpan();
1576 if( nRowSpan != 1 && pBox->GetFrameFormat()->GetFrameSize().GetWidth() )
1577 {
1578 tools::Long nLeft = lcl_Box2LeftBorder( *pBox );
1579 SwTableLine *pLine = pBox->GetUpper();
1580 sal_uInt16 nLinePos = GetTabLines().GetPos( pLine);
1581 OSL_ENSURE( nLinePos < USHRT_MAX, "Box/table mismatch" );
1582 if( nRowSpan > 1 )
1583 {
1584 if( ++nLinePos < GetTabLines().size() )
1585 {
1586 pLine = GetTabLines()[ nLinePos ];
1587 pBox = lcl_LeftBorder2Box( nLeft, pLine );
1588 OSL_ENSURE( pBox, "RowSpan irritation I" );
1589 if( pBox )
1590 pBox->setRowSpan( --nRowSpan );
1591 }
1592 }
1593 else if( nLinePos > 0 )
1594 {
1595 do
1596 {
1597 pLine = GetTabLines()[ --nLinePos ];
1598 pBox = lcl_LeftBorder2Box( nLeft, pLine );
1599 OSL_ENSURE( pBox, "RowSpan irritation II" );
1600 if( pBox )
1601 {
1602 nRowSpan = pBox->getRowSpan();
1603 if( nRowSpan > 1 )
1604 {
1605 lcl_InvalidateCellFrame( *pBox );
1606 --nRowSpan;
1607 }
1608 else
1609 ++nRowSpan;
1610 pBox->setRowSpan( nRowSpan );
1611 }
1612 else
1613 nRowSpan = 1;
1614 }
1615 while( nRowSpan < 0 && nLinePos > 0 );
1616 }
1617 }
1618 }
1619}
1620
1625static void lcl_SearchSelBox( const SwTable &rTable, SwSelBoxes& rBoxes, tools::Long nMin, tools::Long nMax,
1626 SwTableLine& rLine, bool bChkProtected, bool bColumn )
1627{
1628 tools::Long nLeft = 0;
1629 tools::Long nRight = 0;
1630 tools::Long nMid = ( nMax + nMin )/ 2;
1631 const size_t nCount = rLine.GetTabBoxes().size();
1632 for( size_t nCurrBox = 0; nCurrBox < nCount; ++nCurrBox )
1633 {
1634 SwTableBox* pBox = rLine.GetTabBoxes()[nCurrBox];
1635 OSL_ENSURE( pBox, "Missing table box" );
1636 tools::Long nWidth = pBox->GetFrameFormat()->GetFrameSize().GetWidth();
1637 nRight += nWidth;
1638 if( nRight > nMin )
1639 {
1640 bool bAdd = false;
1641 if( nRight <= nMax )
1642 bAdd = nLeft >= nMin || nRight >= nMid ||
1643 nRight - nMin > nMin - nLeft;
1644 else
1645 bAdd = nLeft <= nMid || nRight - nMax < nMax - nLeft;
1646 sal_Int32 nRowSpan = pBox->getRowSpan();
1647 if( bAdd &&
1648 ( !bChkProtected ||
1650 {
1651 size_t const nOldCnt = rBoxes.size();
1652 rBoxes.insert( pBox );
1653 if( bColumn && nRowSpan != 1 && nOldCnt < rBoxes.size() )
1654 {
1655 SwTableBox *pMasterBox = pBox->getRowSpan() > 0 ? pBox
1656 : &pBox->FindStartOfRowSpan( rTable );
1657 lcl_getAllMergedBoxes( rTable, rBoxes, *pMasterBox );
1658 }
1659 }
1660 }
1661 if( nRight >= nMax )
1662 break;
1663 nLeft = nRight;
1664 }
1665}
1666
1671void SwTable::CreateSelection( const SwPaM& rPam, SwSelBoxes& rBoxes,
1672 const SearchType eSearch, bool bChkProtected ) const
1673{
1674 OSL_ENSURE( m_bNewModel, "Don't call me for old tables" );
1675 if( m_aLines.empty() )
1676 return;
1677 const SwNode* pStartNd = rPam.GetPoint()->GetNode().FindTableBoxStartNode();
1678 const SwNode* pEndNd = rPam.GetMark()->GetNode().FindTableBoxStartNode();
1679 if( !pStartNd || !pEndNd )
1680 return;
1681 CreateSelection( pStartNd, pEndNd, rBoxes, eSearch, bChkProtected );
1682}
1683
1687void SwTable::CreateSelection( const SwNode* pStartNd, const SwNode* pEndNd,
1688 SwSelBoxes& rBoxes, const SearchType eSearch, bool bChkProtected ) const
1689{
1690 rBoxes.clear();
1691 // Looking for start and end of the selection given by SwNode-pointer
1692 const size_t nLines = m_aLines.size();
1693 // nTop becomes the line number of the upper box
1694 // nBottom becomes the line number of the lower box
1695 size_t nTop = 0;
1696 size_t nBottom = 0;
1697 // nUpperMin becomes the left border value of the upper box
1698 // nUpperMax becomes the right border of the upper box
1699 // nLowerMin and nLowerMax the borders of the lower box
1700 tools::Long nUpperMin = 0, nUpperMax = 0;
1701 tools::Long nLowerMin = 0, nLowerMax = 0;
1702 // nFound will incremented if a box is found
1703 // 0 => no box found; 1 => the upper box has been found; 2 => both found
1704 int nFound = 0;
1705 for( size_t nRow = 0; nFound < 2 && nRow < nLines; ++nRow )
1706 {
1707 SwTableLine* pLine = m_aLines[nRow];
1708 OSL_ENSURE( pLine, "Missing table line" );
1709 const size_t nCols = pLine->GetTabBoxes().size();
1710 for( size_t nCol = 0; nCol < nCols; ++nCol )
1711 {
1712 SwTableBox* pBox = pLine->GetTabBoxes()[nCol];
1713 OSL_ENSURE( pBox, "Missing table box" );
1714 if( pBox->GetSttNd() == pEndNd || pBox->GetSttNd() == pStartNd )
1715 {
1716 if( !bChkProtected ||
1718 rBoxes.insert( pBox );
1719 if( nFound )
1720 {
1721 nBottom = nRow;
1722 lcl_CheckMinMax( nLowerMin, nLowerMax, *pLine, nCol, true );
1723 ++nFound;
1724 break;
1725 }
1726 else
1727 {
1728 nTop = nRow;
1729 lcl_CheckMinMax( nUpperMin, nUpperMax, *pLine, nCol, true );
1730 ++nFound;
1731 // If start and end node are identical, we're nearly done...
1732 if( pEndNd == pStartNd )
1733 {
1734 nBottom = nTop;
1735 nLowerMin = nUpperMin;
1736 nLowerMax = nUpperMax;
1737 ++nFound;
1738 }
1739 }
1740 }
1741 }
1742 }
1743 if( nFound < 2 )
1744 return; // At least one node was not a part of the given table
1745 if( eSearch == SEARCH_ROW )
1746 {
1747 // Selection of a row is quiet easy:
1748 // every (unprotected) box between start and end line
1749 // with a positive row span will be collected
1750 for( size_t nRow = nTop; nRow <= nBottom; ++nRow )
1751 {
1752 SwTableLine* pLine = m_aLines[nRow];
1753 OSL_ENSURE( pLine, "Missing table line" );
1754 const size_t nCount = pLine->GetTabBoxes().size();
1755 for( size_t nCurrBox = 0; nCurrBox < nCount; ++nCurrBox )
1756 {
1757 SwTableBox* pBox = pLine->GetTabBoxes()[nCurrBox];
1758 OSL_ENSURE( pBox, "Missing table box" );
1759 if( pBox->getRowSpan() > 0 && ( !bChkProtected ||
1761 rBoxes.insert( pBox );
1762 }
1763 }
1764 return;
1765 }
1766 bool bCombine = nTop == nBottom;
1767 if( !bCombine )
1768 {
1769 tools::Long nMinWidth = nUpperMax - nUpperMin;
1770 tools::Long nTmp = nLowerMax - nLowerMin;
1771 if( nMinWidth > nTmp )
1772 nMinWidth = nTmp;
1773 nTmp = std::min(nLowerMax, nUpperMax);
1774 nTmp -= ( nLowerMin < nUpperMin ) ? nUpperMin : nLowerMin;
1775 // If the overlapping between upper and lower box is less than half
1776 // of the width (of the smaller cell), bCombine is set,
1777 // e.g. if upper and lower cell are in different columns
1778 bCombine = ( nTmp + nTmp < nMinWidth );
1779 }
1780 if( bCombine )
1781 {
1782 if( nUpperMin < nLowerMin )
1783 nLowerMin = nUpperMin;
1784 else
1785 nUpperMin = nLowerMin;
1786 if( nUpperMax > nLowerMax )
1787 nLowerMax = nUpperMax;
1788 else
1789 nUpperMax = nLowerMax;
1790 }
1791 const bool bColumn = eSearch == SEARCH_COL;
1792 if( bColumn )
1793 {
1794 for( size_t i = 0; i < nTop; ++i )
1795 lcl_SearchSelBox( *this, rBoxes, nUpperMin, nUpperMax,
1796 *m_aLines[i], bChkProtected, bColumn );
1797 }
1798
1799 {
1800 tools::Long nMin = std::min(nUpperMin, nLowerMin);
1801 tools::Long nMax = nUpperMax < nLowerMax ? nLowerMax : nUpperMax;
1802 for( size_t i = nTop; i <= nBottom; ++i )
1803 lcl_SearchSelBox( *this, rBoxes, nMin, nMax, *m_aLines[i],
1804 bChkProtected, bColumn );
1805 }
1806 if( bColumn )
1807 {
1808 for( size_t i = nBottom + 1; i < nLines; ++i )
1809 lcl_SearchSelBox( *this, rBoxes, nLowerMin, nLowerMax, *m_aLines[i],
1810 bChkProtected, true );
1811 }
1812}
1813
1819{
1820 OSL_ENSURE( m_bNewModel, "Don't call me for old tables" );
1821 rMin = 0;
1822 rMax = 0;
1823 if( m_aLines.empty() || rBoxes.empty() )
1824 return;
1825
1826 const size_t nLineCnt = m_aLines.size();
1827 const size_t nBoxCnt = rBoxes.size();
1828 size_t nBox = 0;
1829 for( size_t nRow = 0; nRow < nLineCnt && nBox < nBoxCnt; ++nRow )
1830 {
1831 SwTableLine* pLine = m_aLines[nRow];
1832 OSL_ENSURE( pLine, "Missing table line" );
1833 const size_t nCols = pLine->GetTabBoxes().size();
1834 for( size_t nCol = 0; nCol < nCols; ++nCol )
1835 {
1836 SwTableBox* pBox = pLine->GetTabBoxes()[nCol];
1837 OSL_ENSURE( pBox, "Missing table box" );
1838 if( pBox == rBoxes[nBox] )
1839 {
1840 lcl_CheckMinMax( rMin, rMax, *pLine, nCol, nBox == 0 );
1841 if( ++nBox >= nBoxCnt )
1842 break;
1843 }
1844 }
1845 }
1846 for( size_t nRow = 0; nRow < nLineCnt; ++nRow )
1847 {
1848 SwTableLine* pLine = m_aLines[nRow];
1849 const size_t nCols = pLine->GetTabBoxes().size();
1850 tools::Long nRight = 0;
1851 for( size_t nCurrBox = 0; nCurrBox < nCols; ++nCurrBox )
1852 {
1853 tools::Long nLeft = nRight;
1854 SwTableBox* pBox = pLine->GetTabBoxes()[nCurrBox];
1855 nRight += pBox->GetFrameFormat()->GetFrameSize().GetWidth();
1856 if( nLeft >= rMin && nRight <= rMax )
1857 rBoxes.insert( pBox );
1858 }
1859 }
1860}
1861
1866{
1867 OSL_ENSURE( m_bNewModel, "Don't call me for old tables" );
1868 if( m_aLines.empty() || nMax < nMin )
1869 return;
1870 tools::Long nMid = nMin ? ( nMin + nMax ) / 2 : 0;
1872 if( nTabSize == nMax )
1873 nMid = nMax;
1874 const size_t nLineCnt = m_aLines.size();
1875 for( size_t nRow = 0; nRow < nLineCnt; ++nRow )
1876 {
1877 SwTableLine* pLine = m_aLines[nRow];
1878 const size_t nCols = pLine->GetTabBoxes().size();
1879 tools::Long nRight = 0;
1880 for( size_t nCurrBox = 0; nCurrBox < nCols; ++nCurrBox )
1881 {
1882 tools::Long nLeft = nRight;
1883 SwTableBox* pBox = pLine->GetTabBoxes()[nCurrBox];
1884 nRight += pBox->GetFrameFormat()->GetFrameSize().GetWidth();
1885 if( nRight < nMin )
1886 continue;
1887 if( nLeft > nMax )
1888 break;
1889 tools::Long nNewWidth = -1;
1890 if( nLeft < nMin )
1891 {
1892 if( nRight <= nMax )
1893 nNewWidth = nMid - nLeft;
1894 }
1895 else if( nRight > nMax )
1896 nNewWidth = nRight - nMid;
1897 else
1898 nNewWidth = 0;
1899 if( nNewWidth >= 0 )
1900 {
1901 SwFrameFormat* pFrameFormat = pBox->ClaimFrameFormat();
1902 SwFormatFrameSize aFrameSz( pFrameFormat->GetFrameSize() );
1903 aFrameSz.SetWidth( nNewWidth );
1904 pFrameFormat->SetFormatAttr( aFrameSz );
1905 }
1906 }
1907 }
1908}
1909
1915{
1916 for (size_t i = 0; i < rBoxes.size(); ++i)
1917 {
1918 SwTableBox *pBox = rBoxes[i];
1919 sal_Int32 nRowSpan = pBox->getRowSpan();
1920 if( nRowSpan != 1 )
1921 {
1922 SwTableBox *pMasterBox = nRowSpan > 0 ? pBox
1923 : &pBox->FindStartOfRowSpan( *this );
1924 lcl_getAllMergedBoxes( *this, rBoxes, *pMasterBox );
1925 }
1926 }
1927}
1928
1933void SwTable::CheckRowSpan( SwTableLine* &rpLine, bool bUp ) const
1934{
1935 OSL_ENSURE( IsNewModel(), "Don't call me for old tables" );
1936 sal_uInt16 nLineIdx = GetTabLines().GetPos( rpLine );
1937 OSL_ENSURE( nLineIdx < GetTabLines().size(), "Start line out of range" );
1938 bool bChange = true;
1939 if( bUp )
1940 {
1941 while( bChange )
1942 {
1943 bChange = false;
1944 rpLine = GetTabLines()[ nLineIdx ];
1945 const size_t nCols = rpLine->GetTabBoxes().size();
1946 for( size_t nCol = 0; !bChange && nCol < nCols; ++nCol )
1947 {
1948 SwTableBox* pBox = rpLine->GetTabBoxes()[nCol];
1949 if( pBox->getRowSpan() > 1 || pBox->getRowSpan() < -1 )
1950 bChange = true;
1951 }
1952 if( bChange )
1953 {
1954 if( nLineIdx )
1955 --nLineIdx;
1956 else
1957 {
1958 bChange = false;
1959 rpLine = nullptr;
1960 }
1961 }
1962 }
1963 }
1964 else
1965 {
1966 const size_t nMaxLine = GetTabLines().size();
1967 while( bChange )
1968 {
1969 bChange = false;
1970 rpLine = GetTabLines()[ nLineIdx ];
1971 const size_t nCols = rpLine->GetTabBoxes().size();
1972 for( size_t nCol = 0; !bChange && nCol < nCols; ++nCol )
1973 {
1974 SwTableBox* pBox = rpLine->GetTabBoxes()[nCol];
1975 if( pBox->getRowSpan() < 0 )
1976 bChange = true;
1977 }
1978 if( bChange )
1979 {
1980 ++nLineIdx;
1981 if( nLineIdx >= nMaxLine )
1982 {
1983 bChange = false;
1984 rpLine = nullptr;
1985 }
1986 }
1987 }
1988 }
1989}
1990
1991// This structure corrects the row span attributes for a top line of a table
1992// In a top line no negative row span is allowed, so these have to be corrected.
1993// If there has been at least one correction, all values are stored
1994// and can be used by undo of table split
1995SwSaveRowSpan::SwSaveRowSpan( SwTableBoxes& rBoxes, sal_uInt16 nSplitLn )
1996 : mnSplitLine( nSplitLn )
1997{
1998 bool bDontSave = true; // nothing changed, nothing to save
1999 const size_t nColCount = rBoxes.size();
2000 OSL_ENSURE( nColCount, "Empty Table Line" );
2001 mnRowSpans.resize( nColCount );
2002 for( size_t nCurrCol = 0; nCurrCol < nColCount; ++nCurrCol )
2003 {
2004 SwTableBox* pBox = rBoxes[nCurrCol];
2005 OSL_ENSURE( pBox, "Missing Table Box" );
2006 sal_Int32 nRowSp = pBox->getRowSpan();
2007 mnRowSpans[ nCurrCol ] = nRowSp;
2008 if( nRowSp < 0 )
2009 {
2010 bDontSave = false;
2011 nRowSp = -nRowSp;
2012 pBox->setRowSpan( nRowSp ); // correction needed
2013 }
2014 }
2015 if( bDontSave )
2016 mnRowSpans.clear();
2017}
2018
2019// This function is called by undo of table split to restore the old row span
2020// values at the split line
2022{
2023 if( !IsNewModel() ) // for new model only
2024 return;
2025 sal_uInt16 nLineCount = GetTabLines().size();
2026 OSL_ENSURE( rSave.mnSplitLine < nLineCount, "Restore behind last line?" );
2027 if( rSave.mnSplitLine >= nLineCount )
2028 return;
2029
2030 SwTableLine* pLine = GetTabLines()[rSave.mnSplitLine];
2031 const size_t nColCount = pLine->GetTabBoxes().size();
2032 OSL_ENSURE( nColCount, "Empty Table Line" );
2033 OSL_ENSURE( nColCount == rSave.mnRowSpans.size(), "Wrong row span store" );
2034 if( nColCount != rSave.mnRowSpans.size() )
2035 return;
2036
2037 for( size_t nCurrCol = 0; nCurrCol < nColCount; ++nCurrCol )
2038 {
2039 SwTableBox* pBox = pLine->GetTabBoxes()[nCurrCol];
2040 OSL_ENSURE( pBox, "Missing Table Box" );
2041 sal_Int32 nRowSp = pBox->getRowSpan();
2042 if( nRowSp != rSave.mnRowSpans[ nCurrCol ] )
2043 {
2044 OSL_ENSURE( -nRowSp == rSave.mnRowSpans[ nCurrCol ], "Pardon me?!" );
2045 OSL_ENSURE( rSave.mnRowSpans[ nCurrCol ] < 0, "Pardon me?!" );
2046 pBox->setRowSpan( -nRowSp );
2047
2048 sal_uInt16 nLine = rSave.mnSplitLine;
2049 if( nLine )
2050 {
2051 tools::Long nLeftBorder = lcl_Box2LeftBorder( *pBox );
2052 SwTableBox* pNext;
2053 do
2054 {
2055 pNext = lcl_LeftBorder2Box( nLeftBorder, GetTabLines()[--nLine] );
2056 if( pNext )
2057 {
2058 pBox = pNext;
2059 tools::Long nNewSpan = pBox->getRowSpan();
2060 if( pBox->getRowSpan() < 1 )
2061 nNewSpan -= nRowSp;
2062 else
2063 {
2064 nNewSpan += nRowSp;
2065 pNext = nullptr;
2066 }
2067 pBox->setRowSpan( nNewSpan );
2068 }
2069 } while( nLine && pNext );
2070 }
2071 }
2072 }
2073}
2074
2075std::unique_ptr<SwSaveRowSpan> SwTable::CleanUpTopRowSpan( sal_uInt16 nSplitLine )
2076{
2077 if( !IsNewModel() )
2078 return nullptr;
2079 std::unique_ptr<SwSaveRowSpan> pRet(new SwSaveRowSpan( GetTabLines()[0]->GetTabBoxes(), nSplitLine ));
2080 if( pRet->mnRowSpans.empty() )
2081 return nullptr;
2082 return pRet;
2083}
2084
2085void SwTable::CleanUpBottomRowSpan( sal_uInt16 nDelLines )
2086{
2087 if( !IsNewModel() )
2088 return;
2089 const size_t nLastLine = GetTabLines().size()-1;
2090 SwTableLine* pLine = GetTabLines()[nLastLine];
2091 const size_t nColCount = pLine->GetTabBoxes().size();
2092 OSL_ENSURE( nColCount, "Empty Table Line" );
2093 for( size_t nCurrCol = 0; nCurrCol < nColCount; ++nCurrCol )
2094 {
2095 SwTableBox* pBox = pLine->GetTabBoxes()[nCurrCol];
2096 OSL_ENSURE( pBox, "Missing Table Box" );
2097 sal_Int32 nRowSp = pBox->getRowSpan();
2098 if( nRowSp < 0 )
2099 nRowSp = -nRowSp;
2100 if( nRowSp > 1 )
2101 {
2102 lcl_ChangeRowSpan( *this, -static_cast<tools::Long>(nDelLines),
2103 o3tl::narrowing<sal_uInt16>(nLastLine), false );
2104 break;
2105 }
2106 }
2107}
2108
2127void SwTable::ConvertSubtableBox(sal_uInt16 const nRow, sal_uInt16 const nBox)
2128{
2129 SwDoc *const pDoc(GetFrameFormat()->GetDoc());
2130 SwTableLine *const pSourceLine(GetTabLines()[nRow]);
2131 SwTableBox *const pSubTableBox(pSourceLine->GetTabBoxes()[nBox]);
2132 assert(!pSubTableBox->GetTabLines().empty());
2133 // are relative (%) heights possible? apparently not
2134 SwFormatFrameSize const outerSize(pSourceLine->GetFrameFormat()->GetFrameSize());
2135 tools::Long minHeights(0);
2136 {
2137 SwFrameFormat const& rSubLineFormat(*pSubTableBox->GetTabLines()[0]->GetFrameFormat());
2138 SwFormatFrameSize const* pSize = rSubLineFormat.GetItemIfSet(RES_FRM_SIZE);
2139 if (pSize)
2140 { // for first row, apply height from inner row to outer row.
2141 // in case the existing outer row height was larger than the entire
2142 // subtable, the last inserted row needs to be tweaked (below)
2143 pSourceLine->GetFrameFormat()->SetFormatAttr(*pSize);
2145 {
2146 minHeights += pSize->GetHeight();
2147 }
2148 }
2149 }
2150 for (size_t i = 1; i < pSubTableBox->GetTabLines().size(); ++i)
2151 {
2152 SwTableLine *const pSubLine(pSubTableBox->GetTabLines()[i]);
2153 SwTableLine *const pNewLine = new SwTableLine(
2154 static_cast<SwTableLineFormat*>(pSourceLine->GetFrameFormat()),
2155 pSourceLine->GetTabBoxes().size() - 1 + pSubLine->GetTabBoxes().size(),
2156 nullptr);
2157 SwFrameFormat const& rSubLineFormat(*pSubLine->GetFrameFormat());
2158 SwFormatFrameSize const* pSize = rSubLineFormat.GetItemIfSet(RES_FRM_SIZE);
2159 if (pSize)
2160 { // for rows 2..N, copy inner row height to outer row
2161 pNewLine->ClaimFrameFormat();
2162 pNewLine->GetFrameFormat()->SetFormatAttr(*pSize);
2164 {
2165 minHeights += pSize->GetHeight();
2166 }
2167 }
2168 // ensure the sum of the lines is at least as high as the outer line was
2169 if (i == pSubTableBox->GetTabLines().size() - 1
2171 && minHeights < outerSize.GetHeight())
2172 {
2173 SwFormatFrameSize lastSize(pNewLine->GetFrameFormat()->GetFrameSize());
2174 lastSize.SetHeight(lastSize.GetHeight() + outerSize.GetHeight() - minHeights);
2175 if (lastSize.GetHeightSizeType() == SwFrameSize::Variable)
2176 {
2178 }
2179 pNewLine->GetFrameFormat()->SetFormatAttr(lastSize);
2180 }
2181 SfxPoolItem const* pRowBrush(nullptr);
2182 (void)rSubLineFormat.GetItemState(RES_BACKGROUND, true, &pRowBrush);
2183 GetTabLines().insert(GetTabLines().begin() + nRow + i, pNewLine);
2184 for (size_t j = 0; j < pSourceLine->GetTabBoxes().size(); ++j)
2185 {
2186 if (j == nBox)
2187 {
2188 for (size_t k = 0; k < pSubLine->GetTabBoxes().size(); ++k)
2189 {
2190 // move box k to new outer row
2191 SwTableBox *const pSourceBox(pSubLine->GetTabBoxes()[k]);
2192 assert(pSourceBox->getRowSpan() == 1);
2193 // import filter (xmltbli.cxx) converts all box widths to absolute
2194 assert(pSourceBox->GetFrameFormat()->GetFrameSize().GetWidthPercent() == 0);
2195 ::InsTableBox(*pDoc, GetTableNode(), pNewLine,
2196 static_cast<SwTableBoxFormat*>(pSourceBox->GetFrameFormat()),
2197 pSourceBox, j+k, 1);
2198 // insert dummy text node...
2199 pDoc->GetNodes().MakeTextNode(
2200 SwNodeIndex(*pSourceBox->GetSttNd(), +1).GetNode(),
2201 pDoc->GetDfltTextFormatColl());
2202 SwNodeRange content(*pSourceBox->GetSttNd(), SwNodeOffset(+2),
2203 *pSourceBox->GetSttNd()->EndOfSectionNode());
2204 SwTableBox *const pNewBox(pNewLine->GetTabBoxes()[j+k]);
2205 SwNodeIndex insPos(*pNewBox->GetSttNd(), 1);
2206 // MoveNodes would delete the box SwStartNode/SwEndNode
2207 // without the dummy node
2208#if 0
2209 pDoc->GetNodes().MoveNodes(content, pDoc->GetNodes(), insPos, false);
2210#else
2212#endif
2213 // delete the empty node that was bundled in the new box
2214 pDoc->GetNodes().Delete(insPos);
2215 if (pRowBrush)
2216 {
2217 if (pNewBox->GetFrameFormat()->GetItemState(RES_BACKGROUND, true) != SfxItemState::SET)
2218 { // set inner row background on inner cell
2219 pNewBox->ClaimFrameFormat();
2220 pNewBox->GetFrameFormat()->SetFormatAttr(*pRowBrush);
2221 }
2222 }
2223 // assume that the borders can be left as they are, because
2224 // lines don't have borders, only boxes do
2225 }
2226 }
2227 else
2228 {
2229 // insert covered cell for box j
2230 SwTableBox *const pSourceBox(pSourceLine->GetTabBoxes()[j]);
2231 assert(pSourceBox->GetTabLines().empty()); // checked for that
2232 sal_uInt16 const nInsPos(j < nBox ? j : j + pSubLine->GetTabBoxes().size() - 1);
2233 ::InsTableBox(*pDoc, GetTableNode(), pNewLine,
2234 static_cast<SwTableBoxFormat*>(pSourceBox->GetFrameFormat()),
2235 pSourceBox, nInsPos, 1);
2236 // adjust row span:
2237 // N rows in subtable, N-1 rows inserted:
2238 // -1 -> -N ; -(N-1) ... -1
2239 // -2 -> -(N+1) ; -N .. -2
2240 // 1 -> N ; -(N-1) .. -1
2241 // 2 -> N+1 ; -N .. -2
2242 sal_Int32 newSourceRowSpan(pSourceBox->getRowSpan());
2243 sal_Int32 newBoxRowSpan;
2244 if (newSourceRowSpan < 0)
2245 {
2246 newSourceRowSpan -= pSubTableBox->GetTabLines().size() - 1;
2247 newBoxRowSpan = newSourceRowSpan + i;
2248 }
2249 else
2250 {
2251 newSourceRowSpan += pSubTableBox->GetTabLines().size() - 1;
2252 newBoxRowSpan = -(newSourceRowSpan - sal::static_int_cast<tools::Long>(i));
2253 }
2254 pNewLine->GetTabBoxes()[nInsPos]->setRowSpan(newBoxRowSpan);
2255 if (i == pSubTableBox->GetTabLines().size() - 1)
2256 { // only last iteration
2257 pSourceBox->setRowSpan(newSourceRowSpan);
2258 }
2259 }
2260 }
2261 }
2262 // delete inner rows 2..N
2263 while (1 < pSubTableBox->GetTabLines().size())
2264 {
2265 // careful: the last box deletes pSubLine!
2266 SwTableLine *const pSubLine(pSubTableBox->GetTabLines()[1]);
2267 for (size_t j = pSubLine->GetTabBoxes().size(); 0 < j; --j)
2268 {
2269 SwTableBox *const pBox(pSubLine->GetTabBoxes()[0]);
2270 DeleteBox_(*this, pBox, nullptr, false, false, nullptr);
2271 }
2272 }
2273 // fix row spans in lines preceding nRow
2274 lcl_ChangeRowSpan(*this, pSubTableBox->GetTabLines().size() - 1, nRow - 1, false);
2275 // note: the first line of the inner table remains; caller will call
2276 // GCLines() to remove it
2277}
2278
2280{
2281 for (SwTableLine const*const pLine : GetTabLines())
2282 {
2283 bool haveSubtable(false);
2284 for (SwTableBox const*const pBox : pLine->GetTabBoxes())
2285 {
2286 if (pBox->IsFormulaOrValueBox() == RES_BOXATR_FORMULA)
2287 {
2288 return false; // no table box formulas yet
2289 }
2290 if (!pBox->GetTabLines().empty())
2291 {
2292 if (haveSubtable)
2293 { // can't handle 2 subtable in a row yet
2294 return false;
2295 }
2296 haveSubtable = true;
2297 for (SwTableLine const*const pInnerLine : pBox->GetTabLines())
2298 {
2299 // bitmap row background will look different
2300 SwFrameFormat const& rRowFormat(*pInnerLine->GetFrameFormat());
2301 std::unique_ptr<SvxBrushItem> pBrush(rRowFormat.makeBackgroundBrushItem());
2302 assert(pBrush);
2303 if (pBrush->GetGraphicObject() != nullptr)
2304 {
2305 /* TODO: all cells could override this?
2306 for (SwTableBox & rInnerBox : rInnerLine.GetTabBoxes())
2307 */
2308 if (1 < pInnerLine->GetTabBoxes().size()) // except if only 1 cell?
2309 {
2310 return false;
2311 }
2312 }
2313 for (SwTableBox const*const pInnerBox : pInnerLine->GetTabBoxes())
2314 {
2315 if (!pInnerBox->GetTabLines().empty())
2316 {
2317 return false; // nested subtable :(
2318 }
2319 }
2320 }
2321 }
2322 }
2323 }
2324 // note: fields that refer to table cells may be *outside* the table,
2325 // so the entire document needs to be imported before checking here
2326 // (same for table box formulas and charts)
2327 SwDoc *const pDoc(GetFrameFormat()->GetDoc());
2328 SwFieldType const*const pTableFields(
2330 std::vector<SwFormatField*> vFields;
2331 pTableFields->GatherFields(vFields);
2332 if (!vFields.empty())
2333 {
2334 return false; // no formulas in fields yet
2335 }
2337 {
2338 return false; // no table box formulas yet
2339 }
2340 OUString const tableName(GetFrameFormat()->GetName());
2342 while (SwStartNode const*const pStartNode = temp.GetNode().GetStartNode())
2343 {
2344 ++temp;
2345 SwOLENode const*const pOLENode(temp.GetNode().GetOLENode());
2346 if (pOLENode && tableName == pOLENode->GetChartTableName())
2347 { // there are charts that refer to this table
2348 // presumably such charts would need to be adapted somehow?
2349 return false;
2350 }
2351 temp.Assign(*pStartNode->EndOfSectionNode(), +1);
2352 }
2353 return true;
2354}
2355
2357{
2358 for (size_t i = 0; i < GetTabLines().size(); ++i)
2359 {
2360 SwTableLine *const pLine(GetTabLines()[i]);
2361 for (size_t j = 0; j < pLine->GetTabBoxes().size(); ++j)
2362 {
2363 SwTableBox *const pBox(pLine->GetTabBoxes()[j]);
2364 SwTableLines & rInnerLines(pBox->GetTabLines());
2365 if (!rInnerLines.empty())
2366 {
2368 }
2369 }
2370 }
2371 GCLines();
2372 m_bNewModel = true;
2373#if 0
2374 // note: outline nodes (and ordinary lists) are sorted by MoveNodes() itself
2375 // (this could change order inside table of contents, but that's a
2376 // really esoteric use-case)
2377 // nodes were moved - sort marks, redlines, footnotes
2378 SwDoc *const pDoc(GetFrameFormat()->GetDoc());
2382#endif
2383 // assume that there aren't any node indexes to the deleted box start/end nodes
2384 CHECK_TABLE( *this )
2385}
2386
2387#ifdef DBG_UTIL
2388
2389namespace {
2390
2391struct RowSpanCheck
2392{
2393 sal_Int32 nRowSpan;
2394 SwTwips nLeft;
2395 SwTwips nRight;
2396};
2397
2398}
2399
2401{
2402 if( !IsNewModel() )
2403 return;
2404 const size_t nLineCount = GetTabLines().size();
2406 SwTwips nLineWidth = 0;
2407 std::list< RowSpanCheck > aRowSpanCells;
2408 std::list< RowSpanCheck >::iterator aIter = aRowSpanCells.end();
2410 ++index;
2411 for( size_t nCurrLine = 0; nCurrLine < nLineCount; ++nCurrLine )
2412 {
2413 SwTwips nWidth = 0;
2414 SwTableLine* pLine = GetTabLines()[nCurrLine];
2415 SAL_WARN_IF( !pLine, "sw.core", "Missing Table Line" );
2416 const size_t nColCount = pLine->GetTabBoxes().size();
2417 SAL_WARN_IF( !nColCount, "sw.core", "Empty Table Line" );
2418 for( size_t nCurrCol = 0; nCurrCol < nColCount; ++nCurrCol )
2419 {
2420 SwTableBox* pBox = pLine->GetTabBoxes()[nCurrCol];
2421 assert(pBox);
2422 SAL_WARN_IF(GetTableNode()->EndOfSectionIndex() <= index.GetIndex(), "sw.core", "Box not in table nodes");
2423 SAL_WARN_IF(!index.GetNode().IsStartNode(), "sw.core", "No box start node");
2424 index = *index.GetNode().EndOfSectionNode();
2425 ++index;
2426 SwTwips nNewWidth = pBox->GetFrameFormat()->GetFrameSize().GetWidth() + nWidth;
2427 sal_Int32 nRowSp = pBox->getRowSpan();
2428 if( nRowSp < 0 )
2429 {
2430 SAL_WARN_IF( aIter == aRowSpanCells.end(),
2431 "sw.core", "Missing master box");
2432 if (aIter != aRowSpanCells.end())
2433 {
2434 SAL_WARN_IF( aIter->nLeft != nWidth || aIter->nRight != nNewWidth,
2435 "sw.core", "Wrong position/size of overlapped table box");
2436 --(aIter->nRowSpan);
2437 SAL_WARN_IF( aIter->nRowSpan != -nRowSp, "sw.core",
2438 "Wrong row span value" );
2439 if( nRowSp == -1 )
2440 {
2441 aIter = aRowSpanCells.erase(aIter);
2442 }
2443 else
2444 ++aIter;
2445 }
2446 }
2447 else if( nRowSp != 1 )
2448 {
2449 SAL_WARN_IF( !nRowSp, "sw.core", "Zero row span?!" );
2450 RowSpanCheck aEntry;
2451 aEntry.nLeft = nWidth;
2452 aEntry.nRight = nNewWidth;
2453 aEntry.nRowSpan = nRowSp;
2454 aRowSpanCells.insert( aIter, aEntry );
2455 }
2456 nWidth = nNewWidth;
2457 }
2458 if( !nCurrLine )
2459 nLineWidth = nWidth;
2460 SAL_WARN_IF( nWidth != nLineWidth, "sw.core",
2461 "Different Line Widths: first: " << nLineWidth
2462 << " current [" << nCurrLine << "]: " << nWidth);
2463 SAL_WARN_IF( std::abs(nWidth - nTabSize) > 1 /* how tolerant? */, "sw.core",
2464 "Line width differs from table width: " << nTabSize
2465 << " current [" << nCurrLine << "]: " << nWidth);
2466 SAL_WARN_IF( nWidth < 0 || nWidth > USHRT_MAX, "sw.core",
2467 "Width out of range [" << nCurrLine << "]: " << nWidth);
2468 SAL_WARN_IF( aIter != aRowSpanCells.end(), "sw.core",
2469 "Missing overlapped box" );
2470 aIter = aRowSpanCells.begin();
2471 }
2472 bool bEmpty = aRowSpanCells.empty();
2473 SAL_WARN_IF( !bEmpty, "sw.core", "Open row span detected" );
2474 SAL_WARN_IF(GetTableNode()->EndOfSectionNode() != &index.GetNode(), "sw.core", "table end node not found");
2475}
2476
2477#endif
2478
2479/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
sal_Int32 nLineWidth
void SetTableLines(const SwSelBoxes &rBoxes, const SwTable &rTable)
Definition: tblsel.cxx:2097
void MakeFrames(SwTable &rTable)
Definition: tblsel.cxx:2320
void DelFrames(SwTable &rTable)
Definition: tblsel.cxx:2159
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
virtual bool DoesUndo() const =0
Is Undo enabled?
virtual void DoUndo(bool const bDoUndo)=0
Enable/Disable Undo.
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
SwContentIndex & Assign(const SwContentNode *, sal_Int32)
Definition: index.cxx:206
virtual sal_Int32 Len() const
Definition: node.cxx:1257
Definition: doc.hxx:192
IDocumentContentOperations const & getIDocumentContentOperations() const
Definition: doc.cxx:316
IDocumentUndoRedo & GetIDocumentUndoRedo()
Definition: doc.cxx:145
SwNodes & GetNodes()
Definition: doc.hxx:413
IDocumentFieldsAccess const & getIDocumentFieldsAccess() const
Definition: doc.cxx:358
IDocumentRedlineAccess const & getIDocumentRedlineAccess() const
Definition: doc.cxx:336
void DelNumRules(const SwPaM &, SwRootFrame const *pLayout=nullptr)
Definition: docnum.cxx:1314
IDocumentLayoutAccess const & getIDocumentLayoutAccess() const
Definition: doc.cxx:406
SwFootnoteIdxs & GetFootnoteIdxs()
Definition: doc.hxx:638
IDocumentMarkAccess * getIDocumentMarkAccess()
Definition: docbm.cxx:1793
const SwTextFormatColl * GetDfltTextFormatColl() const
Definition: doc.hxx:780
const SwAttrPool & GetAttrPool() const
Definition: doc.hxx:1322
Instances of SwFields and those derived from it occur 0 to n times.
Definition: fldbas.hxx:242
void GatherFields(std::vector< SwFormatField * > &rvFormatFields, bool bCollectOnlyInDocNodes=true) const
Definition: fldbas.cxx:203
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:84
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
const SvxProtectItem & GetProtect(bool=true) const
Definition: frmatr.hxx:82
SfxItemState GetItemState(sal_uInt16 nWhich, bool bSrchInParent=true, const SfxPoolItem **ppItem=nullptr) const
Definition: format.cxx:387
virtual bool SetFormatAttr(const SfxPoolItem &rAttr)
Definition: format.cxx:449
std::unique_ptr< SvxBrushItem > makeBackgroundBrushItem(bool=true) const
Definition: format.cxx:738
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:62
Base class of the Writer layout elements.
Definition: frame.hxx:315
SwFrame * GetLower()
Definition: findfrm.cxx:194
void InvalidateSize_()
Definition: frame.hxx:771
TElementType * Next()
Definition: calbck.hxx:364
TElementType * First()
Definition: calbck.hxx:356
Marks a node in the document model.
Definition: ndindex.hxx:31
SwNodeIndex & Assign(SwNodes const &rNds, SwNodeOffset)
Definition: ndindex.hxx:291
SwNode & GetNode() const
Definition: ndindex.hxx:136
Base class of the Writer document model elements.
Definition: node.hxx:84
SwStartNode * GetStartNode()
Definition: node.hxx:619
SwTextNode * GetTextNode()
Inline methods from Node.hxx.
Definition: ndtxt.hxx:876
const SwStartNode * FindTableBoxStartNode() const
Definition: node.hxx:202
bool IsTextNode() const
Definition: node.hxx:664
const SwStartNode * StartOfSectionNode() const
Definition: node.hxx:137
SwOLENode * GetOLENode()
Inline methods from Node.hxx.
Definition: ndole.hxx:164
SwContentNode * GetContentNode()
Definition: node.hxx:643
const SwEndNode * EndOfSectionNode() const
Definition: node.hxx:710
SwTextNode * MakeTextNode(SwNode &rWhere, SwTextFormatColl *pColl, bool bNewFrames=true)
Implementations of "Make...Node" are in the given .cxx-files.
Definition: ndtxt.cxx:103
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:405
void Delete(const SwNodeIndex &rPos, SwNodeOffset nNodes=SwNodeOffset(1))
Definition: nodes.cxx:1071
const OUString & GetChartTableName() const
Definition: ndole.hxx:155
PaM is Point and Mark: a selection of the document model.
Definition: pam.hxx:187
const SwPosition * GetMark() const
Definition: pam.hxx:263
SwNode & GetPointNode() const
Definition: pam.hxx:283
SwContentNode * GetPointContentNode() const
Definition: pam.hxx:287
const SwPosition * End() const
Definition: pam.hxx:271
const SwPosition * GetPoint() const
Definition: pam.hxx:261
const SwPosition * Start() const
Definition: pam.hxx:266
void Resort()
Definition: docary.hxx:271
Starts a section of nodes in the document model.
Definition: node.hxx:325
SwTableBox is one table cell in the document model.
Definition: swtable.hxx:419
SwTableLine * GetUpper()
Definition: swtable.hxx:453
sal_Int32 getRowSpan() const
Definition: swtable.hxx:514
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:2049
void setRowSpan(sal_Int32 nNewRowSpan)
Definition: swtable.cxx:77
SwFrameFormat * GetFrameFormat()
Definition: swtable.hxx:457
SwTableLines & GetTabLines()
Definition: swtable.hxx:450
const SwStartNode * GetSttNd() const
Definition: swtable.hxx:471
void ChgFrameFormat(SwTableBoxFormat *pNewFormat, bool bNeedToReregister=true)
Definition: swtable.cxx:1937
SwTableBox & FindEndOfRowSpan(const SwTable &, sal_uInt16 nMaxStep)
SwTableBox::FindEndOfRowSpan(..) returns the last overlapped cell if there is any.
SwFrameFormat * ClaimFrameFormat()
Definition: swtable.cxx:1901
SwTableLine is one table row in the document model.
Definition: swtable.hxx:358
SwFrameFormat * GetFrameFormat()
Definition: swtable.hxx:380
SwFrameFormat * ClaimFrameFormat()
Definition: swtable.cxx:1478
SwTableBoxes & GetTabBoxes()
Definition: swtable.hxx:368
sal_uInt16 GetBoxPos(const SwTableBox *pBox) const
Definition: swtable.hxx:370
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:2111
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:355
SwTableLines & GetTabLines()
Definition: swtable.hxx:201
void FindSuperfluousRows_(SwSelBoxes &rBoxes, SwTableLine *, SwTableLine *)
SwTable::FindSuperfluousRows_(..) is looking for superfluous rows, i.e.
Definition: swnewtable.cxx:986
SwTableFormat * GetFrameFormat()
Definition: swtable.hxx:204
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:948
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:188
void CheckConsistency() const
bool InsertRow_(SwDoc *, const SwSelBoxes &, sal_uInt16 nCnt, bool bBehind)
Definition: tblrwcl.cxx:514
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:86
void SetCountedInList(bool bCounted)
Definition: ndtxt.cxx:4316
SwNumRule * GetNumRule(bool bInParent=true) const
Returns numbering rule of this text node.
Definition: ndtxt.cxx:2870
void SetSelBoxes(const SwSelBoxes &rBoxes)
Definition: untbl.cxx:2018
void MoveBoxContent(SwDoc &rDoc, SwNodeRange &rRg, SwNode &rPos)
Definition: untbl.cxx:2003
void AddNewBox(SwNodeOffset nSttNdIdx)
Definition: UndoTable.hxx:241
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
@ 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< SwTableBoxFormula > RES_BOXATR_FORMULA(151)
constexpr TypedWhichId< SvxBrushItem > RES_BACKGROUND(105)
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
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:37
SwNode & GetNode() const
Definition: pam.hxx:80
SwNodeIndex nNode
Definition: pam.hxx:38
SwContentIndex nContent
Definition: pam.hxx:39
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:669
void InsTableBox(SwDoc &rDoc, SwTableNode *pTableNd, SwTableLine *pLine, SwTableBoxFormat *pBoxFrameFormat, SwTableBox *pBox, sal_uInt16 nInsPos, sal_uInt16 nCnt=1)
Definition: swtable.cxx:127
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