update dev300-m58
[ooovba.git] / sw / source / core / table / swnewtable.cxx
blobd83877130ee641c79735be7ebb598fda1b6a161d
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
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
917 if( !nCurrCol )
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
924 if( bMerge )
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];
939 if( 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 ) );
946 else
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 );
962 if( pUndo )
963 pUndo->AddNewBox( pMergeBox->GetSttIdx() );
965 return bMerge;
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() )
978 return;
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 ) )
996 bSuperfl = false;
997 break;
1000 if( bSuperfl )
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 )
1018 return *this;
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() )
1026 SwTableBox* pNext;
1029 pNext = lcl_LeftBorder2Box( nLeftBorder, rTable.GetTabLines()[--nLine] );
1030 if( pNext )
1031 pBox = pNext;
1032 } while( nLine && --nMaxStep && pNext && pBox->getRowSpan() < 1 );
1035 return *pBox;
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();
1045 if( nAbsSpan < 0 )
1046 nAbsSpan = -nAbsSpan;
1047 if( nAbsSpan == 1 || !nMaxStep )
1048 return *this;
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 );
1058 SwTableBox* pBox =
1059 lcl_LeftBorder2Box( nLeftBorder, rTable.GetTabLines()[ nMaxStep ] );
1060 if ( !pBox )
1061 pBox = this;
1063 return *pBox;
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 )
1075 return;
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] );
1083 if( pBox )
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,
1093 BOOL bSameHeight )
1095 SwSelBoxes aBoxes;
1096 lcl_getAllMergedBoxes( rTable, aBoxes, rBox );
1097 USHORT nCount = aBoxes.Count();
1098 if( nCount < 2 )
1099 return;
1100 if( nCnt > nCount )
1101 nCnt = nCount;
1102 USHORT *pSplitIdx = new USHORT[ nCnt ];
1103 if( bSameHeight )
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 ];
1114 SwTwips nSumH = 0;
1115 USHORT nIdx = 0;
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;
1123 delete[] pHeights;
1125 else
1127 for( long i = 1; i <= nCnt; ++i )
1128 pSplitIdx[ i - 1 ] = (USHORT)( ( i * nCount ) / nCnt );
1130 USHORT nIdx = 0;
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 );
1139 delete[] pSplitIdx;
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();
1148 USHORT nCurrBox;
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" );
1161 SwSelBoxes aBoxes;
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 );
1169 if( !nNewHeight )
1170 ++nNewHeight;
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();
1182 if( nRowSpan > 0 )
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.
1198 Input
1199 rTable: the table to manipulate
1200 rBoxes: an array of boxes to split
1201 nCnt: how many parts are wanted
1202 Output
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
1233 USHORT nSum = 1;
1234 while( aBoxes.size() )
1236 // I. step:
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;
1266 ++pCurr;
1267 aBoxes.erase( pDel );
1269 else
1271 bool bBefore = ( pCurr->first - pCurr->second < aLnOfs.first );
1272 // Manipulation of the end line indices as if the rows are
1273 // already inserted
1274 pCurr->first = pCurr->first + aLnOfs.second;
1275 if( bBefore )
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
1281 // this box is done
1282 std::list< SwLineOffset >::iterator pDel = pCurr;
1283 ++pCurr;
1284 aBoxes.erase( pDel );
1286 else
1287 ++pCurr;
1289 else
1290 ++pCurr;
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 )
1305 if( nCnt < 2 )
1306 return 0;
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 )
1325 nFirst = nStart;
1326 if( nEnd > nLast )
1327 nLast = nEnd;
1331 if( aBoxes.empty() )
1332 return 0;
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 );
1353 ++pSplit;
1355 delete[] pLines;
1356 return nFirst;
1359 /** lcl_LineIndex(..) delivers the line index of the line behind or above
1360 the box selection.
1363 USHORT lcl_LineIndex( const SwTable& rTable, const SwSelBoxes& rBoxes,
1364 bool bBehind )
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 )
1375 if( bBehind )
1377 if( nPos > nDirect || nDirect == USHRT_MAX )
1378 nDirect = nPos;
1379 long nRowSpan = pBox->getRowSpan();
1380 if( nRowSpan < 2 )
1381 nSpan = 0;
1382 else if( nSpan )
1384 USHORT nEndOfRowSpan = (USHORT)(nPos + nRowSpan - 1);
1385 if( nEndOfRowSpan > nSpan || nSpan == USHRT_MAX )
1386 nSpan = nEndOfRowSpan;
1389 else if( nPos < nDirect )
1390 nDirect = nPos;
1393 if( nSpan && nSpan < USHRT_MAX )
1394 return nSpan;
1395 return nDirect;
1398 /** SwTable::NewSplitRow(..) splits all selected boxes horizontally.
1401 BOOL SwTable::NewSplitRow( SwDoc* pDoc, const SwSelBoxes& rBoxes, USHORT nCnt,
1402 BOOL bSameHeight )
1404 CHECK_TABLE( *this )
1405 ++nCnt;
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 );
1416 SwTwips nLast = 0;
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 );
1430 nLast = *pSplit;
1431 ++pSplit;
1432 ++nFirst;
1434 if( pSplit != aSplitLines.end() && *pCurr == *pSplit )
1435 ++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 );
1442 nLast = *pCurr;
1443 ++pCurr;
1444 ++nFirst;
1447 else
1449 aFndBox.DelFrms( *this );
1450 bSameHeight = FALSE;
1452 if( !bSameHeight )
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 );
1460 ++pCurr;
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 )
1477 //Layout updaten
1478 aFndBox.MakeFrms( *this );
1480 return TRUE;
1483 /** SwTable::InsertRow(..) inserts one or more rows before or behind the selected
1484 boxes.
1487 BOOL SwTable::InsertRow( SwDoc* pDoc, const SwSelBoxes& rBoxes,
1488 USHORT nCnt, BOOL bBehind )
1490 bool bRet = false;
1491 if( IsNewModel() )
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 );
1502 bRet = true;
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();
1515 if( bBehind )
1517 if( nRowSpan == 1 || nRowSpan == -1 )
1518 nRowSpan = n + 1;
1519 else if( nRowSpan > 1 )
1520 nRowSpan = - nRowSpan;
1522 else
1524 if( nRowSpan > 0 )
1525 nRowSpan = n + 1;
1526 else
1527 --nRowSpan;
1529 pNewLine->GetTabBoxes()[ nCurrBox ]->setRowSpan( nRowSpan - n );
1532 if( bBehind )
1533 ++nRowIdx;
1534 if( nRowIdx )
1535 lcl_ChangeRowSpan( *this, nCnt, --nRowIdx, true );
1536 //Layout update
1537 aFndBox.MakeFrms( *this );
1538 // aFndBox.RestoreChartData( *this );
1540 CHECK_TABLE( *this )
1542 else
1543 bRet = _InsertRow( pDoc, rBoxes, nCnt, bBehind );
1544 return bRet;
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 )
1553 if( IsNewModel() )
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" )
1565 if( nRowSpan > 1 )
1567 if( ++nLinePos < GetTabLines().Count() )
1569 pLine = GetTabLines()[ nLinePos ];
1570 pBox = lcl_LeftBorder2Box( nLeft, pLine );
1571 ASSERT( pBox, "RowSpan irritation I" )
1572 if( pBox )
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" )
1583 if( pBox )
1585 nRowSpan = pBox->getRowSpan();
1586 if( nRowSpan > 1 )
1588 lcl_InvalidateCellFrm( *pBox );
1589 --nRowSpan;
1591 else
1592 ++nRowSpan;
1593 pBox->setRowSpan( nRowSpan );
1595 else
1596 nRowSpan = 1;
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 )
1612 long nLeft = 0;
1613 long nRight = 0;
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();
1621 nRight += nWidth;
1622 if( nRight > nMin )
1624 bool bAdd = false;
1625 if( nRight <= nMax )
1626 bAdd = nLeft >= nMin || nRight >= nMid ||
1627 nRight - nMin > nMin - nLeft;
1628 else
1629 bAdd = nLeft <= nMid || nRight - nMax < nMax - nLeft;
1630 long nRowSpan = pBox->getRowSpan();
1631 if( bAdd &&
1632 //( bColumn || nRowSpan > 0 ) &&
1633 ( !bChkProtected ||
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 )
1647 break;
1648 nLeft = nRight;
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() )
1661 return;
1662 const SwNode* pStartNd = rPam.GetPoint()->nNode.GetNode().FindTableBoxStartNode();
1663 const SwNode* pEndNd = rPam.GetMark()->nNode.GetNode().FindTableBoxStartNode();
1664 if( !pStartNd || !pEndNd )
1665 return;
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
1693 int nFound = 0;
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 );
1708 if( nFound )
1710 nBottom = nRow;
1711 lcl_CheckMinMax( nLowerMin, nLowerMax, *pLine, nCol, true );
1712 ++nFound;
1713 break;
1715 else
1717 nTop = nRow;
1718 lcl_CheckMinMax( nUpperMin, nUpperMax, *pLine, nCol, true );
1719 ++nFound;
1720 // If start and end node are identical, we're nearly done..
1721 if( pEndNd == pStartNd )
1723 nBottom = nTop;
1724 nLowerMin = nUpperMin;
1725 nLowerMax = nUpperMax;
1726 ++nFound;
1732 if( nFound < 2 )
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 );
1753 return;
1755 bool bCombine = nTop == nBottom;
1756 if( !bCombine )
1758 long nMinWidth = nUpperMax - nUpperMin;
1759 long nTmp = nLowerMax - nLowerMin;
1760 if( nMinWidth > nTmp )
1761 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 );
1769 if( bCombine )
1771 if( nUpperMin < nLowerMin )
1772 nLowerMin = nUpperMin;
1773 else
1774 nUpperMin = nLowerMin;
1775 if( nUpperMax > nLowerMax )
1776 nLowerMax = nUpperMax;
1777 else
1778 nUpperMax = nLowerMax;
1780 const bool bColumn = eSearch == SEARCH_COL;
1781 if( bColumn )
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 );
1827 ++nOld;
1828 ++nNew;
1830 else if( pPOld->GetSttIdx() < pPNew->GetSttIdx() )
1831 ++nOld;
1832 else
1833 ++nNew;
1835 } */
1836 if( bColumn )
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" );
1851 rMin = 0;
1852 rMax = 0;
1853 if( !aLines.Count() || !rBoxes.Count() )
1854 return;
1856 USHORT nLineCnt = aLines.Count();
1857 USHORT nBoxCnt = rBoxes.Count();
1858 USHORT nBox = 0;
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 )
1872 break;
1876 nBox = 0;
1877 for( USHORT nRow = 0; nRow < nLineCnt; ++nRow )
1879 SwTableLine* pLine = aLines[nRow];
1880 USHORT nCols = pLine->GetTabBoxes().Count();
1881 long nLeft = 0;
1882 long nRight = 0;
1883 for( USHORT nCurrBox = 0; nCurrBox < nCols; ++nCurrBox )
1885 nLeft = nRight;
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 )
1901 return;
1902 long nMid = nMin ? ( nMin + nMax ) / 2 : 0;
1903 const SwTwips nTabSize = GetFrmFmt()->GetFrmSize().GetWidth();
1904 if( nTabSize == nMax )
1905 nMid = 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();
1911 long nLeft = 0;
1912 long nRight = 0;
1913 for( USHORT nCurrBox = 0; nCurrBox < nCols; ++nCurrBox )
1915 nLeft = nRight;
1916 SwTableBox* pBox = pLine->GetTabBoxes()[nCurrBox];
1917 nRight += pBox->GetFrmFmt()->GetFrmSize().GetWidth();
1918 if( nRight < nMin )
1919 continue;
1920 if( nLeft > nMax )
1921 break;
1922 long nNewWidth = -1;
1923 if( nLeft < nMin )
1925 if( nRight <= nMax )
1926 nNewWidth = nMid - nLeft;
1928 else if( nRight > nMax )
1929 nNewWidth = nRight - nMid;
1930 else
1931 nNewWidth = 0;
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
1946 overlapped by it.
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();
1955 if( nRowSpan != 1 )
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
1965 the previous line.
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;
1974 if( bUp )
1976 while( bChange )
1978 bChange = false;
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 )
1985 bChange = true;
1987 if( bChange )
1989 if( nLineIdx )
1990 --nLineIdx;
1991 else
1993 bChange = false;
1994 rpLine = 0;
1999 else
2001 USHORT nMaxLine = GetTabLines().Count();
2002 while( bChange )
2004 bChange = false;
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 )
2011 bChange = true;
2013 if( bChange )
2015 ++nLineIdx;
2016 if( nLineIdx >= nMaxLine )
2018 bChange = false;
2019 rpLine = 0;
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;
2043 if( nRowSp < 0 )
2045 bDontSave = false;
2046 nRowSp = -nRowSp;
2047 pBox->setRowSpan( nRowSp ); // correction needed
2050 if( bDontSave )
2051 mnRowSpans.clear();
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
2059 return;
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;
2082 if( nLine )
2084 long nLeftBorder = lcl_Box2LeftBorder( *pBox );
2085 SwTableBox* pNext;
2088 pNext = lcl_LeftBorder2Box( nLeftBorder, GetTabLines()[--nLine] );
2089 if( pNext )
2091 pBox = pNext;
2092 long nNewSpan = pBox->getRowSpan();
2093 if( pBox->getRowSpan() < 1 )
2094 nNewSpan -= nRowSp;
2095 else
2097 nNewSpan += nRowSp;
2098 pNext = 0;
2100 pBox->setRowSpan( nNewSpan );
2102 } while( nLine && pNext );
2110 SwSaveRowSpan* SwTable::CleanUpTopRowSpan( USHORT nSplitLine )
2112 SwSaveRowSpan* pRet = 0;
2113 if( !IsNewModel() )
2114 return pRet;
2115 pRet = new SwSaveRowSpan( GetTabLines()[0]->GetTabBoxes(), nSplitLine );
2116 if( pRet->mnRowSpans.size() == 0 )
2118 delete pRet;
2119 pRet = 0;
2121 return pRet;
2124 void SwTable::CleanUpBottomRowSpan( USHORT nDelLines )
2126 if( !IsNewModel() )
2127 return;
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();
2137 if( nRowSp < 0 )
2138 nRowSp = -nRowSp;
2139 if( nRowSp > 1 )
2141 lcl_ChangeRowSpan( *this, -static_cast<long>(nDelLines), nLastLine, false );
2142 break;
2147 #ifndef PRODUCT
2149 struct RowSpanCheck
2151 long nRowSpan;
2152 SwTwips nLeft;
2153 SwTwips nRight;
2156 void SwTable::CheckConsistency() const
2158 if( !IsNewModel() )
2159 return;
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 )
2167 SwTwips nWidth = 0;
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();
2178 if( nRowSp < 0 )
2180 ASSERT( aIter != aRowSpanCells.end(), "Missing master box" )
2181 #ifndef PRODUCT
2182 //RowSpanCheck &rCheck = *aIter;
2183 #endif
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" );
2188 if( nRowSp == -1 )
2190 std::list< RowSpanCheck >::iterator aEraseIter = aIter;
2191 ++aIter;
2192 aRowSpanCells.erase( aEraseIter );
2194 else
2195 ++aIter;
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 );
2206 nWidth = nNewWidth;
2208 if( !nCurrLine )
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" )
2220 #endif
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
2233 private:
2235 struct SwFindRowSpanCacheObj
2237 const SwTableBox* mpKeyBox;
2238 const SwTableBox* mpCacheBox;
2239 USHORT mnSteps;
2240 bool mbStart;
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;
2247 bool mbUseCache;
2248 static SwFindRowSpanCache* mpFindRowSpanCache;
2249 SwFindRowSpanCache();
2251 public:
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,
2276 USHORT nSteps,
2277 bool bStart )
2279 static nCallCount = 0;
2280 static nSuccessCount = 0;
2281 ++nCallCount;
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;
2295 ++nSuccessCount;
2296 break;
2300 return pRet;
2303 const int FindBoxCacheSize = 2;
2305 void SwFindRowSpanCache::SetCachedStartEndOfRowSpan( const SwTableBox& rKeyBox,
2306 const SwTableBox& rCacheBox,
2307 USHORT nSteps,
2308 bool bStart )
2310 if ( !mbUseCache ) return;
2312 const SwFindRowSpanCacheObj aNew( rKeyBox, rCacheBox, nSteps, bStart );
2313 aCache.push_front( aNew );
2314 if ( aCache.size() > FindBoxCacheSize )
2315 aCache.pop_back();
2319 * A small optimization for FindStartEndOfRowSpan END
2322 #endif