1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: swnewtable.cxx,v $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_sw.hxx"
34 #include <swtable.hxx>
36 #include <tblrwcl.hxx>
42 #include <cellfrm.hxx>
43 #include <fmtfsize.hxx>
49 #include <svx/boxitem.hxx>
50 #include <svx/protitem.hxx>
51 #include <swtblfmt.hxx>
54 #define CHECK_TABLE(t)
57 #define CHECK_TABLE(t) (t).CheckConsistency();
59 #define CHECK_TABLE(t)
63 // ---------------------------------------------------------------
65 /** SwBoxSelection is a small helperclass (structure) to handle selections
66 of cells (boxes) between table functions
68 It contains an "array" of table boxes, a rectangulare selection of table boxes.
69 To be more specific, it contains a vector of box selections,
70 every box selection (SwSelBoxes) contains the selected boxes inside one row.
71 The member mnMergeWidth contains the width of the selected boxes
77 std::vector
<const SwSelBoxes
*> aBoxes
;
79 SwBoxSelection() : mnMergeWidth(0) {}
80 bool isEmpty() const { return aBoxes
.size() == 0; }
81 void insertBoxes( const SwSelBoxes
* pNew
){ aBoxes
.insert( aBoxes
.end(), pNew
); }
84 /** NewMerge(..) removes the superfluous cells after cell merge
86 SwTable::NewMerge(..) does some cleaning up,
87 it simply deletes the superfluous cells ("cell span")
88 and notifies the Undo about it.
89 The main work has been done by SwTable::PrepareMerge(..) already.
95 the undo object to notify, maybe empty
97 @return TRUE for compatibility reasons with OldMerge(..)
100 BOOL
SwTable::NewMerge( SwDoc
* pDoc
, const SwSelBoxes
& rBoxes
,
101 const SwSelBoxes
& rMerged
, SwTableBox
*, SwUndoTblMerge
* pUndo
)
104 pUndo
->SetSelBoxes( rBoxes
);
105 DeleteSel( pDoc
, rBoxes
, &rMerged
, 0, TRUE
, TRUE
);
111 /** lcl_CheckMinMax helps evaluating (horizontal) min/max of boxes
113 lcl_CheckMinMax(..) compares the left border and the right border
114 of a given cell with the given range and sets it accordingly.
117 will be decremented if necessary to the left border of the cell
120 will be incremented if necessary to the right border of the cell
123 the row (table line) of the interesting box
126 the index of the box in the table box array of the given row
129 if bSet is false, rMin and rMax will be manipulated if necessary
130 if bSet is true, rMin and rMax will be set to the left and right border of the box
134 void lcl_CheckMinMax( long& rMin
, long& rMax
, const SwTableLine
& rLine
, USHORT nCheck
, bool bSet
)
137 if( rLine
.GetTabBoxes().Count() < nCheck
)
139 ASSERT( false, "Box out of table line" );
140 nCheck
= rLine
.GetTabBoxes().Count();
143 long nNew
= 0; // will be the right border of the current box
144 long nWidth
= 0; // the width of the current box
145 for( USHORT nCurrBox
= 0; nCurrBox
< nCheck
; ++nCurrBox
)
147 SwTableBox
* pBox
= rLine
.GetTabBoxes()[nCurrBox
];
148 ASSERT( pBox
, "Missing table box" );
149 nWidth
= pBox
->GetFrmFmt()->GetFrmSize().GetWidth();
152 // nNew is the right border of the wished box
153 if( bSet
|| nNew
> rMax
)
155 nNew
-= nWidth
; // nNew becomes the left border of the wished box
156 if( bSet
|| nNew
< rMin
)
160 /** lcl_Box2LeftBorder(..) delivers the left (logical) border of a table box
162 The left logical border of a table box is the sum of the cell width before this
166 is the requested table box
168 @return is the left logical border (long, even it cannot be negative)
172 long lcl_Box2LeftBorder( const SwTableBox
& rBox
)
174 if( !rBox
.GetUpper() )
177 const SwTableLine
&rLine
= *rBox
.GetUpper();
178 USHORT nCount
= rLine
.GetTabBoxes().Count();
179 for( USHORT nCurrBox
= 0; nCurrBox
< nCount
; ++nCurrBox
)
181 SwTableBox
* pBox
= rLine
.GetTabBoxes()[nCurrBox
];
182 ASSERT( pBox
, "Missing table box" );
185 nLeft
+= pBox
->GetFrmFmt()->GetFrmSize().GetWidth();
187 ASSERT( false, "Box not found in own upper?" );
191 /** lcl_LeftBorder2Box delivers the box to a given left border
193 It's used to find the master/follow table boxes in previous/next rows.
194 Don't call this function to check if there is such a box,
195 call it if you know there has to be such box.
198 the left border (logical x-value) of the demanded box
201 the row (table line) to be scanned
203 @return a pointer to the table box inside the given row with the wished left border
207 SwTableBox
* lcl_LeftBorder2Box( long nLeft
, const SwTableLine
* pLine
)
212 USHORT nCount
= pLine
->GetTabBoxes().Count();
213 for( USHORT nCurrBox
= 0; nCurrBox
< nCount
; ++nCurrBox
)
215 SwTableBox
* pBox
= pLine
->GetTabBoxes()[nCurrBox
];
216 ASSERT( pBox
, "Missing table box" );
217 if( nCurrLeft
>= nLeft
&& pBox
->GetFrmFmt()->GetFrmSize().GetWidth() )
219 ASSERT( nCurrLeft
== nLeft
, "Wrong box found" );
222 nCurrLeft
+= pBox
->GetFrmFmt()->GetFrmSize().GetWidth();
224 ASSERT( false, "Didn't found wished box" );
228 /** lcl_ChangeRowSpan corrects row span after insertion/deletion of rows
230 lcl_ChangeRowSpan(..) has to be called after an insertion or deletion of rows
231 to adjust the row spans of previous rows accordingly.
232 If rows are deleted, the previous rows with row spans into the deleted area
233 have to be decremented by the number of _overlapped_ inserted rows.
234 If rows are inserted, the previous rows with row span into the inserted area
235 have to be incremented by the number of inserted rows.
236 For those row spans which ends exactly above the inserted area it has to be
237 decided by the parameter bSingle if they have to be expanded or not.
240 the table to manipulate (has to be a new model table)
243 the number of rows which has been inserted (nDiff > 0) or deleted (nDiff < 0)
246 the index of the first row which has to be checked
249 true if the new inserted row should not extend row spans which ends in the row above
250 this is for rows inserted by UI "insert row"
251 false if all cells of an inserted row has to be overlapped by the previous row
252 this is for rows inserted by "split row"
253 false is also needed for deleted rows
257 void lcl_ChangeRowSpan( const SwTable
& rTable
, const long nDiff
,
258 USHORT nRowIdx
, const bool bSingle
)
260 if( !nDiff
|| nRowIdx
>= rTable
.GetTabLines().Count() )
262 ASSERT( !bSingle
|| nDiff
> 0, "Don't set bSingle when deleting lines!" );
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 long nDistance
= bSingle
? 1 : 0;
273 bGoOn
= false; // will be set to true if we found a non-master cell
274 // which has to be manipulated => we have to chekc the previous row, too.
275 const SwTableLine
* pLine
= rTable
.GetTabLines()[ nRowIdx
];
276 USHORT nBoxCount
= pLine
->GetTabBoxes().Count();
277 for( USHORT nCurrBox
= 0; nCurrBox
< nBoxCount
; ++nCurrBox
)
279 long nRowSpan
= pLine
->GetTabBoxes()[nCurrBox
]->getRowSpan();
280 long nAbsSpan
= nRowSpan
> 0 ? nRowSpan
: -nRowSpan
;
281 // Check if the last overlapped cell is above or below
283 if( nAbsSpan
> nDistance
)
288 nRowSpan
+= nDiff
; // increment row span of master cell
291 nRowSpan
-= nDiff
; // increment row span of non-master cell
299 // end of row span behind the deleted area ..
300 if( nRowSpan
- nDistance
> -nDiff
)
302 else // .. or inside the deleted area
303 nRowSpan
= nDistance
+ 1;
306 { // Same for a non-master cell
307 if( nRowSpan
+ nDistance
< nDiff
)
310 nRowSpan
= -nDistance
- 1;
311 bGoOn
= true; // We have to continue
314 pLine
->GetTabBoxes()[ nCurrBox
]->setRowSpan( nRowSpan
);
321 bGoOn
= false; //robust
325 /** CollectBoxSelection(..) create a rectangulare selection based on the given SwPaM
326 and prepares the selected cells for merging
329 SwBoxSelection
* SwTable::CollectBoxSelection( const SwPaM
& rPam
) const
331 ASSERT( bNewModel
, "Don't call me for old tables" );
332 if( !aLines
.Count() )
334 const SwNode
* pStartNd
= rPam
.Start()->nNode
.GetNode().FindTableBoxStartNode();
335 const SwNode
* pEndNd
= rPam
.End()->nNode
.GetNode().FindTableBoxStartNode();
336 if( !pStartNd
|| !pEndNd
|| pStartNd
== pEndNd
)
339 USHORT nLines
= aLines
.Count();
340 USHORT nTop
= 0, nBottom
= 0;
341 long nMin
= 0, nMax
= 0;
343 for( USHORT nRow
= 0; nFound
< 2 && nRow
< nLines
; ++nRow
)
345 SwTableLine
* pLine
= aLines
[nRow
];
346 ASSERT( pLine
, "Missing table line" );
347 USHORT nCols
= pLine
->GetTabBoxes().Count();
348 for( USHORT nCol
= 0; nCol
< nCols
; ++nCol
)
350 SwTableBox
* pBox
= pLine
->GetTabBoxes()[nCol
];
351 ASSERT( pBox
, "Missing table box" );
354 if( pBox
->GetSttNd() == pEndNd
)
357 lcl_CheckMinMax( nMin
, nMax
, *pLine
, nCol
, false );
362 else if( pBox
->GetSttNd() == pStartNd
)
365 lcl_CheckMinMax( nMin
, nMax
, *pLine
, nCol
, true );
374 long nMid
= ( nMin
+ nMax
) / 2;
376 SwBoxSelection
* pRet
= new SwBoxSelection();
377 std::list
< std::pair
< SwTableBox
*, long > > aNewWidthList
;
378 USHORT nCheckBottom
= nBottom
;
381 long nLeftSpanCnt
= 0;
382 long nRightSpanCnt
= 0;
383 for( USHORT nRow
= nTop
; nRow
<= nBottom
&& bOkay
; ++nRow
)
385 SwTableLine
* pLine
= aLines
[nRow
];
386 ASSERT( pLine
, "Missing table line" );
387 SwSelBoxes
*pBoxes
= new SwSelBoxes();
391 USHORT nCount
= pLine
->GetTabBoxes().Count();
392 for( USHORT nCurrBox
= 0; nCurrBox
< nCount
; ++nCurrBox
)
394 SwTableBox
* pBox
= pLine
->GetTabBoxes()[nCurrBox
];
395 ASSERT( pBox
, "Missing table box" );
397 nRight
+= pBox
->GetFrmFmt()->GetFrmSize().GetWidth();
398 nRowSpan
= pBox
->getRowSpan();
401 if( nRight
== nMin
&& nLeftSpanCnt
)
405 SwTableBox
* pInnerBox
= 0;
406 SwTableBox
* pLeftBox
= 0;
407 SwTableBox
* pRightBox
= 0;
412 if( nRight
>= nMid
|| nRight
+ nLeft
>= nMin
+ nMin
)
416 pBoxes
->Insert( pBox
);
418 pLeftBox
= pLine
->GetTabBoxes()[nCurrBox
-1];
419 nDiff
= nMin
- nLeft
;
422 if( nCurrBox
+1 < nCount
)
424 pRightBox
= pLine
->GetTabBoxes()[nCurrBox
+1];
425 nDiff2
= nRight
- nMax
;
430 else if( nRightSpanCnt
&& nRight
== nMax
)
436 else if( nCurrBox
+1 < nCount
)
439 pInnerBox
= pLine
->GetTabBoxes()[nCurrBox
+1];
440 nDiff
= nMin
- nRight
;
445 else if( nRight
<= nMax
)
447 pBoxes
->Insert( pBox
);
448 if( nRow
== nTop
&& nRowSpan
< 0 )
453 if( nRowSpan
> 1 && nRow
+ nRowSpan
- 1 > nBottom
)
454 nBottom
= nRow
+ (USHORT
)nRowSpan
- 1;
455 if( nRowSpan
< -1 && nRow
- nRowSpan
- 1 > nBottom
)
456 nBottom
= (USHORT
)(nRow
- nRowSpan
- 1);
457 if( nRightSpanCnt
&& nRight
== nMax
)
460 else if( nLeft
< nMax
)
462 if( nLeft
<= nMid
|| nRight
+ nLeft
<= nMax
)
464 if( nCurrBox
+1 < nCount
)
466 pBoxes
->Insert( pBox
);
468 pRightBox
= pLine
->GetTabBoxes()[nCurrBox
+1];
469 nDiff
= nRight
- nMax
;
477 pInnerBox
= pLine
->GetTabBoxes()[nCurrBox
-1];
478 nDiff
= nLeft
- nMax
;
487 if( nRow
== nBottom
)
489 long nTmpSpan
= pInnerBox
->getRowSpan();
491 nBottom
+= (USHORT
)nTmpSpan
- 1;
492 else if( nTmpSpan
< -1 )
493 nBottom
= (USHORT
)( nBottom
- nTmpSpan
- 1 );
495 SwTableBox
* pOuterBox
= pLeftBox
;
500 long nOutSpan
= pOuterBox
->getRowSpan();
503 USHORT nCheck
= nRow
;
506 const SwTableBox
& rBox
=
507 pOuterBox
->FindStartOfRowSpan( *this, USHRT_MAX
);
508 nOutSpan
= rBox
.getRowSpan();
509 const SwTableLine
* pTmpL
= rBox
.GetUpper();
510 nCheck
= GetTabLines().C40_GETPOS( SwTableLine
, pTmpL
);
513 if( pOuterBox
== pLeftBox
)
515 if( !nLeftSpanCnt
|| nMin
- nDiff
!= nLeftSpan
)
520 if( !nRightSpanCnt
|| nMax
+ nDiff
!= nRightSpan
)
526 if( pOuterBox
== pLeftBox
)
530 nLeftSpan
= nMin
- nDiff
;
531 nLeftSpanCnt
= nOutSpan
;
537 nRightSpan
= nMax
+ nDiff
;
538 nRightSpanCnt
= nOutSpan
;
541 nCheck
+= (USHORT
)nOutSpan
- 1;
542 if( nCheck
> nCheckBottom
)
543 nCheckBottom
= nCheck
;
545 else if( ( nLeftSpanCnt
&& pLeftBox
== pOuterBox
) ||
546 ( nRightSpanCnt
&& pRightBox
== pOuterBox
) )
548 std::pair
< SwTableBox
*, long > aTmp
;
549 aTmp
.first
= pInnerBox
;
550 aTmp
.second
= -nDiff
;
551 aNewWidthList
.push_back( aTmp
);
552 aTmp
.first
= pOuterBox
;
554 aNewWidthList
.push_back( aTmp
);
556 pOuterBox
= pOuterBox
== pRightBox
? 0 : pRightBox
;
559 } while( pOuterBox
);
566 pRet
->insertBoxes( pBoxes
);
568 pRet
->mnMergeWidth
= nMax
- nMin
;
569 if( nCheckBottom
> nBottom
)
573 std::list
< std::pair
< SwTableBox
*, long > >::iterator
574 pCurr
= aNewWidthList
.begin();
575 while( pCurr
!= aNewWidthList
.end() )
577 SwFrmFmt
* pFmt
= pCurr
->first
->ClaimFrmFmt();
578 long nNewWidth
= pFmt
->GetFrmSize().GetWidth() + pCurr
->second
;
579 pFmt
->SetFmtAttr( SwFmtFrmSize( ATT_VAR_SIZE
, nNewWidth
, 0 ) );
591 /** lcl_InvalidateCellFrm(..) invalidates all layout representations of a given cell
592 to initiate a reformatting
595 void lcl_InvalidateCellFrm( const SwTableBox
& rBox
)
597 SwClientIter
aIter( *rBox
.GetFrmFmt() );
599 for( pLast
= aIter
.First( TYPE( SwFrm
) ); pLast
; pLast
= aIter
.Next() )
601 SwCellFrm
*pCell
= (SwCellFrm
*)pLast
;
602 if( pCell
->GetTabBox() == &rBox
)
604 pCell
->InvalidateSize();
605 SwFrm
* pLower
= pCell
->GetLower();
607 pLower
->_InvalidateSize();
612 /** lcl_InsertPosition(..) evaluates the insert positions in every table line,
613 when a selection of cells is given and returns the average cell widths
616 long lcl_InsertPosition( SwTable
&rTable
, std::vector
<USHORT
>& rInsPos
,
617 const SwSelBoxes
& rBoxes
, BOOL bBehind
)
619 sal_Int32 nAddWidth
= 0;
621 for( USHORT j
= 0; j
< rBoxes
.Count(); ++j
)
623 SwTableBox
*pBox
= rBoxes
[j
];
624 SwTableLine
* pLine
= pBox
->GetUpper();
625 long nWidth
= rBoxes
[j
]->GetFrmFmt()->GetFrmSize().GetWidth();
627 USHORT nCurrBox
= pLine
->GetTabBoxes().C40_GETPOS(SwTableBox
, pBox
);
628 USHORT nCurrLine
= rTable
.GetTabLines().C40_GETPOS(SwTableLine
, pLine
);
629 ASSERT( nCurrLine
!= USHRT_MAX
, "Time to say Good-Bye.." );
630 if( rInsPos
[ nCurrLine
] == USHRT_MAX
)
632 rInsPos
[ nCurrLine
] = nCurrBox
;
635 else if( ( rInsPos
[ nCurrLine
] > nCurrBox
) == !bBehind
)
636 rInsPos
[ nCurrLine
] = nCurrBox
;
643 /** SwTable::NewInsertCol(..) insert new column(s) into a table
653 the number of columns to insert
656 insertion behind (true) or before (false) the selected boxes
658 @return true, if any insertion has been done successfully
662 BOOL
SwTable::NewInsertCol( SwDoc
* pDoc
, const SwSelBoxes
& rBoxes
,
663 USHORT nCnt
, BOOL bBehind
)
665 if( !aLines
.Count() || !nCnt
)
669 long nNewBoxWidth
= 0;
670 std::vector
< USHORT
> aInsPos( aLines
.Count(), USHRT_MAX
);
671 { // Calculation of the insert positions and the width of the new boxes
672 sal_uInt64 nTableWidth
= 0;
673 for( USHORT i
= 0; i
< aLines
[0]->GetTabBoxes().Count(); ++i
)
674 nTableWidth
+= aLines
[0]->GetTabBoxes()[i
]->GetFrmFmt()->GetFrmSize().GetWidth();
676 // Fill the vector of insert positions and the (average) width to insert
677 sal_uInt64 nAddWidth
= lcl_InsertPosition( *this, aInsPos
, rBoxes
, bBehind
);
679 // Given is the (average) width of the selected boxes, if we would
680 // insert nCnt of columns the table would grow
681 // So we will shrink the table first, then insert the new boxes and
682 // get a table with the same width than before.
683 // But we will not shrink the table by the full already calculated value,
684 // we will reduce this value proportional to the old table width
685 nAddWidth
*= nCnt
; // we have to insert nCnt boxes per line
686 sal_uInt64 nResultingWidth
= nAddWidth
+ nTableWidth
;
687 if( !nResultingWidth
)
689 nAddWidth
= (nAddWidth
* nTableWidth
) / nResultingWidth
;
690 nNewBoxWidth
= long( nAddWidth
/ nCnt
); // Rounding
691 nAddWidth
= nNewBoxWidth
* nCnt
; // Rounding
692 if( !nAddWidth
|| nAddWidth
>= nTableWidth
)
694 AdjustWidths( static_cast< long >(nTableWidth
), static_cast< long >(nTableWidth
- nAddWidth
) );
697 _FndBox
aFndBox( 0, 0 );
698 aFndBox
.SetTableLines( rBoxes
, *this );
699 aFndBox
.DelFrms( *this );
700 // aFndBox.SaveChartData( *this );
702 SwTableNode
* pTblNd
= GetTableNode();
703 std::vector
<SwTableBoxFmt
*> aInsFormat( nCnt
, 0 );
704 USHORT nLastLine
= USHRT_MAX
;
705 long nLastRowSpan
= 1;
707 for( USHORT i
= 0; i
< aLines
.Count(); ++i
)
709 SwTableLine
* pLine
= aLines
[ i
];
710 USHORT nInsPos
= aInsPos
[i
];
711 ASSERT( nInsPos
!= USHRT_MAX
, "Didn't found insert position" );
712 SwTableBox
* pBox
= pLine
->GetTabBoxes()[ nInsPos
];
715 SwTableBoxFmt
* pBoxFrmFmt
= (SwTableBoxFmt
*)pBox
->GetFrmFmt();
716 ::_InsTblBox( pDoc
, pTblNd
, pLine
, pBoxFrmFmt
, pBox
, nInsPos
, nCnt
);
717 long nRowSpan
= pBox
->getRowSpan();
718 long nDiff
= i
- nLastLine
;
719 bool bNewSpan
= false;
720 if( nLastLine
!= USHRT_MAX
&& nDiff
<= nLastRowSpan
&&
721 nRowSpan
!= nDiff
- nLastRowSpan
)
724 while( nLastLine
< i
)
726 SwTableLine
* pTmpLine
= aLines
[ nLastLine
];
727 USHORT nTmpPos
= aInsPos
[nLastLine
];
730 for( USHORT j
= 0; j
< nCnt
; ++j
)
731 pTmpLine
->GetTabBoxes()[nTmpPos
+j
]->setRowSpan( nDiff
);
744 nLastRowSpan
= -nRowSpan
;
746 nLastRowSpan
= nRowSpan
;
748 const SvxBoxItem
& aSelBoxItem
= pBoxFrmFmt
->GetBox();
749 SvxBoxItem
* pNoRightBorder
= 0;
750 if( aSelBoxItem
.GetRight() )
752 pNoRightBorder
= new SvxBoxItem( aSelBoxItem
);
753 pNoRightBorder
->SetLine( 0, BOX_LINE_RIGHT
);
755 for( USHORT j
= 0; j
< nCnt
; ++j
)
757 SwTableBox
*pCurrBox
= pLine
->GetTabBoxes()[nInsPos
+j
];
760 pCurrBox
->setRowSpan( nLastRowSpan
);
761 SwFrmFmt
* pFrmFmt
= pCurrBox
->ClaimFrmFmt();
762 SwFmtFrmSize
aFrmSz( pFrmFmt
->GetFrmSize() );
763 aFrmSz
.SetWidth( nNewBoxWidth
);
764 pFrmFmt
->SetFmtAttr( aFrmSz
);
765 if( pNoRightBorder
&& ( !bBehind
|| j
+1 < nCnt
) )
766 pFrmFmt
->SetFmtAttr( *pNoRightBorder
);
767 aInsFormat
[j
] = (SwTableBoxFmt
*)pFrmFmt
;
770 pCurrBox
->ChgFrmFmt( aInsFormat
[j
] );
772 if( bBehind
&& pNoRightBorder
)
774 SwFrmFmt
* pFrmFmt
= pBox
->ClaimFrmFmt();
775 pFrmFmt
->SetFmtAttr( *pNoRightBorder
);
777 delete pNoRightBorder
;
780 aFndBox
.MakeFrms( *this );
781 // aFndBox.RestoreChartData( *this );
784 const SwTableBoxes
&rTabBoxes
= aLines
[0]->GetTabBoxes();
786 for( USHORT i
= 0; i
< rTabBoxes
.Count(); ++i
)
787 nNewWidth
+= rTabBoxes
[i
]->GetFrmFmt()->GetFrmSize().GetWidth();
788 ASSERT( nNewWidth
> 0, "Very small" );
796 /** SwTable::PrepareMerge(..) some preparation for the coming Merge(..)
798 For the old table model, ::GetMergeSel(..) is called only,
799 for the new table model, PrepareMerge does the main work.
800 It modifices all cells to merge (width, border, rowspan etc.) and collects
801 the cells which have to be deleted by Merge(..) afterwards.
802 If there are superfluous rows, these cells are put into the deletion list as well.
805 the selection to merge
808 should be empty at the beginning, at the end it is filled with boxes to delete.
811 will be set to the master cell box
814 the undo object to record all changes
815 can be Null, e.g. when called by Redo(..)
821 bool SwTable::PrepareMerge( const SwPaM
& rPam
, SwSelBoxes
& rBoxes
,
822 SwSelBoxes
& rMerged
, SwTableBox
** ppMergeBox
, SwUndoTblMerge
* pUndo
)
826 ::GetMergeSel( rPam
, rBoxes
, ppMergeBox
, pUndo
);
827 return rBoxes
.Count() > 1;
830 // We have to assert a "rectangular" box selection before we start to merge
831 std::auto_ptr
< SwBoxSelection
> pSel( CollectBoxSelection( rPam
) );
832 if( !pSel
.get() || pSel
->isEmpty() )
834 // Now we should have a rectangle of boxes,
835 // i.e. contiguous cells in contiguous rows
836 bool bMerge
= false; // will be set if any content is transferred from
837 // a "not already overlapped" cell into the new master cell.
838 SwTableBox
*pMergeBox
= (*pSel
->aBoxes
[0])[0]; // the master cell box
841 (*ppMergeBox
) = pMergeBox
;
842 // The new master box will get the left and the top border of the top-left
843 // box of the selection and because the new master cell _is_ the top-left
844 // box, the left and right border does not need to be changed.
845 // The right and bottom border instead has to be derived from the right-
846 // bottom box of the selection. If this is a overlapped cell,
847 // the appropiate master box.
848 SwTableBox
* pLastBox
= 0; // the right-bottom (master) cell
849 SwDoc
* pDoc
= GetFrmFmt()->GetDoc();
850 SwPosition
aInsPos( *pMergeBox
->GetSttNd()->EndOfSectionNode() );
851 SwPaM
aChkPam( aInsPos
);
852 // The number of lines in the selection rectangle: nLineCount
853 const USHORT nLineCount
= USHORT(pSel
->aBoxes
.size());
854 // BTW: nLineCount is the rowspan of the new master cell
855 long nRowSpan
= nLineCount
;
856 // We will need the first and last line of the selection
857 // to check if there any superfluous row after merging
858 SwTableLine
* pFirstLn
= 0;
859 SwTableLine
* pLastLn
= 0;
860 // Iteration over the lines of the selection...
861 for( USHORT nCurrLine
= 0; nCurrLine
< nLineCount
; ++nCurrLine
)
863 // The selected boxes in the current line
864 const SwSelBoxes
* pBoxes
= pSel
->aBoxes
[ nCurrLine
];
865 USHORT nColCount
= pBoxes
->Count();
866 // Iteration over the selected cell in the current row
867 for( USHORT nCurrCol
= 0; nCurrCol
< nColCount
; ++nCurrCol
)
869 SwTableBox
* pBox
= (*pBoxes
)[nCurrCol
];
870 rMerged
.Insert( pBox
);
871 // Only the first selected cell in every row will be alive,
872 // the other will be deleted => put into rBoxes
874 rBoxes
.Insert( pBox
);
878 pFirstLn
= pBox
->GetUpper(); // we need this line later on
879 if( nCurrLine
+ 1 == nLineCount
)
880 pLastLn
= pBox
->GetUpper(); // and this one, too.
882 // A box has to be merged if it's not the master box itself,
883 // but an already overlapped cell must not be merged as well.
884 bool bDoMerge
= pBox
!= pMergeBox
&& pBox
->getRowSpan() > 0;
885 // The last box has to be in the last "column" of the selection
886 // and it has to be a master cell
887 if( nCurrCol
+1 == nColCount
&& pBox
->getRowSpan() > 0 )
892 // If the cell to merge contains only one empty paragraph,
893 // we do not transfer this paragraph.
894 if( !IsEmptyBox( *pBox
, aChkPam
) )
896 SwNodeIndex
& rInsPosNd
= aInsPos
.nNode
;
897 SwPaM
aPam( aInsPos
);
898 aPam
.GetPoint()->nNode
.Assign( *pBox
->GetSttNd()->EndOfSectionNode(), -1 );
899 SwCntntNode
* pCNd
= aPam
.GetCntntNode();
900 USHORT nL
= pCNd
? pCNd
->Len() : 0;
901 aPam
.GetPoint()->nContent
.Assign( pCNd
, nL
);
902 SwNodeIndex
aSttNdIdx( *pBox
->GetSttNd(), 1 );
904 pDoc
->DoUndo( FALSE
);
905 pDoc
->AppendTxtNode( *aPam
.GetPoint() );
907 pDoc
->DoUndo( TRUE
);
908 SwNodeRange
aRg( aSttNdIdx
, aPam
.GetPoint()->nNode
);
910 pUndo
->MoveBoxCntnt( pDoc
, aRg
, rInsPosNd
);
912 pDoc
->Move( aRg
, rInsPosNd
, IDocumentContentOperations::DOC_NO_DELFRMS
);
915 // Only the cell of the first selected column will stay alive
916 // and got a new row span
918 pBox
->setRowSpan( nRowSpan
);
920 if( nRowSpan
> 0 ) // the master cell is done, from now on we set
921 nRowSpan
= -nRowSpan
; // negative row spans
922 ++nRowSpan
; // ... -3, -2, -1
926 // A row containing overlapped cells is superfluous,
927 // these cells can be put into rBoxes for deletion
928 _FindSuperfluousRows( rBoxes
, pFirstLn
, pLastLn
);
929 // pNewFmt will be set to the new master box and the overlapped cells
930 SwFrmFmt
* pNewFmt
= pMergeBox
->ClaimFrmFmt();
931 pNewFmt
->SetFmtAttr( SwFmtFrmSize( ATT_VAR_SIZE
, pSel
->mnMergeWidth
, 0 ) );
932 for( USHORT nCurrLine
= 0; nCurrLine
< nLineCount
; ++nCurrLine
)
934 const SwSelBoxes
* pBoxes
= pSel
->aBoxes
[ nCurrLine
];
935 USHORT nColCount
= pBoxes
->Count();
936 for( USHORT nCurrCol
= 0; nCurrCol
< nColCount
; ++nCurrCol
)
938 SwTableBox
* pBox
= (*pBoxes
)[nCurrCol
];
941 // Even this box will be deleted soon,
942 // we have to correct the width to avoid side effects
943 SwFrmFmt
* pFmt
= pBox
->ClaimFrmFmt();
944 pFmt
->SetFmtAttr( SwFmtFrmSize( ATT_VAR_SIZE
, 0, 0 ) );
947 pBox
->ChgFrmFmt( (SwTableBoxFmt
*)pNewFmt
);
950 if( pLastBox
) // Robust
952 // The new borders of the master cell...
953 SvxBoxItem
aBox( pMergeBox
->GetFrmFmt()->GetBox() );
954 bool bOld
= aBox
.GetRight() || aBox
.GetBottom();
955 const SvxBoxItem
& rBox
= pLastBox
->GetFrmFmt()->GetBox();
956 aBox
.SetLine( rBox
.GetRight(), BOX_LINE_RIGHT
);
957 aBox
.SetLine( rBox
.GetBottom(), BOX_LINE_BOTTOM
);
958 if( bOld
|| aBox
.GetLeft() || aBox
.GetTop() || aBox
.GetRight() || aBox
.GetBottom() )
959 (*ppMergeBox
)->GetFrmFmt()->SetFmtAttr( aBox
);
963 pUndo
->AddNewBox( pMergeBox
->GetSttIdx() );
968 /** SwTable::_FindSuperfluousRows(..) is looking for superfluous rows, i.e. rows
969 containing overlapped cells only.
972 void SwTable::_FindSuperfluousRows( SwSelBoxes
& rBoxes
,
973 SwTableLine
* pFirstLn
, SwTableLine
* pLastLn
)
975 if( !pFirstLn
|| !pLastLn
)
977 if( !rBoxes
.Count() )
979 pFirstLn
= rBoxes
[0]->GetUpper();
980 pLastLn
= rBoxes
[ rBoxes
.Count() - 1 ]->GetUpper();
982 USHORT nFirstLn
= GetTabLines().C40_GETPOS(SwTableLine
, pFirstLn
);
983 USHORT nLastLn
= GetTabLines().C40_GETPOS(SwTableLine
, pLastLn
);
984 for( USHORT nRow
= nFirstLn
; nRow
<= nLastLn
; ++nRow
)
986 SwTableLine
* pLine
= aLines
[nRow
];
987 ASSERT( pLine
, "Missing table line" );
988 USHORT nCols
= pLine
->GetTabBoxes().Count();
989 bool bSuperfl
= true;
990 for( USHORT nCol
= 0; nCol
< nCols
; ++nCol
)
992 SwTableBox
*pBox
= pLine
->GetTabBoxes()[nCol
];
993 if( pBox
->getRowSpan() > 0 &&
994 USHRT_MAX
== rBoxes
.GetPos( pBox
) )
1002 for( USHORT nCol
= 0; nCol
< nCols
; ++nCol
)
1004 SwTableBox
* pBox
= pLine
->GetTabBoxes()[nCol
];
1005 rBoxes
.Insert( pBox
);
1011 /** SwTableBox::FindStartOfRowSpan(..) retruns the "master" cell, the cell which
1012 overlaps the given cell, it maybe the cell itself.
1015 SwTableBox
& SwTableBox::FindStartOfRowSpan( const SwTable
& rTable
, USHORT nMaxStep
)
1017 if( getRowSpan() > 0 || !nMaxStep
)
1020 long nLeftBorder
= lcl_Box2LeftBorder( *this );
1021 SwTableBox
* pBox
= this;
1022 const SwTableLine
* pMyUpper
= GetUpper();
1023 USHORT nLine
= rTable
.GetTabLines().C40_GETPOS(SwTableLine
, pMyUpper
);
1024 if( nLine
&& nLine
< rTable
.GetTabLines().Count() )
1029 pNext
= lcl_LeftBorder2Box( nLeftBorder
, rTable
.GetTabLines()[--nLine
] );
1032 } while( nLine
&& --nMaxStep
&& pNext
&& pBox
->getRowSpan() < 1 );
1038 /** SwTableBox::FindEndOfRowSpan(..) returns the last overlapped cell if there is
1039 any. Otherwise the cell itself will returned.
1042 SwTableBox
& SwTableBox::FindEndOfRowSpan( const SwTable
& rTable
, USHORT nMaxStep
)
1044 long nAbsSpan
= getRowSpan();
1046 nAbsSpan
= -nAbsSpan
;
1047 if( nAbsSpan
== 1 || !nMaxStep
)
1050 if( nMaxStep
> --nAbsSpan
)
1051 nMaxStep
= (USHORT
)nAbsSpan
;
1052 const SwTableLine
* pMyUpper
= GetUpper();
1053 USHORT nLine
= rTable
.GetTabLines().C40_GETPOS(SwTableLine
, pMyUpper
);
1054 nMaxStep
= nLine
+ nMaxStep
;
1055 if( nMaxStep
>= rTable
.GetTabLines().Count() )
1056 nMaxStep
= rTable
.GetTabLines().Count() - 1;
1057 long nLeftBorder
= lcl_Box2LeftBorder( *this );
1059 lcl_LeftBorder2Box( nLeftBorder
, rTable
.GetTabLines()[ nMaxStep
] );
1066 /** lcl_getAllMergedBoxes(..) collects all overlapped boxes to a given (master) box
1069 void lcl_getAllMergedBoxes( const SwTable
& rTable
, SwSelBoxes
& rBoxes
, SwTableBox
& rBox
)
1071 SwTableBox
* pBox
= &rBox
;
1072 ASSERT( pBox
== &rBox
.FindStartOfRowSpan( rTable
, USHRT_MAX
), "Not a master box" );
1073 rBoxes
.Insert( pBox
);
1074 if( pBox
->getRowSpan() == 1 )
1076 const SwTableLine
* pMyUpper
= pBox
->GetUpper();
1077 USHORT nLine
= rTable
.GetTabLines().C40_GETPOS(SwTableLine
, pMyUpper
);
1078 long nLeftBorder
= lcl_Box2LeftBorder( *pBox
);
1079 USHORT nCount
= rTable
.GetTabLines().Count();
1080 while( ++nLine
< nCount
&& pBox
&& pBox
->getRowSpan() != -1 )
1082 pBox
= lcl_LeftBorder2Box( nLeftBorder
, rTable
.GetTabLines()[nLine
] );
1084 rBoxes
.Insert( pBox
);
1088 /** lcl_UnMerge(..) manipulates the row span attribute of a given master cell
1089 and its overlapped cells to split them into several pieces.
1092 void lcl_UnMerge( const SwTable
& rTable
, SwTableBox
& rBox
, USHORT nCnt
,
1096 lcl_getAllMergedBoxes( rTable
, aBoxes
, rBox
);
1097 USHORT nCount
= aBoxes
.Count();
1102 USHORT
*pSplitIdx
= new USHORT
[ nCnt
];
1105 SwTwips
*pHeights
= new SwTwips
[ nCount
];
1106 SwTwips nHeight
= 0;
1107 for( USHORT i
= 0; i
< nCount
; ++i
)
1109 SwTableLine
* pLine
= aBoxes
[ i
]->GetUpper();
1110 SwFrmFmt
*pRowFmt
= pLine
->GetFrmFmt();
1111 pHeights
[ i
] = pRowFmt
->GetFrmSize().GetHeight();
1112 nHeight
+= pHeights
[ i
];
1116 for( USHORT i
= 1; i
<= nCnt
; ++i
)
1118 SwTwips nSplit
= ( i
* nHeight
) / nCnt
;
1119 while( nSumH
< nSplit
&& nIdx
< nCount
)
1120 nSumH
+= pHeights
[ nIdx
++ ];
1121 pSplitIdx
[ i
- 1 ] = nIdx
;
1127 for( long i
= 1; i
<= nCnt
; ++i
)
1128 pSplitIdx
[ i
- 1 ] = (USHORT
)( ( i
* nCount
) / nCnt
);
1131 for( long i
= 0; i
< nCnt
; ++i
)
1133 USHORT nNextIdx
= pSplitIdx
[ i
];
1134 aBoxes
[ nIdx
]->setRowSpan( nNextIdx
- nIdx
);
1135 lcl_InvalidateCellFrm( *aBoxes
[ nIdx
] );
1136 while( ++nIdx
< nNextIdx
)
1137 aBoxes
[ nIdx
]->setRowSpan( nIdx
- nNextIdx
);
1142 /** lcl_FillSelBoxes(..) puts all boxes of a given line into the selection structure
1145 void lcl_FillSelBoxes( SwSelBoxes
&rBoxes
, SwTableLine
&rLine
)
1147 USHORT nBoxCount
= rLine
.GetTabBoxes().Count();
1149 for( nCurrBox
= 0; nCurrBox
< nBoxCount
; ++nCurrBox
)
1150 rBoxes
.Insert( rLine
.GetTabBoxes()[nCurrBox
] );
1153 /** SwTable::InsertSpannedRow(..) inserts "superfluous" rows, i.e. rows containig
1154 overlapped cells only. This is a preparation for an upcoming split.
1157 void SwTable::InsertSpannedRow( SwDoc
* pDoc
, USHORT nRowIdx
, USHORT nCnt
)
1159 CHECK_TABLE( *this )
1160 ASSERT( nCnt
&& nRowIdx
< GetTabLines().Count(), "Wrong call of InsertSpannedRow" );
1162 SwTableLine
& rLine
= *GetTabLines()[ nRowIdx
];
1163 lcl_FillSelBoxes( aBoxes
, rLine
);
1164 SwFmtFrmSize
aFSz( rLine
.GetFrmFmt()->GetFrmSize() );
1165 if( ATT_VAR_SIZE
!= aFSz
.GetHeightSizeType() )
1167 SwFrmFmt
* pFrmFmt
= rLine
.ClaimFrmFmt();
1168 long nNewHeight
= aFSz
.GetHeight() / ( nCnt
+ 1 );
1171 aFSz
.SetHeight( nNewHeight
);
1172 pFrmFmt
->SetFmtAttr( aFSz
);
1174 _InsertRow( pDoc
, aBoxes
, nCnt
, TRUE
);
1175 USHORT nBoxCount
= rLine
.GetTabBoxes().Count();
1176 for( USHORT n
= 0; n
< nCnt
; ++n
)
1178 SwTableLine
*pNewLine
= GetTabLines()[ nRowIdx
+ nCnt
- n
];
1179 for( USHORT nCurrBox
= 0; nCurrBox
< nBoxCount
; ++nCurrBox
)
1181 long nRowSpan
= rLine
.GetTabBoxes()[nCurrBox
]->getRowSpan();
1183 nRowSpan
= - nRowSpan
;
1184 pNewLine
->GetTabBoxes()[ nCurrBox
]->setRowSpan( nRowSpan
- n
);
1187 lcl_ChangeRowSpan( *this, nCnt
, nRowIdx
, false );
1188 CHECK_TABLE( *this )
1191 typedef std::pair
< USHORT
, USHORT
> SwLineOffset
;
1192 typedef std::list
< SwLineOffset
> SwLineOffsetArray
;
1194 /******************************************************************************
1195 When a couple of table boxes has to be split,
1196 lcl_SophisticatedFillLineIndices delivers the information where and how many
1197 rows have to be inserted.
1199 rTable: the table to manipulate
1200 rBoxes: an array of boxes to split
1201 nCnt: how many parts are wanted
1203 rArr: a list of pairs ( line index, number of lines to insert )
1205 ******************************************************************************/
1207 void lcl_SophisticatedFillLineIndices( SwLineOffsetArray
&rArr
,
1208 const SwTable
& rTable
, const SwSelBoxes
& rBoxes
, USHORT nCnt
)
1210 std::list
< SwLineOffset
> aBoxes
;
1211 SwLineOffset
aLnOfs( USHRT_MAX
, USHRT_MAX
);
1212 for( USHORT i
= 0; i
< rBoxes
.Count(); ++i
)
1213 { // Collect all end line indices and the row spans
1214 const SwTableBox
&rBox
= rBoxes
[ i
]->FindStartOfRowSpan( rTable
);
1215 ASSERT( rBox
.getRowSpan() > 0, "Didn't I say 'StartOfRowSpan' ??" )
1216 if( nCnt
> rBox
.getRowSpan() )
1218 const SwTableLine
*pLine
= rBox
.GetUpper();
1219 const USHORT nEnd
= USHORT( rBox
.getRowSpan() +
1220 rTable
.GetTabLines().C40_GETPOS( SwTableLine
, pLine
) );
1221 // The next if statement is a small optimization
1222 if( aLnOfs
.first
!= nEnd
|| aLnOfs
.second
!= rBox
.getRowSpan() )
1224 aLnOfs
.first
= nEnd
; // ok, this is the line behind the box
1225 aLnOfs
.second
= USHORT( rBox
.getRowSpan() ); // the row span
1226 aBoxes
.insert( aBoxes
.end(), aLnOfs
);
1230 // As I said, I noted the line index _behind_ the last line of the boxes
1231 // in the resulting array the index has to be _on_ the line
1232 // nSum is to evaluate the wished value
1234 while( aBoxes
.size() )
1237 // Looking for the "smallest" line end with the smallest row span
1238 std::list
< SwLineOffset
>::iterator pCurr
= aBoxes
.begin();
1239 aLnOfs
= *pCurr
; // the line end and row span of the first box
1240 while( ++pCurr
!= aBoxes
.end() )
1242 if( aLnOfs
.first
> pCurr
->first
)
1243 { // Found a smaller line end
1244 aLnOfs
.first
= pCurr
->first
;
1245 aLnOfs
.second
= pCurr
->second
; // row span
1247 else if( aLnOfs
.first
== pCurr
->first
&&
1248 aLnOfs
.second
< pCurr
->second
)
1249 aLnOfs
.second
= pCurr
->second
; // Found a smaller row span
1251 ASSERT( aLnOfs
.second
< nCnt
, "Clean-up failed" )
1252 aLnOfs
.second
= nCnt
- aLnOfs
.second
; // the number of rows to insert
1253 rArr
.insert( rArr
.end(),
1254 SwLineOffset( aLnOfs
.first
- nSum
, aLnOfs
.second
) );
1255 // the correction has to be incremented because in the following
1256 // loops the line ends were manipulated
1257 nSum
= nSum
+ aLnOfs
.second
;
1259 pCurr
= aBoxes
.begin();
1260 while( pCurr
!= aBoxes
.end() )
1262 if( pCurr
->first
== aLnOfs
.first
)
1263 { // These boxes can be removed because the last insertion
1264 // of rows will expand their row span above the needed value
1265 std::list
< SwLineOffset
>::iterator pDel
= pCurr
;
1267 aBoxes
.erase( pDel
);
1271 bool bBefore
= ( pCurr
->first
- pCurr
->second
< aLnOfs
.first
);
1272 // Manipulation of the end line indices as if the rows are
1274 pCurr
->first
= pCurr
->first
+ aLnOfs
.second
;
1276 { // If the insertion is inside the box,
1277 // its row span has to be incremented
1278 pCurr
->second
= pCurr
->second
+ aLnOfs
.second
;
1279 if( pCurr
->second
>= nCnt
)
1280 { // if the row span is bigger than the split factor
1282 std::list
< SwLineOffset
>::iterator pDel
= pCurr
;
1284 aBoxes
.erase( pDel
);
1296 typedef std::set
< SwTwips
> SwSplitLines
;
1298 /** lcl_CalculateSplitLineHeights(..) delivers all y-positions where table rows have
1299 to be splitted to fulfill the requested "split same height"
1302 USHORT
lcl_CalculateSplitLineHeights( SwSplitLines
&rCurr
, SwSplitLines
&rNew
,
1303 const SwTable
& rTable
, const SwSelBoxes
& rBoxes
, USHORT nCnt
)
1307 std::list
< SwLineOffset
> aBoxes
;
1308 SwLineOffset
aLnOfs( USHRT_MAX
, USHRT_MAX
);
1309 USHORT nFirst
= USHRT_MAX
; // becomes the index of the first line
1310 USHORT nLast
= 0; // becomes the index of the last line of the splitting
1311 for( USHORT i
= 0; i
< rBoxes
.Count(); ++i
)
1312 { // Collect all pairs (start+end) of line indices to split
1313 const SwTableBox
&rBox
= rBoxes
[ i
]->FindStartOfRowSpan( rTable
);
1314 ASSERT( rBox
.getRowSpan() > 0, "Didn't I say 'StartOfRowSpan' ??" )
1315 const SwTableLine
*pLine
= rBox
.GetUpper();
1316 const USHORT nStart
= rTable
.GetTabLines().C40_GETPOS( SwTableLine
, pLine
);
1317 const USHORT nEnd
= USHORT( rBox
.getRowSpan() + nStart
- 1 );
1318 // The next if statement is a small optimization
1319 if( aLnOfs
.first
!= nStart
|| aLnOfs
.second
!= nEnd
)
1321 aLnOfs
.first
= nStart
;
1322 aLnOfs
.second
= nEnd
;
1323 aBoxes
.insert( aBoxes
.end(), aLnOfs
);
1324 if( nStart
< nFirst
)
1331 if( aBoxes
.empty() )
1333 SwTwips nHeight
= 0;
1334 SwTwips
* pLines
= new SwTwips
[ nLast
+ 1 - nFirst
];
1335 for( USHORT i
= nFirst
; i
<= nLast
; ++i
)
1337 bool bLayoutAvailable
= false;
1338 nHeight
+= rTable
.GetTabLines()[ i
]->GetTableLineHeight( bLayoutAvailable
);
1339 rCurr
.insert( rCurr
.end(), nHeight
);
1340 pLines
[ i
- nFirst
] = nHeight
;
1342 std::list
< SwLineOffset
>::iterator pSplit
= aBoxes
.begin();
1343 while( pSplit
!= aBoxes
.end() )
1345 SwTwips nBase
= pSplit
->first
<= nFirst
? 0 :
1346 pLines
[ pSplit
->first
- nFirst
- 1 ];
1347 SwTwips nDiff
= pLines
[ pSplit
->second
- nFirst
] - nBase
;
1348 for( USHORT i
= 1; i
< nCnt
; ++i
)
1350 SwTwips nSplit
= nBase
+ ( i
* nDiff
) / nCnt
;
1351 rNew
.insert( nSplit
);
1359 /** lcl_LineIndex(..) delivers the line index of the line behind or above
1363 USHORT
lcl_LineIndex( const SwTable
& rTable
, const SwSelBoxes
& rBoxes
,
1366 USHORT nDirect
= USHRT_MAX
;
1367 USHORT nSpan
= USHRT_MAX
;
1368 for( USHORT i
= 0; i
< rBoxes
.Count(); ++i
)
1370 SwTableBox
*pBox
= rBoxes
[i
];
1371 const SwTableLine
* pLine
= rBoxes
[i
]->GetUpper();
1372 USHORT nPos
= rTable
.GetTabLines().C40_GETPOS( SwTableLine
, pLine
);
1373 if( USHRT_MAX
!= nPos
)
1377 if( nPos
> nDirect
|| nDirect
== USHRT_MAX
)
1379 long nRowSpan
= pBox
->getRowSpan();
1384 USHORT nEndOfRowSpan
= (USHORT
)(nPos
+ nRowSpan
- 1);
1385 if( nEndOfRowSpan
> nSpan
|| nSpan
== USHRT_MAX
)
1386 nSpan
= nEndOfRowSpan
;
1389 else if( nPos
< nDirect
)
1393 if( nSpan
&& nSpan
< USHRT_MAX
)
1398 /** SwTable::NewSplitRow(..) splits all selected boxes horizontally.
1401 BOOL
SwTable::NewSplitRow( SwDoc
* pDoc
, const SwSelBoxes
& rBoxes
, USHORT nCnt
,
1404 CHECK_TABLE( *this )
1406 _FndBox
aFndBox( 0, 0 );
1407 aFndBox
.SetTableLines( rBoxes
, *this );
1409 if( bSameHeight
&& pDoc
->GetRootFrm() )
1411 SwSplitLines aRowLines
;
1412 SwSplitLines aSplitLines
;
1413 USHORT nFirst
= lcl_CalculateSplitLineHeights( aRowLines
, aSplitLines
,
1414 *this, rBoxes
, nCnt
);
1415 aFndBox
.DelFrms( *this );
1417 SwSplitLines::iterator pSplit
= aSplitLines
.begin();
1418 SwSplitLines::iterator pCurr
= aRowLines
.begin();
1419 while( pCurr
!= aRowLines
.end() )
1421 while( pSplit
!= aSplitLines
.end() && *pSplit
< *pCurr
)
1423 InsertSpannedRow( pDoc
, nFirst
, 1 );
1424 SwTableLine
* pRow
= GetTabLines()[ nFirst
];
1425 SwFrmFmt
* pRowFmt
= pRow
->ClaimFrmFmt();
1426 SwFmtFrmSize
aFSz( pRowFmt
->GetFrmSize() );
1427 aFSz
.SetHeightSizeType( ATT_MIN_SIZE
);
1428 aFSz
.SetHeight( *pSplit
- nLast
);
1429 pRowFmt
->SetFmtAttr( aFSz
);
1434 if( pSplit
!= aSplitLines
.end() && *pCurr
== *pSplit
)
1436 SwTableLine
* pRow
= GetTabLines()[ nFirst
];
1437 SwFrmFmt
* pRowFmt
= pRow
->ClaimFrmFmt();
1438 SwFmtFrmSize
aFSz( pRowFmt
->GetFrmSize() );
1439 aFSz
.SetHeightSizeType( ATT_MIN_SIZE
);
1440 aFSz
.SetHeight( *pCurr
- nLast
);
1441 pRowFmt
->SetFmtAttr( aFSz
);
1449 aFndBox
.DelFrms( *this );
1450 bSameHeight
= FALSE
;
1454 SwLineOffsetArray aLineOffs
;
1455 lcl_SophisticatedFillLineIndices( aLineOffs
, *this, rBoxes
, nCnt
);
1456 SwLineOffsetArray::reverse_iterator
pCurr( aLineOffs
.rbegin() );
1457 while( pCurr
!= aLineOffs
.rend() )
1459 InsertSpannedRow( pDoc
, pCurr
->first
, pCurr
->second
);
1464 std::set
< USHORT
> aIndices
;
1465 for( USHORT i
= 0; i
< rBoxes
.Count(); ++i
)
1467 ASSERT( rBoxes
[i
]->getRowSpan() != 1, "Forgot to split?" )
1468 if( rBoxes
[i
]->getRowSpan() > 1 )
1469 aIndices
.insert( i
);
1472 std::set
< USHORT
>::iterator pCurrBox
= aIndices
.begin();
1473 while( pCurrBox
!= aIndices
.end() )
1474 lcl_UnMerge( *this, *rBoxes
[*pCurrBox
++], nCnt
, bSameHeight
);
1476 CHECK_TABLE( *this )
1478 aFndBox
.MakeFrms( *this );
1483 /** SwTable::InsertRow(..) inserts one or more rows before or behind the selected
1487 BOOL
SwTable::InsertRow( SwDoc
* pDoc
, const SwSelBoxes
& rBoxes
,
1488 USHORT nCnt
, BOOL bBehind
)
1493 CHECK_TABLE( *this )
1494 USHORT nRowIdx
= lcl_LineIndex( *this, rBoxes
, bBehind
);
1495 if( nRowIdx
< USHRT_MAX
)
1497 _FndBox
aFndBox( 0, 0 );
1498 aFndBox
.SetTableLines( rBoxes
, *this );
1499 aFndBox
.DelFrms( *this );
1500 // aFndBox.SaveChartData( *this );
1503 SwTableLine
*pLine
= GetTabLines()[ nRowIdx
];
1504 SwSelBoxes aLineBoxes
;
1505 lcl_FillSelBoxes( aLineBoxes
, *pLine
);
1506 _InsertRow( pDoc
, aLineBoxes
, nCnt
, bBehind
);
1507 USHORT nBoxCount
= pLine
->GetTabBoxes().Count();
1508 USHORT nOfs
= bBehind
? 0 : 1;
1509 for( USHORT n
= 0; n
< nCnt
; ++n
)
1511 SwTableLine
*pNewLine
= GetTabLines()[ nRowIdx
+nCnt
-n
-nOfs
];
1512 for( USHORT nCurrBox
= 0; nCurrBox
< nBoxCount
; ++nCurrBox
)
1514 long nRowSpan
= pLine
->GetTabBoxes()[nCurrBox
]->getRowSpan();
1517 if( nRowSpan
== 1 || nRowSpan
== -1 )
1519 else if( nRowSpan
> 1 )
1520 nRowSpan
= - nRowSpan
;
1529 pNewLine
->GetTabBoxes()[ nCurrBox
]->setRowSpan( nRowSpan
- n
);
1535 lcl_ChangeRowSpan( *this, nCnt
, --nRowIdx
, true );
1537 aFndBox
.MakeFrms( *this );
1538 // aFndBox.RestoreChartData( *this );
1540 CHECK_TABLE( *this )
1543 bRet
= _InsertRow( pDoc
, rBoxes
, nCnt
, bBehind
);
1547 /** SwTable::PrepareDelBoxes(..) adjusts the row span attributes for an upcoming
1548 deletion of table cells and invalidates the layout of these cells.
1551 void SwTable::PrepareDelBoxes( const SwSelBoxes
& rBoxes
)
1555 for( USHORT i
= 0; i
< rBoxes
.Count(); ++i
)
1557 SwTableBox
* pBox
= rBoxes
[i
];
1558 long nRowSpan
= pBox
->getRowSpan();
1559 if( nRowSpan
!= 1 && pBox
->GetFrmFmt()->GetFrmSize().GetWidth() )
1561 long nLeft
= lcl_Box2LeftBorder( *pBox
);
1562 SwTableLine
*pLine
= pBox
->GetUpper();
1563 USHORT nLinePos
= GetTabLines().C40_GETPOS(SwTableLine
, pLine
);
1564 ASSERT( nLinePos
< USHRT_MAX
, "Box/table mismatch" )
1567 if( ++nLinePos
< GetTabLines().Count() )
1569 pLine
= GetTabLines()[ nLinePos
];
1570 pBox
= lcl_LeftBorder2Box( nLeft
, pLine
);
1571 ASSERT( pBox
, "RowSpan irritation I" )
1573 pBox
->setRowSpan( --nRowSpan
);
1576 else if( nLinePos
> 0 )
1580 pLine
= GetTabLines()[ --nLinePos
];
1581 pBox
= lcl_LeftBorder2Box( nLeft
, pLine
);
1582 ASSERT( pBox
, "RowSpan irritation II" )
1585 nRowSpan
= pBox
->getRowSpan();
1588 lcl_InvalidateCellFrm( *pBox
);
1593 pBox
->setRowSpan( nRowSpan
);
1598 while( nRowSpan
< 0 && nLinePos
> 0 );
1605 /** lcl_SearchSelBox(..) adds cells of a given table row to the selection structure
1606 if it overlaps with the given x-position range
1609 void lcl_SearchSelBox( const SwTable
&rTable
, SwSelBoxes
& rBoxes
, long nMin
, long nMax
,
1610 SwTableLine
& rLine
, bool bChkProtected
, bool bColumn
)
1614 long nMid
= ( nMax
+ nMin
)/ 2;
1615 USHORT nCount
= rLine
.GetTabBoxes().Count();
1616 for( USHORT nCurrBox
= 0; nCurrBox
< nCount
; ++nCurrBox
)
1618 SwTableBox
* pBox
= rLine
.GetTabBoxes()[nCurrBox
];
1619 ASSERT( pBox
, "Missing table box" );
1620 long nWidth
= pBox
->GetFrmFmt()->GetFrmSize().GetWidth();
1625 if( nRight
<= nMax
)
1626 bAdd
= nLeft
>= nMin
|| nRight
>= nMid
||
1627 nRight
- nMin
> nMin
- nLeft
;
1629 bAdd
= nLeft
<= nMid
|| nRight
- nMax
< nMax
- nLeft
;
1630 long nRowSpan
= pBox
->getRowSpan();
1632 //( bColumn || nRowSpan > 0 ) &&
1634 !pBox
->GetFrmFmt()->GetProtect().IsCntntProtected() ) )
1636 USHORT nOldCnt
= rBoxes
.Count();
1637 rBoxes
.Insert( pBox
);
1638 if( bColumn
&& nRowSpan
!= 1 && nOldCnt
< rBoxes
.Count() )
1640 SwTableBox
*pMasterBox
= pBox
->getRowSpan() > 0 ? pBox
1641 : &pBox
->FindStartOfRowSpan( rTable
, USHRT_MAX
);
1642 lcl_getAllMergedBoxes( rTable
, rBoxes
, *pMasterBox
);
1646 if( nRight
>= nMax
)
1652 /** void SwTable::CreateSelection(..) fills the selection structure with table cells
1653 for a given SwPaM, ie. start and end position inside a table
1656 void SwTable::CreateSelection( const SwPaM
& rPam
, SwSelBoxes
& rBoxes
,
1657 const SearchType eSearch
, bool bChkProtected
) const
1659 ASSERT( bNewModel
, "Don't call me for old tables" );
1660 if( !aLines
.Count() )
1662 const SwNode
* pStartNd
= rPam
.GetPoint()->nNode
.GetNode().FindTableBoxStartNode();
1663 const SwNode
* pEndNd
= rPam
.GetMark()->nNode
.GetNode().FindTableBoxStartNode();
1664 if( !pStartNd
|| !pEndNd
)
1666 CreateSelection( pStartNd
, pEndNd
, rBoxes
, eSearch
, bChkProtected
);
1669 /** void SwTable::CreateSelection(..) fills the selection structure with table cells
1670 for given start and end nodes inside a table
1672 void SwTable::CreateSelection( const SwNode
* pStartNd
, const SwNode
* pEndNd
,
1673 SwSelBoxes
& rBoxes
, const SearchType eSearch
, bool bChkProtected
) const
1675 // SwSelBoxes aKeepBoxes;
1676 if( rBoxes
.Count() )
1678 // aKeepBoxes.Insert( &rBoxes );
1679 rBoxes
.Remove( USHORT(0), rBoxes
.Count() );
1681 // Looking for start and end of the selection given by SwNode-pointer
1682 USHORT nLines
= aLines
.Count();
1683 // nTop becomes the line number of the upper box
1684 // nBottom becomes the line number of the lower box
1685 USHORT nTop
= 0, nBottom
= 0;
1686 // nUpperMin becomes the left border value of the upper box
1687 // nUpperMax becomes the right border of the upper box
1688 // nLowerMin and nLowerMax the borders of the lower box
1689 long nUpperMin
= 0, nUpperMax
= 0;
1690 long nLowerMin
= 0, nLowerMax
= 0;
1691 // nFound will incremented if a box is found
1692 // 0 => no box found; 1 => the upper box has been found; 2 => both found
1694 for( USHORT nRow
= 0; nFound
< 2 && nRow
< nLines
; ++nRow
)
1696 SwTableLine
* pLine
= aLines
[nRow
];
1697 ASSERT( pLine
, "Missing table line" );
1698 USHORT nCols
= pLine
->GetTabBoxes().Count();
1699 for( USHORT nCol
= 0; nCol
< nCols
; ++nCol
)
1701 SwTableBox
* pBox
= pLine
->GetTabBoxes()[nCol
];
1702 ASSERT( pBox
, "Missing table box" );
1703 if( pBox
->GetSttNd() == pEndNd
|| pBox
->GetSttNd() == pStartNd
)
1705 if( !bChkProtected
||
1706 !pBox
->GetFrmFmt()->GetProtect().IsCntntProtected() )
1707 rBoxes
.Insert( pBox
);
1711 lcl_CheckMinMax( nLowerMin
, nLowerMax
, *pLine
, nCol
, true );
1718 lcl_CheckMinMax( nUpperMin
, nUpperMax
, *pLine
, nCol
, true );
1720 // If start and end node are identical, we're nearly done..
1721 if( pEndNd
== pStartNd
)
1724 nLowerMin
= nUpperMin
;
1725 nLowerMax
= nUpperMax
;
1733 return; // At least one node was not a part of the given table
1734 if( eSearch
== SEARCH_ROW
)
1736 // Selection of a row is quiet easy:
1737 // every (unprotected) box between start and end line
1738 // with a positive row span will be collected
1739 for( USHORT nRow
= nTop
; nRow
<= nBottom
; ++nRow
)
1741 SwTableLine
* pLine
= aLines
[nRow
];
1742 ASSERT( pLine
, "Missing table line" );
1743 USHORT nCount
= pLine
->GetTabBoxes().Count();
1744 for( USHORT nCurrBox
= 0; nCurrBox
< nCount
; ++nCurrBox
)
1746 SwTableBox
* pBox
= pLine
->GetTabBoxes()[nCurrBox
];
1747 ASSERT( pBox
, "Missing table box" );
1748 if( pBox
->getRowSpan() > 0 && ( !bChkProtected
||
1749 !pBox
->GetFrmFmt()->GetProtect().IsCntntProtected() ) )
1750 rBoxes
.Insert( pBox
);
1755 bool bCombine
= nTop
== nBottom
;
1758 long nMinWidth
= nUpperMax
- nUpperMin
;
1759 long nTmp
= nLowerMax
- nLowerMin
;
1760 if( nMinWidth
> nTmp
)
1762 nTmp
= nLowerMax
< nUpperMax
? nLowerMax
: nUpperMax
;
1763 nTmp
-= ( nLowerMin
< nUpperMin
) ? nUpperMin
: nLowerMin
;
1764 // If the overlapping between upper and lower box is less than half
1765 // of the width (of the smaller cell), bCombine is set,
1766 // e.g. if upper and lower cell are in different columns
1767 bCombine
= ( nTmp
+ nTmp
< nMinWidth
);
1771 if( nUpperMin
< nLowerMin
)
1772 nLowerMin
= nUpperMin
;
1774 nUpperMin
= nLowerMin
;
1775 if( nUpperMax
> nLowerMax
)
1776 nLowerMax
= nUpperMax
;
1778 nUpperMax
= nLowerMax
;
1780 const bool bColumn
= eSearch
== SEARCH_COL
;
1783 for( USHORT i
= 0; i
< nTop
; ++i
)
1784 lcl_SearchSelBox( *this, rBoxes
, nUpperMin
, nUpperMax
,
1785 *aLines
[i
], bChkProtected
, bColumn
);
1789 long nMin
= nUpperMin
< nLowerMin
? nUpperMin
: nLowerMin
;
1790 long nMax
= nUpperMax
< nLowerMax
? nLowerMax
: nUpperMax
;
1791 for( USHORT i
= nTop
; i
<= nBottom
; ++i
)
1792 lcl_SearchSelBox( *this, rBoxes
, nMin
, nMax
, *aLines
[i
],
1793 bChkProtected
, bColumn
);
1795 /* if( nTop + 1 < nBottom )
1797 long nInnerMin = nUpperMin < nLowerMin ? nLowerMin : nUpperMin;
1798 long nInnerMax = nUpperMax < nLowerMax ? nUpperMax : nLowerMax;
1799 for( USHORT i = nTop + 1; i < nBottom; ++i )
1800 lcl_SearchSelBox( *this, rBoxes, nInnerMin, nInnerMax, *aLines[i],
1801 bChkProtected, bColumn );
1803 if( bCombine ) // => nUpperMin == nLowerMin, nUpperMax == nLowerMax
1805 if( nBottom > nTop )
1806 lcl_SearchSelBox( *this, rBoxes, nUpperMin, nUpperMax, *aLines[nTop],
1807 bChkProtected, bColumn );
1808 lcl_SearchSelBox( *this, rBoxes, nLowerMin, nLowerMax, *aLines[nBottom],
1809 bChkProtected, bColumn );
1811 else if( aKeepBoxes.Count() )
1813 long nMin = nUpperMin < nLowerMin ? nUpperMin : nLowerMin;
1814 long nMax = nUpperMax < nLowerMax ? nLowerMax : nUpperMax;
1815 SwSelBoxes aCandidates;
1816 for( USHORT i = nTop; i <= nBottom; ++i )
1817 lcl_SearchSelBox( *this, aCandidates, nMin, nMax, *aLines[i],
1818 bChkProtected, bColumn );
1819 USHORT nOld = 0, nNew = 0;
1820 while ( nOld < aKeepBoxes.Count() && nNew < aCandidates.Count() )
1822 const SwTableBox* pPOld = *( aKeepBoxes.GetData() + nOld );
1823 SwTableBox* pPNew = *( aCandidates.GetData() + nNew );
1824 if( pPOld == pPNew )
1825 { // this box will stay
1826 rBoxes.Insert( pPNew );
1830 else if( pPOld->GetSttIdx() < pPNew->GetSttIdx() )
1838 for( USHORT i
= nBottom
+ 1; i
< nLines
; ++i
)
1839 lcl_SearchSelBox( *this, rBoxes
, nLowerMin
, nLowerMax
, *aLines
[i
],
1840 bChkProtected
, true );
1844 /** void SwTable::ExpandColumnSelection(..) adds cell to the give selection to
1845 assure that at least one cell of every row is part of the selection.
1848 void SwTable::ExpandColumnSelection( SwSelBoxes
& rBoxes
, long &rMin
, long &rMax
) const
1850 ASSERT( bNewModel
, "Don't call me for old tables" );
1853 if( !aLines
.Count() || !rBoxes
.Count() )
1856 USHORT nLineCnt
= aLines
.Count();
1857 USHORT nBoxCnt
= rBoxes
.Count();
1859 for( USHORT nRow
= 0; nRow
< nLineCnt
&& nBox
< nBoxCnt
; ++nRow
)
1861 SwTableLine
* pLine
= aLines
[nRow
];
1862 ASSERT( pLine
, "Missing table line" );
1863 USHORT nCols
= pLine
->GetTabBoxes().Count();
1864 for( USHORT nCol
= 0; nCol
< nCols
; ++nCol
)
1866 SwTableBox
* pBox
= pLine
->GetTabBoxes()[nCol
];
1867 ASSERT( pBox
, "Missing table box" );
1868 if( pBox
== rBoxes
[nBox
] )
1870 lcl_CheckMinMax( rMin
, rMax
, *pLine
, nCol
, nBox
== 0 );
1871 if( ++nBox
>= nBoxCnt
)
1877 for( USHORT nRow
= 0; nRow
< nLineCnt
; ++nRow
)
1879 SwTableLine
* pLine
= aLines
[nRow
];
1880 USHORT nCols
= pLine
->GetTabBoxes().Count();
1883 for( USHORT nCurrBox
= 0; nCurrBox
< nCols
; ++nCurrBox
)
1886 SwTableBox
* pBox
= pLine
->GetTabBoxes()[nCurrBox
];
1887 nRight
+= pBox
->GetFrmFmt()->GetFrmSize().GetWidth();
1888 if( nLeft
>= rMin
&& nRight
<= rMax
)
1889 rBoxes
.Insert( pBox
);
1894 /** SwTable::PrepareDeleteCol(..) adjusts the widths of the neighbour cells of
1895 a cell selection for an upcoming (column) deletion
1897 void SwTable::PrepareDeleteCol( long nMin
, long nMax
)
1899 ASSERT( bNewModel
, "Don't call me for old tables" );
1900 if( !aLines
.Count() || nMax
< nMin
)
1902 long nMid
= nMin
? ( nMin
+ nMax
) / 2 : 0;
1903 const SwTwips nTabSize
= GetFrmFmt()->GetFrmSize().GetWidth();
1904 if( nTabSize
== nMax
)
1906 USHORT nLineCnt
= aLines
.Count();
1907 for( USHORT nRow
= 0; nRow
< nLineCnt
; ++nRow
)
1909 SwTableLine
* pLine
= aLines
[nRow
];
1910 USHORT nCols
= pLine
->GetTabBoxes().Count();
1913 for( USHORT nCurrBox
= 0; nCurrBox
< nCols
; ++nCurrBox
)
1916 SwTableBox
* pBox
= pLine
->GetTabBoxes()[nCurrBox
];
1917 nRight
+= pBox
->GetFrmFmt()->GetFrmSize().GetWidth();
1922 long nNewWidth
= -1;
1925 if( nRight
<= nMax
)
1926 nNewWidth
= nMid
- nLeft
;
1928 else if( nRight
> nMax
)
1929 nNewWidth
= nRight
- nMid
;
1932 if( nNewWidth
>= 0 )
1934 SwFrmFmt
* pFrmFmt
= pBox
->ClaimFrmFmt();
1935 SwFmtFrmSize
aFrmSz( pFrmFmt
->GetFrmSize() );
1936 aFrmSz
.SetWidth( nNewWidth
);
1937 pFrmFmt
->SetFmtAttr( aFrmSz
);
1945 /** SwTable::ExpandSelection(..) adds all boxes to the box selections which are
1949 void SwTable::ExpandSelection( SwSelBoxes
& rBoxes
) const
1951 for( USHORT i
= 0; i
< rBoxes
.Count(); ++i
)
1953 SwTableBox
*pBox
= rBoxes
[i
];
1954 long nRowSpan
= pBox
->getRowSpan();
1957 SwTableBox
*pMasterBox
= nRowSpan
> 0 ? pBox
1958 : &pBox
->FindStartOfRowSpan( *this, USHRT_MAX
);
1959 lcl_getAllMergedBoxes( *this, rBoxes
, *pMasterBox
);
1964 /** SwTable::CheckRowSpan(..) looks for the next line without an overlapping to
1968 void SwTable::CheckRowSpan( SwTableLinePtr
&rpLine
, bool bUp
) const
1970 ASSERT( IsNewModel(), "Don't call me for old tables" );
1971 USHORT nLineIdx
= GetTabLines().C40_GETPOS( SwTableLine
, rpLine
);
1972 ASSERT( nLineIdx
< GetTabLines().Count(), "Start line out of range" );
1973 bool bChange
= true;
1979 rpLine
= GetTabLines()[ nLineIdx
];
1980 USHORT nCols
= rpLine
->GetTabBoxes().Count();
1981 for( USHORT nCol
= 0; !bChange
&& nCol
< nCols
; ++nCol
)
1983 SwTableBox
* pBox
= rpLine
->GetTabBoxes()[nCol
];
1984 if( pBox
->getRowSpan() > 1 || pBox
->getRowSpan() < -1 )
2001 USHORT nMaxLine
= GetTabLines().Count();
2005 rpLine
= GetTabLines()[ nLineIdx
];
2006 USHORT nCols
= rpLine
->GetTabBoxes().Count();
2007 for( USHORT nCol
= 0; !bChange
&& nCol
< nCols
; ++nCol
)
2009 SwTableBox
* pBox
= rpLine
->GetTabBoxes()[nCol
];
2010 if( pBox
->getRowSpan() < 0 )
2016 if( nLineIdx
>= nMaxLine
)
2026 // This structure corrects the row span attributes for a top line of a table
2027 // In a top line no negative row span is allowed, so these have to be corrected.
2028 // If there has been at least one correction, all values are stored
2029 // and can be used by undo of table split
2030 SwSaveRowSpan::SwSaveRowSpan( SwTableBoxes
& rBoxes
, USHORT nSplitLn
)
2031 : mnSplitLine( nSplitLn
)
2033 bool bDontSave
= true; // nothing changed, nothing to save
2034 USHORT nColCount
= rBoxes
.Count();
2035 ASSERT( nColCount
, "Empty Table Line" )
2036 mnRowSpans
.resize( nColCount
);
2037 for( USHORT nCurrCol
= 0; nCurrCol
< nColCount
; ++nCurrCol
)
2039 SwTableBox
* pBox
= rBoxes
[nCurrCol
];
2040 ASSERT( pBox
, "Missing Table Box" );
2041 long nRowSp
= pBox
->getRowSpan();
2042 mnRowSpans
[ nCurrCol
] = nRowSp
;
2047 pBox
->setRowSpan( nRowSp
); // correction needed
2054 // This function is called by undo of table split to restore the old row span
2055 // values at the split line
2056 void SwTable::RestoreRowSpan( const SwSaveRowSpan
& rSave
)
2058 if( !IsNewModel() ) // for new model only
2060 USHORT nLineCount
= GetTabLines().Count();
2061 ASSERT( rSave
.mnSplitLine
< nLineCount
, "Restore behind last line?" )
2062 if( rSave
.mnSplitLine
< nLineCount
)
2064 SwTableLine
* pLine
= GetTabLines()[rSave
.mnSplitLine
];
2065 USHORT nColCount
= pLine
->GetTabBoxes().Count();
2066 ASSERT( nColCount
, "Empty Table Line" )
2067 ASSERT( nColCount
== rSave
.mnRowSpans
.size(), "Wrong row span store" )
2068 if( nColCount
== rSave
.mnRowSpans
.size() )
2070 for( USHORT nCurrCol
= 0; nCurrCol
< nColCount
; ++nCurrCol
)
2072 SwTableBox
* pBox
= pLine
->GetTabBoxes()[nCurrCol
];
2073 ASSERT( pBox
, "Missing Table Box" );
2074 long nRowSp
= pBox
->getRowSpan();
2075 if( nRowSp
!= rSave
.mnRowSpans
[ nCurrCol
] )
2077 ASSERT( -nRowSp
== rSave
.mnRowSpans
[ nCurrCol
], "Pardon me?!" )
2078 ASSERT( rSave
.mnRowSpans
[ nCurrCol
] < 0, "Pardon me?!" )
2079 pBox
->setRowSpan( -nRowSp
);
2081 USHORT nLine
= rSave
.mnSplitLine
;
2084 long nLeftBorder
= lcl_Box2LeftBorder( *pBox
);
2088 pNext
= lcl_LeftBorder2Box( nLeftBorder
, GetTabLines()[--nLine
] );
2092 long nNewSpan
= pBox
->getRowSpan();
2093 if( pBox
->getRowSpan() < 1 )
2100 pBox
->setRowSpan( nNewSpan
);
2102 } while( nLine
&& pNext
);
2110 SwSaveRowSpan
* SwTable::CleanUpTopRowSpan( USHORT nSplitLine
)
2112 SwSaveRowSpan
* pRet
= 0;
2115 pRet
= new SwSaveRowSpan( GetTabLines()[0]->GetTabBoxes(), nSplitLine
);
2116 if( pRet
->mnRowSpans
.size() == 0 )
2124 void SwTable::CleanUpBottomRowSpan( USHORT nDelLines
)
2128 USHORT nLastLine
= GetTabLines().Count()-1;
2129 SwTableLine
* pLine
= GetTabLines()[nLastLine
];
2130 USHORT nColCount
= pLine
->GetTabBoxes().Count();
2131 ASSERT( nColCount
, "Empty Table Line" )
2132 for( USHORT nCurrCol
= 0; nCurrCol
< nColCount
; ++nCurrCol
)
2134 SwTableBox
* pBox
= pLine
->GetTabBoxes()[nCurrCol
];
2135 ASSERT( pBox
, "Missing Table Box" );
2136 long nRowSp
= pBox
->getRowSpan();
2141 lcl_ChangeRowSpan( *this, -static_cast<long>(nDelLines
), nLastLine
, false );
2156 void SwTable::CheckConsistency() const
2160 USHORT nLineCount
= GetTabLines().Count();
2161 const SwTwips nTabSize
= GetFrmFmt()->GetFrmSize().GetWidth();
2162 SwTwips nLineWidth
= 0;
2163 std::list
< RowSpanCheck
> aRowSpanCells
;
2164 std::list
< RowSpanCheck
>::iterator aIter
= aRowSpanCells
.end();
2165 for( USHORT nCurrLine
= 0; nCurrLine
< nLineCount
; ++nCurrLine
)
2168 SwTableLine
* pLine
= GetTabLines()[nCurrLine
];
2169 ASSERT( pLine
, "Missing Table Line" )
2170 USHORT nColCount
= pLine
->GetTabBoxes().Count();
2171 ASSERT( nColCount
, "Empty Table Line" )
2172 for( USHORT nCurrCol
= 0; nCurrCol
< nColCount
; ++nCurrCol
)
2174 SwTableBox
* pBox
= pLine
->GetTabBoxes()[nCurrCol
];
2175 ASSERT( pBox
, "Missing Table Box" );
2176 SwTwips nNewWidth
= pBox
->GetFrmFmt()->GetFrmSize().GetWidth() + nWidth
;
2177 long nRowSp
= pBox
->getRowSpan();
2180 ASSERT( aIter
!= aRowSpanCells
.end(), "Missing master box" )
2182 //RowSpanCheck &rCheck = *aIter;
2184 ASSERT( aIter
->nLeft
== nWidth
&& aIter
->nRight
== nNewWidth
,
2185 "Wrong position/size of overlapped table box" );
2186 --(aIter
->nRowSpan
);
2187 ASSERT( aIter
->nRowSpan
== -nRowSp
, "Wrong row span value" );
2190 std::list
< RowSpanCheck
>::iterator aEraseIter
= aIter
;
2192 aRowSpanCells
.erase( aEraseIter
);
2197 else if( nRowSp
!= 1 )
2199 ASSERT( nRowSp
, "Zero row span?!" );
2200 RowSpanCheck aEntry
;
2201 aEntry
.nLeft
= nWidth
;
2202 aEntry
.nRight
= nNewWidth
;
2203 aEntry
.nRowSpan
= nRowSp
;
2204 aRowSpanCells
.insert( aIter
, aEntry
);
2209 nLineWidth
= nWidth
;
2210 ASSERT( nWidth
== nLineWidth
, "Different Line Widths" )
2211 ASSERT( nWidth
== nTabSize
, "Boxen der Line zu klein/gross" )
2212 ASSERT( nWidth
>= 0 && nWidth
<= USHRT_MAX
, "Width out of range" )
2213 ASSERT( aIter
== aRowSpanCells
.end(), "Missing overlapped box" )
2214 aIter
= aRowSpanCells
.begin();
2216 bool bEmpty
= aRowSpanCells
.empty();
2217 ASSERT( bEmpty
, "Open row span detected" )
2223 #ifdef FINDSTARTENDOFROWSPANCACHE
2225 * A small optimization for FindStartEndOfRowSpan START
2227 * NOTE: Results of some measurement revealed that this cache
2228 * does not improve performance!
2231 class SwFindRowSpanCache
2235 struct SwFindRowSpanCacheObj
2237 const SwTableBox
* mpKeyBox
;
2238 const SwTableBox
* mpCacheBox
;
2242 SwFindRowSpanCacheObj( const SwTableBox
& rKeyBox
, const SwTableBox
& rCacheBox
, USHORT nSteps
, bool bStart
) :
2243 mpKeyBox( &rKeyBox
), mpCacheBox( &rCacheBox
), mnSteps( nSteps
), mbStart( bStart
) {}
2246 std::list
< SwFindRowSpanCacheObj
> aCache
;
2248 static SwFindRowSpanCache
* mpFindRowSpanCache
;
2249 SwFindRowSpanCache();
2253 static SwFindRowSpanCache
& getSwFindRowSpanCache();
2254 const SwTableBox
* FindCachedStartEndOfRowSpan( const SwTableBox
& rKeyBox
, USHORT nSteps
, bool bStart
);
2255 void SetCachedStartEndOfRowSpan( const SwTableBox
& rKeyBox
, const SwTableBox
& rCacheBox
, USHORT nSteps
, bool bStart
);
2256 void SetUseCache( bool bNew
);
2259 SwFindRowSpanCache
* SwFindRowSpanCache::mpFindRowSpanCache
= 0;
2260 SwFindRowSpanCache
& SwFindRowSpanCache::getSwFindRowSpanCache()
2262 if ( !mpFindRowSpanCache
) mpFindRowSpanCache
= new SwFindRowSpanCache
;
2263 return *mpFindRowSpanCache
;
2266 SwFindRowSpanCache::SwFindRowSpanCache() : mbUseCache( false )
2270 void SwFindRowSpanCache::SetUseCache( bool bNew
)
2272 mbUseCache
= bNew
; aCache
.clear();
2275 const SwTableBox
* SwFindRowSpanCache::FindCachedStartEndOfRowSpan( const SwTableBox
& rKeyBox
,
2279 static nCallCount
= 0;
2280 static nSuccessCount
= 0;
2283 if ( !mbUseCache
) return 0;
2285 const SwTableBox
* pRet
= 0;
2287 std::list
< SwFindRowSpanCacheObj
>::const_iterator aIter
;
2288 for ( aIter
= aCache
.begin(); aIter
!= aCache
.end(); ++aIter
)
2290 if ( aIter
->mpKeyBox
== &rKeyBox
&&
2291 aIter
->mnSteps
== nSteps
&&
2292 aIter
->mbStart
== bStart
)
2294 pRet
= aIter
->mpCacheBox
;
2303 const int FindBoxCacheSize
= 2;
2305 void SwFindRowSpanCache::SetCachedStartEndOfRowSpan( const SwTableBox
& rKeyBox
,
2306 const SwTableBox
& rCacheBox
,
2310 if ( !mbUseCache
) return;
2312 const SwFindRowSpanCacheObj
aNew( rKeyBox
, rCacheBox
, nSteps
, bStart
);
2313 aCache
.push_front( aNew
);
2314 if ( aCache
.size() > FindBoxCacheSize
)
2319 * A small optimization for FindStartEndOfRowSpan END