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