xref: /aoo42x/main/sw/source/core/table/swnewtable.cxx (revision cdf0e10c)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_sw.hxx"
30 
31 #include <swtable.hxx>
32 #include <tblsel.hxx>
33 #include <tblrwcl.hxx>
34 #include <node.hxx>
35 #include <UndoTable.hxx>
36 #include <pam.hxx>
37 #include <frmfmt.hxx>
38 #include <frmatr.hxx>
39 #include <cellfrm.hxx>
40 #include <fmtfsize.hxx>
41 #include <doc.hxx>
42 #include <IDocumentUndoRedo.hxx>
43 #include <vector>
44 #include <set>
45 #include <list>
46 #include <memory>
47 #include <editeng/boxitem.hxx>
48 #include <editeng/protitem.hxx>
49 #include <swtblfmt.hxx>
50 #include <switerator.hxx>
51 
52 #ifndef DBG_UTIL
53 #define CHECK_TABLE(t)
54 #else
55 #ifdef DEBUG
56 #define CHECK_TABLE(t) (t).CheckConsistency();
57 #else
58 #define CHECK_TABLE(t)
59 #endif
60 #endif
61 
62 // ---------------------------------------------------------------
63 
64 /** SwBoxSelection is a small helperclass (structure) to handle selections
65     of cells (boxes) between table functions
66 
67     It contains an "array" of table boxes, a rectangulare selection of table boxes.
68     To be more specific, it contains a vector of box selections,
69     every box selection (SwSelBoxes) contains the selected boxes inside one row.
70     The member mnMergeWidth contains the width of the selected boxes
71 */
72 
73 class SwBoxSelection
74 {
75 public:
76     std::vector<const SwSelBoxes*> aBoxes;
77     long mnMergeWidth;
78     SwBoxSelection() : mnMergeWidth(0) {}
79     bool isEmpty() const { return aBoxes.size() == 0; }
80     void insertBoxes( const SwSelBoxes* pNew ){ aBoxes.insert( aBoxes.end(), pNew ); }
81 };
82 
83 /** NewMerge(..) removes the superfluous cells after cell merge
84 
85 SwTable::NewMerge(..) does some cleaning up,
86 it simply deletes the superfluous cells ("cell span")
87 and notifies the Undo about it.
88 The main work has been done by SwTable::PrepareMerge(..) already.
89 
90 @param rBoxes
91 the boxes to remove
92 
93 @param pUndo
94 the undo object to notify, maybe empty
95 
96 @return sal_True for compatibility reasons with OldMerge(..)
97 */
98 
99 sal_Bool SwTable::NewMerge( SwDoc* pDoc, const SwSelBoxes& rBoxes,
100      const SwSelBoxes& rMerged, SwTableBox*, SwUndoTblMerge* pUndo )
101 {
102     if( pUndo )
103 		pUndo->SetSelBoxes( rBoxes );
104 	DeleteSel( pDoc, rBoxes, &rMerged, 0, sal_True, sal_True );
105 
106     CHECK_TABLE( *this )
107     return sal_True;
108 }
109 
110 /** lcl_CheckMinMax helps evaluating (horizontal) min/max of boxes
111 
112 lcl_CheckMinMax(..) compares the left border and the right border
113 of a given cell with the given range and sets it accordingly.
114 
115 @param rMin
116 will be decremented if necessary to the left border of the cell
117 
118 @param rMax
119 will be incremented if necessary to the right border of the cell
120 
121 @param rLine
122 the row (table line) of the interesting box
123 
124 @param nCheck
125 the index of the box in the table box array of the given row
126 
127 @param bSet
128 if bSet is false, rMin and rMax will be manipulated if necessary
129 if bSet is true, rMin and rMax will be set to the left and right border of the box
130 
131 */
132 
133 void lcl_CheckMinMax( long& rMin, long& rMax, const SwTableLine& rLine, sal_uInt16 nCheck, bool bSet )
134 {
135     ++nCheck;
136     if( rLine.GetTabBoxes().Count() < nCheck )
137     {   // robust
138         ASSERT( false, "Box out of table line" );
139         nCheck = rLine.GetTabBoxes().Count();
140     }
141 
142     long nNew = 0; // will be the right border of the current box
143     long nWidth = 0; // the width of the current box
144     for( sal_uInt16 nCurrBox = 0; nCurrBox < nCheck; ++nCurrBox )
145     {
146         SwTableBox* pBox = rLine.GetTabBoxes()[nCurrBox];
147         ASSERT( pBox, "Missing table box" );
148         nWidth = pBox->GetFrmFmt()->GetFrmSize().GetWidth();
149         nNew += nWidth;
150     }
151     // nNew is the right border of the wished box
152     if( bSet || nNew > rMax )
153         rMax = nNew;
154     nNew -= nWidth; // nNew becomes the left border of the wished box
155     if( bSet || nNew < rMin )
156         rMin = nNew;
157 }
158 
159 /** lcl_Box2LeftBorder(..) delivers the left (logical) border of a table box
160 
161 The left logical border of a table box is the sum of the cell width before this
162 box.
163 
164 @param rBox
165 is the requested table box
166 
167 @return is the left logical border (long, even it cannot be negative)
168 
169 */
170 
171 long lcl_Box2LeftBorder( const SwTableBox& rBox )
172 {
173     if( !rBox.GetUpper() )
174         return 0;
175     long nLeft = 0;
176     const SwTableLine &rLine = *rBox.GetUpper();
177     sal_uInt16 nCount = rLine.GetTabBoxes().Count();
178     for( sal_uInt16 nCurrBox = 0; nCurrBox < nCount; ++nCurrBox )
179     {
180         SwTableBox* pBox = rLine.GetTabBoxes()[nCurrBox];
181         ASSERT( pBox, "Missing table box" );
182         if( pBox == &rBox )
183             return nLeft;
184         nLeft += pBox->GetFrmFmt()->GetFrmSize().GetWidth();
185     }
186     ASSERT( false, "Box not found in own upper?" );
187     return nLeft;
188 }
189 
190 /** lcl_LeftBorder2Box delivers the box to a given left border
191 
192 It's used to find the master/follow table boxes in previous/next rows.
193 Don't call this function to check if there is such a box,
194 call it if you know there has to be such box.
195 
196 @param nLeft
197 the left border (logical x-value) of the demanded box
198 
199 @param rLine
200 the row (table line) to be scanned
201 
202 @return a pointer to the table box inside the given row with the wished left border
203 
204 */
205 
206 SwTableBox* lcl_LeftBorder2Box( long nLeft, const SwTableLine* pLine )
207 {
208     if( !pLine )
209         return 0;
210     long nCurrLeft = 0;
211     sal_uInt16 nCount = pLine->GetTabBoxes().Count();
212     for( sal_uInt16 nCurrBox = 0; nCurrBox < nCount; ++nCurrBox )
213     {
214         SwTableBox* pBox = pLine->GetTabBoxes()[nCurrBox];
215         ASSERT( pBox, "Missing table box" );
216         if( nCurrLeft >= nLeft && pBox->GetFrmFmt()->GetFrmSize().GetWidth() )
217         {
218             ASSERT( nCurrLeft == nLeft, "Wrong box found" );
219             return pBox;
220         }
221         nCurrLeft += pBox->GetFrmFmt()->GetFrmSize().GetWidth();
222     }
223     ASSERT( false, "Didn't found wished box" );
224     return 0;
225 }
226 
227 /** lcl_ChangeRowSpan corrects row span after insertion/deletion of rows
228 
229 lcl_ChangeRowSpan(..) has to be called after an insertion or deletion of rows
230 to adjust the row spans of previous rows accordingly.
231 If rows are deleted, the previous rows with row spans into the deleted area
232 have to be decremented by the number of _overlapped_ inserted rows.
233 If rows are inserted, the previous rows with row span into the inserted area
234 have to be incremented by the number of inserted rows.
235 For those row spans which ends exactly above the inserted area it has to be
236 decided by the parameter bSingle if they have to be expanded or not.
237 
238 @param rTable
239 the table to manipulate (has to be a new model table)
240 
241 @param nDiff
242 the number of rows which has been inserted (nDiff > 0) or deleted (nDiff < 0)
243 
244 @param nRowIdx
245 the index of the first row which has to be checked
246 
247 @param bSingle
248 true if the new inserted row should not extend row spans which ends in the row above
249 this is for rows inserted by UI "insert row"
250 false if all cells of an inserted row has to be overlapped by the previous row
251 this is for rows inserted by "split row"
252 false is also needed for deleted rows
253 
254 */
255 
256 void lcl_ChangeRowSpan( const SwTable& rTable, const long nDiff,
257                         sal_uInt16 nRowIdx, const bool bSingle )
258 {
259     if( !nDiff || nRowIdx >= rTable.GetTabLines().Count() )
260         return;
261     ASSERT( !bSingle || nDiff > 0, "Don't set bSingle when deleting lines!" );
262     bool bGoOn;
263     // nDistance is the distance between the current row and the critical row,
264     // e.g. the deleted rows or the inserted rows.
265     // If the row span is lower than the distance there is nothing to do
266     // because the row span ends before the critical area.
267     // When the inserted rows should not be overlapped by row spans which ends
268     // exactly in the row above, the trick is to start with a distance of 1.
269     long nDistance = bSingle ? 1 : 0;
270     do
271     {
272         bGoOn = false; // will be set to true if we found a non-master cell
273         // which has to be manipulated => we have to chekc the previous row, too.
274         const SwTableLine* pLine = rTable.GetTabLines()[ nRowIdx ];
275         sal_uInt16 nBoxCount = pLine->GetTabBoxes().Count();
276         for( sal_uInt16 nCurrBox = 0; nCurrBox < nBoxCount; ++nCurrBox )
277         {
278             long nRowSpan = pLine->GetTabBoxes()[nCurrBox]->getRowSpan();
279             long nAbsSpan = nRowSpan > 0 ? nRowSpan : -nRowSpan;
280             // Check if the last overlapped cell is above or below
281             // the critical area
282             if( nAbsSpan > nDistance )
283             {
284                 if( nDiff > 0 )
285                 {
286                     if( nRowSpan > 0 )
287                         nRowSpan += nDiff; // increment row span of master cell
288                     else
289                     {
290                         nRowSpan -= nDiff; // increment row span of non-master cell
291                         bGoOn = true;
292                     }
293                 }
294                 else
295                 {
296                     if( nRowSpan > 0 )
297                     {   // A master cell
298                          // end of row span behind the deleted area ..
299                         if( nRowSpan - nDistance > -nDiff )
300                             nRowSpan += nDiff;
301                         else // .. or inside the deleted area
302                             nRowSpan = nDistance + 1;
303                     }
304                     else
305                     {   // Same for a non-master cell
306                         if( nRowSpan + nDistance < nDiff )
307                             nRowSpan -= nDiff;
308                         else
309                             nRowSpan = -nDistance - 1;
310                         bGoOn = true; // We have to continue
311                     }
312                 }
313                 pLine->GetTabBoxes()[ nCurrBox ]->setRowSpan( nRowSpan );
314             }
315         }
316         ++nDistance;
317         if( nRowIdx )
318             --nRowIdx;
319         else
320             bGoOn = false; //robust
321     } while( bGoOn );
322 }
323 
324 /** CollectBoxSelection(..) create a rectangulare selection based on the given SwPaM
325     and prepares the selected cells for merging
326 */
327 
328 SwBoxSelection* SwTable::CollectBoxSelection( const SwPaM& rPam ) const
329 {
330     ASSERT( bNewModel, "Don't call me for old tables" );
331     if( !aLines.Count() )
332         return 0;
333     const SwNode* pStartNd = rPam.Start()->nNode.GetNode().FindTableBoxStartNode();
334     const SwNode* pEndNd = rPam.End()->nNode.GetNode().FindTableBoxStartNode();
335     if( !pStartNd || !pEndNd || pStartNd == pEndNd )
336         return 0;
337 
338     sal_uInt16 nLines = aLines.Count();
339     sal_uInt16 nTop = 0, nBottom = 0;
340     long nMin = 0, nMax = 0;
341     int nFound = 0;
342     for( sal_uInt16 nRow = 0; nFound < 2 && nRow < nLines; ++nRow )
343     {
344         SwTableLine* pLine = aLines[nRow];
345         ASSERT( pLine, "Missing table line" );
346         sal_uInt16 nCols = pLine->GetTabBoxes().Count();
347         for( sal_uInt16 nCol = 0; nCol < nCols; ++nCol )
348         {
349             SwTableBox* pBox = pLine->GetTabBoxes()[nCol];
350             ASSERT( pBox, "Missing table box" );
351             if( nFound )
352             {
353                 if( pBox->GetSttNd() == pEndNd )
354                 {
355                     nBottom = nRow;
356                     lcl_CheckMinMax( nMin, nMax, *pLine, nCol, false );
357                     ++nFound;
358                     break;
359                 }
360             }
361             else if( pBox->GetSttNd() == pStartNd )
362             {
363                 nTop = nRow;
364                 lcl_CheckMinMax( nMin, nMax, *pLine, nCol, true );
365                 ++nFound;
366             }
367         }
368     }
369     if( nFound < 2 )
370         return 0;
371 
372     bool bOkay = true;
373     long nMid = ( nMin + nMax ) / 2;
374 
375     SwBoxSelection* pRet = new SwBoxSelection();
376     std::list< std::pair< SwTableBox*, long > > aNewWidthList;
377     sal_uInt16 nCheckBottom = nBottom;
378     long nLeftSpan = 0;
379     long nRightSpan = 0;
380     long nLeftSpanCnt = 0;
381     long nRightSpanCnt = 0;
382     for( sal_uInt16 nRow = nTop; nRow <= nBottom && bOkay; ++nRow )
383     {
384         SwTableLine* pLine = aLines[nRow];
385         ASSERT( pLine, "Missing table line" );
386         SwSelBoxes *pBoxes = new SwSelBoxes();
387         long nLeft = 0;
388         long nRight = 0;
389         long nRowSpan = 1;
390         sal_uInt16 nCount = pLine->GetTabBoxes().Count();
391         for( sal_uInt16 nCurrBox = 0; nCurrBox < nCount; ++nCurrBox )
392         {
393             SwTableBox* pBox = pLine->GetTabBoxes()[nCurrBox];
394             ASSERT( pBox, "Missing table box" );
395             nLeft = nRight;
396             nRight += pBox->GetFrmFmt()->GetFrmSize().GetWidth();
397             nRowSpan = pBox->getRowSpan();
398             if( nRight <= nMin )
399             {
400                 if( nRight == nMin && nLeftSpanCnt )
401                     bOkay = false;
402                 continue;
403             }
404             SwTableBox* pInnerBox = 0;
405             SwTableBox* pLeftBox = 0;
406             SwTableBox* pRightBox = 0;
407             long nDiff = 0;
408             long nDiff2 = 0;
409             if( nLeft < nMin )
410             {
411                 if( nRight >= nMid || nRight + nLeft >= nMin + nMin )
412                 {
413                     if( nCurrBox )
414                     {
415                         pBoxes->Insert( pBox );
416                         pInnerBox = pBox;
417                         pLeftBox = pLine->GetTabBoxes()[nCurrBox-1];
418                         nDiff = nMin - nLeft;
419                         if( nRight > nMax )
420                         {
421                             if( nCurrBox+1 < nCount )
422                             {
423                                 pRightBox = pLine->GetTabBoxes()[nCurrBox+1];
424                                 nDiff2 = nRight - nMax;
425                             }
426                             else
427                                 bOkay = false;
428                         }
429                         else if( nRightSpanCnt && nRight == nMax )
430                             bOkay = false;
431                     }
432                     else
433                         bOkay = false;
434                 }
435                 else if( nCurrBox+1 < nCount )
436                 {
437                     pLeftBox = pBox;
438                     pInnerBox = pLine->GetTabBoxes()[nCurrBox+1];
439                     nDiff = nMin - nRight;
440                 }
441                 else
442                     bOkay = false;
443             }
444             else if( nRight <= nMax )
445             {
446                 pBoxes->Insert( pBox );
447                 if( nRow == nTop && nRowSpan < 0 )
448                 {
449                     bOkay = false;
450                     break;
451                 }
452                 if( nRowSpan > 1 && nRow + nRowSpan - 1 > nBottom )
453                     nBottom = nRow + (sal_uInt16)nRowSpan - 1;
454                 if( nRowSpan < -1 && nRow - nRowSpan - 1 > nBottom )
455                     nBottom = (sal_uInt16)(nRow - nRowSpan - 1);
456                 if( nRightSpanCnt && nRight == nMax )
457                     bOkay = false;
458             }
459             else if( nLeft < nMax )
460             {
461                 if( nLeft <= nMid || nRight + nLeft <= nMax )
462                 {
463                     if( nCurrBox+1 < nCount )
464                     {
465                         pBoxes->Insert( pBox );
466                         pInnerBox = pBox;
467                         pRightBox = pLine->GetTabBoxes()[nCurrBox+1];
468                         nDiff = nRight - nMax;
469                     }
470                     else
471                         bOkay = false;
472                 }
473                 else if( nCurrBox )
474                 {
475                     pRightBox = pBox;
476                     pInnerBox = pLine->GetTabBoxes()[nCurrBox-1];
477                     nDiff = nLeft - nMax;
478                 }
479                 else
480                     bOkay = false;
481             }
482             else
483                 break;
484             if( pInnerBox )
485             {
486                 if( nRow == nBottom )
487                 {
488                     long nTmpSpan = pInnerBox->getRowSpan();
489                     if( nTmpSpan > 1 )
490                         nBottom += (sal_uInt16)nTmpSpan - 1;
491                     else if( nTmpSpan < -1 )
492                         nBottom = (sal_uInt16)( nBottom - nTmpSpan - 1 );
493                 }
494                 SwTableBox* pOuterBox = pLeftBox;
495                 do
496                 {
497                     if( pOuterBox )
498                     {
499                         long nOutSpan = pOuterBox->getRowSpan();
500                         if( nOutSpan != 1 )
501                         {
502                             sal_uInt16 nCheck = nRow;
503                             if( nOutSpan < 0 )
504                             {
505                                 const SwTableBox& rBox =
506                                     pOuterBox->FindStartOfRowSpan( *this, USHRT_MAX );
507                                 nOutSpan = rBox.getRowSpan();
508                                 const SwTableLine* pTmpL = rBox.GetUpper();
509                                 nCheck = GetTabLines().C40_GETPOS( SwTableLine, pTmpL );
510                                 if( nCheck < nTop )
511                                     bOkay = false;
512                                 if( pOuterBox == pLeftBox )
513                                 {
514                                     if( !nLeftSpanCnt || nMin - nDiff != nLeftSpan )
515                                         bOkay = false;
516                                 }
517                                 else
518                                 {
519                                     if( !nRightSpanCnt || nMax + nDiff != nRightSpan )
520                                         bOkay = false;
521                                 }
522                             }
523                             else
524                             {
525                                 if( pOuterBox == pLeftBox )
526                                 {
527                                     if( nLeftSpanCnt )
528                                         bOkay = false;
529                                     nLeftSpan = nMin - nDiff;
530                                     nLeftSpanCnt = nOutSpan;
531                                 }
532                                 else
533                                 {
534                                     if( nRightSpanCnt )
535                                         bOkay = false;
536                                     nRightSpan = nMax + nDiff;
537                                     nRightSpanCnt = nOutSpan;
538                                 }
539                             }
540                             nCheck += (sal_uInt16)nOutSpan - 1;
541                             if( nCheck > nCheckBottom )
542                                 nCheckBottom = nCheck;
543                         }
544                         else if( ( nLeftSpanCnt && pLeftBox == pOuterBox ) ||
545                             ( nRightSpanCnt && pRightBox == pOuterBox ) )
546                             bOkay = false;
547                         std::pair< SwTableBox*, long > aTmp;
548                         aTmp.first = pInnerBox;
549                         aTmp.second = -nDiff;
550                         aNewWidthList.push_back( aTmp );
551                         aTmp.first = pOuterBox;
552                         aTmp.second = nDiff;
553                         aNewWidthList.push_back( aTmp );
554                     }
555 					pOuterBox = pOuterBox == pRightBox ? 0 : pRightBox;
556                     if( nDiff2 )
557                         nDiff = nDiff2;
558                 } while( pOuterBox );
559             }
560         }
561         if( nLeftSpanCnt )
562             --nLeftSpanCnt;
563         if( nRightSpanCnt )
564             --nRightSpanCnt;
565         pRet->insertBoxes( pBoxes );
566     }
567     pRet->mnMergeWidth = nMax - nMin;
568     if( nCheckBottom > nBottom )
569         bOkay = false;
570     if( bOkay )
571     {
572         std::list< std::pair< SwTableBox*, long > >::iterator
573             pCurr = aNewWidthList.begin();
574         while( pCurr != aNewWidthList.end() )
575         {
576             SwFrmFmt* pFmt = pCurr->first->ClaimFrmFmt();
577             long nNewWidth = pFmt->GetFrmSize().GetWidth() + pCurr->second;
578             pFmt->SetFmtAttr( SwFmtFrmSize( ATT_VAR_SIZE, nNewWidth, 0 ) );
579             ++pCurr;
580         }
581     }
582     else
583     {
584         delete pRet;
585         pRet = 0;
586     }
587     return pRet;
588 }
589 
590 /** lcl_InvalidateCellFrm(..) invalidates all layout representations of a given cell
591     to initiate a reformatting
592 */
593 
594 void lcl_InvalidateCellFrm( const SwTableBox& rBox )
595 {
596 	SwIterator<SwCellFrm,SwFmt> aIter( *rBox.GetFrmFmt() );
597 	for( SwCellFrm* pCell = aIter.First(); pCell; pCell = aIter.Next() )
598 	{
599 		if( pCell->GetTabBox() == &rBox )
600         {
601 			pCell->InvalidateSize();
602             SwFrm* pLower = pCell->GetLower();
603             if( pLower )
604                 pLower->_InvalidateSize();
605         }
606     }
607 }
608 
609 /** lcl_InsertPosition(..) evaluates the insert positions in every table line,
610     when a selection of cells is given and returns the average cell widths
611 */
612 
613 long lcl_InsertPosition( SwTable &rTable, std::vector<sal_uInt16>& rInsPos,
614     const SwSelBoxes& rBoxes, sal_Bool bBehind )
615 {
616     sal_Int32 nAddWidth = 0;
617     long nCount = 0;
618     for( sal_uInt16 j = 0; j < rBoxes.Count(); ++j )
619     {
620         SwTableBox *pBox = rBoxes[j];
621         SwTableLine* pLine = pBox->GetUpper();
622         long nWidth = rBoxes[j]->GetFrmFmt()->GetFrmSize().GetWidth();
623         nAddWidth += nWidth;
624         sal_uInt16 nCurrBox = pLine->GetTabBoxes().C40_GETPOS(SwTableBox, pBox );
625         sal_uInt16 nCurrLine = rTable.GetTabLines().C40_GETPOS(SwTableLine, pLine );
626         ASSERT( nCurrLine != USHRT_MAX, "Time to say Good-Bye.." );
627         if( rInsPos[ nCurrLine ] == USHRT_MAX )
628         {
629             rInsPos[ nCurrLine ] = nCurrBox;
630             ++nCount;
631         }
632         else if( ( rInsPos[ nCurrLine ] > nCurrBox ) == !bBehind )
633             rInsPos[ nCurrLine ] = nCurrBox;
634     }
635     if( nCount )
636         nAddWidth /= nCount;
637     return nAddWidth;
638 }
639 
640 /** SwTable::NewInsertCol(..) insert new column(s) into a table
641 
642 
643 @param pDoc
644 the document
645 
646 @param rBoxes
647 the selected boxes
648 
649 @param nCnt
650 the number of columns to insert
651 
652 @param bBehind
653 insertion behind (true) or before (false) the selected boxes
654 
655 @return true, if any insertion has been done successfully
656 
657 */
658 
659 sal_Bool SwTable::NewInsertCol( SwDoc* pDoc, const SwSelBoxes& rBoxes,
660     sal_uInt16 nCnt, sal_Bool bBehind )
661 {
662     if( !aLines.Count() || !nCnt )
663         return sal_False;
664 
665     CHECK_TABLE( *this )
666     long nNewBoxWidth = 0;
667     std::vector< sal_uInt16 > aInsPos( aLines.Count(), USHRT_MAX );
668     { // Calculation of the insert positions and the width of the new boxes
669         sal_uInt64 nTableWidth = 0;
670         for( sal_uInt16 i = 0; i < aLines[0]->GetTabBoxes().Count(); ++i )
671             nTableWidth += aLines[0]->GetTabBoxes()[i]->GetFrmFmt()->GetFrmSize().GetWidth();
672 
673         // Fill the vector of insert positions and the (average) width to insert
674         sal_uInt64 nAddWidth = lcl_InsertPosition( *this, aInsPos, rBoxes, bBehind );
675 
676         // Given is the (average) width of the selected boxes, if we would
677         // insert nCnt of columns the table would grow
678         // So we will shrink the table first, then insert the new boxes and
679         // get a table with the same width than before.
680         // But we will not shrink the table by the full already calculated value,
681         // we will reduce this value proportional to the old table width
682         nAddWidth *= nCnt; // we have to insert nCnt boxes per line
683         sal_uInt64 nResultingWidth = nAddWidth + nTableWidth;
684         if( !nResultingWidth )
685             return sal_False;
686         nAddWidth = (nAddWidth * nTableWidth) / nResultingWidth;
687         nNewBoxWidth = long( nAddWidth / nCnt ); // Rounding
688         nAddWidth = nNewBoxWidth * nCnt; // Rounding
689         if( !nAddWidth || nAddWidth >= nTableWidth )
690             return sal_False;
691         AdjustWidths( static_cast< long >(nTableWidth), static_cast< long >(nTableWidth - nAddWidth) );
692     }
693 
694 	_FndBox aFndBox( 0, 0 );
695 	aFndBox.SetTableLines( rBoxes, *this );
696 	aFndBox.DelFrms( *this );
697 // 	aFndBox.SaveChartData( *this );
698 
699     SwTableNode* pTblNd = GetTableNode();
700     std::vector<SwTableBoxFmt*> aInsFormat( nCnt, 0 );
701     sal_uInt16 nLastLine = USHRT_MAX;
702     long nLastRowSpan = 1;
703 
704     for( sal_uInt16 i = 0; i < aLines.Count(); ++i )
705     {
706         SwTableLine* pLine = aLines[ i ];
707         sal_uInt16 nInsPos = aInsPos[i];
708         ASSERT( nInsPos != USHRT_MAX, "Didn't found insert position" );
709         SwTableBox* pBox = pLine->GetTabBoxes()[ nInsPos ];
710         if( bBehind )
711             ++nInsPos;
712         SwTableBoxFmt* pBoxFrmFmt = (SwTableBoxFmt*)pBox->GetFrmFmt();
713         ::_InsTblBox( pDoc, pTblNd, pLine, pBoxFrmFmt, pBox, nInsPos, nCnt );
714         long nRowSpan = pBox->getRowSpan();
715         long nDiff = i - nLastLine;
716         bool bNewSpan = false;
717         if( nLastLine != USHRT_MAX && nDiff <= nLastRowSpan &&
718             nRowSpan != nDiff - nLastRowSpan )
719         {
720             bNewSpan = true;
721             while( nLastLine < i )
722             {
723                 SwTableLine* pTmpLine = aLines[ nLastLine ];
724                 sal_uInt16 nTmpPos = aInsPos[nLastLine];
725                 if( bBehind )
726                     ++nTmpPos;
727                 for( sal_uInt16 j = 0; j < nCnt; ++j )
728                     pTmpLine->GetTabBoxes()[nTmpPos+j]->setRowSpan( nDiff );
729                 if( nDiff > 0 )
730                     nDiff = -nDiff;
731                 ++nDiff;
732                 ++nLastLine;
733             }
734         }
735         if( nRowSpan > 0 )
736             bNewSpan = true;
737         if( bNewSpan )
738         {
739             nLastLine = i;
740             if( nRowSpan < 0 )
741                 nLastRowSpan = -nRowSpan;
742             else
743                 nLastRowSpan = nRowSpan;
744         }
745 		const SvxBoxItem& aSelBoxItem = pBoxFrmFmt->GetBox();
746 		SvxBoxItem* pNoRightBorder = 0;
747         if( aSelBoxItem.GetRight() )
748         {
749             pNoRightBorder = new SvxBoxItem( aSelBoxItem );
750 			pNoRightBorder->SetLine( 0, BOX_LINE_RIGHT );
751         }
752         for( sal_uInt16 j = 0; j < nCnt; ++j )
753         {
754             SwTableBox *pCurrBox = pLine->GetTabBoxes()[nInsPos+j];
755             if( bNewSpan )
756             {
757                 pCurrBox->setRowSpan( nLastRowSpan );
758                 SwFrmFmt* pFrmFmt = pCurrBox->ClaimFrmFmt();
759                 SwFmtFrmSize aFrmSz( pFrmFmt->GetFrmSize() );
760                 aFrmSz.SetWidth( nNewBoxWidth );
761                 pFrmFmt->SetFmtAttr( aFrmSz );
762                 if( pNoRightBorder && ( !bBehind || j+1 < nCnt ) )
763                     pFrmFmt->SetFmtAttr( *pNoRightBorder );
764                 aInsFormat[j] = (SwTableBoxFmt*)pFrmFmt;
765             }
766             else
767                 pCurrBox->ChgFrmFmt( aInsFormat[j] );
768         }
769         if( bBehind && pNoRightBorder )
770         {
771             SwFrmFmt* pFrmFmt = pBox->ClaimFrmFmt();
772             pFrmFmt->SetFmtAttr( *pNoRightBorder );
773         }
774         delete pNoRightBorder;
775     }
776 
777 	aFndBox.MakeFrms( *this );
778 // 	aFndBox.RestoreChartData( *this );
779 #ifdef DBG_UTIL
780     {
781         const SwTableBoxes &rTabBoxes = aLines[0]->GetTabBoxes();
782         long nNewWidth = 0;
783         for( sal_uInt16 i = 0; i < rTabBoxes.Count(); ++i )
784             nNewWidth += rTabBoxes[i]->GetFrmFmt()->GetFrmSize().GetWidth();
785         ASSERT( nNewWidth > 0, "Very small" );
786     }
787 #endif
788     CHECK_TABLE( *this )
789 
790     return sal_True;
791 }
792 
793 /** SwTable::PrepareMerge(..) some preparation for the coming Merge(..)
794 
795 For the old table model, ::GetMergeSel(..) is called only,
796 for the new table model, PrepareMerge does the main work.
797 It modifices all cells to merge (width, border, rowspan etc.) and collects
798 the cells which have to be deleted by Merge(..) afterwards.
799 If there are superfluous rows, these cells are put into the deletion list as well.
800 
801 @param rPam
802 the selection to merge
803 
804 @param rBoxes
805 should be empty at the beginning, at the end it is filled with boxes to delete.
806 
807 @param ppMergeBox
808 will be set to the master cell box
809 
810 @param pUndo
811 the undo object to record all changes
812 can be Null, e.g. when called by Redo(..)
813 
814 @return
815 
816 */
817 
818 bool SwTable::PrepareMerge( const SwPaM& rPam, SwSelBoxes& rBoxes,
819    SwSelBoxes& rMerged, SwTableBox** ppMergeBox, SwUndoTblMerge* pUndo )
820 {
821     if( !bNewModel )
822     {
823         ::GetMergeSel( rPam, rBoxes, ppMergeBox, pUndo );
824         return rBoxes.Count() > 1;
825     }
826     CHECK_TABLE( *this )
827     // We have to assert a "rectangular" box selection before we start to merge
828     std::auto_ptr< SwBoxSelection > pSel( CollectBoxSelection( rPam ) );
829     if( !pSel.get() || pSel->isEmpty() )
830         return false;
831     // Now we should have a rectangle of boxes,
832     // i.e. contiguous cells in contiguous rows
833     bool bMerge = false; // will be set if any content is transferred from
834     // a "not already overlapped" cell into the new master cell.
835     SwTableBox *pMergeBox = (*pSel->aBoxes[0])[0]; // the master cell box
836     if( !pMergeBox )
837         return false;
838     (*ppMergeBox) = pMergeBox;
839     // The new master box will get the left and the top border of the top-left
840     // box of the selection and because the new master cell _is_ the top-left
841     // box, the left and right border does not need to be changed.
842     // The right and bottom border instead has to be derived from the right-
843     // bottom box of the selection. If this is a overlapped cell,
844     // the appropiate master box.
845     SwTableBox* pLastBox = 0; // the right-bottom (master) cell
846     SwDoc* pDoc = GetFrmFmt()->GetDoc();
847     SwPosition aInsPos( *pMergeBox->GetSttNd()->EndOfSectionNode() );
848     SwPaM aChkPam( aInsPos );
849     // The number of lines in the selection rectangle: nLineCount
850     const sal_uInt16 nLineCount = sal_uInt16(pSel->aBoxes.size());
851     // BTW: nLineCount is the rowspan of the new master cell
852     long nRowSpan = nLineCount;
853     // We will need the first and last line of the selection
854     // to check if there any superfluous row after merging
855     SwTableLine* pFirstLn = 0;
856     SwTableLine* pLastLn = 0;
857     // Iteration over the lines of the selection...
858     for( sal_uInt16 nCurrLine = 0; nCurrLine < nLineCount; ++nCurrLine )
859     {
860         // The selected boxes in the current line
861         const SwSelBoxes* pBoxes = pSel->aBoxes[ nCurrLine ];
862         sal_uInt16 nColCount = pBoxes->Count();
863         // Iteration over the selected cell in the current row
864         for( sal_uInt16 nCurrCol = 0; nCurrCol < nColCount; ++nCurrCol )
865         {
866             SwTableBox* pBox = (*pBoxes)[nCurrCol];
867             rMerged.Insert( pBox );
868             // Only the first selected cell in every row will be alive,
869             // the other will be deleted => put into rBoxes
870             if( nCurrCol )
871                 rBoxes.Insert( pBox );
872             else
873             {
874                 if( nCurrLine == 1 )
875                     pFirstLn = pBox->GetUpper(); // we need this line later on
876                 if( nCurrLine + 1 == nLineCount )
877                     pLastLn = pBox->GetUpper(); // and this one, too.
878             }
879             // A box has to be merged if it's not the master box itself,
880             // but an already overlapped cell must not be merged as well.
881             bool bDoMerge = pBox != pMergeBox && pBox->getRowSpan() > 0;
882             // The last box has to be in the last "column" of the selection
883             // and it has to be a master cell
884             if( nCurrCol+1 == nColCount && pBox->getRowSpan() > 0 )
885                 pLastBox = pBox;
886             if( bDoMerge )
887             {
888                 bMerge = true;
889                 // If the cell to merge contains only one empty paragraph,
890                 // we do not transfer this paragraph.
891                 if( !IsEmptyBox( *pBox, aChkPam ) )
892                 {
893                     SwNodeIndex& rInsPosNd = aInsPos.nNode;
894                     SwPaM aPam( aInsPos );
895                     aPam.GetPoint()->nNode.Assign( *pBox->GetSttNd()->EndOfSectionNode(), -1 );
896                     SwCntntNode* pCNd = aPam.GetCntntNode();
897                     sal_uInt16 nL = pCNd ? pCNd->Len() : 0;
898                     aPam.GetPoint()->nContent.Assign( pCNd, nL );
899                     SwNodeIndex aSttNdIdx( *pBox->GetSttNd(), 1 );
900                     bool const bUndo = pDoc->GetIDocumentUndoRedo().DoesUndo();
901                     if( pUndo )
902                     {
903                         pDoc->GetIDocumentUndoRedo().DoUndo(false);
904                     }
905                     pDoc->AppendTxtNode( *aPam.GetPoint() );
906                     if( pUndo )
907                     {
908                         pDoc->GetIDocumentUndoRedo().DoUndo(bUndo);
909                     }
910                     SwNodeRange aRg( aSttNdIdx, aPam.GetPoint()->nNode );
911                     if( pUndo )
912                         pUndo->MoveBoxCntnt( pDoc, aRg, rInsPosNd );
913                     else
914                     {
915                         pDoc->MoveNodeRange( aRg, rInsPosNd,
916                             IDocumentContentOperations::DOC_NO_DELFRMS );
917                     }
918                 }
919             }
920             // Only the cell of the first selected column will stay alive
921             // and got a new row span
922             if( !nCurrCol )
923                 pBox->setRowSpan( nRowSpan );
924         }
925         if( nRowSpan > 0 ) // the master cell is done, from now on we set
926             nRowSpan = -nRowSpan; // negative row spans
927         ++nRowSpan; // ... -3, -2, -1
928     }
929     if( bMerge )
930     {
931         // A row containing overlapped cells is superfluous,
932         // these cells can be put into rBoxes for deletion
933         _FindSuperfluousRows( rBoxes, pFirstLn, pLastLn );
934         // pNewFmt will be set to the new master box and the overlapped cells
935         SwFrmFmt* pNewFmt = pMergeBox->ClaimFrmFmt();
936         pNewFmt->SetFmtAttr( SwFmtFrmSize( ATT_VAR_SIZE, pSel->mnMergeWidth, 0 ) );
937         for( sal_uInt16 nCurrLine = 0; nCurrLine < nLineCount; ++nCurrLine )
938         {
939             const SwSelBoxes* pBoxes = pSel->aBoxes[ nCurrLine ];
940             sal_uInt16 nColCount = pBoxes->Count();
941             for( sal_uInt16 nCurrCol = 0; nCurrCol < nColCount; ++nCurrCol )
942             {
943                 SwTableBox* pBox = (*pBoxes)[nCurrCol];
944                 if( nCurrCol )
945                 {
946                     // Even this box will be deleted soon,
947                     // we have to correct the width to avoid side effects
948                     SwFrmFmt* pFmt = pBox->ClaimFrmFmt();
949                     pFmt->SetFmtAttr( SwFmtFrmSize( ATT_VAR_SIZE, 0, 0 ) );
950                 }
951                 else
952                     pBox->ChgFrmFmt( (SwTableBoxFmt*)pNewFmt );
953             }
954         }
955         if( pLastBox ) // Robust
956         {
957             // The new borders of the master cell...
958             SvxBoxItem aBox( pMergeBox->GetFrmFmt()->GetBox() );
959             bool bOld = aBox.GetRight() || aBox.GetBottom();
960             const SvxBoxItem& rBox = pLastBox->GetFrmFmt()->GetBox();
961             aBox.SetLine( rBox.GetRight(), BOX_LINE_RIGHT );
962             aBox.SetLine( rBox.GetBottom(), BOX_LINE_BOTTOM );
963             if( bOld || aBox.GetLeft() || aBox.GetTop() || aBox.GetRight() || aBox.GetBottom() )
964                 (*ppMergeBox)->GetFrmFmt()->SetFmtAttr( aBox );
965         }
966 
967         if( pUndo )
968             pUndo->AddNewBox( pMergeBox->GetSttIdx() );
969     }
970     return bMerge;
971 }
972 
973 /** SwTable::_FindSuperfluousRows(..) is looking for superfluous rows, i.e. rows
974     containing overlapped cells only.
975 */
976 
977 void SwTable::_FindSuperfluousRows( SwSelBoxes& rBoxes,
978     SwTableLine* pFirstLn, SwTableLine* pLastLn )
979 {
980     if( !pFirstLn || !pLastLn )
981     {
982         if( !rBoxes.Count() )
983             return;
984         pFirstLn = rBoxes[0]->GetUpper();
985         pLastLn = rBoxes[ rBoxes.Count() - 1 ]->GetUpper();
986     }
987     sal_uInt16 nFirstLn = GetTabLines().C40_GETPOS(SwTableLine, pFirstLn );
988     sal_uInt16 nLastLn = GetTabLines().C40_GETPOS(SwTableLine, pLastLn );
989     for( sal_uInt16 nRow = nFirstLn; nRow <= nLastLn; ++nRow )
990     {
991         SwTableLine* pLine = aLines[nRow];
992         ASSERT( pLine, "Missing table line" );
993         sal_uInt16 nCols = pLine->GetTabBoxes().Count();
994         bool bSuperfl = true;
995         for( sal_uInt16 nCol = 0; nCol < nCols; ++nCol )
996         {
997             SwTableBox *pBox = pLine->GetTabBoxes()[nCol];
998             if( pBox->getRowSpan() > 0 &&
999                 USHRT_MAX == rBoxes.GetPos( pBox ) )
1000             {
1001                 bSuperfl = false;
1002                 break;
1003             }
1004         }
1005         if( bSuperfl )
1006         {
1007             for( sal_uInt16 nCol = 0; nCol < nCols; ++nCol )
1008             {
1009                 SwTableBox* pBox = pLine->GetTabBoxes()[nCol];
1010                 rBoxes.Insert( pBox );
1011             }
1012         }
1013     }
1014 }
1015 
1016 /** SwTableBox::FindStartOfRowSpan(..) retruns the "master" cell, the cell which
1017     overlaps the given cell, it maybe the cell itself.
1018 */
1019 
1020 SwTableBox& SwTableBox::FindStartOfRowSpan( const SwTable& rTable, sal_uInt16 nMaxStep )
1021 {
1022     if( getRowSpan() > 0 || !nMaxStep )
1023         return *this;
1024 
1025     long nLeftBorder = lcl_Box2LeftBorder( *this );
1026     SwTableBox* pBox = this;
1027     const SwTableLine* pMyUpper = GetUpper();
1028     sal_uInt16 nLine = rTable.GetTabLines().C40_GETPOS(SwTableLine, pMyUpper );
1029     if( nLine && nLine < rTable.GetTabLines().Count() )
1030     {
1031         SwTableBox* pNext;
1032         do
1033         {
1034             pNext = lcl_LeftBorder2Box( nLeftBorder, rTable.GetTabLines()[--nLine] );
1035             if( pNext )
1036                 pBox = pNext;
1037         } while( nLine && --nMaxStep && pNext && pBox->getRowSpan() < 1 );
1038     }
1039 
1040     return *pBox;
1041 }
1042 
1043 /** SwTableBox::FindEndOfRowSpan(..) returns the last overlapped cell if there is
1044     any. Otherwise the cell itself will returned.
1045 */
1046 
1047 SwTableBox& SwTableBox::FindEndOfRowSpan( const SwTable& rTable, sal_uInt16 nMaxStep )
1048 {
1049     long nAbsSpan = getRowSpan();
1050     if( nAbsSpan < 0 )
1051         nAbsSpan = -nAbsSpan;
1052     if( nAbsSpan == 1 || !nMaxStep )
1053         return *this;
1054 
1055     if( nMaxStep > --nAbsSpan )
1056         nMaxStep = (sal_uInt16)nAbsSpan;
1057     const SwTableLine* pMyUpper = GetUpper();
1058     sal_uInt16 nLine = rTable.GetTabLines().C40_GETPOS(SwTableLine, pMyUpper );
1059     nMaxStep = nLine + nMaxStep;
1060     if( nMaxStep >= rTable.GetTabLines().Count() )
1061         nMaxStep = rTable.GetTabLines().Count() - 1;
1062     long nLeftBorder = lcl_Box2LeftBorder( *this );
1063     SwTableBox* pBox =
1064         lcl_LeftBorder2Box( nLeftBorder, rTable.GetTabLines()[ nMaxStep ] );
1065     if ( !pBox )
1066         pBox = this;
1067 
1068     return *pBox;
1069 }
1070 
1071 /** lcl_getAllMergedBoxes(..) collects all overlapped boxes to a given (master) box
1072 */
1073 
1074 void lcl_getAllMergedBoxes( const SwTable& rTable, SwSelBoxes& rBoxes, SwTableBox& rBox )
1075 {
1076     SwTableBox* pBox = &rBox;
1077     ASSERT( pBox == &rBox.FindStartOfRowSpan( rTable, USHRT_MAX ), "Not a master box" );
1078     rBoxes.Insert( pBox );
1079     if( pBox->getRowSpan() == 1 )
1080         return;
1081     const SwTableLine* pMyUpper = pBox->GetUpper();
1082     sal_uInt16 nLine = rTable.GetTabLines().C40_GETPOS(SwTableLine, pMyUpper );
1083     long nLeftBorder = lcl_Box2LeftBorder( *pBox );
1084     sal_uInt16 nCount = rTable.GetTabLines().Count();
1085     while( ++nLine < nCount && pBox && pBox->getRowSpan() != -1 )
1086     {
1087         pBox = lcl_LeftBorder2Box( nLeftBorder, rTable.GetTabLines()[nLine] );
1088         if( pBox )
1089             rBoxes.Insert( pBox );
1090     };
1091 }
1092 
1093 /** lcl_UnMerge(..) manipulates the row span attribute of a given master cell
1094     and its overlapped cells to split them into several pieces.
1095 */
1096 
1097 void lcl_UnMerge( const SwTable& rTable, SwTableBox& rBox, sal_uInt16 nCnt,
1098     sal_Bool bSameHeight )
1099 {
1100     SwSelBoxes aBoxes;
1101     lcl_getAllMergedBoxes( rTable, aBoxes, rBox );
1102     sal_uInt16 nCount = aBoxes.Count();
1103     if( nCount < 2 )
1104         return;
1105     if( nCnt > nCount )
1106         nCnt = nCount;
1107     sal_uInt16 *pSplitIdx = new sal_uInt16[ nCnt ];
1108     if( bSameHeight )
1109     {
1110         SwTwips *pHeights = new SwTwips[ nCount ];
1111         SwTwips nHeight = 0;
1112         for( sal_uInt16 i = 0; i < nCount; ++i )
1113         {
1114             SwTableLine* pLine = aBoxes[ i ]->GetUpper();
1115             SwFrmFmt *pRowFmt = pLine->GetFrmFmt();
1116             pHeights[ i ] = pRowFmt->GetFrmSize().GetHeight();
1117             nHeight += pHeights[ i ];
1118         }
1119         SwTwips nSumH = 0;
1120         sal_uInt16 nIdx = 0;
1121         for( sal_uInt16 i = 1; i <= nCnt; ++i )
1122         {
1123             SwTwips nSplit = ( i * nHeight ) / nCnt;
1124             while( nSumH < nSplit && nIdx < nCount )
1125                 nSumH += pHeights[ nIdx++ ];
1126             pSplitIdx[ i - 1 ] = nIdx;
1127         }
1128         delete[] pHeights;
1129     }
1130     else
1131     {
1132         for( long i = 1; i <= nCnt; ++i )
1133             pSplitIdx[ i - 1 ] = (sal_uInt16)( ( i * nCount ) / nCnt );
1134     }
1135     sal_uInt16 nIdx = 0;
1136     for( long i = 0; i < nCnt; ++i )
1137     {
1138         sal_uInt16 nNextIdx = pSplitIdx[ i ];
1139         aBoxes[ nIdx ]->setRowSpan( nNextIdx - nIdx );
1140         lcl_InvalidateCellFrm( *aBoxes[ nIdx ] );
1141         while( ++nIdx < nNextIdx )
1142             aBoxes[ nIdx ]->setRowSpan( nIdx - nNextIdx );
1143     }
1144     delete[] pSplitIdx;
1145 }
1146 
1147 /** lcl_FillSelBoxes(..) puts all boxes of a given line into the selection structure
1148 */
1149 
1150 void lcl_FillSelBoxes( SwSelBoxes &rBoxes, SwTableLine &rLine )
1151 {
1152     sal_uInt16 nBoxCount = rLine.GetTabBoxes().Count();
1153     sal_uInt16 nCurrBox;
1154     for( nCurrBox = 0; nCurrBox < nBoxCount; ++nCurrBox )
1155         rBoxes.Insert( rLine.GetTabBoxes()[nCurrBox] );
1156 }
1157 
1158 /** SwTable::InsertSpannedRow(..) inserts "superfluous" rows, i.e. rows containig
1159     overlapped cells only. This is a preparation for an upcoming split.
1160 */
1161 
1162 void SwTable::InsertSpannedRow( SwDoc* pDoc, sal_uInt16 nRowIdx, sal_uInt16 nCnt )
1163 {
1164     CHECK_TABLE( *this )
1165     ASSERT( nCnt && nRowIdx < GetTabLines().Count(), "Wrong call of InsertSpannedRow" );
1166     SwSelBoxes aBoxes;
1167     SwTableLine& rLine = *GetTabLines()[ nRowIdx ];
1168     lcl_FillSelBoxes( aBoxes, rLine );
1169     SwFmtFrmSize aFSz( rLine.GetFrmFmt()->GetFrmSize() );
1170     if( ATT_VAR_SIZE != aFSz.GetHeightSizeType() )
1171     {
1172         SwFrmFmt* pFrmFmt = rLine.ClaimFrmFmt();
1173         long nNewHeight = aFSz.GetHeight() / ( nCnt + 1 );
1174         if( !nNewHeight )
1175             ++nNewHeight;
1176         aFSz.SetHeight( nNewHeight );
1177         pFrmFmt->SetFmtAttr( aFSz );
1178     }
1179     _InsertRow( pDoc, aBoxes, nCnt, sal_True );
1180     sal_uInt16 nBoxCount = rLine.GetTabBoxes().Count();
1181     for( sal_uInt16 n = 0; n < nCnt; ++n )
1182     {
1183         SwTableLine *pNewLine = GetTabLines()[ nRowIdx + nCnt - n ];
1184         for( sal_uInt16 nCurrBox = 0; nCurrBox < nBoxCount; ++nCurrBox )
1185         {
1186             long nRowSpan = rLine.GetTabBoxes()[nCurrBox]->getRowSpan();
1187             if( nRowSpan > 0 )
1188                 nRowSpan = - nRowSpan;
1189             pNewLine->GetTabBoxes()[ nCurrBox ]->setRowSpan( nRowSpan - n );
1190         }
1191     }
1192     lcl_ChangeRowSpan( *this, nCnt, nRowIdx, false );
1193     CHECK_TABLE( *this )
1194 }
1195 
1196 typedef std::pair< sal_uInt16, sal_uInt16 > SwLineOffset;
1197 typedef std::list< SwLineOffset > SwLineOffsetArray;
1198 
1199 /******************************************************************************
1200 When a couple of table boxes has to be split,
1201 lcl_SophisticatedFillLineIndices delivers the information where and how many
1202 rows have to be inserted.
1203 Input
1204     rTable: the table to manipulate
1205     rBoxes: an array of boxes to split
1206     nCnt:   how many parts are wanted
1207 Output
1208     rArr:   a list of pairs ( line index, number of lines to insert )
1209 
1210 ******************************************************************************/
1211 
1212 void lcl_SophisticatedFillLineIndices( SwLineOffsetArray &rArr,
1213     const SwTable& rTable, const SwSelBoxes& rBoxes, sal_uInt16 nCnt )
1214 {
1215     std::list< SwLineOffset > aBoxes;
1216     SwLineOffset aLnOfs( USHRT_MAX, USHRT_MAX );
1217     for( sal_uInt16 i = 0; i < rBoxes.Count(); ++i )
1218     {   // Collect all end line indices and the row spans
1219         const SwTableBox &rBox = rBoxes[ i ]->FindStartOfRowSpan( rTable );
1220         ASSERT( rBox.getRowSpan() > 0, "Didn't I say 'StartOfRowSpan' ??" )
1221         if( nCnt > rBox.getRowSpan() )
1222         {
1223             const SwTableLine *pLine = rBox.GetUpper();
1224             const sal_uInt16 nEnd = sal_uInt16( rBox.getRowSpan() +
1225                 rTable.GetTabLines().C40_GETPOS( SwTableLine, pLine ) );
1226             // The next if statement is a small optimization
1227             if( aLnOfs.first != nEnd || aLnOfs.second != rBox.getRowSpan() )
1228             {
1229                 aLnOfs.first = nEnd; // ok, this is the line behind the box
1230                 aLnOfs.second = sal_uInt16( rBox.getRowSpan() ); // the row span
1231                 aBoxes.insert( aBoxes.end(), aLnOfs );
1232             }
1233         }
1234     }
1235     // As I said, I noted the line index _behind_ the last line of the boxes
1236     // in the resulting array the index has to be _on_ the line
1237     // nSum is to evaluate the wished value
1238     sal_uInt16 nSum = 1;
1239     while( aBoxes.size() )
1240     {
1241         // I. step:
1242         // Looking for the "smallest" line end with the smallest row span
1243         std::list< SwLineOffset >::iterator pCurr = aBoxes.begin();
1244         aLnOfs = *pCurr; // the line end and row span of the first box
1245         while( ++pCurr != aBoxes.end() )
1246         {
1247             if( aLnOfs.first > pCurr->first )
1248             {   // Found a smaller line end
1249                 aLnOfs.first = pCurr->first;
1250                 aLnOfs.second = pCurr->second; // row span
1251             }
1252             else if( aLnOfs.first == pCurr->first &&
1253                      aLnOfs.second < pCurr->second )
1254                 aLnOfs.second = pCurr->second; // Found a smaller row span
1255         }
1256         ASSERT( aLnOfs.second < nCnt, "Clean-up failed" )
1257         aLnOfs.second = nCnt - aLnOfs.second; // the number of rows to insert
1258         rArr.insert( rArr.end(),
1259             SwLineOffset( aLnOfs.first - nSum, aLnOfs.second ) );
1260         // the correction has to be incremented because in the following
1261         // loops the line ends were manipulated
1262         nSum = nSum + aLnOfs.second;
1263 
1264         pCurr = aBoxes.begin();
1265         while( pCurr != aBoxes.end() )
1266         {
1267             if( pCurr->first == aLnOfs.first )
1268             {   // These boxes can be removed because the last insertion
1269                 // of rows will expand their row span above the needed value
1270                 std::list< SwLineOffset >::iterator pDel = pCurr;
1271                 ++pCurr;
1272                 aBoxes.erase( pDel );
1273             }
1274             else
1275             {
1276                 bool bBefore = ( pCurr->first - pCurr->second < aLnOfs.first );
1277                 // Manipulation of the end line indices as if the rows are
1278                 // already inserted
1279                 pCurr->first = pCurr->first + aLnOfs.second;
1280                 if( bBefore )
1281                 {   // If the insertion is inside the box,
1282                     // its row span has to be incremented
1283                     pCurr->second = pCurr->second + aLnOfs.second;
1284                     if( pCurr->second >= nCnt )
1285                     {   // if the row span is bigger than the split factor
1286                         // this box is done
1287                         std::list< SwLineOffset >::iterator pDel = pCurr;
1288                         ++pCurr;
1289                         aBoxes.erase( pDel );
1290                     }
1291                     else
1292                         ++pCurr;
1293                 }
1294                 else
1295                     ++pCurr;
1296             }
1297         }
1298     }
1299 }
1300 
1301 typedef std::set< SwTwips > SwSplitLines;
1302 
1303 /** lcl_CalculateSplitLineHeights(..) delivers all y-positions where table rows have
1304     to be splitted to fulfill the requested "split same height"
1305 */
1306 
1307 sal_uInt16 lcl_CalculateSplitLineHeights( SwSplitLines &rCurr, SwSplitLines &rNew,
1308     const SwTable& rTable, const SwSelBoxes& rBoxes, sal_uInt16 nCnt )
1309 {
1310     if( nCnt < 2 )
1311         return 0;
1312     std::list< SwLineOffset > aBoxes;
1313     SwLineOffset aLnOfs( USHRT_MAX, USHRT_MAX );
1314     sal_uInt16 nFirst = USHRT_MAX; // becomes the index of the first line
1315     sal_uInt16 nLast = 0; // becomes the index of the last line of the splitting
1316     for( sal_uInt16 i = 0; i < rBoxes.Count(); ++i )
1317     {   // Collect all pairs (start+end) of line indices to split
1318         const SwTableBox &rBox = rBoxes[ i ]->FindStartOfRowSpan( rTable );
1319         ASSERT( rBox.getRowSpan() > 0, "Didn't I say 'StartOfRowSpan' ??" )
1320         const SwTableLine *pLine = rBox.GetUpper();
1321         const sal_uInt16 nStart = rTable.GetTabLines().C40_GETPOS( SwTableLine, pLine );
1322         const sal_uInt16 nEnd = sal_uInt16( rBox.getRowSpan() + nStart - 1 );
1323         // The next if statement is a small optimization
1324         if( aLnOfs.first != nStart || aLnOfs.second != nEnd )
1325         {
1326             aLnOfs.first = nStart;
1327             aLnOfs.second = nEnd;
1328             aBoxes.insert( aBoxes.end(), aLnOfs );
1329             if( nStart < nFirst )
1330                 nFirst = nStart;
1331             if( nEnd > nLast )
1332                 nLast = nEnd;
1333         }
1334     }
1335 
1336     if( aBoxes.empty() )
1337         return 0;
1338     SwTwips nHeight = 0;
1339     SwTwips* pLines = new SwTwips[ nLast + 1 - nFirst ];
1340     for( sal_uInt16 i = nFirst; i <= nLast; ++i )
1341     {
1342         bool bLayoutAvailable = false;
1343         nHeight += rTable.GetTabLines()[ i ]->GetTableLineHeight( bLayoutAvailable );
1344         rCurr.insert( rCurr.end(), nHeight );
1345         pLines[ i - nFirst ] = nHeight;
1346     }
1347     std::list< SwLineOffset >::iterator pSplit = aBoxes.begin();
1348     while( pSplit != aBoxes.end() )
1349     {
1350         SwTwips nBase = pSplit->first <= nFirst ? 0 :
1351                         pLines[ pSplit->first - nFirst - 1 ];
1352         SwTwips nDiff = pLines[ pSplit->second - nFirst ] - nBase;
1353         for( sal_uInt16 i = 1; i < nCnt; ++i )
1354         {
1355             SwTwips nSplit = nBase + ( i * nDiff ) / nCnt;
1356             rNew.insert( nSplit );
1357         }
1358         ++pSplit;
1359     }
1360     delete[] pLines;
1361     return nFirst;
1362 }
1363 
1364 /** lcl_LineIndex(..) delivers the line index of the line behind or above
1365     the box selection.
1366 */
1367 
1368 sal_uInt16 lcl_LineIndex( const SwTable& rTable, const SwSelBoxes& rBoxes,
1369                       bool bBehind )
1370 {
1371     sal_uInt16 nDirect = USHRT_MAX;
1372     sal_uInt16 nSpan = USHRT_MAX;
1373     for( sal_uInt16 i = 0; i < rBoxes.Count(); ++i )
1374     {
1375         SwTableBox *pBox = rBoxes[i];
1376         const SwTableLine* pLine = rBoxes[i]->GetUpper();
1377         sal_uInt16 nPos = rTable.GetTabLines().C40_GETPOS( SwTableLine, pLine );
1378         if( USHRT_MAX != nPos )
1379         {
1380             if( bBehind )
1381             {
1382                 if( nPos > nDirect || nDirect == USHRT_MAX )
1383                     nDirect = nPos;
1384                 long nRowSpan = pBox->getRowSpan();
1385                 if( nRowSpan < 2 )
1386                     nSpan = 0;
1387                 else if( nSpan )
1388                 {
1389                     sal_uInt16 nEndOfRowSpan = (sal_uInt16)(nPos + nRowSpan - 1);
1390                     if( nEndOfRowSpan > nSpan || nSpan == USHRT_MAX )
1391                         nSpan = nEndOfRowSpan;
1392                 }
1393             }
1394             else if( nPos < nDirect )
1395                 nDirect = nPos;
1396         }
1397     }
1398     if( nSpan && nSpan < USHRT_MAX )
1399         return nSpan;
1400     return nDirect;
1401 }
1402 
1403 /** SwTable::NewSplitRow(..) splits all selected boxes horizontally.
1404 */
1405 
1406 sal_Bool SwTable::NewSplitRow( SwDoc* pDoc, const SwSelBoxes& rBoxes, sal_uInt16 nCnt,
1407     sal_Bool bSameHeight )
1408 {
1409     CHECK_TABLE( *this )
1410     ++nCnt;
1411 	_FndBox aFndBox( 0, 0 );
1412 	aFndBox.SetTableLines( rBoxes, *this );
1413 
1414     if( bSameHeight && pDoc->GetCurrentViewShell() )	//swmod 071108//swmod 071225
1415     {
1416         SwSplitLines aRowLines;
1417         SwSplitLines aSplitLines;
1418         sal_uInt16 nFirst = lcl_CalculateSplitLineHeights( aRowLines, aSplitLines,
1419             *this, rBoxes, nCnt );
1420         aFndBox.DelFrms( *this );
1421         SwTwips nLast = 0;
1422         SwSplitLines::iterator pSplit = aSplitLines.begin();
1423         SwSplitLines::iterator pCurr = aRowLines.begin();
1424         while( pCurr != aRowLines.end() )
1425         {
1426             while( pSplit != aSplitLines.end() && *pSplit < *pCurr )
1427             {
1428                 InsertSpannedRow( pDoc, nFirst, 1 );
1429                 SwTableLine* pRow = GetTabLines()[ nFirst ];
1430                 SwFrmFmt* pRowFmt = pRow->ClaimFrmFmt();
1431                 SwFmtFrmSize aFSz( pRowFmt->GetFrmSize() );
1432                 aFSz.SetHeightSizeType( ATT_MIN_SIZE );
1433                 aFSz.SetHeight( *pSplit - nLast );
1434                 pRowFmt->SetFmtAttr( aFSz );
1435                 nLast = *pSplit;
1436                 ++pSplit;
1437                 ++nFirst;
1438             }
1439             if( pSplit != aSplitLines.end() && *pCurr == *pSplit )
1440                 ++pSplit;
1441             SwTableLine* pRow = GetTabLines()[ nFirst ];
1442             SwFrmFmt* pRowFmt = pRow->ClaimFrmFmt();
1443             SwFmtFrmSize aFSz( pRowFmt->GetFrmSize() );
1444             aFSz.SetHeightSizeType( ATT_MIN_SIZE );
1445             aFSz.SetHeight( *pCurr - nLast );
1446             pRowFmt->SetFmtAttr( aFSz );
1447             nLast = *pCurr;
1448             ++pCurr;
1449             ++nFirst;
1450         }
1451     }
1452     else
1453     {
1454         aFndBox.DelFrms( *this );
1455         bSameHeight = sal_False;
1456     }
1457     if( !bSameHeight )
1458     {
1459         SwLineOffsetArray aLineOffs;
1460         lcl_SophisticatedFillLineIndices( aLineOffs, *this, rBoxes, nCnt );
1461         SwLineOffsetArray::reverse_iterator pCurr( aLineOffs.rbegin() );
1462         while( pCurr != aLineOffs.rend() )
1463         {
1464             InsertSpannedRow( pDoc, pCurr->first, pCurr->second );
1465             ++pCurr;
1466         }
1467     }
1468 
1469     std::set< sal_uInt16> aIndices;
1470     for( sal_uInt16 i = 0; i < rBoxes.Count(); ++i )
1471     {
1472         ASSERT( rBoxes[i]->getRowSpan() != 1, "Forgot to split?" )
1473         if( rBoxes[i]->getRowSpan() > 1 )
1474             aIndices.insert( i );
1475     }
1476 
1477     std::set< sal_uInt16 >::iterator pCurrBox = aIndices.begin();
1478     while( pCurrBox != aIndices.end() )
1479         lcl_UnMerge( *this, *rBoxes[*pCurrBox++], nCnt, bSameHeight );
1480 
1481     CHECK_TABLE( *this )
1482 	//Layout updaten
1483 	aFndBox.MakeFrms( *this );
1484 
1485     return sal_True;
1486 }
1487 
1488 /** SwTable::InsertRow(..) inserts one or more rows before or behind the selected
1489     boxes.
1490 */
1491 
1492 sal_Bool SwTable::InsertRow( SwDoc* pDoc, const SwSelBoxes& rBoxes,
1493 						sal_uInt16 nCnt, sal_Bool bBehind )
1494 {
1495     bool bRet = false;
1496     if( IsNewModel() )
1497     {
1498         CHECK_TABLE( *this )
1499         sal_uInt16 nRowIdx = lcl_LineIndex( *this, rBoxes, bBehind );
1500         if( nRowIdx < USHRT_MAX )
1501         {
1502             _FndBox aFndBox( 0, 0 );
1503             aFndBox.SetTableLines( rBoxes, *this );
1504             aFndBox.DelFrms( *this );
1505 //             aFndBox.SaveChartData( *this );
1506 
1507             bRet = true;
1508             SwTableLine *pLine = GetTabLines()[ nRowIdx ];
1509             SwSelBoxes aLineBoxes;
1510             lcl_FillSelBoxes( aLineBoxes, *pLine );
1511             _InsertRow( pDoc, aLineBoxes, nCnt, bBehind );
1512             sal_uInt16 nBoxCount = pLine->GetTabBoxes().Count();
1513             sal_uInt16 nOfs = bBehind ? 0 : 1;
1514             for( sal_uInt16 n = 0; n < nCnt; ++n )
1515             {
1516                 SwTableLine *pNewLine = GetTabLines()[ nRowIdx+nCnt-n-nOfs];
1517                 for( sal_uInt16 nCurrBox = 0; nCurrBox < nBoxCount; ++nCurrBox )
1518                 {
1519                     long nRowSpan = pLine->GetTabBoxes()[nCurrBox]->getRowSpan();
1520                     if( bBehind )
1521                     {
1522                         if( nRowSpan == 1 || nRowSpan == -1 )
1523                             nRowSpan = n + 1;
1524                         else if( nRowSpan > 1 )
1525                             nRowSpan = - nRowSpan;
1526                     }
1527                     else
1528                     {
1529                         if( nRowSpan > 0 )
1530                             nRowSpan = n + 1;
1531                         else
1532                             --nRowSpan;
1533                     }
1534                     pNewLine->GetTabBoxes()[ nCurrBox ]->setRowSpan( nRowSpan - n );
1535                 }
1536             }
1537             if( bBehind )
1538                 ++nRowIdx;
1539             if( nRowIdx )
1540                 lcl_ChangeRowSpan( *this, nCnt, --nRowIdx, true );
1541             //Layout update
1542             aFndBox.MakeFrms( *this );
1543 //             aFndBox.RestoreChartData( *this );
1544         }
1545         CHECK_TABLE( *this )
1546     }
1547     else
1548         bRet = _InsertRow( pDoc, rBoxes, nCnt, bBehind );
1549     return bRet;
1550 }
1551 
1552 /** SwTable::PrepareDelBoxes(..) adjusts the row span attributes for an upcoming
1553     deletion of table cells and invalidates the layout of these cells.
1554 */
1555 
1556 void SwTable::PrepareDelBoxes( const SwSelBoxes& rBoxes )
1557 {
1558     if( IsNewModel() )
1559     {
1560         for( sal_uInt16 i = 0; i < rBoxes.Count(); ++i )
1561         {
1562             SwTableBox* pBox = rBoxes[i];
1563             long nRowSpan = pBox->getRowSpan();
1564             if( nRowSpan != 1 && pBox->GetFrmFmt()->GetFrmSize().GetWidth() )
1565             {
1566                 long nLeft = lcl_Box2LeftBorder( *pBox );
1567                 SwTableLine *pLine = pBox->GetUpper();
1568                 sal_uInt16 nLinePos = GetTabLines().C40_GETPOS(SwTableLine, pLine);
1569                 ASSERT( nLinePos < USHRT_MAX, "Box/table mismatch" )
1570                 if( nRowSpan > 1 )
1571                 {
1572                     if( ++nLinePos < GetTabLines().Count() )
1573                     {
1574                         pLine = GetTabLines()[ nLinePos ];
1575                         pBox = lcl_LeftBorder2Box( nLeft, pLine );
1576                         ASSERT( pBox, "RowSpan irritation I" )
1577                         if( pBox )
1578                             pBox->setRowSpan( --nRowSpan );
1579                     }
1580                 }
1581                 else if( nLinePos > 0 )
1582                 {
1583                     do
1584                     {
1585                         pLine = GetTabLines()[ --nLinePos ];
1586                         pBox = lcl_LeftBorder2Box( nLeft, pLine );
1587                         ASSERT( pBox, "RowSpan irritation II" )
1588                         if( pBox )
1589                         {
1590                             nRowSpan = pBox->getRowSpan();
1591                             if( nRowSpan > 1 )
1592                             {
1593                                 lcl_InvalidateCellFrm( *pBox );
1594                                 --nRowSpan;
1595                             }
1596                             else
1597                                 ++nRowSpan;
1598                             pBox->setRowSpan( nRowSpan );
1599                         }
1600                         else
1601                             nRowSpan = 1;
1602                     }
1603                     while( nRowSpan < 0 && nLinePos > 0 );
1604                 }
1605             }
1606         }
1607     }
1608 }
1609 
1610 /** lcl_SearchSelBox(..) adds cells of a given table row to the selection structure
1611     if it overlaps with the given x-position range
1612 */
1613 
1614 void lcl_SearchSelBox( const SwTable &rTable, SwSelBoxes& rBoxes, long nMin, long nMax,
1615                        SwTableLine& rLine, bool bChkProtected, bool bColumn )
1616 {
1617     long nLeft = 0;
1618     long nRight = 0;
1619     long nMid = ( nMax + nMin )/ 2;
1620     sal_uInt16 nCount = rLine.GetTabBoxes().Count();
1621     for( sal_uInt16 nCurrBox = 0; nCurrBox < nCount; ++nCurrBox )
1622     {
1623         SwTableBox* pBox = rLine.GetTabBoxes()[nCurrBox];
1624         ASSERT( pBox, "Missing table box" );
1625         long nWidth = pBox->GetFrmFmt()->GetFrmSize().GetWidth();
1626         nRight += nWidth;
1627         if( nRight > nMin )
1628         {
1629             bool bAdd = false;
1630             if( nRight <= nMax )
1631                 bAdd = nLeft >= nMin || nRight >= nMid ||
1632                        nRight - nMin > nMin - nLeft;
1633             else
1634                 bAdd = nLeft <= nMid || nRight - nMax < nMax - nLeft;
1635             long nRowSpan = pBox->getRowSpan();
1636             if( bAdd &&
1637                 //( bColumn || nRowSpan > 0 ) &&
1638                 ( !bChkProtected ||
1639                 !pBox->GetFrmFmt()->GetProtect().IsCntntProtected() ) )
1640             {
1641                 sal_uInt16 nOldCnt = rBoxes.Count();
1642                 rBoxes.Insert( pBox );
1643                 if( bColumn && nRowSpan != 1 && nOldCnt < rBoxes.Count() )
1644                 {
1645                     SwTableBox *pMasterBox = pBox->getRowSpan() > 0 ? pBox
1646                         : &pBox->FindStartOfRowSpan( rTable, USHRT_MAX );
1647                     lcl_getAllMergedBoxes( rTable, rBoxes, *pMasterBox );
1648                 }
1649             }
1650         }
1651         if( nRight >= nMax )
1652             break;
1653         nLeft = nRight;
1654     }
1655 }
1656 
1657 /** void SwTable::CreateSelection(..) fills the selection structure with table cells
1658     for a given SwPaM, ie. start and end position inside a table
1659 */
1660 
1661 void SwTable::CreateSelection(  const SwPaM& rPam, SwSelBoxes& rBoxes,
1662     const SearchType eSearch, bool bChkProtected ) const
1663 {
1664     ASSERT( bNewModel, "Don't call me for old tables" );
1665     if( !aLines.Count() )
1666         return;
1667     const SwNode* pStartNd = rPam.GetPoint()->nNode.GetNode().FindTableBoxStartNode();
1668     const SwNode* pEndNd = rPam.GetMark()->nNode.GetNode().FindTableBoxStartNode();
1669     if( !pStartNd || !pEndNd )
1670         return;
1671     CreateSelection( pStartNd, pEndNd, rBoxes, eSearch, bChkProtected );
1672 }
1673 
1674 /** void SwTable::CreateSelection(..) fills the selection structure with table cells
1675     for given start and end nodes inside a table
1676 */
1677 void SwTable::CreateSelection( const SwNode* pStartNd, const SwNode* pEndNd,
1678     SwSelBoxes& rBoxes, const SearchType eSearch, bool bChkProtected ) const
1679 {
1680     // SwSelBoxes aKeepBoxes;
1681     if( rBoxes.Count() )
1682     {
1683         // aKeepBoxes.Insert( &rBoxes );
1684 		rBoxes.Remove( sal_uInt16(0), rBoxes.Count() );
1685     }
1686     // Looking for start and end of the selection given by SwNode-pointer
1687     sal_uInt16 nLines = aLines.Count();
1688     // nTop becomes the line number of the upper box
1689     // nBottom becomes the line number of the lower box
1690     sal_uInt16 nTop = 0, nBottom = 0;
1691     // nUpperMin becomes the left border value of the upper box
1692     // nUpperMax becomes the right border of the upper box
1693     // nLowerMin and nLowerMax the borders of the lower box
1694     long nUpperMin = 0, nUpperMax = 0;
1695     long nLowerMin = 0, nLowerMax = 0;
1696     // nFound will incremented if a box is found
1697     // 0 => no box found; 1 => the upper box has been found; 2 => both found
1698     int nFound = 0;
1699     for( sal_uInt16 nRow = 0; nFound < 2 && nRow < nLines; ++nRow )
1700     {
1701         SwTableLine* pLine = aLines[nRow];
1702         ASSERT( pLine, "Missing table line" );
1703         sal_uInt16 nCols = pLine->GetTabBoxes().Count();
1704         for( sal_uInt16 nCol = 0; nCol < nCols; ++nCol )
1705         {
1706             SwTableBox* pBox = pLine->GetTabBoxes()[nCol];
1707             ASSERT( pBox, "Missing table box" );
1708             if( pBox->GetSttNd() == pEndNd || pBox->GetSttNd() == pStartNd )
1709             {
1710                 if( !bChkProtected ||
1711                     !pBox->GetFrmFmt()->GetProtect().IsCntntProtected() )
1712                     rBoxes.Insert( pBox );
1713                 if( nFound )
1714                 {
1715                     nBottom = nRow;
1716                     lcl_CheckMinMax( nLowerMin, nLowerMax, *pLine, nCol, true );
1717                     ++nFound;
1718                     break;
1719                 }
1720                 else
1721                 {
1722                     nTop = nRow;
1723                     lcl_CheckMinMax( nUpperMin, nUpperMax, *pLine, nCol, true );
1724                     ++nFound;
1725                      // If start and end node are identical, we're nearly done..
1726                     if( pEndNd == pStartNd )
1727                     {
1728                         nBottom = nTop;
1729                         nLowerMin = nUpperMin;
1730                         nLowerMax = nUpperMax;
1731                         ++nFound;
1732                     }
1733                 }
1734             }
1735         }
1736     }
1737     if( nFound < 2 )
1738         return; // At least one node was not a part of the given table
1739     if( eSearch == SEARCH_ROW )
1740     {
1741         // Selection of a row is quiet easy:
1742         // every (unprotected) box between start and end line
1743         // with a positive row span will be collected
1744         for( sal_uInt16 nRow = nTop; nRow <= nBottom; ++nRow )
1745         {
1746             SwTableLine* pLine = aLines[nRow];
1747             ASSERT( pLine, "Missing table line" );
1748             sal_uInt16 nCount = pLine->GetTabBoxes().Count();
1749             for( sal_uInt16 nCurrBox = 0; nCurrBox < nCount; ++nCurrBox )
1750             {
1751                 SwTableBox* pBox = pLine->GetTabBoxes()[nCurrBox];
1752                 ASSERT( pBox, "Missing table box" );
1753 				if( pBox->getRowSpan() > 0 && ( !bChkProtected ||
1754                     !pBox->GetFrmFmt()->GetProtect().IsCntntProtected() ) )
1755                     rBoxes.Insert( pBox );
1756             }
1757         }
1758         return;
1759     }
1760     bool bCombine = nTop == nBottom;
1761     if( !bCombine )
1762     {
1763         long nMinWidth = nUpperMax - nUpperMin;
1764         long nTmp = nLowerMax - nLowerMin;
1765         if( nMinWidth > nTmp )
1766             nMinWidth = nTmp;
1767         nTmp = nLowerMax < nUpperMax ? nLowerMax : nUpperMax;
1768         nTmp -= ( nLowerMin < nUpperMin ) ? nUpperMin : nLowerMin;
1769         // If the overlapping between upper and lower box is less than half
1770         // of the width (of the smaller cell), bCombine is set,
1771         // e.g. if upper and lower cell are in different columns
1772         bCombine = ( nTmp + nTmp < nMinWidth );
1773     }
1774     if( bCombine )
1775     {
1776         if( nUpperMin < nLowerMin )
1777             nLowerMin = nUpperMin;
1778         else
1779             nUpperMin = nLowerMin;
1780         if( nUpperMax > nLowerMax )
1781             nLowerMax = nUpperMax;
1782         else
1783             nUpperMax = nLowerMax;
1784     }
1785     const bool bColumn = eSearch == SEARCH_COL;
1786     if( bColumn )
1787     {
1788         for( sal_uInt16 i = 0; i < nTop; ++i )
1789             lcl_SearchSelBox( *this, rBoxes, nUpperMin, nUpperMax,
1790                               *aLines[i], bChkProtected, bColumn );
1791     }
1792 
1793     {
1794         long nMin = nUpperMin < nLowerMin ? nUpperMin : nLowerMin;
1795         long nMax = nUpperMax < nLowerMax ? nLowerMax : nUpperMax;
1796         for( sal_uInt16 i = nTop; i <= nBottom; ++i )
1797             lcl_SearchSelBox( *this, rBoxes, nMin, nMax, *aLines[i],
1798                               bChkProtected, bColumn );
1799     }
1800     /* if( nTop + 1 < nBottom )
1801     {
1802         long nInnerMin = nUpperMin < nLowerMin ? nLowerMin : nUpperMin;
1803         long nInnerMax = nUpperMax < nLowerMax ? nUpperMax : nLowerMax;
1804         for( sal_uInt16 i = nTop + 1; i < nBottom; ++i )
1805             lcl_SearchSelBox( *this, rBoxes, nInnerMin, nInnerMax, *aLines[i],
1806                               bChkProtected, bColumn );
1807     }
1808     if( bCombine ) // => nUpperMin == nLowerMin, nUpperMax == nLowerMax
1809     {
1810         if( nBottom > nTop )
1811             lcl_SearchSelBox( *this, rBoxes, nUpperMin, nUpperMax, *aLines[nTop],
1812                               bChkProtected, bColumn );
1813         lcl_SearchSelBox( *this, rBoxes, nLowerMin, nLowerMax, *aLines[nBottom],
1814                           bChkProtected, bColumn );
1815     }
1816     else if( aKeepBoxes.Count() )
1817     {
1818         long nMin = nUpperMin < nLowerMin ? nUpperMin : nLowerMin;
1819         long nMax = nUpperMax < nLowerMax ? nLowerMax : nUpperMax;
1820         SwSelBoxes aCandidates;
1821         for( sal_uInt16 i = nTop; i <= nBottom; ++i )
1822             lcl_SearchSelBox( *this, aCandidates, nMin, nMax, *aLines[i],
1823                               bChkProtected, bColumn );
1824         sal_uInt16 nOld = 0, nNew = 0;
1825         while ( nOld < aKeepBoxes.Count() && nNew < aCandidates.Count() )
1826         {
1827             const SwTableBox* pPOld = *( aKeepBoxes.GetData() + nOld );
1828             SwTableBox* pPNew = *( aCandidates.GetData() + nNew );
1829             if( pPOld == pPNew )
1830             {   // this box will stay
1831                 rBoxes.Insert( pPNew );
1832                 ++nOld;
1833                 ++nNew;
1834             }
1835             else if( pPOld->GetSttIdx() < pPNew->GetSttIdx() )
1836                 ++nOld;
1837             else
1838                 ++nNew;
1839         }
1840     } */
1841     if( bColumn )
1842     {
1843         for( sal_uInt16 i = nBottom + 1; i < nLines; ++i )
1844             lcl_SearchSelBox( *this, rBoxes, nLowerMin, nLowerMax, *aLines[i],
1845                               bChkProtected, true );
1846     }
1847 }
1848 
1849 /** void SwTable::ExpandColumnSelection(..) adds cell to the give selection to
1850     assure that at least one cell of every row is part of the selection.
1851 */
1852 
1853 void SwTable::ExpandColumnSelection( SwSelBoxes& rBoxes, long &rMin, long &rMax ) const
1854 {
1855     ASSERT( bNewModel, "Don't call me for old tables" );
1856     rMin = 0;
1857     rMax = 0;
1858     if( !aLines.Count() || !rBoxes.Count() )
1859         return;
1860 
1861     sal_uInt16 nLineCnt = aLines.Count();
1862     sal_uInt16 nBoxCnt = rBoxes.Count();
1863     sal_uInt16 nBox = 0;
1864     for( sal_uInt16 nRow = 0; nRow < nLineCnt && nBox < nBoxCnt; ++nRow )
1865     {
1866         SwTableLine* pLine = aLines[nRow];
1867         ASSERT( pLine, "Missing table line" );
1868         sal_uInt16 nCols = pLine->GetTabBoxes().Count();
1869         for( sal_uInt16 nCol = 0; nCol < nCols; ++nCol )
1870         {
1871             SwTableBox* pBox = pLine->GetTabBoxes()[nCol];
1872             ASSERT( pBox, "Missing table box" );
1873             if( pBox == rBoxes[nBox] )
1874             {
1875                 lcl_CheckMinMax( rMin, rMax, *pLine, nCol, nBox == 0 );
1876                 if( ++nBox >= nBoxCnt )
1877                     break;
1878             }
1879         }
1880     }
1881     nBox = 0;
1882     for( sal_uInt16 nRow = 0; nRow < nLineCnt; ++nRow )
1883     {
1884         SwTableLine* pLine = aLines[nRow];
1885         sal_uInt16 nCols = pLine->GetTabBoxes().Count();
1886         long nLeft = 0;
1887         long nRight = 0;
1888         for( sal_uInt16 nCurrBox = 0; nCurrBox < nCols; ++nCurrBox )
1889         {
1890             nLeft = nRight;
1891             SwTableBox* pBox = pLine->GetTabBoxes()[nCurrBox];
1892             nRight += pBox->GetFrmFmt()->GetFrmSize().GetWidth();
1893             if( nLeft >= rMin && nRight <= rMax )
1894                 rBoxes.Insert( pBox );
1895         }
1896     }
1897 }
1898 
1899 /** SwTable::PrepareDeleteCol(..) adjusts the widths of the neighbour cells of
1900     a cell selection for an upcoming (column) deletion
1901 */
1902 void SwTable::PrepareDeleteCol( long nMin, long nMax )
1903 {
1904     ASSERT( bNewModel, "Don't call me for old tables" );
1905     if( !aLines.Count() || nMax < nMin )
1906         return;
1907     long nMid = nMin ? ( nMin + nMax ) / 2 : 0;
1908 	const SwTwips nTabSize = GetFrmFmt()->GetFrmSize().GetWidth();
1909     if( nTabSize == nMax )
1910         nMid = nMax;
1911     sal_uInt16 nLineCnt = aLines.Count();
1912     for( sal_uInt16 nRow = 0; nRow < nLineCnt; ++nRow )
1913     {
1914         SwTableLine* pLine = aLines[nRow];
1915         sal_uInt16 nCols = pLine->GetTabBoxes().Count();
1916         long nLeft = 0;
1917         long nRight = 0;
1918         for( sal_uInt16 nCurrBox = 0; nCurrBox < nCols; ++nCurrBox )
1919         {
1920             nLeft = nRight;
1921             SwTableBox* pBox = pLine->GetTabBoxes()[nCurrBox];
1922             nRight += pBox->GetFrmFmt()->GetFrmSize().GetWidth();
1923             if( nRight < nMin )
1924                 continue;
1925             if( nLeft > nMax )
1926                 break;
1927             long nNewWidth = -1;
1928             if( nLeft < nMin )
1929             {
1930                 if( nRight <= nMax )
1931                     nNewWidth = nMid - nLeft;
1932             }
1933             else if( nRight > nMax )
1934                 nNewWidth = nRight - nMid;
1935             else
1936                 nNewWidth = 0;
1937             if( nNewWidth >= 0 )
1938             {
1939                 SwFrmFmt* pFrmFmt = pBox->ClaimFrmFmt();
1940                 SwFmtFrmSize aFrmSz( pFrmFmt->GetFrmSize() );
1941                 aFrmSz.SetWidth( nNewWidth );
1942                 pFrmFmt->SetFmtAttr( aFrmSz );
1943             }
1944         }
1945     }
1946 }
1947 
1948 
1949 
1950 /** SwTable::ExpandSelection(..) adds all boxes to the box selections which are
1951     overlapped by it.
1952 */
1953 
1954 void SwTable::ExpandSelection( SwSelBoxes& rBoxes ) const
1955 {
1956     for( sal_uInt16 i = 0; i < rBoxes.Count(); ++i )
1957     {
1958         SwTableBox *pBox = rBoxes[i];
1959         long nRowSpan = pBox->getRowSpan();
1960         if( nRowSpan != 1 )
1961         {
1962             SwTableBox *pMasterBox = nRowSpan > 0 ? pBox
1963                     : &pBox->FindStartOfRowSpan( *this, USHRT_MAX );
1964             lcl_getAllMergedBoxes( *this, rBoxes, *pMasterBox );
1965         }
1966     }
1967 }
1968 
1969 /** SwTable::CheckRowSpan(..) looks for the next line without an overlapping to
1970     the previous line.
1971 */
1972 
1973 void SwTable::CheckRowSpan( SwTableLinePtr &rpLine, bool bUp ) const
1974 {
1975     ASSERT( IsNewModel(), "Don't call me for old tables" );
1976     sal_uInt16 nLineIdx = GetTabLines().C40_GETPOS( SwTableLine, rpLine );
1977     ASSERT( nLineIdx < GetTabLines().Count(), "Start line out of range" );
1978     bool bChange = true;
1979     if( bUp )
1980     {
1981         while( bChange )
1982         {
1983             bChange = false;
1984             rpLine = GetTabLines()[ nLineIdx ];
1985             sal_uInt16 nCols = rpLine->GetTabBoxes().Count();
1986             for( sal_uInt16 nCol = 0; !bChange && nCol < nCols; ++nCol )
1987             {
1988                 SwTableBox* pBox = rpLine->GetTabBoxes()[nCol];
1989                 if( pBox->getRowSpan() > 1 || pBox->getRowSpan() < -1 )
1990                     bChange = true;
1991             }
1992             if( bChange )
1993             {
1994                 if( nLineIdx )
1995                     --nLineIdx;
1996                 else
1997                 {
1998                     bChange = false;
1999                     rpLine = 0;
2000                 }
2001             }
2002         }
2003     }
2004     else
2005     {
2006         sal_uInt16 nMaxLine = GetTabLines().Count();
2007         while( bChange )
2008         {
2009             bChange = false;
2010             rpLine = GetTabLines()[ nLineIdx ];
2011             sal_uInt16 nCols = rpLine->GetTabBoxes().Count();
2012             for( sal_uInt16 nCol = 0; !bChange && nCol < nCols; ++nCol )
2013             {
2014                 SwTableBox* pBox = rpLine->GetTabBoxes()[nCol];
2015                 if( pBox->getRowSpan() < 0 )
2016                     bChange = true;
2017             }
2018             if( bChange )
2019             {
2020                 ++nLineIdx;
2021                 if( nLineIdx >= nMaxLine )
2022                 {
2023                     bChange = false;
2024                     rpLine = 0;
2025                 }
2026             }
2027         }
2028     }
2029 }
2030 
2031 // This structure corrects the row span attributes for a top line of a table
2032 // In a top line no negative row span is allowed, so these have to be corrected.
2033 // If there has been at least one correction, all values are stored
2034 // and can be used by undo of table split
2035 SwSaveRowSpan::SwSaveRowSpan( SwTableBoxes& rBoxes, sal_uInt16 nSplitLn )
2036     : mnSplitLine( nSplitLn )
2037 {
2038     bool bDontSave = true; // nothing changed, nothing to save
2039     sal_uInt16 nColCount = rBoxes.Count();
2040     ASSERT( nColCount, "Empty Table Line" )
2041     mnRowSpans.resize( nColCount );
2042     for( sal_uInt16 nCurrCol = 0; nCurrCol < nColCount; ++nCurrCol )
2043     {
2044         SwTableBox* pBox = rBoxes[nCurrCol];
2045         ASSERT( pBox, "Missing Table Box" );
2046         long nRowSp = pBox->getRowSpan();
2047         mnRowSpans[ nCurrCol ] = nRowSp;
2048         if( nRowSp < 0 )
2049         {
2050             bDontSave = false;
2051             nRowSp = -nRowSp;
2052             pBox->setRowSpan( nRowSp ); // correction needed
2053         }
2054     }
2055     if( bDontSave )
2056         mnRowSpans.clear();
2057 }
2058 
2059 // This function is called by undo of table split to restore the old row span
2060 // values at the split line
2061 void SwTable::RestoreRowSpan( const SwSaveRowSpan& rSave )
2062 {
2063     if( !IsNewModel() ) // for new model only
2064         return;
2065     sal_uInt16 nLineCount = GetTabLines().Count();
2066     ASSERT( rSave.mnSplitLine < nLineCount, "Restore behind last line?" )
2067     if( rSave.mnSplitLine < nLineCount )
2068     {
2069         SwTableLine* pLine = GetTabLines()[rSave.mnSplitLine];
2070         sal_uInt16 nColCount = pLine->GetTabBoxes().Count();
2071         ASSERT( nColCount, "Empty Table Line" )
2072         ASSERT( nColCount == rSave.mnRowSpans.size(), "Wrong row span store" )
2073         if( nColCount == rSave.mnRowSpans.size() )
2074         {
2075             for( sal_uInt16 nCurrCol = 0; nCurrCol < nColCount; ++nCurrCol )
2076             {
2077                 SwTableBox* pBox = pLine->GetTabBoxes()[nCurrCol];
2078                 ASSERT( pBox, "Missing Table Box" );
2079                 long nRowSp = pBox->getRowSpan();
2080                 if( nRowSp != rSave.mnRowSpans[ nCurrCol ] )
2081                 {
2082                     ASSERT( -nRowSp == rSave.mnRowSpans[ nCurrCol ], "Pardon me?!" )
2083                     ASSERT( rSave.mnRowSpans[ nCurrCol ] < 0, "Pardon me?!" )
2084                     pBox->setRowSpan( -nRowSp );
2085 
2086                     sal_uInt16 nLine = rSave.mnSplitLine;
2087                     if( nLine )
2088                     {
2089                         long nLeftBorder = lcl_Box2LeftBorder( *pBox );
2090                         SwTableBox* pNext;
2091                         do
2092                         {
2093                             pNext = lcl_LeftBorder2Box( nLeftBorder, GetTabLines()[--nLine] );
2094                             if( pNext )
2095                             {
2096                                 pBox = pNext;
2097                                 long nNewSpan = pBox->getRowSpan();
2098                                 if( pBox->getRowSpan() < 1 )
2099                                     nNewSpan -= nRowSp;
2100                                 else
2101                                 {
2102                                     nNewSpan += nRowSp;
2103                                     pNext = 0;
2104                                 }
2105                                 pBox->setRowSpan( nNewSpan );
2106                             }
2107                         } while( nLine && pNext );
2108                     }
2109                 }
2110             }
2111         }
2112     }
2113 }
2114 
2115 SwSaveRowSpan* SwTable::CleanUpTopRowSpan( sal_uInt16 nSplitLine )
2116 {
2117     SwSaveRowSpan* pRet = 0;
2118     if( !IsNewModel() )
2119         return pRet;
2120     pRet = new SwSaveRowSpan( GetTabLines()[0]->GetTabBoxes(), nSplitLine );
2121     if( pRet->mnRowSpans.size() == 0 )
2122     {
2123         delete pRet;
2124         pRet = 0;
2125     }
2126     return pRet;
2127 }
2128 
2129 void SwTable::CleanUpBottomRowSpan( sal_uInt16 nDelLines )
2130 {
2131     if( !IsNewModel() )
2132         return;
2133     sal_uInt16 nLastLine = GetTabLines().Count()-1;
2134     SwTableLine* pLine = GetTabLines()[nLastLine];
2135     sal_uInt16 nColCount = pLine->GetTabBoxes().Count();
2136     ASSERT( nColCount, "Empty Table Line" )
2137     for( sal_uInt16 nCurrCol = 0; nCurrCol < nColCount; ++nCurrCol )
2138     {
2139         SwTableBox* pBox = pLine->GetTabBoxes()[nCurrCol];
2140         ASSERT( pBox, "Missing Table Box" );
2141         long nRowSp = pBox->getRowSpan();
2142         if( nRowSp < 0 )
2143             nRowSp = -nRowSp;
2144         if( nRowSp > 1 )
2145         {
2146             lcl_ChangeRowSpan( *this, -static_cast<long>(nDelLines), nLastLine, false );
2147             break;
2148         }
2149     }
2150 }
2151 
2152 #ifdef DBG_UTIL
2153 
2154 struct RowSpanCheck
2155 {
2156     long nRowSpan;
2157     SwTwips nLeft;
2158     SwTwips nRight;
2159 };
2160 
2161 void SwTable::CheckConsistency() const
2162 {
2163     if( !IsNewModel() )
2164         return;
2165     sal_uInt16 nLineCount = GetTabLines().Count();
2166 	const SwTwips nTabSize = GetFrmFmt()->GetFrmSize().GetWidth();
2167     SwTwips nLineWidth = 0;
2168     std::list< RowSpanCheck > aRowSpanCells;
2169     std::list< RowSpanCheck >::iterator aIter = aRowSpanCells.end();
2170     for( sal_uInt16 nCurrLine = 0; nCurrLine < nLineCount; ++nCurrLine )
2171     {
2172         SwTwips nWidth = 0;
2173         SwTableLine* pLine = GetTabLines()[nCurrLine];
2174         ASSERT( pLine, "Missing Table Line" )
2175         sal_uInt16 nColCount = pLine->GetTabBoxes().Count();
2176         ASSERT( nColCount, "Empty Table Line" )
2177         for( sal_uInt16 nCurrCol = 0; nCurrCol < nColCount; ++nCurrCol )
2178         {
2179             SwTableBox* pBox = pLine->GetTabBoxes()[nCurrCol];
2180             ASSERT( pBox, "Missing Table Box" );
2181             SwTwips nNewWidth = pBox->GetFrmFmt()->GetFrmSize().GetWidth() + nWidth;
2182             long nRowSp = pBox->getRowSpan();
2183             if( nRowSp < 0 )
2184             {
2185                 ASSERT( aIter != aRowSpanCells.end(), "Missing master box" )
2186 #ifdef DBG_UTIL
2187                 //RowSpanCheck &rCheck = *aIter;
2188 #endif
2189                 ASSERT( aIter->nLeft == nWidth && aIter->nRight == nNewWidth,
2190                     "Wrong position/size of overlapped table box" );
2191                 --(aIter->nRowSpan);
2192                 ASSERT( aIter->nRowSpan == -nRowSp, "Wrong row span value" );
2193                 if( nRowSp == -1 )
2194                 {
2195                     std::list< RowSpanCheck >::iterator aEraseIter = aIter;
2196                     ++aIter;
2197                     aRowSpanCells.erase( aEraseIter );
2198                 }
2199                 else
2200                     ++aIter;
2201             }
2202             else if( nRowSp != 1 )
2203             {
2204                 ASSERT( nRowSp, "Zero row span?!" );
2205                 RowSpanCheck aEntry;
2206                 aEntry.nLeft = nWidth;
2207                 aEntry.nRight = nNewWidth;
2208                 aEntry.nRowSpan = nRowSp;
2209                 aRowSpanCells.insert( aIter, aEntry );
2210             }
2211             nWidth = nNewWidth;
2212         }
2213         if( !nCurrLine )
2214             nLineWidth = nWidth;
2215         ASSERT( nWidth == nLineWidth, "Different Line Widths" )
2216         ASSERT( nWidth == nTabSize, "Boxen der Line zu klein/gross" )
2217         ASSERT( nWidth >= 0 && nWidth <= USHRT_MAX, "Width out of range" )
2218         ASSERT( aIter == aRowSpanCells.end(), "Missing overlapped box" )
2219         aIter = aRowSpanCells.begin();
2220     }
2221     bool bEmpty = aRowSpanCells.empty();
2222     ASSERT( bEmpty, "Open row span detected" )
2223 }
2224 
2225 #endif
2226 
2227 
2228 #ifdef FINDSTARTENDOFROWSPANCACHE
2229 /*
2230  * A small optimization for FindStartEndOfRowSpan START
2231  *
2232  * NOTE: Results of some measurement revealed that this cache
2233  * does not improve performance!
2234  */
2235 
2236 class SwFindRowSpanCache
2237 {
2238 private:
2239 
2240     struct SwFindRowSpanCacheObj
2241     {
2242         const SwTableBox* mpKeyBox;
2243         const SwTableBox* mpCacheBox;
2244         sal_uInt16 mnSteps;
2245         bool mbStart;
2246 
2247         SwFindRowSpanCacheObj( const SwTableBox& rKeyBox, const SwTableBox& rCacheBox, sal_uInt16 nSteps, bool bStart ) :
2248             mpKeyBox( &rKeyBox ), mpCacheBox( &rCacheBox ), mnSteps( nSteps ), mbStart( bStart ) {}
2249     };
2250 
2251     std::list< SwFindRowSpanCacheObj > aCache;
2252     bool mbUseCache;
2253     static SwFindRowSpanCache* mpFindRowSpanCache;
2254     SwFindRowSpanCache();
2255 
2256 public:
2257 
2258     static SwFindRowSpanCache& getSwFindRowSpanCache();
2259     const SwTableBox* FindCachedStartEndOfRowSpan( const SwTableBox& rKeyBox, sal_uInt16 nSteps, bool bStart );
2260     void SetCachedStartEndOfRowSpan( const SwTableBox& rKeyBox, const SwTableBox& rCacheBox, sal_uInt16 nSteps, bool bStart );
2261     void SetUseCache( bool bNew );
2262 };
2263 
2264 SwFindRowSpanCache* SwFindRowSpanCache::mpFindRowSpanCache = 0;
2265 SwFindRowSpanCache& SwFindRowSpanCache::getSwFindRowSpanCache()
2266 {
2267     if ( !mpFindRowSpanCache ) mpFindRowSpanCache = new SwFindRowSpanCache;
2268     return *mpFindRowSpanCache;
2269 }
2270 
2271 SwFindRowSpanCache::SwFindRowSpanCache() : mbUseCache( false )
2272 {
2273 }
2274 
2275 void SwFindRowSpanCache::SetUseCache( bool bNew )
2276 {
2277     mbUseCache = bNew; aCache.clear();
2278 }
2279 
2280 const SwTableBox* SwFindRowSpanCache::FindCachedStartEndOfRowSpan( const SwTableBox& rKeyBox,
2281                                                                    sal_uInt16 nSteps,
2282                                                                    bool bStart )
2283 {
2284     static nCallCount = 0;
2285     static nSuccessCount = 0;
2286     ++nCallCount;
2287 
2288     if ( !mbUseCache ) return 0;
2289 
2290     const SwTableBox* pRet = 0;
2291 
2292     std::list< SwFindRowSpanCacheObj >::const_iterator aIter;
2293     for ( aIter = aCache.begin(); aIter != aCache.end(); ++aIter )
2294     {
2295         if ( aIter->mpKeyBox == &rKeyBox &&
2296              aIter->mnSteps == nSteps &&
2297              aIter->mbStart == bStart )
2298         {
2299             pRet = aIter->mpCacheBox;
2300             ++nSuccessCount;
2301             break;
2302         }
2303     }
2304 
2305     return pRet;
2306 }
2307 
2308 const int FindBoxCacheSize = 2;
2309 
2310 void SwFindRowSpanCache::SetCachedStartEndOfRowSpan( const SwTableBox& rKeyBox,
2311                                                      const SwTableBox& rCacheBox,
2312                                                      sal_uInt16 nSteps,
2313                                                      bool bStart )
2314 {
2315     if ( !mbUseCache ) return;
2316 
2317     const SwFindRowSpanCacheObj aNew( rKeyBox, rCacheBox, nSteps, bStart );
2318     aCache.push_front( aNew );
2319     if ( aCache.size() > FindBoxCacheSize )
2320         aCache.pop_back();
2321 }
2322 
2323 /*
2324  * A small optimization for FindStartEndOfRowSpan END
2325  */
2326 
2327 #endif
2328 
2329