merge the formfield patch from ooo-build
[ooovba.git] / sw / source / core / table / swnewtable.cxx
blob587914ae0b70cc0b68b8884c2b7c882509de8b58
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: swnewtable.cxx,v $
10 * $Revision: 1.13 $
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>
35 #include <tblsel.hxx>
36 #include <tblrwcl.hxx>
37 #include <node.hxx>
38 #include <undobj.hxx>
39 #include <pam.hxx>
40 #include <frmfmt.hxx>
41 #include <frmatr.hxx>
42 #include <cellfrm.hxx>
43 #include <fmtfsize.hxx>
44 #include <doc.hxx>
45 #include <vector>
46 #include <set>
47 #include <list>
48 #include <memory>
49 #include <svx/boxitem.hxx>
50 #include <svx/protitem.hxx>
51 #include <swtblfmt.hxx>
53 #ifdef PRODUCT
54 #define CHECK_TABLE(t)
55 #else
56 #ifdef DEBUG
57 #define CHECK_TABLE(t) (t).CheckConsistency();
58 #else
59 #define CHECK_TABLE(t)
60 #endif
61 #endif
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
74 class SwBoxSelection
76 public:
77 std::vector<const SwSelBoxes*> aBoxes;
78 long mnMergeWidth;
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.
91 @param rBoxes
92 the boxes to remove
94 @param pUndo
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 )
103 if( pUndo )
104 pUndo->SetSelBoxes( rBoxes );
105 DeleteSel( pDoc, rBoxes, &rMerged, 0, TRUE, TRUE );
107 CHECK_TABLE( *this )
108 return 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.
116 @param rMin
117 will be decremented if necessary to the left border of the cell
119 @param rMax
120 will be incremented if necessary to the right border of the cell
122 @param rLine
123 the row (table line) of the interesting box
125 @param nCheck
126 the index of the box in the table box array of the given row
128 @param bSet
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 )
136 ++nCheck;
137 if( rLine.GetTabBoxes().Count() < nCheck )
138 { // robust
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();
150 nNew += nWidth;
152 // nNew is the right border of the wished box
153 if( bSet || nNew > rMax )
154 rMax = nNew;
155 nNew -= nWidth; // nNew becomes the left border of the wished box
156 if( bSet || nNew < rMin )
157 rMin = nNew;
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
163 box.
165 @param rBox
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() )
175 return 0;
176 long nLeft = 0;
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" );
183 if( pBox == &rBox )
184 return nLeft;
185 nLeft += pBox->GetFrmFmt()->GetFrmSize().GetWidth();
187 ASSERT( false, "Box not found in own upper?" );
188 return nLeft;
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.
197 @param nLeft
198 the left border (logical x-value) of the demanded box
200 @param rLine
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 )
209 if( !pLine )
210 return 0;
211 long nCurrLeft = 0;
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" );
220 return pBox;
222 nCurrLeft += pBox->GetFrmFmt()->GetFrmSize().GetWidth();
224 ASSERT( false, "Didn't found wished box" );
225 return 0;
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.
239 @param rTable
240 the table to manipulate (has to be a new model table)
242 @param nDiff
243 the number of rows which has been inserted (nDiff > 0) or deleted (nDiff < 0)
245 @param nRowIdx
246 the index of the first row which has to be checked
248 @param bSingle
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() )
261 return;
262 ASSERT( !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 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
282 // the critical area
283 if( nAbsSpan > nDistance )
285 if( nDiff > 0 )
287 if( nRowSpan > 0 )
288 nRowSpan += nDiff; // increment row span of master cell
289 else
291 nRowSpan -= nDiff; // increment row span of non-master cell
292 bGoOn = true;
295 else
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;
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
314 pLine->GetTabBoxes()[ nCurrBox ]->setRowSpan( nRowSpan );
317 ++nDistance;
318 if( nRowIdx )
319 --nRowIdx;
320 else
321 bGoOn = false; //robust
322 } while( bGoOn );
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() )
333 return 0;
334 const SwNode* pStartNd = rPam.Start()->nNode.GetNode().FindTableBoxStartNode();
335 const SwNode* pEndNd = rPam.End()->nNode.GetNode().FindTableBoxStartNode();
336 if( !pStartNd || !pEndNd || pStartNd == pEndNd )
337 return 0;
339 USHORT nLines = aLines.Count();
340 USHORT nTop = 0, nBottom = 0;
341 long nMin = 0, nMax = 0;
342 int nFound = 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" );
352 if( nFound )
354 if( pBox->GetSttNd() == pEndNd )
356 nBottom = nRow;
357 lcl_CheckMinMax( nMin, nMax, *pLine, nCol, false );
358 ++nFound;
359 break;
362 else if( pBox->GetSttNd() == pStartNd )
364 nTop = nRow;
365 lcl_CheckMinMax( nMin, nMax, *pLine, nCol, true );
366 ++nFound;
370 if( nFound < 2 )
371 return 0;
373 bool bOkay = true;
374 long nMid = ( nMin + nMax ) / 2;
376 SwBoxSelection* pRet = new SwBoxSelection();
377 std::list< std::pair< SwTableBox*, long > > aNewWidthList;
378 USHORT nCheckBottom = nBottom;
379 long nLeftSpan = 0;
380 long nRightSpan = 0;
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();
388 long nLeft = 0;
389 long nRight = 0;
390 long nRowSpan = 1;
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" );
396 nLeft = nRight;
397 nRight += pBox->GetFrmFmt()->GetFrmSize().GetWidth();
398 nRowSpan = pBox->getRowSpan();
399 if( nRight <= nMin )
401 if( nRight == nMin && nLeftSpanCnt )
402 bOkay = false;
403 continue;
405 SwTableBox* pInnerBox = 0;
406 SwTableBox* pLeftBox = 0;
407 SwTableBox* pRightBox = 0;
408 long nDiff = 0;
409 long nDiff2 = 0;
410 if( nLeft < nMin )
412 if( nRight >= nMid || nRight + nLeft >= nMin + nMin )
414 if( nCurrBox )
416 pBoxes->Insert( pBox );
417 pInnerBox = pBox;
418 pLeftBox = pLine->GetTabBoxes()[nCurrBox-1];
419 nDiff = nMin - nLeft;
420 if( nRight > nMax )
422 if( nCurrBox+1 < nCount )
424 pRightBox = pLine->GetTabBoxes()[nCurrBox+1];
425 nDiff2 = nRight - nMax;
427 else
428 bOkay = false;
430 else if( nRightSpanCnt && nRight == nMax )
431 bOkay = false;
433 else
434 bOkay = false;
436 else if( nCurrBox+1 < nCount )
438 pLeftBox = pBox;
439 pInnerBox = pLine->GetTabBoxes()[nCurrBox+1];
440 nDiff = nMin - nRight;
442 else
443 bOkay = false;
445 else if( nRight <= nMax )
447 pBoxes->Insert( pBox );
448 if( nRow == nTop && nRowSpan < 0 )
450 bOkay = false;
451 break;
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 )
458 bOkay = false;
460 else if( nLeft < nMax )
462 if( nLeft <= nMid || nRight + nLeft <= nMax )
464 if( nCurrBox+1 < nCount )
466 pBoxes->Insert( pBox );
467 pInnerBox = pBox;
468 pRightBox = pLine->GetTabBoxes()[nCurrBox+1];
469 nDiff = nRight - nMax;
471 else
472 bOkay = false;
474 else if( nCurrBox )
476 pRightBox = pBox;
477 pInnerBox = pLine->GetTabBoxes()[nCurrBox-1];
478 nDiff = nLeft - nMax;
480 else
481 bOkay = false;
483 else
484 break;
485 if( pInnerBox )
487 if( nRow == nBottom )
489 long nTmpSpan = pInnerBox->getRowSpan();
490 if( nTmpSpan > 1 )
491 nBottom += (USHORT)nTmpSpan - 1;
492 else if( nTmpSpan < -1 )
493 nBottom = (USHORT)( nBottom - nTmpSpan - 1 );
495 SwTableBox* pOuterBox = pLeftBox;
498 if( pOuterBox )
500 long nOutSpan = pOuterBox->getRowSpan();
501 if( nOutSpan != 1 )
503 USHORT nCheck = nRow;
504 if( nOutSpan < 0 )
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 );
511 if( nCheck < nTop )
512 bOkay = false;
513 if( pOuterBox == pLeftBox )
515 if( !nLeftSpanCnt || nMin - nDiff != nLeftSpan )
516 bOkay = false;
518 else
520 if( !nRightSpanCnt || nMax + nDiff != nRightSpan )
521 bOkay = false;
524 else
526 if( pOuterBox == pLeftBox )
528 if( nLeftSpanCnt )
529 bOkay = false;
530 nLeftSpan = nMin - nDiff;
531 nLeftSpanCnt = nOutSpan;
533 else
535 if( nRightSpanCnt )
536 bOkay = false;
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 ) )
547 bOkay = false;
548 std::pair< SwTableBox*, long > aTmp;
549 aTmp.first = pInnerBox;
550 aTmp.second = -nDiff;
551 aNewWidthList.push_back( aTmp );
552 aTmp.first = pOuterBox;
553 aTmp.second = nDiff;
554 aNewWidthList.push_back( aTmp );
556 pOuterBox = pOuterBox == pRightBox ? 0 : pRightBox;
557 if( nDiff2 )
558 nDiff = nDiff2;
559 } while( pOuterBox );
562 if( nLeftSpanCnt )
563 --nLeftSpanCnt;
564 if( nRightSpanCnt )
565 --nRightSpanCnt;
566 pRet->insertBoxes( pBoxes );
568 pRet->mnMergeWidth = nMax - nMin;
569 if( nCheckBottom > nBottom )
570 bOkay = false;
571 if( bOkay )
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 ) );
580 ++pCurr;
583 else
585 delete pRet;
586 pRet = 0;
588 return pRet;
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() );
598 SwClient* pLast;
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();
606 if( pLower )
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;
620 long nCount = 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();
626 nAddWidth += nWidth;
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;
633 ++nCount;
635 else if( ( rInsPos[ nCurrLine ] > nCurrBox ) == !bBehind )
636 rInsPos[ nCurrLine ] = nCurrBox;
638 if( nCount )
639 nAddWidth /= nCount;
640 return nAddWidth;
643 /** SwTable::NewInsertCol(..) insert new column(s) into a table
646 @param pDoc
647 the document
649 @param rBoxes
650 the selected boxes
652 @param nCnt
653 the number of columns to insert
655 @param bBehind
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 )
666 return FALSE;
668 CHECK_TABLE( *this )
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 )
688 return FALSE;
689 nAddWidth = (nAddWidth * nTableWidth) / nResultingWidth;
690 nNewBoxWidth = long( nAddWidth / nCnt ); // Rounding
691 nAddWidth = nNewBoxWidth * nCnt; // Rounding
692 if( !nAddWidth || nAddWidth >= nTableWidth )
693 return FALSE;
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 ];
713 if( bBehind )
714 ++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 )
723 bNewSpan = true;
724 while( nLastLine < i )
726 SwTableLine* pTmpLine = aLines[ nLastLine ];
727 USHORT nTmpPos = aInsPos[nLastLine];
728 if( bBehind )
729 ++nTmpPos;
730 for( USHORT j = 0; j < nCnt; ++j )
731 pTmpLine->GetTabBoxes()[nTmpPos+j]->setRowSpan( nDiff );
732 if( nDiff > 0 )
733 nDiff = -nDiff;
734 ++nDiff;
735 ++nLastLine;
738 if( nRowSpan > 0 )
739 bNewSpan = true;
740 if( bNewSpan )
742 nLastLine = i;
743 if( nRowSpan < 0 )
744 nLastRowSpan = -nRowSpan;
745 else
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];
758 if( bNewSpan )
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;
769 else
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 );
782 #ifndef PRODUCT
784 const SwTableBoxes &rTabBoxes = aLines[0]->GetTabBoxes();
785 long nNewWidth = 0;
786 for( USHORT i = 0; i < rTabBoxes.Count(); ++i )
787 nNewWidth += rTabBoxes[i]->GetFrmFmt()->GetFrmSize().GetWidth();
788 ASSERT( nNewWidth > 0, "Very small" );
790 #endif
791 CHECK_TABLE( *this )
793 return TRUE;
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.
804 @param rPam
805 the selection to merge
807 @param rBoxes
808 should be empty at the beginning, at the end it is filled with boxes to delete.
810 @param ppMergeBox
811 will be set to the master cell box
813 @param pUndo
814 the undo object to record all changes
815 can be Null, e.g. when called by Redo(..)
817 @return
821 bool SwTable::PrepareMerge( const SwPaM& rPam, SwSelBoxes& rBoxes,
822 SwSelBoxes& rMerged, SwTableBox** ppMergeBox, SwUndoTblMerge* pUndo )
824 if( !bNewModel )
826 ::GetMergeSel( rPam, rBoxes, ppMergeBox, pUndo );
827 return rBoxes.Count() > 1;
829 CHECK_TABLE( *this )
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() )
833 return false;
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
839 if( !pMergeBox )
840 return false;
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
873 if( nCurrCol )
874 rBoxes.Insert( pBox );
875 else
877 if( nCurrLine == 1 )
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 )
888 pLastBox = pBox;
889 if( bDoMerge )
891 bMerge = true;
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 );
903 if( pUndo )
904 pDoc->DoUndo( FALSE );
905 pDoc->AppendTxtNode( *aPam.GetPoint() );
906 if( pUndo )
907 pDoc->DoUndo( TRUE );
908 SwNodeRange aRg( aSttNdIdx, aPam.GetPoint()->nNode );
909 if( pUndo )
910 pUndo->MoveBoxCntnt( pDoc, aRg, rInsPosNd );
911 else
913 pDoc->MoveNodeRange( aRg, rInsPosNd,
914 IDocumentContentOperations::DOC_NO_DELFRMS );
918 // Only the cell of the first selected column will stay alive
919 // and got a new row span
920 if( !nCurrCol )
921 pBox->setRowSpan( nRowSpan );
923 if( nRowSpan > 0 ) // the master cell is done, from now on we set
924 nRowSpan = -nRowSpan; // negative row spans
925 ++nRowSpan; // ... -3, -2, -1
927 if( bMerge )
929 // A row containing overlapped cells is superfluous,
930 // these cells can be put into rBoxes for deletion
931 _FindSuperfluousRows( rBoxes, pFirstLn, pLastLn );
932 // pNewFmt will be set to the new master box and the overlapped cells
933 SwFrmFmt* pNewFmt = pMergeBox->ClaimFrmFmt();
934 pNewFmt->SetFmtAttr( SwFmtFrmSize( ATT_VAR_SIZE, pSel->mnMergeWidth, 0 ) );
935 for( USHORT nCurrLine = 0; nCurrLine < nLineCount; ++nCurrLine )
937 const SwSelBoxes* pBoxes = pSel->aBoxes[ nCurrLine ];
938 USHORT nColCount = pBoxes->Count();
939 for( USHORT nCurrCol = 0; nCurrCol < nColCount; ++nCurrCol )
941 SwTableBox* pBox = (*pBoxes)[nCurrCol];
942 if( nCurrCol )
944 // Even this box will be deleted soon,
945 // we have to correct the width to avoid side effects
946 SwFrmFmt* pFmt = pBox->ClaimFrmFmt();
947 pFmt->SetFmtAttr( SwFmtFrmSize( ATT_VAR_SIZE, 0, 0 ) );
949 else
950 pBox->ChgFrmFmt( (SwTableBoxFmt*)pNewFmt );
953 if( pLastBox ) // Robust
955 // The new borders of the master cell...
956 SvxBoxItem aBox( pMergeBox->GetFrmFmt()->GetBox() );
957 bool bOld = aBox.GetRight() || aBox.GetBottom();
958 const SvxBoxItem& rBox = pLastBox->GetFrmFmt()->GetBox();
959 aBox.SetLine( rBox.GetRight(), BOX_LINE_RIGHT );
960 aBox.SetLine( rBox.GetBottom(), BOX_LINE_BOTTOM );
961 if( bOld || aBox.GetLeft() || aBox.GetTop() || aBox.GetRight() || aBox.GetBottom() )
962 (*ppMergeBox)->GetFrmFmt()->SetFmtAttr( aBox );
965 if( pUndo )
966 pUndo->AddNewBox( pMergeBox->GetSttIdx() );
968 return bMerge;
971 /** SwTable::_FindSuperfluousRows(..) is looking for superfluous rows, i.e. rows
972 containing overlapped cells only.
975 void SwTable::_FindSuperfluousRows( SwSelBoxes& rBoxes,
976 SwTableLine* pFirstLn, SwTableLine* pLastLn )
978 if( !pFirstLn || !pLastLn )
980 if( !rBoxes.Count() )
981 return;
982 pFirstLn = rBoxes[0]->GetUpper();
983 pLastLn = rBoxes[ rBoxes.Count() - 1 ]->GetUpper();
985 USHORT nFirstLn = GetTabLines().C40_GETPOS(SwTableLine, pFirstLn );
986 USHORT nLastLn = GetTabLines().C40_GETPOS(SwTableLine, pLastLn );
987 for( USHORT nRow = nFirstLn; nRow <= nLastLn; ++nRow )
989 SwTableLine* pLine = aLines[nRow];
990 ASSERT( pLine, "Missing table line" );
991 USHORT nCols = pLine->GetTabBoxes().Count();
992 bool bSuperfl = true;
993 for( USHORT nCol = 0; nCol < nCols; ++nCol )
995 SwTableBox *pBox = pLine->GetTabBoxes()[nCol];
996 if( pBox->getRowSpan() > 0 &&
997 USHRT_MAX == rBoxes.GetPos( pBox ) )
999 bSuperfl = false;
1000 break;
1003 if( bSuperfl )
1005 for( USHORT nCol = 0; nCol < nCols; ++nCol )
1007 SwTableBox* pBox = pLine->GetTabBoxes()[nCol];
1008 rBoxes.Insert( pBox );
1014 /** SwTableBox::FindStartOfRowSpan(..) retruns the "master" cell, the cell which
1015 overlaps the given cell, it maybe the cell itself.
1018 SwTableBox& SwTableBox::FindStartOfRowSpan( const SwTable& rTable, USHORT nMaxStep )
1020 if( getRowSpan() > 0 || !nMaxStep )
1021 return *this;
1023 long nLeftBorder = lcl_Box2LeftBorder( *this );
1024 SwTableBox* pBox = this;
1025 const SwTableLine* pMyUpper = GetUpper();
1026 USHORT nLine = rTable.GetTabLines().C40_GETPOS(SwTableLine, pMyUpper );
1027 if( nLine && nLine < rTable.GetTabLines().Count() )
1029 SwTableBox* pNext;
1032 pNext = lcl_LeftBorder2Box( nLeftBorder, rTable.GetTabLines()[--nLine] );
1033 if( pNext )
1034 pBox = pNext;
1035 } while( nLine && --nMaxStep && pNext && pBox->getRowSpan() < 1 );
1038 return *pBox;
1041 /** SwTableBox::FindEndOfRowSpan(..) returns the last overlapped cell if there is
1042 any. Otherwise the cell itself will returned.
1045 SwTableBox& SwTableBox::FindEndOfRowSpan( const SwTable& rTable, USHORT nMaxStep )
1047 long nAbsSpan = getRowSpan();
1048 if( nAbsSpan < 0 )
1049 nAbsSpan = -nAbsSpan;
1050 if( nAbsSpan == 1 || !nMaxStep )
1051 return *this;
1053 if( nMaxStep > --nAbsSpan )
1054 nMaxStep = (USHORT)nAbsSpan;
1055 const SwTableLine* pMyUpper = GetUpper();
1056 USHORT nLine = rTable.GetTabLines().C40_GETPOS(SwTableLine, pMyUpper );
1057 nMaxStep = nLine + nMaxStep;
1058 if( nMaxStep >= rTable.GetTabLines().Count() )
1059 nMaxStep = rTable.GetTabLines().Count() - 1;
1060 long nLeftBorder = lcl_Box2LeftBorder( *this );
1061 SwTableBox* pBox =
1062 lcl_LeftBorder2Box( nLeftBorder, rTable.GetTabLines()[ nMaxStep ] );
1063 if ( !pBox )
1064 pBox = this;
1066 return *pBox;
1069 /** lcl_getAllMergedBoxes(..) collects all overlapped boxes to a given (master) box
1072 void lcl_getAllMergedBoxes( const SwTable& rTable, SwSelBoxes& rBoxes, SwTableBox& rBox )
1074 SwTableBox* pBox = &rBox;
1075 ASSERT( pBox == &rBox.FindStartOfRowSpan( rTable, USHRT_MAX ), "Not a master box" );
1076 rBoxes.Insert( pBox );
1077 if( pBox->getRowSpan() == 1 )
1078 return;
1079 const SwTableLine* pMyUpper = pBox->GetUpper();
1080 USHORT nLine = rTable.GetTabLines().C40_GETPOS(SwTableLine, pMyUpper );
1081 long nLeftBorder = lcl_Box2LeftBorder( *pBox );
1082 USHORT nCount = rTable.GetTabLines().Count();
1083 while( ++nLine < nCount && pBox && pBox->getRowSpan() != -1 )
1085 pBox = lcl_LeftBorder2Box( nLeftBorder, rTable.GetTabLines()[nLine] );
1086 if( pBox )
1087 rBoxes.Insert( pBox );
1091 /** lcl_UnMerge(..) manipulates the row span attribute of a given master cell
1092 and its overlapped cells to split them into several pieces.
1095 void lcl_UnMerge( const SwTable& rTable, SwTableBox& rBox, USHORT nCnt,
1096 BOOL bSameHeight )
1098 SwSelBoxes aBoxes;
1099 lcl_getAllMergedBoxes( rTable, aBoxes, rBox );
1100 USHORT nCount = aBoxes.Count();
1101 if( nCount < 2 )
1102 return;
1103 if( nCnt > nCount )
1104 nCnt = nCount;
1105 USHORT *pSplitIdx = new USHORT[ nCnt ];
1106 if( bSameHeight )
1108 SwTwips *pHeights = new SwTwips[ nCount ];
1109 SwTwips nHeight = 0;
1110 for( USHORT i = 0; i < nCount; ++i )
1112 SwTableLine* pLine = aBoxes[ i ]->GetUpper();
1113 SwFrmFmt *pRowFmt = pLine->GetFrmFmt();
1114 pHeights[ i ] = pRowFmt->GetFrmSize().GetHeight();
1115 nHeight += pHeights[ i ];
1117 SwTwips nSumH = 0;
1118 USHORT nIdx = 0;
1119 for( USHORT i = 1; i <= nCnt; ++i )
1121 SwTwips nSplit = ( i * nHeight ) / nCnt;
1122 while( nSumH < nSplit && nIdx < nCount )
1123 nSumH += pHeights[ nIdx++ ];
1124 pSplitIdx[ i - 1 ] = nIdx;
1126 delete[] pHeights;
1128 else
1130 for( long i = 1; i <= nCnt; ++i )
1131 pSplitIdx[ i - 1 ] = (USHORT)( ( i * nCount ) / nCnt );
1133 USHORT nIdx = 0;
1134 for( long i = 0; i < nCnt; ++i )
1136 USHORT nNextIdx = pSplitIdx[ i ];
1137 aBoxes[ nIdx ]->setRowSpan( nNextIdx - nIdx );
1138 lcl_InvalidateCellFrm( *aBoxes[ nIdx ] );
1139 while( ++nIdx < nNextIdx )
1140 aBoxes[ nIdx ]->setRowSpan( nIdx - nNextIdx );
1142 delete[] pSplitIdx;
1145 /** lcl_FillSelBoxes(..) puts all boxes of a given line into the selection structure
1148 void lcl_FillSelBoxes( SwSelBoxes &rBoxes, SwTableLine &rLine )
1150 USHORT nBoxCount = rLine.GetTabBoxes().Count();
1151 USHORT nCurrBox;
1152 for( nCurrBox = 0; nCurrBox < nBoxCount; ++nCurrBox )
1153 rBoxes.Insert( rLine.GetTabBoxes()[nCurrBox] );
1156 /** SwTable::InsertSpannedRow(..) inserts "superfluous" rows, i.e. rows containig
1157 overlapped cells only. This is a preparation for an upcoming split.
1160 void SwTable::InsertSpannedRow( SwDoc* pDoc, USHORT nRowIdx, USHORT nCnt )
1162 CHECK_TABLE( *this )
1163 ASSERT( nCnt && nRowIdx < GetTabLines().Count(), "Wrong call of InsertSpannedRow" );
1164 SwSelBoxes aBoxes;
1165 SwTableLine& rLine = *GetTabLines()[ nRowIdx ];
1166 lcl_FillSelBoxes( aBoxes, rLine );
1167 SwFmtFrmSize aFSz( rLine.GetFrmFmt()->GetFrmSize() );
1168 if( ATT_VAR_SIZE != aFSz.GetHeightSizeType() )
1170 SwFrmFmt* pFrmFmt = rLine.ClaimFrmFmt();
1171 long nNewHeight = aFSz.GetHeight() / ( nCnt + 1 );
1172 if( !nNewHeight )
1173 ++nNewHeight;
1174 aFSz.SetHeight( nNewHeight );
1175 pFrmFmt->SetFmtAttr( aFSz );
1177 _InsertRow( pDoc, aBoxes, nCnt, TRUE );
1178 USHORT nBoxCount = rLine.GetTabBoxes().Count();
1179 for( USHORT n = 0; n < nCnt; ++n )
1181 SwTableLine *pNewLine = GetTabLines()[ nRowIdx + nCnt - n ];
1182 for( USHORT nCurrBox = 0; nCurrBox < nBoxCount; ++nCurrBox )
1184 long nRowSpan = rLine.GetTabBoxes()[nCurrBox]->getRowSpan();
1185 if( nRowSpan > 0 )
1186 nRowSpan = - nRowSpan;
1187 pNewLine->GetTabBoxes()[ nCurrBox ]->setRowSpan( nRowSpan - n );
1190 lcl_ChangeRowSpan( *this, nCnt, nRowIdx, false );
1191 CHECK_TABLE( *this )
1194 typedef std::pair< USHORT, USHORT > SwLineOffset;
1195 typedef std::list< SwLineOffset > SwLineOffsetArray;
1197 /******************************************************************************
1198 When a couple of table boxes has to be split,
1199 lcl_SophisticatedFillLineIndices delivers the information where and how many
1200 rows have to be inserted.
1201 Input
1202 rTable: the table to manipulate
1203 rBoxes: an array of boxes to split
1204 nCnt: how many parts are wanted
1205 Output
1206 rArr: a list of pairs ( line index, number of lines to insert )
1208 ******************************************************************************/
1210 void lcl_SophisticatedFillLineIndices( SwLineOffsetArray &rArr,
1211 const SwTable& rTable, const SwSelBoxes& rBoxes, USHORT nCnt )
1213 std::list< SwLineOffset > aBoxes;
1214 SwLineOffset aLnOfs( USHRT_MAX, USHRT_MAX );
1215 for( USHORT i = 0; i < rBoxes.Count(); ++i )
1216 { // Collect all end line indices and the row spans
1217 const SwTableBox &rBox = rBoxes[ i ]->FindStartOfRowSpan( rTable );
1218 ASSERT( rBox.getRowSpan() > 0, "Didn't I say 'StartOfRowSpan' ??" )
1219 if( nCnt > rBox.getRowSpan() )
1221 const SwTableLine *pLine = rBox.GetUpper();
1222 const USHORT nEnd = USHORT( rBox.getRowSpan() +
1223 rTable.GetTabLines().C40_GETPOS( SwTableLine, pLine ) );
1224 // The next if statement is a small optimization
1225 if( aLnOfs.first != nEnd || aLnOfs.second != rBox.getRowSpan() )
1227 aLnOfs.first = nEnd; // ok, this is the line behind the box
1228 aLnOfs.second = USHORT( rBox.getRowSpan() ); // the row span
1229 aBoxes.insert( aBoxes.end(), aLnOfs );
1233 // As I said, I noted the line index _behind_ the last line of the boxes
1234 // in the resulting array the index has to be _on_ the line
1235 // nSum is to evaluate the wished value
1236 USHORT nSum = 1;
1237 while( aBoxes.size() )
1239 // I. step:
1240 // Looking for the "smallest" line end with the smallest row span
1241 std::list< SwLineOffset >::iterator pCurr = aBoxes.begin();
1242 aLnOfs = *pCurr; // the line end and row span of the first box
1243 while( ++pCurr != aBoxes.end() )
1245 if( aLnOfs.first > pCurr->first )
1246 { // Found a smaller line end
1247 aLnOfs.first = pCurr->first;
1248 aLnOfs.second = pCurr->second; // row span
1250 else if( aLnOfs.first == pCurr->first &&
1251 aLnOfs.second < pCurr->second )
1252 aLnOfs.second = pCurr->second; // Found a smaller row span
1254 ASSERT( aLnOfs.second < nCnt, "Clean-up failed" )
1255 aLnOfs.second = nCnt - aLnOfs.second; // the number of rows to insert
1256 rArr.insert( rArr.end(),
1257 SwLineOffset( aLnOfs.first - nSum, aLnOfs.second ) );
1258 // the correction has to be incremented because in the following
1259 // loops the line ends were manipulated
1260 nSum = nSum + aLnOfs.second;
1262 pCurr = aBoxes.begin();
1263 while( pCurr != aBoxes.end() )
1265 if( pCurr->first == aLnOfs.first )
1266 { // These boxes can be removed because the last insertion
1267 // of rows will expand their row span above the needed value
1268 std::list< SwLineOffset >::iterator pDel = pCurr;
1269 ++pCurr;
1270 aBoxes.erase( pDel );
1272 else
1274 bool bBefore = ( pCurr->first - pCurr->second < aLnOfs.first );
1275 // Manipulation of the end line indices as if the rows are
1276 // already inserted
1277 pCurr->first = pCurr->first + aLnOfs.second;
1278 if( bBefore )
1279 { // If the insertion is inside the box,
1280 // its row span has to be incremented
1281 pCurr->second = pCurr->second + aLnOfs.second;
1282 if( pCurr->second >= nCnt )
1283 { // if the row span is bigger than the split factor
1284 // this box is done
1285 std::list< SwLineOffset >::iterator pDel = pCurr;
1286 ++pCurr;
1287 aBoxes.erase( pDel );
1289 else
1290 ++pCurr;
1292 else
1293 ++pCurr;
1299 typedef std::set< SwTwips > SwSplitLines;
1301 /** lcl_CalculateSplitLineHeights(..) delivers all y-positions where table rows have
1302 to be splitted to fulfill the requested "split same height"
1305 USHORT lcl_CalculateSplitLineHeights( SwSplitLines &rCurr, SwSplitLines &rNew,
1306 const SwTable& rTable, const SwSelBoxes& rBoxes, USHORT nCnt )
1308 if( nCnt < 2 )
1309 return 0;
1310 std::list< SwLineOffset > aBoxes;
1311 SwLineOffset aLnOfs( USHRT_MAX, USHRT_MAX );
1312 USHORT nFirst = USHRT_MAX; // becomes the index of the first line
1313 USHORT nLast = 0; // becomes the index of the last line of the splitting
1314 for( USHORT i = 0; i < rBoxes.Count(); ++i )
1315 { // Collect all pairs (start+end) of line indices to split
1316 const SwTableBox &rBox = rBoxes[ i ]->FindStartOfRowSpan( rTable );
1317 ASSERT( rBox.getRowSpan() > 0, "Didn't I say 'StartOfRowSpan' ??" )
1318 const SwTableLine *pLine = rBox.GetUpper();
1319 const USHORT nStart = rTable.GetTabLines().C40_GETPOS( SwTableLine, pLine );
1320 const USHORT nEnd = USHORT( rBox.getRowSpan() + nStart - 1 );
1321 // The next if statement is a small optimization
1322 if( aLnOfs.first != nStart || aLnOfs.second != nEnd )
1324 aLnOfs.first = nStart;
1325 aLnOfs.second = nEnd;
1326 aBoxes.insert( aBoxes.end(), aLnOfs );
1327 if( nStart < nFirst )
1328 nFirst = nStart;
1329 if( nEnd > nLast )
1330 nLast = nEnd;
1334 if( aBoxes.empty() )
1335 return 0;
1336 SwTwips nHeight = 0;
1337 SwTwips* pLines = new SwTwips[ nLast + 1 - nFirst ];
1338 for( USHORT i = nFirst; i <= nLast; ++i )
1340 bool bLayoutAvailable = false;
1341 nHeight += rTable.GetTabLines()[ i ]->GetTableLineHeight( bLayoutAvailable );
1342 rCurr.insert( rCurr.end(), nHeight );
1343 pLines[ i - nFirst ] = nHeight;
1345 std::list< SwLineOffset >::iterator pSplit = aBoxes.begin();
1346 while( pSplit != aBoxes.end() )
1348 SwTwips nBase = pSplit->first <= nFirst ? 0 :
1349 pLines[ pSplit->first - nFirst - 1 ];
1350 SwTwips nDiff = pLines[ pSplit->second - nFirst ] - nBase;
1351 for( USHORT i = 1; i < nCnt; ++i )
1353 SwTwips nSplit = nBase + ( i * nDiff ) / nCnt;
1354 rNew.insert( nSplit );
1356 ++pSplit;
1358 delete[] pLines;
1359 return nFirst;
1362 /** lcl_LineIndex(..) delivers the line index of the line behind or above
1363 the box selection.
1366 USHORT lcl_LineIndex( const SwTable& rTable, const SwSelBoxes& rBoxes,
1367 bool bBehind )
1369 USHORT nDirect = USHRT_MAX;
1370 USHORT nSpan = USHRT_MAX;
1371 for( USHORT i = 0; i < rBoxes.Count(); ++i )
1373 SwTableBox *pBox = rBoxes[i];
1374 const SwTableLine* pLine = rBoxes[i]->GetUpper();
1375 USHORT nPos = rTable.GetTabLines().C40_GETPOS( SwTableLine, pLine );
1376 if( USHRT_MAX != nPos )
1378 if( bBehind )
1380 if( nPos > nDirect || nDirect == USHRT_MAX )
1381 nDirect = nPos;
1382 long nRowSpan = pBox->getRowSpan();
1383 if( nRowSpan < 2 )
1384 nSpan = 0;
1385 else if( nSpan )
1387 USHORT nEndOfRowSpan = (USHORT)(nPos + nRowSpan - 1);
1388 if( nEndOfRowSpan > nSpan || nSpan == USHRT_MAX )
1389 nSpan = nEndOfRowSpan;
1392 else if( nPos < nDirect )
1393 nDirect = nPos;
1396 if( nSpan && nSpan < USHRT_MAX )
1397 return nSpan;
1398 return nDirect;
1401 /** SwTable::NewSplitRow(..) splits all selected boxes horizontally.
1404 BOOL SwTable::NewSplitRow( SwDoc* pDoc, const SwSelBoxes& rBoxes, USHORT nCnt,
1405 BOOL bSameHeight )
1407 CHECK_TABLE( *this )
1408 ++nCnt;
1409 _FndBox aFndBox( 0, 0 );
1410 aFndBox.SetTableLines( rBoxes, *this );
1412 if( bSameHeight && pDoc->GetRootFrm() )
1414 SwSplitLines aRowLines;
1415 SwSplitLines aSplitLines;
1416 USHORT nFirst = lcl_CalculateSplitLineHeights( aRowLines, aSplitLines,
1417 *this, rBoxes, nCnt );
1418 aFndBox.DelFrms( *this );
1419 SwTwips nLast = 0;
1420 SwSplitLines::iterator pSplit = aSplitLines.begin();
1421 SwSplitLines::iterator pCurr = aRowLines.begin();
1422 while( pCurr != aRowLines.end() )
1424 while( pSplit != aSplitLines.end() && *pSplit < *pCurr )
1426 InsertSpannedRow( pDoc, nFirst, 1 );
1427 SwTableLine* pRow = GetTabLines()[ nFirst ];
1428 SwFrmFmt* pRowFmt = pRow->ClaimFrmFmt();
1429 SwFmtFrmSize aFSz( pRowFmt->GetFrmSize() );
1430 aFSz.SetHeightSizeType( ATT_MIN_SIZE );
1431 aFSz.SetHeight( *pSplit - nLast );
1432 pRowFmt->SetFmtAttr( aFSz );
1433 nLast = *pSplit;
1434 ++pSplit;
1435 ++nFirst;
1437 if( pSplit != aSplitLines.end() && *pCurr == *pSplit )
1438 ++pSplit;
1439 SwTableLine* pRow = GetTabLines()[ nFirst ];
1440 SwFrmFmt* pRowFmt = pRow->ClaimFrmFmt();
1441 SwFmtFrmSize aFSz( pRowFmt->GetFrmSize() );
1442 aFSz.SetHeightSizeType( ATT_MIN_SIZE );
1443 aFSz.SetHeight( *pCurr - nLast );
1444 pRowFmt->SetFmtAttr( aFSz );
1445 nLast = *pCurr;
1446 ++pCurr;
1447 ++nFirst;
1450 else
1452 aFndBox.DelFrms( *this );
1453 bSameHeight = FALSE;
1455 if( !bSameHeight )
1457 SwLineOffsetArray aLineOffs;
1458 lcl_SophisticatedFillLineIndices( aLineOffs, *this, rBoxes, nCnt );
1459 SwLineOffsetArray::reverse_iterator pCurr( aLineOffs.rbegin() );
1460 while( pCurr != aLineOffs.rend() )
1462 InsertSpannedRow( pDoc, pCurr->first, pCurr->second );
1463 ++pCurr;
1467 std::set< USHORT> aIndices;
1468 for( USHORT i = 0; i < rBoxes.Count(); ++i )
1470 ASSERT( rBoxes[i]->getRowSpan() != 1, "Forgot to split?" )
1471 if( rBoxes[i]->getRowSpan() > 1 )
1472 aIndices.insert( i );
1475 std::set< USHORT >::iterator pCurrBox = aIndices.begin();
1476 while( pCurrBox != aIndices.end() )
1477 lcl_UnMerge( *this, *rBoxes[*pCurrBox++], nCnt, bSameHeight );
1479 CHECK_TABLE( *this )
1480 //Layout updaten
1481 aFndBox.MakeFrms( *this );
1483 return TRUE;
1486 /** SwTable::InsertRow(..) inserts one or more rows before or behind the selected
1487 boxes.
1490 BOOL SwTable::InsertRow( SwDoc* pDoc, const SwSelBoxes& rBoxes,
1491 USHORT nCnt, BOOL bBehind )
1493 bool bRet = false;
1494 if( IsNewModel() )
1496 CHECK_TABLE( *this )
1497 USHORT nRowIdx = lcl_LineIndex( *this, rBoxes, bBehind );
1498 if( nRowIdx < USHRT_MAX )
1500 _FndBox aFndBox( 0, 0 );
1501 aFndBox.SetTableLines( rBoxes, *this );
1502 aFndBox.DelFrms( *this );
1503 // aFndBox.SaveChartData( *this );
1505 bRet = true;
1506 SwTableLine *pLine = GetTabLines()[ nRowIdx ];
1507 SwSelBoxes aLineBoxes;
1508 lcl_FillSelBoxes( aLineBoxes, *pLine );
1509 _InsertRow( pDoc, aLineBoxes, nCnt, bBehind );
1510 USHORT nBoxCount = pLine->GetTabBoxes().Count();
1511 USHORT nOfs = bBehind ? 0 : 1;
1512 for( USHORT n = 0; n < nCnt; ++n )
1514 SwTableLine *pNewLine = GetTabLines()[ nRowIdx+nCnt-n-nOfs];
1515 for( USHORT nCurrBox = 0; nCurrBox < nBoxCount; ++nCurrBox )
1517 long nRowSpan = pLine->GetTabBoxes()[nCurrBox]->getRowSpan();
1518 if( bBehind )
1520 if( nRowSpan == 1 || nRowSpan == -1 )
1521 nRowSpan = n + 1;
1522 else if( nRowSpan > 1 )
1523 nRowSpan = - nRowSpan;
1525 else
1527 if( nRowSpan > 0 )
1528 nRowSpan = n + 1;
1529 else
1530 --nRowSpan;
1532 pNewLine->GetTabBoxes()[ nCurrBox ]->setRowSpan( nRowSpan - n );
1535 if( bBehind )
1536 ++nRowIdx;
1537 if( nRowIdx )
1538 lcl_ChangeRowSpan( *this, nCnt, --nRowIdx, true );
1539 //Layout update
1540 aFndBox.MakeFrms( *this );
1541 // aFndBox.RestoreChartData( *this );
1543 CHECK_TABLE( *this )
1545 else
1546 bRet = _InsertRow( pDoc, rBoxes, nCnt, bBehind );
1547 return bRet;
1550 /** SwTable::PrepareDelBoxes(..) adjusts the row span attributes for an upcoming
1551 deletion of table cells and invalidates the layout of these cells.
1554 void SwTable::PrepareDelBoxes( const SwSelBoxes& rBoxes )
1556 if( IsNewModel() )
1558 for( USHORT i = 0; i < rBoxes.Count(); ++i )
1560 SwTableBox* pBox = rBoxes[i];
1561 long nRowSpan = pBox->getRowSpan();
1562 if( nRowSpan != 1 && pBox->GetFrmFmt()->GetFrmSize().GetWidth() )
1564 long nLeft = lcl_Box2LeftBorder( *pBox );
1565 SwTableLine *pLine = pBox->GetUpper();
1566 USHORT nLinePos = GetTabLines().C40_GETPOS(SwTableLine, pLine);
1567 ASSERT( nLinePos < USHRT_MAX, "Box/table mismatch" )
1568 if( nRowSpan > 1 )
1570 if( ++nLinePos < GetTabLines().Count() )
1572 pLine = GetTabLines()[ nLinePos ];
1573 pBox = lcl_LeftBorder2Box( nLeft, pLine );
1574 ASSERT( pBox, "RowSpan irritation I" )
1575 if( pBox )
1576 pBox->setRowSpan( --nRowSpan );
1579 else if( nLinePos > 0 )
1583 pLine = GetTabLines()[ --nLinePos ];
1584 pBox = lcl_LeftBorder2Box( nLeft, pLine );
1585 ASSERT( pBox, "RowSpan irritation II" )
1586 if( pBox )
1588 nRowSpan = pBox->getRowSpan();
1589 if( nRowSpan > 1 )
1591 lcl_InvalidateCellFrm( *pBox );
1592 --nRowSpan;
1594 else
1595 ++nRowSpan;
1596 pBox->setRowSpan( nRowSpan );
1598 else
1599 nRowSpan = 1;
1601 while( nRowSpan < 0 && nLinePos > 0 );
1608 /** lcl_SearchSelBox(..) adds cells of a given table row to the selection structure
1609 if it overlaps with the given x-position range
1612 void lcl_SearchSelBox( const SwTable &rTable, SwSelBoxes& rBoxes, long nMin, long nMax,
1613 SwTableLine& rLine, bool bChkProtected, bool bColumn )
1615 long nLeft = 0;
1616 long nRight = 0;
1617 long nMid = ( nMax + nMin )/ 2;
1618 USHORT nCount = rLine.GetTabBoxes().Count();
1619 for( USHORT nCurrBox = 0; nCurrBox < nCount; ++nCurrBox )
1621 SwTableBox* pBox = rLine.GetTabBoxes()[nCurrBox];
1622 ASSERT( pBox, "Missing table box" );
1623 long nWidth = pBox->GetFrmFmt()->GetFrmSize().GetWidth();
1624 nRight += nWidth;
1625 if( nRight > nMin )
1627 bool bAdd = false;
1628 if( nRight <= nMax )
1629 bAdd = nLeft >= nMin || nRight >= nMid ||
1630 nRight - nMin > nMin - nLeft;
1631 else
1632 bAdd = nLeft <= nMid || nRight - nMax < nMax - nLeft;
1633 long nRowSpan = pBox->getRowSpan();
1634 if( bAdd &&
1635 //( bColumn || nRowSpan > 0 ) &&
1636 ( !bChkProtected ||
1637 !pBox->GetFrmFmt()->GetProtect().IsCntntProtected() ) )
1639 USHORT nOldCnt = rBoxes.Count();
1640 rBoxes.Insert( pBox );
1641 if( bColumn && nRowSpan != 1 && nOldCnt < rBoxes.Count() )
1643 SwTableBox *pMasterBox = pBox->getRowSpan() > 0 ? pBox
1644 : &pBox->FindStartOfRowSpan( rTable, USHRT_MAX );
1645 lcl_getAllMergedBoxes( rTable, rBoxes, *pMasterBox );
1649 if( nRight >= nMax )
1650 break;
1651 nLeft = nRight;
1655 /** void SwTable::CreateSelection(..) fills the selection structure with table cells
1656 for a given SwPaM, ie. start and end position inside a table
1659 void SwTable::CreateSelection( const SwPaM& rPam, SwSelBoxes& rBoxes,
1660 const SearchType eSearch, bool bChkProtected ) const
1662 ASSERT( bNewModel, "Don't call me for old tables" );
1663 if( !aLines.Count() )
1664 return;
1665 const SwNode* pStartNd = rPam.GetPoint()->nNode.GetNode().FindTableBoxStartNode();
1666 const SwNode* pEndNd = rPam.GetMark()->nNode.GetNode().FindTableBoxStartNode();
1667 if( !pStartNd || !pEndNd )
1668 return;
1669 CreateSelection( pStartNd, pEndNd, rBoxes, eSearch, bChkProtected );
1672 /** void SwTable::CreateSelection(..) fills the selection structure with table cells
1673 for given start and end nodes inside a table
1675 void SwTable::CreateSelection( const SwNode* pStartNd, const SwNode* pEndNd,
1676 SwSelBoxes& rBoxes, const SearchType eSearch, bool bChkProtected ) const
1678 // SwSelBoxes aKeepBoxes;
1679 if( rBoxes.Count() )
1681 // aKeepBoxes.Insert( &rBoxes );
1682 rBoxes.Remove( USHORT(0), rBoxes.Count() );
1684 // Looking for start and end of the selection given by SwNode-pointer
1685 USHORT nLines = aLines.Count();
1686 // nTop becomes the line number of the upper box
1687 // nBottom becomes the line number of the lower box
1688 USHORT nTop = 0, nBottom = 0;
1689 // nUpperMin becomes the left border value of the upper box
1690 // nUpperMax becomes the right border of the upper box
1691 // nLowerMin and nLowerMax the borders of the lower box
1692 long nUpperMin = 0, nUpperMax = 0;
1693 long nLowerMin = 0, nLowerMax = 0;
1694 // nFound will incremented if a box is found
1695 // 0 => no box found; 1 => the upper box has been found; 2 => both found
1696 int nFound = 0;
1697 for( USHORT nRow = 0; nFound < 2 && nRow < nLines; ++nRow )
1699 SwTableLine* pLine = aLines[nRow];
1700 ASSERT( pLine, "Missing table line" );
1701 USHORT nCols = pLine->GetTabBoxes().Count();
1702 for( USHORT nCol = 0; nCol < nCols; ++nCol )
1704 SwTableBox* pBox = pLine->GetTabBoxes()[nCol];
1705 ASSERT( pBox, "Missing table box" );
1706 if( pBox->GetSttNd() == pEndNd || pBox->GetSttNd() == pStartNd )
1708 if( !bChkProtected ||
1709 !pBox->GetFrmFmt()->GetProtect().IsCntntProtected() )
1710 rBoxes.Insert( pBox );
1711 if( nFound )
1713 nBottom = nRow;
1714 lcl_CheckMinMax( nLowerMin, nLowerMax, *pLine, nCol, true );
1715 ++nFound;
1716 break;
1718 else
1720 nTop = nRow;
1721 lcl_CheckMinMax( nUpperMin, nUpperMax, *pLine, nCol, true );
1722 ++nFound;
1723 // If start and end node are identical, we're nearly done..
1724 if( pEndNd == pStartNd )
1726 nBottom = nTop;
1727 nLowerMin = nUpperMin;
1728 nLowerMax = nUpperMax;
1729 ++nFound;
1735 if( nFound < 2 )
1736 return; // At least one node was not a part of the given table
1737 if( eSearch == SEARCH_ROW )
1739 // Selection of a row is quiet easy:
1740 // every (unprotected) box between start and end line
1741 // with a positive row span will be collected
1742 for( USHORT nRow = nTop; nRow <= nBottom; ++nRow )
1744 SwTableLine* pLine = aLines[nRow];
1745 ASSERT( pLine, "Missing table line" );
1746 USHORT nCount = pLine->GetTabBoxes().Count();
1747 for( USHORT nCurrBox = 0; nCurrBox < nCount; ++nCurrBox )
1749 SwTableBox* pBox = pLine->GetTabBoxes()[nCurrBox];
1750 ASSERT( pBox, "Missing table box" );
1751 if( pBox->getRowSpan() > 0 && ( !bChkProtected ||
1752 !pBox->GetFrmFmt()->GetProtect().IsCntntProtected() ) )
1753 rBoxes.Insert( pBox );
1756 return;
1758 bool bCombine = nTop == nBottom;
1759 if( !bCombine )
1761 long nMinWidth = nUpperMax - nUpperMin;
1762 long nTmp = nLowerMax - nLowerMin;
1763 if( nMinWidth > nTmp )
1764 nMinWidth = nTmp;
1765 nTmp = nLowerMax < nUpperMax ? nLowerMax : nUpperMax;
1766 nTmp -= ( nLowerMin < nUpperMin ) ? nUpperMin : nLowerMin;
1767 // If the overlapping between upper and lower box is less than half
1768 // of the width (of the smaller cell), bCombine is set,
1769 // e.g. if upper and lower cell are in different columns
1770 bCombine = ( nTmp + nTmp < nMinWidth );
1772 if( bCombine )
1774 if( nUpperMin < nLowerMin )
1775 nLowerMin = nUpperMin;
1776 else
1777 nUpperMin = nLowerMin;
1778 if( nUpperMax > nLowerMax )
1779 nLowerMax = nUpperMax;
1780 else
1781 nUpperMax = nLowerMax;
1783 const bool bColumn = eSearch == SEARCH_COL;
1784 if( bColumn )
1786 for( USHORT i = 0; i < nTop; ++i )
1787 lcl_SearchSelBox( *this, rBoxes, nUpperMin, nUpperMax,
1788 *aLines[i], bChkProtected, bColumn );
1792 long nMin = nUpperMin < nLowerMin ? nUpperMin : nLowerMin;
1793 long nMax = nUpperMax < nLowerMax ? nLowerMax : nUpperMax;
1794 for( USHORT i = nTop; i <= nBottom; ++i )
1795 lcl_SearchSelBox( *this, rBoxes, nMin, nMax, *aLines[i],
1796 bChkProtected, bColumn );
1798 /* if( nTop + 1 < nBottom )
1800 long nInnerMin = nUpperMin < nLowerMin ? nLowerMin : nUpperMin;
1801 long nInnerMax = nUpperMax < nLowerMax ? nUpperMax : nLowerMax;
1802 for( USHORT i = nTop + 1; i < nBottom; ++i )
1803 lcl_SearchSelBox( *this, rBoxes, nInnerMin, nInnerMax, *aLines[i],
1804 bChkProtected, bColumn );
1806 if( bCombine ) // => nUpperMin == nLowerMin, nUpperMax == nLowerMax
1808 if( nBottom > nTop )
1809 lcl_SearchSelBox( *this, rBoxes, nUpperMin, nUpperMax, *aLines[nTop],
1810 bChkProtected, bColumn );
1811 lcl_SearchSelBox( *this, rBoxes, nLowerMin, nLowerMax, *aLines[nBottom],
1812 bChkProtected, bColumn );
1814 else if( aKeepBoxes.Count() )
1816 long nMin = nUpperMin < nLowerMin ? nUpperMin : nLowerMin;
1817 long nMax = nUpperMax < nLowerMax ? nLowerMax : nUpperMax;
1818 SwSelBoxes aCandidates;
1819 for( USHORT i = nTop; i <= nBottom; ++i )
1820 lcl_SearchSelBox( *this, aCandidates, nMin, nMax, *aLines[i],
1821 bChkProtected, bColumn );
1822 USHORT nOld = 0, nNew = 0;
1823 while ( nOld < aKeepBoxes.Count() && nNew < aCandidates.Count() )
1825 const SwTableBox* pPOld = *( aKeepBoxes.GetData() + nOld );
1826 SwTableBox* pPNew = *( aCandidates.GetData() + nNew );
1827 if( pPOld == pPNew )
1828 { // this box will stay
1829 rBoxes.Insert( pPNew );
1830 ++nOld;
1831 ++nNew;
1833 else if( pPOld->GetSttIdx() < pPNew->GetSttIdx() )
1834 ++nOld;
1835 else
1836 ++nNew;
1838 } */
1839 if( bColumn )
1841 for( USHORT i = nBottom + 1; i < nLines; ++i )
1842 lcl_SearchSelBox( *this, rBoxes, nLowerMin, nLowerMax, *aLines[i],
1843 bChkProtected, true );
1847 /** void SwTable::ExpandColumnSelection(..) adds cell to the give selection to
1848 assure that at least one cell of every row is part of the selection.
1851 void SwTable::ExpandColumnSelection( SwSelBoxes& rBoxes, long &rMin, long &rMax ) const
1853 ASSERT( bNewModel, "Don't call me for old tables" );
1854 rMin = 0;
1855 rMax = 0;
1856 if( !aLines.Count() || !rBoxes.Count() )
1857 return;
1859 USHORT nLineCnt = aLines.Count();
1860 USHORT nBoxCnt = rBoxes.Count();
1861 USHORT nBox = 0;
1862 for( USHORT nRow = 0; nRow < nLineCnt && nBox < nBoxCnt; ++nRow )
1864 SwTableLine* pLine = aLines[nRow];
1865 ASSERT( pLine, "Missing table line" );
1866 USHORT nCols = pLine->GetTabBoxes().Count();
1867 for( USHORT nCol = 0; nCol < nCols; ++nCol )
1869 SwTableBox* pBox = pLine->GetTabBoxes()[nCol];
1870 ASSERT( pBox, "Missing table box" );
1871 if( pBox == rBoxes[nBox] )
1873 lcl_CheckMinMax( rMin, rMax, *pLine, nCol, nBox == 0 );
1874 if( ++nBox >= nBoxCnt )
1875 break;
1879 nBox = 0;
1880 for( USHORT nRow = 0; nRow < nLineCnt; ++nRow )
1882 SwTableLine* pLine = aLines[nRow];
1883 USHORT nCols = pLine->GetTabBoxes().Count();
1884 long nLeft = 0;
1885 long nRight = 0;
1886 for( USHORT nCurrBox = 0; nCurrBox < nCols; ++nCurrBox )
1888 nLeft = nRight;
1889 SwTableBox* pBox = pLine->GetTabBoxes()[nCurrBox];
1890 nRight += pBox->GetFrmFmt()->GetFrmSize().GetWidth();
1891 if( nLeft >= rMin && nRight <= rMax )
1892 rBoxes.Insert( pBox );
1897 /** SwTable::PrepareDeleteCol(..) adjusts the widths of the neighbour cells of
1898 a cell selection for an upcoming (column) deletion
1900 void SwTable::PrepareDeleteCol( long nMin, long nMax )
1902 ASSERT( bNewModel, "Don't call me for old tables" );
1903 if( !aLines.Count() || nMax < nMin )
1904 return;
1905 long nMid = nMin ? ( nMin + nMax ) / 2 : 0;
1906 const SwTwips nTabSize = GetFrmFmt()->GetFrmSize().GetWidth();
1907 if( nTabSize == nMax )
1908 nMid = nMax;
1909 USHORT nLineCnt = aLines.Count();
1910 for( USHORT nRow = 0; nRow < nLineCnt; ++nRow )
1912 SwTableLine* pLine = aLines[nRow];
1913 USHORT nCols = pLine->GetTabBoxes().Count();
1914 long nLeft = 0;
1915 long nRight = 0;
1916 for( USHORT nCurrBox = 0; nCurrBox < nCols; ++nCurrBox )
1918 nLeft = nRight;
1919 SwTableBox* pBox = pLine->GetTabBoxes()[nCurrBox];
1920 nRight += pBox->GetFrmFmt()->GetFrmSize().GetWidth();
1921 if( nRight < nMin )
1922 continue;
1923 if( nLeft > nMax )
1924 break;
1925 long nNewWidth = -1;
1926 if( nLeft < nMin )
1928 if( nRight <= nMax )
1929 nNewWidth = nMid - nLeft;
1931 else if( nRight > nMax )
1932 nNewWidth = nRight - nMid;
1933 else
1934 nNewWidth = 0;
1935 if( nNewWidth >= 0 )
1937 SwFrmFmt* pFrmFmt = pBox->ClaimFrmFmt();
1938 SwFmtFrmSize aFrmSz( pFrmFmt->GetFrmSize() );
1939 aFrmSz.SetWidth( nNewWidth );
1940 pFrmFmt->SetFmtAttr( aFrmSz );
1948 /** SwTable::ExpandSelection(..) adds all boxes to the box selections which are
1949 overlapped by it.
1952 void SwTable::ExpandSelection( SwSelBoxes& rBoxes ) const
1954 for( USHORT i = 0; i < rBoxes.Count(); ++i )
1956 SwTableBox *pBox = rBoxes[i];
1957 long nRowSpan = pBox->getRowSpan();
1958 if( nRowSpan != 1 )
1960 SwTableBox *pMasterBox = nRowSpan > 0 ? pBox
1961 : &pBox->FindStartOfRowSpan( *this, USHRT_MAX );
1962 lcl_getAllMergedBoxes( *this, rBoxes, *pMasterBox );
1967 /** SwTable::CheckRowSpan(..) looks for the next line without an overlapping to
1968 the previous line.
1971 void SwTable::CheckRowSpan( SwTableLinePtr &rpLine, bool bUp ) const
1973 ASSERT( IsNewModel(), "Don't call me for old tables" );
1974 USHORT nLineIdx = GetTabLines().C40_GETPOS( SwTableLine, rpLine );
1975 ASSERT( nLineIdx < GetTabLines().Count(), "Start line out of range" );
1976 bool bChange = true;
1977 if( bUp )
1979 while( bChange )
1981 bChange = false;
1982 rpLine = GetTabLines()[ nLineIdx ];
1983 USHORT nCols = rpLine->GetTabBoxes().Count();
1984 for( USHORT nCol = 0; !bChange && nCol < nCols; ++nCol )
1986 SwTableBox* pBox = rpLine->GetTabBoxes()[nCol];
1987 if( pBox->getRowSpan() > 1 || pBox->getRowSpan() < -1 )
1988 bChange = true;
1990 if( bChange )
1992 if( nLineIdx )
1993 --nLineIdx;
1994 else
1996 bChange = false;
1997 rpLine = 0;
2002 else
2004 USHORT nMaxLine = GetTabLines().Count();
2005 while( bChange )
2007 bChange = false;
2008 rpLine = GetTabLines()[ nLineIdx ];
2009 USHORT nCols = rpLine->GetTabBoxes().Count();
2010 for( USHORT nCol = 0; !bChange && nCol < nCols; ++nCol )
2012 SwTableBox* pBox = rpLine->GetTabBoxes()[nCol];
2013 if( pBox->getRowSpan() < 0 )
2014 bChange = true;
2016 if( bChange )
2018 ++nLineIdx;
2019 if( nLineIdx >= nMaxLine )
2021 bChange = false;
2022 rpLine = 0;
2029 // This structure corrects the row span attributes for a top line of a table
2030 // In a top line no negative row span is allowed, so these have to be corrected.
2031 // If there has been at least one correction, all values are stored
2032 // and can be used by undo of table split
2033 SwSaveRowSpan::SwSaveRowSpan( SwTableBoxes& rBoxes, USHORT nSplitLn )
2034 : mnSplitLine( nSplitLn )
2036 bool bDontSave = true; // nothing changed, nothing to save
2037 USHORT nColCount = rBoxes.Count();
2038 ASSERT( nColCount, "Empty Table Line" )
2039 mnRowSpans.resize( nColCount );
2040 for( USHORT nCurrCol = 0; nCurrCol < nColCount; ++nCurrCol )
2042 SwTableBox* pBox = rBoxes[nCurrCol];
2043 ASSERT( pBox, "Missing Table Box" );
2044 long nRowSp = pBox->getRowSpan();
2045 mnRowSpans[ nCurrCol ] = nRowSp;
2046 if( nRowSp < 0 )
2048 bDontSave = false;
2049 nRowSp = -nRowSp;
2050 pBox->setRowSpan( nRowSp ); // correction needed
2053 if( bDontSave )
2054 mnRowSpans.clear();
2057 // This function is called by undo of table split to restore the old row span
2058 // values at the split line
2059 void SwTable::RestoreRowSpan( const SwSaveRowSpan& rSave )
2061 if( !IsNewModel() ) // for new model only
2062 return;
2063 USHORT nLineCount = GetTabLines().Count();
2064 ASSERT( rSave.mnSplitLine < nLineCount, "Restore behind last line?" )
2065 if( rSave.mnSplitLine < nLineCount )
2067 SwTableLine* pLine = GetTabLines()[rSave.mnSplitLine];
2068 USHORT nColCount = pLine->GetTabBoxes().Count();
2069 ASSERT( nColCount, "Empty Table Line" )
2070 ASSERT( nColCount == rSave.mnRowSpans.size(), "Wrong row span store" )
2071 if( nColCount == rSave.mnRowSpans.size() )
2073 for( USHORT nCurrCol = 0; nCurrCol < nColCount; ++nCurrCol )
2075 SwTableBox* pBox = pLine->GetTabBoxes()[nCurrCol];
2076 ASSERT( pBox, "Missing Table Box" );
2077 long nRowSp = pBox->getRowSpan();
2078 if( nRowSp != rSave.mnRowSpans[ nCurrCol ] )
2080 ASSERT( -nRowSp == rSave.mnRowSpans[ nCurrCol ], "Pardon me?!" )
2081 ASSERT( rSave.mnRowSpans[ nCurrCol ] < 0, "Pardon me?!" )
2082 pBox->setRowSpan( -nRowSp );
2084 USHORT nLine = rSave.mnSplitLine;
2085 if( nLine )
2087 long nLeftBorder = lcl_Box2LeftBorder( *pBox );
2088 SwTableBox* pNext;
2091 pNext = lcl_LeftBorder2Box( nLeftBorder, GetTabLines()[--nLine] );
2092 if( pNext )
2094 pBox = pNext;
2095 long nNewSpan = pBox->getRowSpan();
2096 if( pBox->getRowSpan() < 1 )
2097 nNewSpan -= nRowSp;
2098 else
2100 nNewSpan += nRowSp;
2101 pNext = 0;
2103 pBox->setRowSpan( nNewSpan );
2105 } while( nLine && pNext );
2113 SwSaveRowSpan* SwTable::CleanUpTopRowSpan( USHORT nSplitLine )
2115 SwSaveRowSpan* pRet = 0;
2116 if( !IsNewModel() )
2117 return pRet;
2118 pRet = new SwSaveRowSpan( GetTabLines()[0]->GetTabBoxes(), nSplitLine );
2119 if( pRet->mnRowSpans.size() == 0 )
2121 delete pRet;
2122 pRet = 0;
2124 return pRet;
2127 void SwTable::CleanUpBottomRowSpan( USHORT nDelLines )
2129 if( !IsNewModel() )
2130 return;
2131 USHORT nLastLine = GetTabLines().Count()-1;
2132 SwTableLine* pLine = GetTabLines()[nLastLine];
2133 USHORT nColCount = pLine->GetTabBoxes().Count();
2134 ASSERT( nColCount, "Empty Table Line" )
2135 for( USHORT nCurrCol = 0; nCurrCol < nColCount; ++nCurrCol )
2137 SwTableBox* pBox = pLine->GetTabBoxes()[nCurrCol];
2138 ASSERT( pBox, "Missing Table Box" );
2139 long nRowSp = pBox->getRowSpan();
2140 if( nRowSp < 0 )
2141 nRowSp = -nRowSp;
2142 if( nRowSp > 1 )
2144 lcl_ChangeRowSpan( *this, -static_cast<long>(nDelLines), nLastLine, false );
2145 break;
2150 #ifndef PRODUCT
2152 struct RowSpanCheck
2154 long nRowSpan;
2155 SwTwips nLeft;
2156 SwTwips nRight;
2159 void SwTable::CheckConsistency() const
2161 if( !IsNewModel() )
2162 return;
2163 USHORT nLineCount = GetTabLines().Count();
2164 const SwTwips nTabSize = GetFrmFmt()->GetFrmSize().GetWidth();
2165 SwTwips nLineWidth = 0;
2166 std::list< RowSpanCheck > aRowSpanCells;
2167 std::list< RowSpanCheck >::iterator aIter = aRowSpanCells.end();
2168 for( USHORT nCurrLine = 0; nCurrLine < nLineCount; ++nCurrLine )
2170 SwTwips nWidth = 0;
2171 SwTableLine* pLine = GetTabLines()[nCurrLine];
2172 ASSERT( pLine, "Missing Table Line" )
2173 USHORT nColCount = pLine->GetTabBoxes().Count();
2174 ASSERT( nColCount, "Empty Table Line" )
2175 for( USHORT nCurrCol = 0; nCurrCol < nColCount; ++nCurrCol )
2177 SwTableBox* pBox = pLine->GetTabBoxes()[nCurrCol];
2178 ASSERT( pBox, "Missing Table Box" );
2179 SwTwips nNewWidth = pBox->GetFrmFmt()->GetFrmSize().GetWidth() + nWidth;
2180 long nRowSp = pBox->getRowSpan();
2181 if( nRowSp < 0 )
2183 ASSERT( aIter != aRowSpanCells.end(), "Missing master box" )
2184 #ifndef PRODUCT
2185 //RowSpanCheck &rCheck = *aIter;
2186 #endif
2187 ASSERT( aIter->nLeft == nWidth && aIter->nRight == nNewWidth,
2188 "Wrong position/size of overlapped table box" );
2189 --(aIter->nRowSpan);
2190 ASSERT( aIter->nRowSpan == -nRowSp, "Wrong row span value" );
2191 if( nRowSp == -1 )
2193 std::list< RowSpanCheck >::iterator aEraseIter = aIter;
2194 ++aIter;
2195 aRowSpanCells.erase( aEraseIter );
2197 else
2198 ++aIter;
2200 else if( nRowSp != 1 )
2202 ASSERT( nRowSp, "Zero row span?!" );
2203 RowSpanCheck aEntry;
2204 aEntry.nLeft = nWidth;
2205 aEntry.nRight = nNewWidth;
2206 aEntry.nRowSpan = nRowSp;
2207 aRowSpanCells.insert( aIter, aEntry );
2209 nWidth = nNewWidth;
2211 if( !nCurrLine )
2212 nLineWidth = nWidth;
2213 ASSERT( nWidth == nLineWidth, "Different Line Widths" )
2214 ASSERT( nWidth == nTabSize, "Boxen der Line zu klein/gross" )
2215 ASSERT( nWidth >= 0 && nWidth <= USHRT_MAX, "Width out of range" )
2216 ASSERT( aIter == aRowSpanCells.end(), "Missing overlapped box" )
2217 aIter = aRowSpanCells.begin();
2219 bool bEmpty = aRowSpanCells.empty();
2220 ASSERT( bEmpty, "Open row span detected" )
2223 #endif
2226 #ifdef FINDSTARTENDOFROWSPANCACHE
2228 * A small optimization for FindStartEndOfRowSpan START
2230 * NOTE: Results of some measurement revealed that this cache
2231 * does not improve performance!
2234 class SwFindRowSpanCache
2236 private:
2238 struct SwFindRowSpanCacheObj
2240 const SwTableBox* mpKeyBox;
2241 const SwTableBox* mpCacheBox;
2242 USHORT mnSteps;
2243 bool mbStart;
2245 SwFindRowSpanCacheObj( const SwTableBox& rKeyBox, const SwTableBox& rCacheBox, USHORT nSteps, bool bStart ) :
2246 mpKeyBox( &rKeyBox ), mpCacheBox( &rCacheBox ), mnSteps( nSteps ), mbStart( bStart ) {}
2249 std::list< SwFindRowSpanCacheObj > aCache;
2250 bool mbUseCache;
2251 static SwFindRowSpanCache* mpFindRowSpanCache;
2252 SwFindRowSpanCache();
2254 public:
2256 static SwFindRowSpanCache& getSwFindRowSpanCache();
2257 const SwTableBox* FindCachedStartEndOfRowSpan( const SwTableBox& rKeyBox, USHORT nSteps, bool bStart );
2258 void SetCachedStartEndOfRowSpan( const SwTableBox& rKeyBox, const SwTableBox& rCacheBox, USHORT nSteps, bool bStart );
2259 void SetUseCache( bool bNew );
2262 SwFindRowSpanCache* SwFindRowSpanCache::mpFindRowSpanCache = 0;
2263 SwFindRowSpanCache& SwFindRowSpanCache::getSwFindRowSpanCache()
2265 if ( !mpFindRowSpanCache ) mpFindRowSpanCache = new SwFindRowSpanCache;
2266 return *mpFindRowSpanCache;
2269 SwFindRowSpanCache::SwFindRowSpanCache() : mbUseCache( false )
2273 void SwFindRowSpanCache::SetUseCache( bool bNew )
2275 mbUseCache = bNew; aCache.clear();
2278 const SwTableBox* SwFindRowSpanCache::FindCachedStartEndOfRowSpan( const SwTableBox& rKeyBox,
2279 USHORT nSteps,
2280 bool bStart )
2282 static nCallCount = 0;
2283 static nSuccessCount = 0;
2284 ++nCallCount;
2286 if ( !mbUseCache ) return 0;
2288 const SwTableBox* pRet = 0;
2290 std::list< SwFindRowSpanCacheObj >::const_iterator aIter;
2291 for ( aIter = aCache.begin(); aIter != aCache.end(); ++aIter )
2293 if ( aIter->mpKeyBox == &rKeyBox &&
2294 aIter->mnSteps == nSteps &&
2295 aIter->mbStart == bStart )
2297 pRet = aIter->mpCacheBox;
2298 ++nSuccessCount;
2299 break;
2303 return pRet;
2306 const int FindBoxCacheSize = 2;
2308 void SwFindRowSpanCache::SetCachedStartEndOfRowSpan( const SwTableBox& rKeyBox,
2309 const SwTableBox& rCacheBox,
2310 USHORT nSteps,
2311 bool bStart )
2313 if ( !mbUseCache ) return;
2315 const SwFindRowSpanCacheObj aNew( rKeyBox, rCacheBox, nSteps, bStart );
2316 aCache.push_front( aNew );
2317 if ( aCache.size() > FindBoxCacheSize )
2318 aCache.pop_back();
2322 * A small optimization for FindStartEndOfRowSpan END
2325 #endif