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 found 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 chekc 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 rectangulare 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 Good-Bye.." );
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 found 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 modifices 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 appropiate 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(..) retruns 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 containig
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 //Layout updaten
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 //Layout update
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, ie. 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