1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
4 *
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 */
19
20 #include <sal/config.h>
21 #include <sal/log.hxx>
22
23 #include <algorithm>
24 #include <memory>
25
26 #include <fmtornt.hxx>
27 #include <fmtfsize.hxx>
28 #include <frmfmt.hxx>
29 #include <ndtxt.hxx>
30 #include <doc.hxx>
31 #include <IDocumentLayoutAccess.hxx>
32 #include <swtable.hxx>
33 #include <rootfrm.hxx>
34 #include <flyfrm.hxx>
35 #include <poolfmt.hxx>
36 #include <viewsh.hxx>
37 #include <tabfrm.hxx>
38 #include <viewopt.hxx>
39 #include <htmltbl.hxx>
40 #include <calbck.hxx>
41 #include <o3tl/numeric.hxx>
42 #ifdef DBG_UTIL
43 #include <tblrwcl.hxx>
44 #endif
45
46 using namespace ::com::sun::star;
47
48 #define COLFUZZY 20
49 #define MAX_TABWIDTH (USHRT_MAX - 2001)
50
51 class SwHTMLTableLayoutConstraints
52 {
53 sal_uInt16 const nRow; // start row
54 sal_uInt16 const nCol; // start column
55 sal_uInt16 const nColSpan; // the column's COLSPAN
56
57 std::unique_ptr<SwHTMLTableLayoutConstraints> pNext; // the next constraint
58
59 sal_uLong nMinNoAlign, nMaxNoAlign; // provisional result of AL-Pass 1
60
61 public:
62 SwHTMLTableLayoutConstraints( sal_uLong nMin, sal_uLong nMax, sal_uInt16 nRow,
63 sal_uInt16 nCol, sal_uInt16 nColSp );
64
GetMinNoAlign() const65 sal_uLong GetMinNoAlign() const { return nMinNoAlign; }
GetMaxNoAlign() const66 sal_uLong GetMaxNoAlign() const { return nMaxNoAlign; }
67
68 SwHTMLTableLayoutConstraints *InsertNext( SwHTMLTableLayoutConstraints *pNxt );
GetNext() const69 SwHTMLTableLayoutConstraints* GetNext() const { return pNext.get(); }
70
GetColSpan() const71 sal_uInt16 GetColSpan() const { return nColSpan; }
GetColumn() const72 sal_uInt16 GetColumn() const { return nCol; }
73 };
74
SwHTMLTableLayoutCnts(const SwStartNode * pSttNd,std::shared_ptr<SwHTMLTableLayout> const & rTab,bool bNoBrTag,std::shared_ptr<SwHTMLTableLayoutCnts> const & rNxt)75 SwHTMLTableLayoutCnts::SwHTMLTableLayoutCnts(const SwStartNode *pSttNd,
76 std::shared_ptr<SwHTMLTableLayout> const& rTab,
77 bool bNoBrTag,
78 std::shared_ptr<SwHTMLTableLayoutCnts> const& rNxt ) :
79 xNext( rNxt ), pBox( nullptr ), xTable( rTab ), pStartNode( pSttNd ),
80 nPass1Done( 0 ), nWidthSet( 0 ), bNoBreakTag( bNoBrTag )
81 {}
82
GetStartNode() const83 const SwStartNode *SwHTMLTableLayoutCnts::GetStartNode() const
84 {
85 return pBox ? pBox->GetSttNd() : pStartNode;
86 }
87
SwHTMLTableLayoutCell(std::shared_ptr<SwHTMLTableLayoutCnts> const & rCnts,sal_uInt16 nRSpan,sal_uInt16 nCSpan,sal_uInt16 nWidth,bool bPrcWidth,bool bNWrapOpt)88 SwHTMLTableLayoutCell::SwHTMLTableLayoutCell(std::shared_ptr<SwHTMLTableLayoutCnts> const& rCnts,
89 sal_uInt16 nRSpan, sal_uInt16 nCSpan,
90 sal_uInt16 nWidth, bool bPrcWidth,
91 bool bNWrapOpt ) :
92 xContents(rCnts),
93 nRowSpan( nRSpan ), nColSpan( nCSpan ),
94 nWidthOption( nWidth ), bPrcWidthOption( bPrcWidth ),
95 bNoWrapOption( bNWrapOpt )
96 {}
97
SwHTMLTableLayoutColumn(sal_uInt16 nWidth,bool bRelWidth,bool bLBorder)98 SwHTMLTableLayoutColumn::SwHTMLTableLayoutColumn( sal_uInt16 nWidth,
99 bool bRelWidth,
100 bool bLBorder ) :
101 nMinNoAlign(MINLAY), nMaxNoAlign(MINLAY), nAbsMinNoAlign(MINLAY),
102 nMin(0), nMax(0),
103 nAbsColWidth(0), nRelColWidth(0),
104 nWidthOption( nWidth ), bRelWidthOption( bRelWidth ),
105 bLeftBorder( bLBorder )
106 {}
107
SwHTMLTableLayoutConstraints(sal_uLong nMin,sal_uLong nMax,sal_uInt16 nRw,sal_uInt16 nColumn,sal_uInt16 nColSp)108 SwHTMLTableLayoutConstraints::SwHTMLTableLayoutConstraints(
109 sal_uLong nMin, sal_uLong nMax, sal_uInt16 nRw, sal_uInt16 nColumn, sal_uInt16 nColSp ):
110 nRow( nRw ), nCol( nColumn ), nColSpan( nColSp ),
111 nMinNoAlign( nMin ), nMaxNoAlign( nMax )
112 {}
113
InsertNext(SwHTMLTableLayoutConstraints * pNxt)114 SwHTMLTableLayoutConstraints *SwHTMLTableLayoutConstraints::InsertNext(
115 SwHTMLTableLayoutConstraints *pNxt )
116 {
117 SwHTMLTableLayoutConstraints *pPrev = nullptr;
118 SwHTMLTableLayoutConstraints *pConstr = this;
119 while( pConstr )
120 {
121 if( pConstr->nRow > pNxt->nRow ||
122 pConstr->GetColumn() > pNxt->GetColumn() )
123 break;
124 pPrev = pConstr;
125 pConstr = pConstr->GetNext();
126 }
127
128 if( pPrev )
129 {
130 pNxt->pNext = std::move(pPrev->pNext);
131 pPrev->pNext.reset( pNxt );
132 pConstr = this;
133 }
134 else
135 {
136 pNxt->pNext.reset( this );
137 pConstr = pNxt;
138 }
139
140 return pConstr;
141 }
142
SwHTMLTableLayout(const SwTable * pTable,sal_uInt16 nRws,sal_uInt16 nCls,bool bColsOpt,bool bColTgs,sal_uInt16 nWdth,bool bPrcWdth,sal_uInt16 nBorderOpt,sal_uInt16 nCellPad,sal_uInt16 nCellSp,SvxAdjust eAdjust,sal_uInt16 nLMargin,sal_uInt16 nRMargin,sal_uInt16 nBWidth,sal_uInt16 nLeftBWidth,sal_uInt16 nRightBWidth)143 SwHTMLTableLayout::SwHTMLTableLayout( const SwTable * pTable,
144 sal_uInt16 nRws, sal_uInt16 nCls,
145 bool bColsOpt, bool bColTgs,
146 sal_uInt16 nWdth, bool bPrcWdth,
147 sal_uInt16 nBorderOpt, sal_uInt16 nCellPad,
148 sal_uInt16 nCellSp, SvxAdjust eAdjust,
149 sal_uInt16 nLMargin, sal_uInt16 nRMargin,
150 sal_uInt16 nBWidth, sal_uInt16 nLeftBWidth,
151 sal_uInt16 nRightBWidth )
152 : m_aColumns( nCls )
153 , m_aCells( static_cast<size_t>(nRws)*nCls )
154 , m_pSwTable( pTable )
155 , m_nMin( 0 )
156 , m_nMax( 0 )
157 , m_nRows( nRws )
158 , m_nCols( nCls )
159 , m_nLeftMargin( nLMargin )
160 , m_nRightMargin( nRMargin )
161 , m_nInhAbsLeftSpace( 0 )
162 , m_nInhAbsRightSpace( 0 )
163 , m_nRelLeftFill( 0 )
164 , m_nRelRightFill( 0 )
165 , m_nRelTabWidth( 0 )
166 , m_nWidthOption( nWdth )
167 , m_nCellPadding( nCellPad )
168 , m_nCellSpacing( nCellSp )
169 , m_nBorder( nBorderOpt )
170 , m_nLeftBorderWidth( nLeftBWidth )
171 , m_nRightBorderWidth( nRightBWidth )
172 , m_nInhLeftBorderWidth( 0 )
173 , m_nInhRightBorderWidth( 0 )
174 , m_nBorderWidth( nBWidth )
175 , m_nDelayedResizeAbsAvail( 0 )
176 , m_nLastResizeAbsAvail( 0 )
177 , m_nPass1Done( 0 )
178 , m_nWidthSet( 0 )
179 , m_eTableAdjust( eAdjust )
180 , m_bColsOption( bColsOpt )
181 , m_bColTags( bColTgs )
182 , m_bPrcWidthOption( bPrcWdth )
183 , m_bUseRelWidth( false )
184 , m_bMustResize( true )
185 , m_bExportable( true )
186 , m_bBordersChanged( false )
187 , m_bMayBeInFlyFrame( false )
188 , m_bDelayedResizeRecalc( false)
189 , m_bMustNotResize( false )
190 , m_bMustNotRecalc( false )
191 {
192 m_aResizeTimer.SetInvokeHandler( LINK( this, SwHTMLTableLayout,
193 DelayedResize_Impl ) );
194 }
195
~SwHTMLTableLayout()196 SwHTMLTableLayout::~SwHTMLTableLayout()
197 {
198 }
199
200 /// The border widths are calculated like in Netscape:
201 /// Outer border: BORDER + CELLSPACING + CELLPADDING
202 /// Inner border: CELLSPACING + CELLPADDING
203 /// However, we respect the border widths in SW if bSwBorders is set,
204 /// so that we don't wrap wrongly.
205 /// We also need to respect the distance to the content. Even if
206 /// only the opposite side has a border.
GetLeftCellSpace(sal_uInt16 nCol,sal_uInt16 nColSpan,bool bSwBorders) const207 sal_uInt16 SwHTMLTableLayout::GetLeftCellSpace( sal_uInt16 nCol, sal_uInt16 nColSpan,
208 bool bSwBorders ) const
209 {
210 sal_uInt16 nSpace = m_nCellSpacing + m_nCellPadding;
211
212 if( nCol == 0 )
213 {
214 nSpace = nSpace + m_nBorder;
215
216 if( bSwBorders && nSpace < m_nLeftBorderWidth )
217 nSpace = m_nLeftBorderWidth;
218 }
219 else if( bSwBorders )
220 {
221 if( GetColumn(nCol)->HasLeftBorder() )
222 {
223 if( nSpace < m_nBorderWidth )
224 nSpace = m_nBorderWidth;
225 }
226 else if( nCol+nColSpan == m_nCols && m_nRightBorderWidth &&
227 nSpace < MIN_BORDER_DIST )
228 {
229 OSL_ENSURE( !m_nCellPadding, "GetLeftCellSpace: CELLPADDING!=0" );
230 // If the opposite side has a border we need to respect at
231 // least the minimum distance to the content.
232 // Additionally, we could also use nCellPadding for this.
233 nSpace = MIN_BORDER_DIST;
234 }
235 }
236
237 return nSpace;
238 }
239
GetRightCellSpace(sal_uInt16 nCol,sal_uInt16 nColSpan,bool bSwBorders) const240 sal_uInt16 SwHTMLTableLayout::GetRightCellSpace( sal_uInt16 nCol, sal_uInt16 nColSpan,
241 bool bSwBorders ) const
242 {
243 sal_uInt16 nSpace = m_nCellPadding;
244
245 if( nCol+nColSpan == m_nCols )
246 {
247 nSpace += m_nBorder + m_nCellSpacing;
248 if( bSwBorders && nSpace < m_nRightBorderWidth )
249 nSpace = m_nRightBorderWidth;
250 }
251 else if( bSwBorders && GetColumn(nCol)->HasLeftBorder() &&
252 nSpace < MIN_BORDER_DIST )
253 {
254 OSL_ENSURE( !m_nCellPadding, "GetRightCellSpace: CELLPADDING!=0" );
255 // If the opposite side has a border we need to respect at
256 // least the minimum distance to the content.
257 // Additionally, we could also use nCellPadding for this.
258 nSpace = MIN_BORDER_DIST;
259 }
260
261 return nSpace;
262 }
263
AddBorderWidth(sal_uLong & rMin,sal_uLong & rMax,sal_uLong & rAbsMin,sal_uInt16 nCol,sal_uInt16 nColSpan,bool bSwBorders) const264 void SwHTMLTableLayout::AddBorderWidth( sal_uLong &rMin, sal_uLong &rMax,
265 sal_uLong &rAbsMin,
266 sal_uInt16 nCol, sal_uInt16 nColSpan,
267 bool bSwBorders ) const
268 {
269 sal_uLong nAdd = GetLeftCellSpace( nCol, nColSpan, bSwBorders ) +
270 GetRightCellSpace( nCol, nColSpan, bSwBorders );
271
272 rMin += nAdd;
273 rMax += nAdd;
274 rAbsMin += nAdd;
275 }
276
SetBoxWidth(SwTableBox * pBox,sal_uInt16 nCol,sal_uInt16 nColSpan) const277 void SwHTMLTableLayout::SetBoxWidth( SwTableBox *pBox, sal_uInt16 nCol,
278 sal_uInt16 nColSpan ) const
279 {
280 SwFrameFormat *pFrameFormat = pBox->GetFrameFormat();
281
282 // calculate the box's width
283 SwTwips nFrameWidth = 0;
284 while( nColSpan-- )
285 nFrameWidth += GetColumn( nCol++ )->GetRelColWidth();
286
287 // and reset
288 pFrameFormat->SetFormatAttr( SwFormatFrameSize( ATT_VAR_SIZE, nFrameWidth, 0 ));
289 }
290
GetAvail(sal_uInt16 nCol,sal_uInt16 nColSpan,sal_uInt16 & rAbsAvail,sal_uInt16 & rRelAvail) const291 void SwHTMLTableLayout::GetAvail( sal_uInt16 nCol, sal_uInt16 nColSpan,
292 sal_uInt16& rAbsAvail, sal_uInt16& rRelAvail ) const
293 {
294 rAbsAvail = 0;
295 rRelAvail = 0;
296 for( sal_uInt16 i=nCol; i<nCol+nColSpan;i++ )
297 {
298 const SwHTMLTableLayoutColumn *pColumn = GetColumn(i);
299 rAbsAvail = rAbsAvail + pColumn->GetAbsColWidth();
300 rRelAvail = rRelAvail + pColumn->GetRelColWidth();
301 }
302 }
303
GetBrowseWidthByVisArea(const SwDoc & rDoc)304 sal_uInt16 SwHTMLTableLayout::GetBrowseWidthByVisArea( const SwDoc& rDoc )
305 {
306 SwViewShell const *pVSh = rDoc.getIDocumentLayoutAccess().GetCurrentViewShell();
307 if( pVSh )
308 {
309 return static_cast<sal_uInt16>(pVSh->GetBrowseWidth());
310 }
311
312 return 0;
313 }
314
GetBrowseWidth(const SwDoc & rDoc)315 sal_uInt16 SwHTMLTableLayout::GetBrowseWidth( const SwDoc& rDoc )
316 {
317 // If we have a layout, we can get the width from there.
318 const SwRootFrame *pRootFrame = rDoc.getIDocumentLayoutAccess().GetCurrentLayout();
319 if( pRootFrame )
320 {
321 const SwFrame *pPageFrame = pRootFrame->GetLower();
322 if( pPageFrame )
323 return static_cast<sal_uInt16>(pPageFrame->getFramePrintArea().Width());
324 }
325
326 // #i91658#
327 // Assertion removed which state that no browse width is available.
328 // Investigation reveals that all calls can handle the case that no browse
329 // width is provided.
330 return GetBrowseWidthByVisArea( rDoc );
331 }
332
GetBrowseWidthByTabFrame(const SwTabFrame & rTabFrame) const333 sal_uInt16 SwHTMLTableLayout::GetBrowseWidthByTabFrame(
334 const SwTabFrame& rTabFrame ) const
335 {
336 SwTwips nWidth = 0;
337
338 const SwFrame *pUpper = rTabFrame.GetUpper();
339 if( MayBeInFlyFrame() && pUpper->IsFlyFrame() &&
340 static_cast<const SwFlyFrame *>(pUpper)->GetAnchorFrame() )
341 {
342 // If the table is located within a self-created frame, the anchor's
343 // width is relevant not the frame's width.
344 // For paragraph-bound frames we don't respect paragraph indents.
345 const SwFrame *pAnchor = static_cast<const SwFlyFrame *>(pUpper)->GetAnchorFrame();
346 if( pAnchor->IsTextFrame() )
347 nWidth = pAnchor->getFrameArea().Width();
348 else
349 nWidth = pAnchor->getFramePrintArea().Width();
350 }
351 else
352 {
353 nWidth = pUpper->getFramePrintArea().Width();
354 }
355
356 SwTwips nUpperDummy = 0;
357 long nRightOffset = 0,
358 nLeftOffset = 0;
359 rTabFrame.CalcFlyOffsets( nUpperDummy, nLeftOffset, nRightOffset );
360 nWidth -= (nLeftOffset + nRightOffset);
361
362 return static_cast<sal_uInt16>(std::min(nWidth, SwTwips(SAL_MAX_UINT16)));
363 }
364
GetBrowseWidthByTable(const SwDoc & rDoc) const365 sal_uInt16 SwHTMLTableLayout::GetBrowseWidthByTable( const SwDoc& rDoc ) const
366 {
367 sal_uInt16 nBrowseWidth = 0;
368 SwTabFrame* pFrame = SwIterator<SwTabFrame,SwFormat>( *m_pSwTable->GetFrameFormat() ).First();
369 if( pFrame )
370 {
371 nBrowseWidth = GetBrowseWidthByTabFrame( *pFrame );
372 }
373 else
374 {
375 nBrowseWidth = SwHTMLTableLayout::GetBrowseWidth( rDoc );
376 }
377
378 return nBrowseWidth;
379 }
380
GetAnyBoxStartNode() const381 const SwStartNode *SwHTMLTableLayout::GetAnyBoxStartNode() const
382 {
383 const SwStartNode *pBoxSttNd;
384
385 const SwTableBox* pBox = m_pSwTable->GetTabLines()[0]->GetTabBoxes()[0];
386 while( nullptr == (pBoxSttNd = pBox->GetSttNd()) )
387 {
388 OSL_ENSURE( !pBox->GetTabLines().empty(),
389 "Box without start node and lines" );
390 OSL_ENSURE( !pBox->GetTabLines().front()->GetTabBoxes().empty(),
391 "Line without boxes" );
392 pBox = pBox->GetTabLines().front()->GetTabBoxes().front();
393 }
394
395 return pBoxSttNd;
396 }
397
FindFlyFrameFormat() const398 SwFrameFormat *SwHTMLTableLayout::FindFlyFrameFormat() const
399 {
400 const SwTableNode *pTableNd = GetAnyBoxStartNode()->FindTableNode();
401 OSL_ENSURE( pTableNd, "No Table-Node?" );
402 return pTableNd->GetFlyFormat();
403 }
404
lcl_GetMinMaxSize(sal_uLong & rMinNoAlignCnts,sal_uLong & rMaxNoAlignCnts,sal_uLong & rAbsMinNoAlignCnts,SwTextNode const * pTextNd,sal_uLong nIdx,bool bNoBreak)405 static void lcl_GetMinMaxSize( sal_uLong& rMinNoAlignCnts, sal_uLong& rMaxNoAlignCnts,
406 sal_uLong& rAbsMinNoAlignCnts,
407 SwTextNode const *pTextNd, sal_uLong nIdx, bool bNoBreak )
408 {
409 pTextNd->GetMinMaxSize( nIdx, rMinNoAlignCnts, rMaxNoAlignCnts,
410 rAbsMinNoAlignCnts );
411 OSL_ENSURE( rAbsMinNoAlignCnts <= rMinNoAlignCnts,
412 "GetMinMaxSize: absmin > min" );
413 OSL_ENSURE( rMinNoAlignCnts <= rMaxNoAlignCnts,
414 "GetMinMaxSize: max > min" );
415
416 // The maximal width for a <PRE> paragraph is the minimal width
417 const SwFormatColl *pColl = &pTextNd->GetAnyFormatColl();
418 while( pColl && !pColl->IsDefault() &&
419 (USER_FMT & pColl->GetPoolFormatId()) )
420 {
421 pColl = static_cast<const SwFormatColl *>(pColl->DerivedFrom());
422 }
423
424 // <NOBR> in the whole cell apply to text but not to tables.
425 // Netscape only considers this for graphics.
426 if( (pColl && RES_POOLCOLL_HTML_PRE==pColl->GetPoolFormatId()) || bNoBreak )
427 {
428 rMinNoAlignCnts = rMaxNoAlignCnts;
429 rAbsMinNoAlignCnts = rMaxNoAlignCnts;
430 }
431 }
432
AutoLayoutPass1()433 void SwHTMLTableLayout::AutoLayoutPass1()
434 {
435 m_nPass1Done++;
436
437 m_nMin = m_nMax = 0; // clear pass1 info
438
439 bool bFixRelWidths = false;
440 sal_uInt16 i;
441
442 std::unique_ptr<SwHTMLTableLayoutConstraints> xConstraints;
443
444 for( i=0; i<m_nCols; i++ )
445 {
446 SwHTMLTableLayoutColumn *pColumn = GetColumn( i );
447 pColumn->ClearPass1Info( !HasColTags() );
448 sal_uInt16 nMinColSpan = USHRT_MAX; // Column count to which the calculated width refers to
449 sal_uInt16 nColSkip = USHRT_MAX; // How many columns need to be skipped
450
451 for( sal_uInt16 j=0; j<m_nRows; j++ )
452 {
453 SwHTMLTableLayoutCell *pCell = GetCell(j,i);
454 SwHTMLTableLayoutCnts *pCnts = pCell->GetContents().get();
455
456 // We need to examine all rows in order to
457 // get the column that should be calculated next.
458 sal_uInt16 nColSpan = pCell->GetColSpan();
459 if( nColSpan < nColSkip )
460 nColSkip = nColSpan;
461
462 if( !pCnts || !pCnts->IsPass1Done(m_nPass1Done) )
463 {
464 // The cell is empty or it's content was not edited
465 if( nColSpan < nMinColSpan )
466 nMinColSpan = nColSpan;
467
468 sal_uLong nMinNoAlignCell = 0;
469 sal_uLong nMaxNoAlignCell = 0;
470 sal_uLong nAbsMinNoAlignCell = 0;
471 sal_uLong nMaxTableCell = 0;
472 sal_uLong nAbsMinTableCell = 0;
473
474 while( pCnts )
475 {
476 const SwStartNode *pSttNd = pCnts->GetStartNode();
477 if( pSttNd )
478 {
479 const SwDoc *pDoc = pSttNd->GetDoc();
480 sal_uLong nIdx = pSttNd->GetIndex();
481 while( !(pDoc->GetNodes()[nIdx])->IsEndNode() )
482 {
483 SwTextNode *pTextNd = (pDoc->GetNodes()[nIdx])->GetTextNode();
484 if( pTextNd )
485 {
486 sal_uLong nMinNoAlignCnts = 0;
487 sal_uLong nMaxNoAlignCnts = 0;
488 sal_uLong nAbsMinNoAlignCnts = 0;
489
490 lcl_GetMinMaxSize( nMinNoAlignCnts,
491 nMaxNoAlignCnts,
492 nAbsMinNoAlignCnts,
493 pTextNd, nIdx,
494 pCnts->HasNoBreakTag() );
495
496 if( nMinNoAlignCnts > nMinNoAlignCell )
497 nMinNoAlignCell = nMinNoAlignCnts;
498 if( nMaxNoAlignCnts > nMaxNoAlignCell )
499 nMaxNoAlignCell = nMaxNoAlignCnts;
500 if( nAbsMinNoAlignCnts > nAbsMinNoAlignCell )
501 nAbsMinNoAlignCell = nAbsMinNoAlignCnts;
502 }
503 else
504 {
505 SwTableNode *pTabNd = (pDoc->GetNodes()[nIdx])->GetTableNode();
506 if( pTabNd )
507 {
508 SwHTMLTableLayout *pChild = pTabNd->GetTable().GetHTMLTableLayout();
509 if( pChild )
510 {
511 pChild->AutoLayoutPass1();
512 sal_uLong nMaxTableCnts = pChild->m_nMax;
513 sal_uLong nAbsMinTableCnts = pChild->m_nMin;
514
515 // A fixed table width is taken over as minimum and
516 // maximum at the same time
517 if( !pChild->m_bPrcWidthOption && pChild->m_nWidthOption )
518 {
519 sal_uLong nTabWidth = pChild->m_nWidthOption;
520 if( nTabWidth >= nAbsMinTableCnts )
521 {
522 nMaxTableCnts = nTabWidth;
523 nAbsMinTableCnts = nTabWidth;
524 }
525 else
526 {
527 nMaxTableCnts = nAbsMinTableCnts;
528 }
529 }
530
531 if( nMaxTableCnts > nMaxTableCell )
532 nMaxTableCell = nMaxTableCnts;
533 if( nAbsMinTableCnts > nAbsMinTableCell )
534 nAbsMinTableCell = nAbsMinTableCnts;
535 }
536 nIdx = pTabNd->EndOfSectionNode()->GetIndex();
537 }
538 }
539 nIdx++;
540 }
541 }
542 else if (SwHTMLTableLayout *pChild = pCnts->GetTable())
543 {
544 OSL_ENSURE( false, "Sub tables in HTML import?" );
545 pChild->AutoLayoutPass1();
546 sal_uLong nMaxTableCnts = pChild->m_nMax;
547 sal_uLong nAbsMinTableCnts = pChild->m_nMin;
548
549 // A fixed table width is taken over as minimum and
550 // maximum at the same time
551 if( !pChild->m_bPrcWidthOption && pChild->m_nWidthOption )
552 {
553 sal_uLong nTabWidth = pChild->m_nWidthOption;
554 if( nTabWidth >= nAbsMinTableCnts )
555 {
556 nMaxTableCnts = nTabWidth;
557 nAbsMinTableCnts = nTabWidth;
558 }
559 else
560 {
561 nMaxTableCnts = nAbsMinTableCnts;
562 }
563 }
564
565 if( nMaxTableCnts > nMaxTableCell )
566 nMaxTableCell = nMaxTableCnts;
567 if( nAbsMinTableCnts > nAbsMinTableCell )
568 nAbsMinTableCell = nAbsMinTableCnts;
569 }
570 pCnts->SetPass1Done( m_nPass1Done );
571 pCnts = pCnts->GetNext().get();
572 }
573
574 // This code previously came after AddBorderWidth
575 // If a table's width is wider in a cell than what we've calculated
576 // for the other content we need to use the table's width.
577 if( nMaxTableCell > nMaxNoAlignCell )
578 nMaxNoAlignCell = nMaxTableCell;
579 if( nAbsMinTableCell > nAbsMinNoAlignCell )
580 {
581 nAbsMinNoAlignCell = nAbsMinTableCell;
582 if( nMinNoAlignCell < nAbsMinNoAlignCell )
583 nMinNoAlignCell = nAbsMinNoAlignCell;
584 if( nMaxNoAlignCell < nMinNoAlignCell )
585 nMaxNoAlignCell = nMinNoAlignCell;
586 }
587 // This code previously came after AddBorderWidth
588
589 bool bRelWidth = pCell->IsPrcWidthOption();
590 sal_uInt16 nWidth = pCell->GetWidthOption();
591
592 // A NOWRAP option applies to text and tables, but is
593 // not applied for fixed cell width.
594 // Instead, the stated cell width behaves like a minimal
595 // width.
596 if( pCell->HasNoWrapOption() )
597 {
598 if( nWidth==0 || bRelWidth )
599 {
600 nMinNoAlignCell = nMaxNoAlignCell;
601 nAbsMinNoAlignCell = nMaxNoAlignCell;
602 }
603 else
604 {
605 if( nWidth>nMinNoAlignCell )
606 nMinNoAlignCell = nWidth;
607 if( nWidth>nAbsMinNoAlignCell )
608 nAbsMinNoAlignCell = nWidth;
609 }
610 }
611
612 // Respect minimum width for content
613 if( nMinNoAlignCell < MINLAY )
614 nMinNoAlignCell = MINLAY;
615 if( nMaxNoAlignCell < MINLAY )
616 nMaxNoAlignCell = MINLAY;
617 if( nAbsMinNoAlignCell < MINLAY )
618 nAbsMinNoAlignCell = MINLAY;
619
620 // Respect the border and distance to the content
621 AddBorderWidth( nMinNoAlignCell, nMaxNoAlignCell,
622 nAbsMinNoAlignCell, i, nColSpan );
623
624 if( 1==nColSpan )
625 {
626 // take over the values directly
627 pColumn->MergeMinMaxNoAlign( nMinNoAlignCell,
628 nMaxNoAlignCell,
629 nAbsMinNoAlignCell );
630
631 // the widest WIDTH wins
632 if( !HasColTags() )
633 pColumn->MergeCellWidthOption( nWidth, bRelWidth );
634 }
635 else
636 {
637 // Process the data line by line from left to right at the end
638
639 // When which values is taken over will be explained further down.
640 if( !HasColTags() && nWidth && !bRelWidth )
641 {
642 sal_uLong nAbsWidth = nWidth, nDummy = 0, nDummy2 = 0;
643 AddBorderWidth( nAbsWidth, nDummy, nDummy2,
644 i, nColSpan, false );
645
646 if( nAbsWidth >= nMinNoAlignCell )
647 {
648 nMaxNoAlignCell = nAbsWidth;
649 if( HasColsOption() )
650 nMinNoAlignCell = nAbsWidth;
651 }
652 else if( nAbsWidth >= nAbsMinNoAlignCell )
653 {
654 nMaxNoAlignCell = nAbsWidth;
655 nMinNoAlignCell = nAbsWidth;
656 }
657 else
658 {
659 nMaxNoAlignCell = nAbsMinNoAlignCell;
660 nMinNoAlignCell = nAbsMinNoAlignCell;
661 }
662 }
663 else if( HasColsOption() || HasColTags() )
664 nMinNoAlignCell = nAbsMinNoAlignCell;
665
666 SwHTMLTableLayoutConstraints *pConstr =
667 new SwHTMLTableLayoutConstraints( nMinNoAlignCell,
668 nMaxNoAlignCell, j, i, nColSpan );
669 if (xConstraints)
670 {
671 SwHTMLTableLayoutConstraints* pConstraints = xConstraints->InsertNext(pConstr);
672 xConstraints.release();
673 xConstraints.reset(pConstraints);
674 }
675 else
676 xConstraints.reset(pConstr);
677 }
678 }
679 }
680
681 OSL_ENSURE( nMinColSpan>0 && nColSkip>0 && nColSkip <= nMinColSpan,
682 "Layout pass 1: Columns are being forgotten!" );
683 OSL_ENSURE( nMinColSpan!=USHRT_MAX,
684 "Layout pass 1: unnecessary pass through the loop or a bug" );
685
686 if( 1==nMinColSpan )
687 {
688 // There are cells with COLSPAN 1 and therefore also useful
689 // values in pColumn
690
691 // Take over values according to the following table (Netscape 4.0 pv 3):
692
693 // WIDTH: no COLS COLS
694
695 // none min = min min = absmin
696 // max = max max = max
697
698 // >= min min = min min = width
699 // max = width max = width
700
701 // >= absmin min = width(*) min = width
702 // max = width max = width
703
704 // < absmin min = absmin min = absmin
705 // max = absmin max = absmin
706
707 // (*) Netscape uses the minimum width without a break before
708 // the last graphic here. We don't have that (yet?),
709 // so we leave it set to width.
710
711 if( pColumn->GetWidthOption() && !pColumn->IsRelWidthOption() )
712 {
713 // Take over absolute widths as minimal and maximal widths.
714 sal_uLong nAbsWidth = pColumn->GetWidthOption();
715 sal_uLong nDummy = 0, nDummy2 = 0;
716 AddBorderWidth( nAbsWidth, nDummy, nDummy2, i, 1, false );
717
718 if( nAbsWidth >= pColumn->GetMinNoAlign() )
719 {
720 pColumn->SetMinMax( HasColsOption() ? nAbsWidth
721 : pColumn->GetMinNoAlign(),
722 nAbsWidth );
723 }
724 else if( nAbsWidth >= pColumn->GetAbsMinNoAlign() )
725 {
726 pColumn->SetMinMax( nAbsWidth, nAbsWidth );
727 }
728 else
729 {
730 pColumn->SetMinMax( pColumn->GetAbsMinNoAlign(),
731 pColumn->GetAbsMinNoAlign() );
732 }
733 }
734 else
735 {
736 pColumn->SetMinMax( HasColsOption() ? pColumn->GetAbsMinNoAlign()
737 : pColumn->GetMinNoAlign(),
738 pColumn->GetMaxNoAlign() );
739 }
740 }
741 else if( USHRT_MAX!=nMinColSpan )
742 {
743 // Can be anything != 0, because it is altered by the constraints.
744 pColumn->SetMinMax( MINLAY, MINLAY );
745
746 // the next columns need not to be processed
747 i += (nColSkip-1);
748 }
749
750 m_nMin += pColumn->GetMin();
751 m_nMax += pColumn->GetMax();
752 if (pColumn->IsRelWidthOption()) bFixRelWidths = true;
753 }
754
755 // Now process the constraints
756 SwHTMLTableLayoutConstraints *pConstr = xConstraints.get();
757 while( pConstr )
758 {
759 // At first we need to process the width in the same way
760 // as the column widths
761 sal_uInt16 nCol = pConstr->GetColumn();
762 sal_uInt16 nColSpan = pConstr->GetColSpan();
763 sal_uLong nConstrMin = pConstr->GetMinNoAlign();
764 sal_uLong nConstrMax = pConstr->GetMaxNoAlign();
765
766 // We get the hitherto width of the spanned columns
767 sal_uLong nColsMin = 0;
768 sal_uLong nColsMax = 0;
769 for( sal_uInt16 j=nCol; j<nCol+nColSpan; j++ )
770 {
771 SwHTMLTableLayoutColumn *pColumn = GetColumn( j );
772 nColsMin += pColumn->GetMin();
773 nColsMax += pColumn->GetMax();
774 }
775
776 if( nColsMin<nConstrMin )
777 {
778 // Proportionately distribute the minimum value to the columns
779 sal_uLong nMinD = nConstrMin-nColsMin;
780
781 if( nConstrMin > nColsMax )
782 {
783 // Proportional according to the minimum widths
784 sal_uInt16 nEndCol = nCol+nColSpan;
785 sal_uLong nDiff = nMinD;
786 for( sal_uInt16 ic=nCol; ic<nEndCol; ic++ )
787 {
788 SwHTMLTableLayoutColumn *pColumn = GetColumn( ic );
789
790 sal_uLong nColMin = pColumn->GetMin();
791 sal_uLong nColMax = pColumn->GetMax();
792
793 m_nMin -= nColMin;
794 sal_uLong nAdd;
795 if (ic < nEndCol-1)
796 {
797 if (nColsMin == 0)
798 throw o3tl::divide_by_zero();
799 nAdd = (nColMin * nMinD) / nColsMin;
800 }
801 else
802 {
803 nAdd = nDiff;
804 }
805 nColMin += nAdd;
806 m_nMin += nColMin;
807 OSL_ENSURE( nDiff >= nAdd, "Ooops: nDiff is not correct anymore" );
808 nDiff -= nAdd;
809
810 if( nColMax < nColMin )
811 {
812 m_nMax -= nColMax;
813 nColsMax -= nColMax;
814 nColMax = nColMin;
815 m_nMax += nColMax;
816 nColsMax += nColMax;
817 }
818
819 pColumn->SetMinMax( nColMin, nColMax );
820 }
821 }
822 else
823 {
824 // Proportional according to the difference of max and min
825 for( sal_uInt16 ic=nCol; ic<nCol+nColSpan; ic++ )
826 {
827 SwHTMLTableLayoutColumn *pColumn = GetColumn( ic );
828
829 sal_uLong nDiff = pColumn->GetMax()-pColumn->GetMin();
830 if( nMinD < nDiff )
831 nDiff = nMinD;
832
833 pColumn->AddToMin( nDiff );
834
835 OSL_ENSURE( pColumn->GetMax() >= pColumn->GetMin(),
836 "Why is the Column suddenly too narrow?" );
837
838 m_nMin += nDiff;
839 nMinD -= nDiff;
840 }
841 }
842 }
843
844 if( !HasColTags() && nColsMax<nConstrMax )
845 {
846 sal_uLong nMaxD = nConstrMax-nColsMax;
847
848 for( sal_uInt16 ic=nCol; ic<nCol+nColSpan; ic++ )
849 {
850 SwHTMLTableLayoutColumn *pColumn = GetColumn( ic );
851
852 m_nMax -= pColumn->GetMax();
853
854 pColumn->AddToMax( (pColumn->GetMax() * nMaxD) / nColsMax );
855
856 m_nMax += pColumn->GetMax();
857 }
858 }
859
860 pConstr = pConstr->GetNext();
861 }
862
863 if( bFixRelWidths )
864 {
865 if( HasColTags() )
866 {
867 // To adapt the relative widths, in a first step we multiply the
868 // minimum width of all affected cells with the relative width
869 // of the column.
870 // Thus, the width ratio among the columns is correct.
871
872 // Furthermore, a factor is calculated that says by how much the
873 // cell has gotten wider than the minimum width.
874
875 // In the second step the calculated widths are divided by this
876 // factor. Thereby a cell's width is preserved and serves as a
877 // basis for the other cells.
878 // We only change the maximum widths here!
879
880 sal_uLong nAbsMin = 0; // absolute minimum width of all widths with relative width
881 sal_uLong nRel = 0; // sum of all relative widths of all columns
882 for( i=0; i<m_nCols; i++ )
883 {
884 SwHTMLTableLayoutColumn *pColumn = GetColumn( i );
885 if( pColumn->IsRelWidthOption() && pColumn->GetWidthOption() )
886 {
887 nAbsMin += pColumn->GetMin();
888 nRel += pColumn->GetWidthOption();
889 }
890 }
891
892 sal_uLong nQuot = ULONG_MAX;
893 for( i=0; i<m_nCols; i++ )
894 {
895 SwHTMLTableLayoutColumn *pColumn = GetColumn( i );
896 if( pColumn->IsRelWidthOption() )
897 {
898 m_nMax -= pColumn->GetMax();
899 if( pColumn->GetWidthOption() && pColumn->GetMin() )
900 {
901 pColumn->SetMax( nAbsMin * pColumn->GetWidthOption() );
902 sal_uLong nColQuot = pColumn->GetMax() / pColumn->GetMin();
903 if( nColQuot<nQuot )
904 nQuot = nColQuot;
905 }
906 }
907 }
908 OSL_ENSURE( 0==nRel || nQuot!=ULONG_MAX,
909 "Where did the relative columns go?" );
910 for( i=0; i<m_nCols; i++ )
911 {
912 SwHTMLTableLayoutColumn *pColumn = GetColumn( i );
913 if( pColumn->IsRelWidthOption() )
914 {
915 if( pColumn->GetWidthOption() )
916 pColumn->SetMax( pColumn->GetMax() / nQuot );
917 else
918 pColumn->SetMax( pColumn->GetMin() );
919 OSL_ENSURE( pColumn->GetMax() >= pColumn->GetMin(),
920 "Maximum column width is lower than the minimum column width" );
921 m_nMax += pColumn->GetMax();
922 }
923 }
924 }
925 else
926 {
927 sal_uInt16 nRel = 0; // sum of the relative widths of all columns
928 sal_uInt16 nRelCols = 0; // count of the columns with a relative setting
929 sal_uLong nRelMax = 0; // fraction of the maximum of this column
930 for( i=0; i<m_nCols; i++ )
931 {
932 OSL_ENSURE( nRel<=100, "relative width of all columns > 100%" );
933 SwHTMLTableLayoutColumn *pColumn = GetColumn( i );
934 if( pColumn->IsRelWidthOption() && pColumn->GetWidthOption() )
935 {
936 // Make sure that the relative widths don't go above 100%
937 sal_uInt16 nColWidth = pColumn->GetWidthOption();
938 if( nRel+nColWidth > 100 )
939 {
940 nColWidth = 100 - nRel;
941 pColumn->SetWidthOption( nColWidth );
942 }
943 nRelMax += pColumn->GetMax();
944 nRel = nRel + nColWidth;
945 nRelCols++;
946 }
947 else if( !pColumn->GetMin() )
948 {
949 // The column is empty (so it was solely created by
950 // COLSPAN) and therefore must not be assigned a % width.
951 nRelCols++;
952 }
953 }
954
955 // If there are percentages left we distribute them to the columns
956 // that don't have a width setting. Like in Netscape we distribute
957 // the remaining percentages according to the ratio of the maximum
958 // width of the affected columns.
959 // For the maximum widths we also take the fixed-width columns
960 // into account. Is that correct?
961 sal_uLong nFixMax = 0;
962 if( nRel < 100 && nRelCols < m_nCols )
963 {
964 nFixMax = m_nMax - nRelMax;
965 SAL_WARN_IF(!nFixMax, "sw.core", "bad fixed width max");
966 }
967 if (nFixMax)
968 {
969 sal_uInt16 nRelLeft = 100 - nRel;
970 for( i=0; i<m_nCols; i++ )
971 {
972 SwHTMLTableLayoutColumn *pColumn = GetColumn( i );
973 if( !pColumn->IsRelWidthOption() &&
974 !pColumn->GetWidthOption() &&
975 pColumn->GetMin() )
976 {
977 // the next column gets the rest
978 sal_uInt16 nColWidth =
979 static_cast<sal_uInt16>((pColumn->GetMax() * nRelLeft) / nFixMax);
980 pColumn->SetWidthOption( nColWidth );
981 }
982 }
983 }
984
985 // adjust the maximum widths now accordingly
986 sal_uLong nQuotMax = ULONG_MAX;
987 sal_uLong nOldMax = m_nMax;
988 m_nMax = 0;
989 for( i=0; i<m_nCols; i++ )
990 {
991 // Columns with a % setting are adapted accordingly.
992 // Columns, that
993 // - do not have a % setting and are located within a tables
994 // with COLS and WIDTH, or
995 // - their width is 0%
996 // get set to the minimum width.
997 SwHTMLTableLayoutColumn *pColumn = GetColumn( i );
998 if( pColumn->IsRelWidthOption() && pColumn->GetWidthOption() )
999 {
1000 sal_uLong nNewMax;
1001 sal_uLong nColQuotMax;
1002 if( !m_nWidthOption )
1003 {
1004 nNewMax = nOldMax * pColumn->GetWidthOption();
1005 nColQuotMax = nNewMax / pColumn->GetMax();
1006 }
1007 else
1008 {
1009 nNewMax = m_nMin * pColumn->GetWidthOption();
1010 nColQuotMax = nNewMax / pColumn->GetMin();
1011 }
1012 pColumn->SetMax( nNewMax );
1013 if( nColQuotMax < nQuotMax )
1014 nQuotMax = nColQuotMax;
1015 }
1016 else if( HasColsOption() || m_nWidthOption ||
1017 (pColumn->IsRelWidthOption() &&
1018 !pColumn->GetWidthOption()) )
1019 pColumn->SetMax( pColumn->GetMin() );
1020 }
1021 // and divide by the quotient
1022 SAL_WARN_IF(!nQuotMax, "sw.core", "Where did the relative columns go?");
1023 for (i = 0; i < m_nCols; ++i)
1024 {
1025 SwHTMLTableLayoutColumn *pColumn = GetColumn( i );
1026 if (pColumn->IsRelWidthOption() && pColumn->GetWidthOption() && nQuotMax)
1027 {
1028 pColumn->SetMax( pColumn->GetMax() / nQuotMax );
1029 OSL_ENSURE( pColumn->GetMax() >= pColumn->GetMin(),
1030 "Minimum width is one column bigger than maximum" );
1031 if( pColumn->GetMax() < pColumn->GetMin() )
1032 pColumn->SetMax( pColumn->GetMin() );
1033 }
1034 m_nMax += pColumn->GetMax();
1035 }
1036 }
1037 }
1038 }
1039
1040 //TODO: provide documentation
1041 /**
1042
1043 @param nAbsAvail available space in TWIPS.
1044 @param nRelAvail available space related to USHRT_MAX or 0
1045 @param nAbsSpace fraction of nAbsAvail, which is reserved by the surrounding
1046 cell for the border and the distance to the paragraph.
1047 */
AutoLayoutPass2(sal_uInt16 nAbsAvail,sal_uInt16 nRelAvail,sal_uInt16 nAbsLeftSpace,sal_uInt16 nAbsRightSpace,sal_uInt16 nParentInhAbsSpace)1048 void SwHTMLTableLayout::AutoLayoutPass2( sal_uInt16 nAbsAvail, sal_uInt16 nRelAvail,
1049 sal_uInt16 nAbsLeftSpace,
1050 sal_uInt16 nAbsRightSpace,
1051 sal_uInt16 nParentInhAbsSpace )
1052 {
1053 // For a start we do a lot of plausibility tests
1054
1055 // An absolute width always has to be passed
1056 OSL_ENSURE( nAbsAvail, "AutoLayout pass 2: No absolute width given" );
1057
1058 // A relative width must only be passed for tables within tables (?)
1059 OSL_ENSURE( IsTopTable() == (nRelAvail==0),
1060 "AutoLayout pass 2: Relative width at table in table or the other way around" );
1061
1062 // The table's minimum width must not be bigger than its maximum width
1063 OSL_ENSURE( m_nMin<=m_nMax, "AutoLayout pass 2: nMin > nMax" );
1064
1065 // Remember the available width for which the table was calculated.
1066 // This is a good place as we pass by here for the initial calculation
1067 // of the table in the parser and for each Resize_ call.
1068 m_nLastResizeAbsAvail = nAbsAvail;
1069
1070 // Step 1: The available space is readjusted for the left/right border,
1071 // possibly existing filler cells and distances.
1072
1073 // Distance to the content and border
1074 sal_uInt16 nAbsLeftFill = 0, nAbsRightFill = 0;
1075 if( !IsTopTable() &&
1076 GetMin() + nAbsLeftSpace + nAbsRightSpace <= nAbsAvail )
1077 {
1078 nAbsLeftFill = nAbsLeftSpace;
1079 nAbsRightFill = nAbsRightSpace;
1080 }
1081
1082 // Left and right distance
1083 if( m_nLeftMargin || m_nRightMargin )
1084 {
1085 if( IsTopTable() )
1086 {
1087 // For the top table we always respect the borders, because we
1088 // never go below the table's minimum width.
1089 nAbsAvail -= (m_nLeftMargin + m_nRightMargin);
1090 }
1091 else if( GetMin() + m_nLeftMargin + m_nRightMargin <= nAbsAvail )
1092 {
1093 // Else, we only respect the borders if there's space available
1094 // for them (nMin has already been calculated!)
1095 nAbsLeftFill = nAbsLeftFill + m_nLeftMargin;
1096 nAbsRightFill = nAbsRightFill + m_nRightMargin;
1097 }
1098 }
1099
1100 // Read just the available space
1101 m_nRelLeftFill = 0;
1102 m_nRelRightFill = 0;
1103 if( !IsTopTable() && (nAbsLeftFill>0 || nAbsRightFill) )
1104 {
1105 sal_uLong nAbsLeftFillL = nAbsLeftFill, nAbsRightFillL = nAbsRightFill;
1106
1107 m_nRelLeftFill = static_cast<sal_uInt16>((nAbsLeftFillL * nRelAvail) / nAbsAvail);
1108 m_nRelRightFill = static_cast<sal_uInt16>((nAbsRightFillL * nRelAvail) / nAbsAvail);
1109
1110 nAbsAvail -= (nAbsLeftFill + nAbsRightFill);
1111 if( nRelAvail )
1112 nRelAvail -= (m_nRelLeftFill + m_nRelRightFill);
1113 }
1114
1115 // Step 2: Calculate the absolute table width.
1116 sal_uInt16 nAbsTabWidth = 0;
1117 m_bUseRelWidth = false;
1118 if( m_nWidthOption )
1119 {
1120 if( m_bPrcWidthOption )
1121 {
1122 OSL_ENSURE( m_nWidthOption<=100, "Percentage value too high" );
1123 if( m_nWidthOption > 100 )
1124 m_nWidthOption = 100;
1125
1126 // The absolute width is equal to the given percentage of
1127 // the available width.
1128 // Top tables only get a relative width if the available space
1129 // is *strictly larger* than the minimum width.
1130
1131 // CAUTION: We need the "strictly larger" because changing from a
1132 // relative width to an absolute width by resizing would lead
1133 // to an infinite loop.
1134
1135 // Because we do not call resize for tables in frames if the
1136 // frame has a non-relative width, we cannot play such games.
1137
1138 // Let's play such games now anyway. We had a graphic in a 1% wide
1139 // table and it didn't fit in of course.
1140 nAbsTabWidth = static_cast<sal_uInt16>( (static_cast<sal_uLong>(nAbsAvail) * m_nWidthOption) / 100 );
1141 if( IsTopTable() &&
1142 ( /*MayBeInFlyFrame() ||*/ static_cast<sal_uLong>(nAbsTabWidth) > m_nMin ) )
1143 {
1144 nRelAvail = USHRT_MAX;
1145 m_bUseRelWidth = true;
1146 }
1147 }
1148 else
1149 {
1150 nAbsTabWidth = m_nWidthOption;
1151 if( nAbsTabWidth > MAX_TABWIDTH )
1152 nAbsTabWidth = MAX_TABWIDTH;
1153
1154 // Tables within tables must never get wider than the available
1155 // space.
1156 if( !IsTopTable() && nAbsTabWidth > nAbsAvail )
1157 nAbsTabWidth = nAbsAvail;
1158 }
1159 }
1160
1161 OSL_ENSURE( IsTopTable() || nAbsTabWidth<=nAbsAvail,
1162 "AutoLayout pass 2: nAbsTabWidth > nAbsAvail for table in table" );
1163 OSL_ENSURE( !nRelAvail || nAbsTabWidth<=nAbsAvail,
1164 "AutoLayout pass 2: nAbsTabWidth > nAbsAvail for relative width" );
1165
1166 // Catch for the two asserts above (we never know!)
1167 if( (!IsTopTable() || nRelAvail>0) && nAbsTabWidth>nAbsAvail )
1168 nAbsTabWidth = nAbsAvail;
1169
1170 // Step 3: Identify the column width and, if applicable, the absolute
1171 // and relative table widths.
1172 if( (!IsTopTable() && m_nMin > static_cast<sal_uLong>(nAbsAvail)) ||
1173 m_nMin > MAX_TABWIDTH )
1174 {
1175 // If
1176 // - an inner table's minimum is larger than the available space, or
1177 // - a top table's minimum is larger than USHORT_MAX the table
1178 // has to be adapted to the available space or USHORT_MAX.
1179 // We preserve the widths' ratio amongst themselves, however.
1180
1181 nAbsTabWidth = IsTopTable() ? MAX_TABWIDTH : nAbsAvail;
1182 m_nRelTabWidth = (nRelAvail ? nRelAvail : nAbsTabWidth );
1183
1184 // First of all, we check whether we can fit the layout constrains,
1185 // which are: Every cell's width excluding the borders must be at least
1186 // MINLAY:
1187
1188 sal_uLong nRealMin = 0;
1189 for( sal_uInt16 i=0; i<m_nCols; i++ )
1190 {
1191 sal_uLong nRealColMin = MINLAY, nDummy1 = 0, nDummy2 = 0;
1192 AddBorderWidth( nRealColMin, nDummy1, nDummy2, i, 1 );
1193 nRealMin += nRealColMin;
1194 }
1195 if( (nRealMin >= nAbsTabWidth) || (nRealMin >= m_nMin) )
1196 {
1197 // "Rien ne va plus": we cannot get the minimum column widths
1198 // the layout wants to have.
1199
1200 sal_uInt16 nAbs = 0, nRel = 0;
1201 SwHTMLTableLayoutColumn *pColumn;
1202 for( sal_uInt16 i=0; i<m_nCols-1; i++ )
1203 {
1204 pColumn = GetColumn( i );
1205 sal_uLong nColMin = pColumn->GetMin();
1206 if( nColMin <= USHRT_MAX )
1207 {
1208 pColumn->SetAbsColWidth(
1209 static_cast<sal_uInt16>((nColMin * nAbsTabWidth) / m_nMin) );
1210 pColumn->SetRelColWidth(
1211 static_cast<sal_uInt16>((nColMin * m_nRelTabWidth) / m_nMin) );
1212 }
1213 else
1214 {
1215 double nColMinD = nColMin;
1216 pColumn->SetAbsColWidth(
1217 static_cast<sal_uInt16>((nColMinD * nAbsTabWidth) / m_nMin) );
1218 pColumn->SetRelColWidth(
1219 static_cast<sal_uInt16>((nColMinD * m_nRelTabWidth) / m_nMin) );
1220 }
1221
1222 nAbs = nAbs + pColumn->GetAbsColWidth();
1223 nRel = nRel + pColumn->GetRelColWidth();
1224 }
1225 pColumn = GetColumn( m_nCols-1 );
1226 pColumn->SetAbsColWidth( nAbsTabWidth - nAbs );
1227 pColumn->SetRelColWidth( m_nRelTabWidth - nRel );
1228 }
1229 else
1230 {
1231 sal_uLong nDistAbs = nAbsTabWidth - nRealMin;
1232 sal_uLong nDistRel = m_nRelTabWidth - nRealMin;
1233 sal_uLong nDistMin = m_nMin - nRealMin;
1234 sal_uInt16 nAbs = 0, nRel = 0;
1235 SwHTMLTableLayoutColumn *pColumn;
1236 for( sal_uInt16 i=0; i<m_nCols-1; i++ )
1237 {
1238 pColumn = GetColumn( i );
1239 sal_uLong nColMin = pColumn->GetMin();
1240 sal_uLong nRealColMin = MINLAY, nDummy1 = 0, nDummy2 = 0;
1241 AddBorderWidth( nRealColMin, nDummy1, nDummy2, i, 1 );
1242
1243 if( nColMin <= USHRT_MAX )
1244 {
1245 pColumn->SetAbsColWidth(
1246 static_cast<sal_uInt16>((((nColMin-nRealColMin) * nDistAbs) / nDistMin) + nRealColMin) );
1247 pColumn->SetRelColWidth(
1248 static_cast<sal_uInt16>((((nColMin-nRealColMin) * nDistRel) / nDistMin) + nRealColMin) );
1249 }
1250 else
1251 {
1252 double nColMinD = nColMin;
1253 pColumn->SetAbsColWidth(
1254 static_cast<sal_uInt16>((((nColMinD-nRealColMin) * nDistAbs) / nDistMin) + nRealColMin) );
1255 pColumn->SetRelColWidth(
1256 static_cast<sal_uInt16>((((nColMinD-nRealColMin) * nDistRel) / nDistMin) + nRealColMin) );
1257 }
1258
1259 nAbs = nAbs + pColumn->GetAbsColWidth();
1260 nRel = nRel + pColumn->GetRelColWidth();
1261 }
1262 pColumn = GetColumn( m_nCols-1 );
1263 pColumn->SetAbsColWidth( nAbsTabWidth - nAbs );
1264 pColumn->SetRelColWidth( m_nRelTabWidth - nRel );
1265 }
1266 }
1267 else if( m_nMax <= static_cast<sal_uLong>(nAbsTabWidth ? nAbsTabWidth : nAbsAvail) )
1268 {
1269 // If
1270 // - the table has a fixed width and the table's maximum is
1271 // smaller, or
1272 //- the maximum is smaller than the available space,
1273 // we can take over the maximum as it is. Respectively
1274 // the table can only be adapted to the fixed width by
1275 // respecting the maximum.
1276
1277 // No fixed width, use the maximum.
1278 if( !nAbsTabWidth )
1279 nAbsTabWidth = static_cast<sal_uInt16>(m_nMax);
1280
1281 // A top table may also get wider then the available space.
1282 if( nAbsTabWidth > nAbsAvail )
1283 {
1284 OSL_ENSURE( IsTopTable(),
1285 "Table in table should get wider than the surrounding cell." );
1286 nAbsAvail = nAbsTabWidth;
1287 }
1288
1289 // Only use the relative widths' fraction, that is used for the
1290 // absolute width.
1291 sal_uLong nAbsTabWidthL = nAbsTabWidth;
1292 if (nRelAvail)
1293 {
1294 if (nAbsAvail == 0)
1295 throw o3tl::divide_by_zero();
1296 m_nRelTabWidth = static_cast<sal_uInt16>((nAbsTabWidthL * nRelAvail) / nAbsAvail);
1297 }
1298 else
1299 m_nRelTabWidth = nAbsTabWidth;
1300
1301 // Are there columns width a percentage setting and some without one?
1302 sal_uLong nFixMax = m_nMax;
1303 for( sal_uInt16 i=0; i<m_nCols; i++ )
1304 {
1305 const SwHTMLTableLayoutColumn *pColumn = GetColumn( i );
1306 if( pColumn->IsRelWidthOption() && pColumn->GetWidthOption()>0 )
1307 nFixMax -= pColumn->GetMax();
1308 }
1309
1310 if( nFixMax > 0 && nFixMax < m_nMax )
1311 {
1312 // Yes, distribute the to-be-distributed space only to the
1313 // columns with a percentage setting.
1314
1315 // In this case (and in this case only) there are columns
1316 // that exactly keep their maximum width, that is they neither
1317 // get smaller nor wider. When calculating the absolute width
1318 // from the relative width we can get rounding errors.
1319 // To correct this, we first make the fixed widths compensate for
1320 // this error. We then fix the relative widths the same way.
1321
1322 sal_uInt16 nAbs = 0, nRel = 0;
1323 sal_uInt16 nFixedCols = 0;
1324 sal_uInt16 i;
1325
1326 for( i = 0; i < m_nCols; i++ )
1327 {
1328 SwHTMLTableLayoutColumn *pColumn = GetColumn( i );
1329 if( !pColumn->IsRelWidthOption() || !pColumn->GetWidthOption() )
1330 {
1331 // The column keeps its width.
1332 nFixedCols++;
1333 sal_uLong nColMax = pColumn->GetMax();
1334 pColumn->SetAbsColWidth( static_cast<sal_uInt16>(nColMax) );
1335
1336 sal_uLong nRelColWidth =
1337 (nColMax * m_nRelTabWidth) / nAbsTabWidth;
1338 sal_uLong nChkWidth =
1339 (nRelColWidth * nAbsTabWidth) / m_nRelTabWidth;
1340 if( nChkWidth < nColMax )
1341 nRelColWidth++;
1342 else if( nChkWidth > nColMax )
1343 nRelColWidth--;
1344 pColumn->SetRelColWidth( static_cast<sal_uInt16>(nRelColWidth) );
1345
1346 nAbs = nAbs + static_cast<sal_uInt16>(nColMax);
1347 nRel = nRel + static_cast<sal_uInt16>(nRelColWidth);
1348 }
1349 }
1350
1351 // The to-be-distributed percentage of the maximum, the
1352 // relative and absolute widths. Here, nFixMax corresponds
1353 // to nAbs, so that we could've called it nAbs.
1354 // The code is, however, more readable like that.
1355 OSL_ENSURE( nFixMax == nAbs, "Two loops, two sums?" );
1356 sal_uLong nDistMax = m_nMax - nFixMax;
1357 sal_uInt16 nDistAbsTabWidth = nAbsTabWidth - nAbs;
1358 sal_uInt16 nDistRelTabWidth = m_nRelTabWidth - nRel;
1359
1360 for( i=0; i<m_nCols; i++ )
1361 {
1362 SwHTMLTableLayoutColumn *pColumn = GetColumn( i );
1363 if( pColumn->IsRelWidthOption() && pColumn->GetWidthOption() > 0 )
1364 {
1365 // The column gets proportionately wider.
1366 nFixedCols++;
1367 if( nFixedCols == m_nCols )
1368 {
1369 pColumn->SetAbsColWidth( nAbsTabWidth-nAbs );
1370 pColumn->SetRelColWidth( m_nRelTabWidth-nRel );
1371 }
1372 else
1373 {
1374 sal_uLong nColMax = pColumn->GetMax();
1375 pColumn->SetAbsColWidth(
1376 static_cast<sal_uInt16>((nColMax * nDistAbsTabWidth) / nDistMax) );
1377 pColumn->SetRelColWidth(
1378 static_cast<sal_uInt16>((nColMax * nDistRelTabWidth) / nDistMax) );
1379 }
1380 nAbs = nAbs + pColumn->GetAbsColWidth();
1381 nRel = nRel + pColumn->GetRelColWidth();
1382 }
1383 }
1384 OSL_ENSURE( m_nCols==nFixedCols, "Missed a column!" );
1385 }
1386 else if (m_nCols > 0)
1387 {
1388 if (m_nMax == 0)
1389 throw o3tl::divide_by_zero();
1390 // No. So distribute the space regularly among all columns.
1391 for (sal_uInt16 i=0; i < m_nCols; ++i)
1392 {
1393 sal_uLong nColMax = GetColumn( i )->GetMax();
1394 GetColumn( i )->SetAbsColWidth(
1395 static_cast<sal_uInt16>((nColMax * nAbsTabWidth) / m_nMax) );
1396 GetColumn( i )->SetRelColWidth(
1397 static_cast<sal_uInt16>((nColMax * m_nRelTabWidth) / m_nMax) );
1398 }
1399 }
1400 }
1401 else
1402 {
1403 // Proportionately distribute the space that extends over the minimum
1404 // width among the columns.
1405 if( !nAbsTabWidth )
1406 nAbsTabWidth = nAbsAvail;
1407 if( nAbsTabWidth < m_nMin )
1408 nAbsTabWidth = static_cast<sal_uInt16>(m_nMin);
1409
1410 if( nAbsTabWidth > nAbsAvail )
1411 {
1412 OSL_ENSURE( IsTopTable(),
1413 "A nested table should become wider than the available space." );
1414 nAbsAvail = nAbsTabWidth;
1415 }
1416
1417 sal_uLong nAbsTabWidthL = nAbsTabWidth;
1418 if (nRelAvail)
1419 {
1420 if (nAbsAvail == 0)
1421 throw o3tl::divide_by_zero();
1422 m_nRelTabWidth = static_cast<sal_uInt16>((nAbsTabWidthL * nRelAvail) / nAbsAvail);
1423 }
1424 else
1425 m_nRelTabWidth = nAbsTabWidth;
1426 double nW = nAbsTabWidth - m_nMin;
1427 double nD = (m_nMax==m_nMin ? 1 : m_nMax-m_nMin);
1428 sal_uInt16 nAbs = 0, nRel = 0;
1429 for( sal_uInt16 i=0; i<m_nCols-1; i++ )
1430 {
1431 double nd = GetColumn( i )->GetMax() - GetColumn( i )->GetMin();
1432 sal_uLong nAbsColWidth = GetColumn( i )->GetMin() + static_cast<sal_uLong>((nd*nW)/nD);
1433 sal_uLong nRelColWidth = nRelAvail
1434 ? (nAbsColWidth * m_nRelTabWidth) / nAbsTabWidth
1435 : nAbsColWidth;
1436
1437 GetColumn( i )->SetAbsColWidth( static_cast<sal_uInt16>(nAbsColWidth) );
1438 GetColumn( i )->SetRelColWidth( static_cast<sal_uInt16>(nRelColWidth) );
1439 nAbs = nAbs + static_cast<sal_uInt16>(nAbsColWidth);
1440 nRel = nRel + static_cast<sal_uInt16>(nRelColWidth);
1441 }
1442 GetColumn( m_nCols-1 )->SetAbsColWidth( nAbsTabWidth - nAbs );
1443 GetColumn( m_nCols-1 )->SetRelColWidth( m_nRelTabWidth - nRel );
1444
1445 }
1446
1447 // Step 4: For nested tables we can have balancing cells on the
1448 // left or right. Here we calculate their width.
1449 m_nInhAbsLeftSpace = 0;
1450 m_nInhAbsRightSpace = 0;
1451 if( !IsTopTable() && (m_nRelLeftFill>0 || m_nRelRightFill>0 ||
1452 nAbsTabWidth<nAbsAvail) )
1453 {
1454 // Calculate the width of additional cells we use for
1455 // aligning inner tables.
1456 sal_uInt16 nAbsDist = static_cast<sal_uInt16>(nAbsAvail-nAbsTabWidth);
1457 sal_uInt16 nRelDist = static_cast<sal_uInt16>(nRelAvail-m_nRelTabWidth);
1458 sal_uInt16 nParentInhAbsLeftSpace = 0, nParentInhAbsRightSpace = 0;
1459
1460 // Calculate the size and position of the additional cells.
1461 switch( m_eTableAdjust )
1462 {
1463 case SvxAdjust::Right:
1464 nAbsLeftFill = nAbsLeftFill + nAbsDist;
1465 m_nRelLeftFill = m_nRelLeftFill + nRelDist;
1466 nParentInhAbsLeftSpace = nParentInhAbsSpace;
1467 break;
1468 case SvxAdjust::Center:
1469 {
1470 sal_uInt16 nAbsLeftDist = nAbsDist / 2;
1471 nAbsLeftFill = nAbsLeftFill + nAbsLeftDist;
1472 nAbsRightFill += nAbsDist - nAbsLeftDist;
1473 sal_uInt16 nRelLeftDist = nRelDist / 2;
1474 m_nRelLeftFill = m_nRelLeftFill + nRelLeftDist;
1475 m_nRelRightFill += nRelDist - nRelLeftDist;
1476 nParentInhAbsLeftSpace = nParentInhAbsSpace / 2;
1477 nParentInhAbsRightSpace = nParentInhAbsSpace -
1478 nParentInhAbsLeftSpace;
1479 }
1480 break;
1481 case SvxAdjust::Left:
1482 default:
1483 nAbsRightFill = nAbsRightFill + nAbsDist;
1484 m_nRelRightFill = m_nRelRightFill + nRelDist;
1485 nParentInhAbsRightSpace = nParentInhAbsSpace;
1486 break;
1487 }
1488
1489 // Filler widths are added to the outer columns, if there are no boxes
1490 // for them after the first pass (nWidth>0) or their width would become
1491 // too small or if there are COL tags and the filler width corresponds
1492 // to the border width.
1493 // In the last case we probably exported the table ourselves.
1494 if( m_nRelLeftFill &&
1495 ( m_nWidthSet>0 || nAbsLeftFill<MINLAY+m_nInhLeftBorderWidth ||
1496 (HasColTags() && nAbsLeftFill < nAbsLeftSpace+nParentInhAbsLeftSpace+20) ) )
1497 {
1498 SwHTMLTableLayoutColumn *pColumn = GetColumn( 0 );
1499 pColumn->SetAbsColWidth( pColumn->GetAbsColWidth()+nAbsLeftFill );
1500 pColumn->SetRelColWidth( pColumn->GetRelColWidth()+m_nRelLeftFill );
1501 m_nRelLeftFill = 0;
1502 m_nInhAbsLeftSpace = nAbsLeftSpace + nParentInhAbsLeftSpace;
1503 }
1504 if( m_nRelRightFill &&
1505 ( m_nWidthSet>0 || nAbsRightFill<MINLAY+m_nInhRightBorderWidth ||
1506 (HasColTags() && nAbsRightFill < nAbsRightSpace+nParentInhAbsRightSpace+20) ) )
1507 {
1508 SwHTMLTableLayoutColumn *pColumn = GetColumn( m_nCols-1 );
1509 pColumn->SetAbsColWidth( pColumn->GetAbsColWidth()+nAbsRightFill );
1510 pColumn->SetRelColWidth( pColumn->GetRelColWidth()+m_nRelRightFill );
1511 m_nRelRightFill = 0;
1512 m_nInhAbsRightSpace = nAbsRightSpace + nParentInhAbsRightSpace;
1513 }
1514 }
1515 }
1516
1517 static void lcl_ResizeLine( const SwTableLine* pLine, SwTwips *pWidth );
1518
lcl_ResizeBox(const SwTableBox * pBox,SwTwips * pWidth)1519 static void lcl_ResizeBox( const SwTableBox* pBox, SwTwips* pWidth )
1520 {
1521 if( !pBox->GetSttNd() )
1522 {
1523 SwTwips nWidth = 0;
1524 for( const SwTableLine *pLine : pBox->GetTabLines() )
1525 lcl_ResizeLine( pLine, &nWidth );
1526 pBox->GetFrameFormat()->SetFormatAttr( SwFormatFrameSize( ATT_VAR_SIZE, nWidth, 0 ));
1527 *pWidth = *pWidth + nWidth;
1528 }
1529 else
1530 {
1531 *pWidth = *pWidth + pBox->GetFrameFormat()->GetFrameSize().GetSize().Width();
1532 }
1533 }
1534
lcl_ResizeLine(const SwTableLine * pLine,SwTwips * pWidth)1535 static void lcl_ResizeLine( const SwTableLine* pLine, SwTwips *pWidth )
1536 {
1537 SwTwips nOldWidth = *pWidth;
1538 *pWidth = 0;
1539 for( const SwTableBox* pBox : pLine->GetTabBoxes() )
1540 lcl_ResizeBox(pBox, pWidth );
1541
1542 SAL_WARN_IF( nOldWidth && std::abs(*pWidth-nOldWidth) >= COLFUZZY, "sw.core",
1543 "A box's rows have all a different length" );
1544 }
1545
SetWidths(bool bCallPass2,sal_uInt16 nAbsAvail,sal_uInt16 nRelAvail,sal_uInt16 nAbsLeftSpace,sal_uInt16 nAbsRightSpace,sal_uInt16 nParentInhAbsSpace)1546 void SwHTMLTableLayout::SetWidths( bool bCallPass2, sal_uInt16 nAbsAvail,
1547 sal_uInt16 nRelAvail, sal_uInt16 nAbsLeftSpace,
1548 sal_uInt16 nAbsRightSpace,
1549 sal_uInt16 nParentInhAbsSpace )
1550 {
1551 // SetWidth must have been passed through once more for every cell in the
1552 // end.
1553 m_nWidthSet++;
1554
1555 // Step 0: If necessary, we call the layout algorithm of Pass2.
1556 if( bCallPass2 )
1557 AutoLayoutPass2( nAbsAvail, nRelAvail, nAbsLeftSpace, nAbsRightSpace,
1558 nParentInhAbsSpace );
1559
1560 // Step 1: Set the new width in all content boxes.
1561 // Because the boxes don't know anything about the HTML table structure,
1562 // we iterate over the HTML table structure.
1563 // For tables in tables in tables we call SetWidth recursively.
1564 for( sal_uInt16 i=0; i<m_nRows; i++ )
1565 {
1566 for( sal_uInt16 j=0; j<m_nCols; j++ )
1567 {
1568 SwHTMLTableLayoutCell *pCell = GetCell( i, j );
1569
1570 SwHTMLTableLayoutCnts* pContents = pCell->GetContents().get();
1571 while( pContents && !pContents->IsWidthSet(m_nWidthSet) )
1572 {
1573 SwTableBox *pBox = pContents->GetTableBox();
1574 if( pBox )
1575 {
1576 SetBoxWidth( pBox, j, pCell->GetColSpan() );
1577 }
1578 else if (SwHTMLTableLayout *pTable = pContents->GetTable())
1579 {
1580 sal_uInt16 nAbs = 0, nRel = 0, nLSpace = 0, nRSpace = 0,
1581 nInhSpace = 0;
1582 if( bCallPass2 )
1583 {
1584 sal_uInt16 nColSpan = pCell->GetColSpan();
1585 GetAvail( j, nColSpan, nAbs, nRel );
1586 nLSpace = GetLeftCellSpace( j, nColSpan );
1587 nRSpace = GetRightCellSpace( j, nColSpan );
1588 nInhSpace = GetInhCellSpace( j, nColSpan );
1589 }
1590 pTable->SetWidths( bCallPass2, nAbs, nRel,
1591 nLSpace, nRSpace,
1592 nInhSpace );
1593 }
1594
1595 pContents->SetWidthSet( m_nWidthSet );
1596 pContents = pContents->GetNext().get();
1597 }
1598 }
1599 }
1600
1601 // Step 2: If we have a top table, we adapt the formats of the
1602 // non-content-boxes. Because they are not known in the HTML table
1603 // due to garbage collection there, we need the iterate over the
1604 // whole table.
1605 // We also adapt the table frame format. For nested tables we set the
1606 // filler cell's width instead.
1607 if( IsTopTable() )
1608 {
1609 SwTwips nCalcTabWidth = 0;
1610 for( const SwTableLine *pLine : m_pSwTable->GetTabLines() )
1611 lcl_ResizeLine( pLine, &nCalcTabWidth );
1612 SAL_WARN_IF( std::abs( m_nRelTabWidth-nCalcTabWidth ) >= COLFUZZY, "sw.core",
1613 "Table width is not equal to the row width" );
1614
1615 // Lock the table format when altering it, or else the box formats
1616 // are altered again.
1617 // Also, we need to preserve a percent setting if it exists.
1618 SwFrameFormat *pFrameFormat = m_pSwTable->GetFrameFormat();
1619 const_cast<SwTable *>(m_pSwTable)->LockModify();
1620 SwFormatFrameSize aFrameSize( pFrameFormat->GetFrameSize() );
1621 aFrameSize.SetWidth( m_nRelTabWidth );
1622 bool bRel = m_bUseRelWidth &&
1623 text::HoriOrientation::FULL!=pFrameFormat->GetHoriOrient().GetHoriOrient();
1624 aFrameSize.SetWidthPercent( static_cast<sal_uInt8>(bRel ? m_nWidthOption : 0) );
1625 pFrameFormat->SetFormatAttr( aFrameSize );
1626 const_cast<SwTable *>(m_pSwTable)->UnlockModify();
1627
1628 // If the table is located in a frame, we also need to adapt the
1629 // frame's width.
1630 if( MayBeInFlyFrame() )
1631 {
1632 SwFrameFormat *pFlyFrameFormat = FindFlyFrameFormat();
1633 if( pFlyFrameFormat )
1634 {
1635 SwFormatFrameSize aFlyFrameSize( ATT_VAR_SIZE, m_nRelTabWidth, MINLAY );
1636
1637 if( m_bUseRelWidth )
1638 {
1639 // For percentage settings we set the width to the minimum.
1640 aFlyFrameSize.SetWidth( m_nMin > USHRT_MAX ? USHRT_MAX
1641 : m_nMin );
1642 aFlyFrameSize.SetWidthPercent( static_cast<sal_uInt8>(m_nWidthOption) );
1643 }
1644 pFlyFrameFormat->SetFormatAttr( aFlyFrameSize );
1645 }
1646 }
1647
1648 #ifdef DBG_UTIL
1649 {
1650 // check if the tables have correct widths
1651 SwTwips nSize = m_pSwTable->GetFrameFormat()->GetFrameSize().GetWidth();
1652 const SwTableLines& rLines = m_pSwTable->GetTabLines();
1653 for (size_t n = 0; n < rLines.size(); ++n)
1654 {
1655 CheckBoxWidth( *rLines[ n ], nSize );
1656 }
1657 }
1658 #endif
1659
1660 }
1661 }
1662
Resize_(sal_uInt16 nAbsAvail,bool bRecalc)1663 void SwHTMLTableLayout::Resize_( sal_uInt16 nAbsAvail, bool bRecalc )
1664 {
1665 // If bRecalc is set, the table's content changed.
1666 // We need to execute pass 1 again.
1667 if( bRecalc )
1668 AutoLayoutPass1();
1669
1670 SwRootFrame *pRoot = GetDoc()->getIDocumentLayoutAccess().GetCurrentViewShell()->GetLayout();
1671 if ( pRoot && pRoot->IsCallbackActionEnabled() )
1672 pRoot->StartAllAction();
1673
1674 // Else we can set the widths, in which we have to run Pass 2 in each case.
1675 SetWidths( true, nAbsAvail );
1676
1677 if ( pRoot && pRoot->IsCallbackActionEnabled() )
1678 pRoot->EndAllAction( true ); //True per VirDev (browsing is calmer)
1679 }
1680
IMPL_LINK_NOARG(SwHTMLTableLayout,DelayedResize_Impl,Timer *,void)1681 IMPL_LINK_NOARG( SwHTMLTableLayout, DelayedResize_Impl, Timer*, void )
1682 {
1683 m_aResizeTimer.Stop();
1684 Resize_( m_nDelayedResizeAbsAvail, m_bDelayedResizeRecalc );
1685 }
1686
Resize(sal_uInt16 nAbsAvail,bool bRecalc,bool bForce,sal_uLong nDelay)1687 bool SwHTMLTableLayout::Resize( sal_uInt16 nAbsAvail, bool bRecalc,
1688 bool bForce, sal_uLong nDelay )
1689 {
1690 if( 0 == nAbsAvail )
1691 return false;
1692 OSL_ENSURE( IsTopTable(), "Resize must only be called for top tables!" );
1693
1694 // May the table be resized at all? Or is it forced?
1695 if( m_bMustNotResize && !bForce )
1696 return false;
1697
1698 // May the table be recalculated? Or is it forced?
1699 if( m_bMustNotRecalc && !bForce )
1700 bRecalc = false;
1701
1702 const SwDoc *pDoc = GetDoc();
1703
1704 // If there is a layout, the root frame's size instead of the
1705 // VisArea's size was potentially passed.
1706 // If we're not in a frame we need to calculate the table for the VisArea,
1707 // because switching from relative to absolute wouldn't work.
1708 if( pDoc->getIDocumentLayoutAccess().GetCurrentViewShell() && pDoc->getIDocumentLayoutAccess().GetCurrentViewShell()->GetViewOptions()->getBrowseMode() )
1709 {
1710 const sal_uInt16 nVisAreaWidth = GetBrowseWidthByVisArea( *pDoc );
1711 if( nVisAreaWidth < nAbsAvail && !FindFlyFrameFormat() )
1712 nAbsAvail = nVisAreaWidth;
1713 }
1714
1715 if( nDelay==0 && m_aResizeTimer.IsActive() )
1716 {
1717 m_nDelayedResizeAbsAvail = nAbsAvail;
1718 return false;
1719 }
1720
1721 // Optimisation:
1722 // If the minimum or maximum should not be recalculated and
1723 // - the table's width never needs to be recalculated, or
1724 // - the table was already calculated for the passed width, or
1725 // - the available space is less or equal to the minimum width
1726 // and the table already has the minimum width, or
1727 // - the available space is larger than the maximum width and
1728 // the table already has the maximum width
1729 // nothing will happen to the table.
1730 if( !bRecalc && ( !m_bMustResize ||
1731 (m_nLastResizeAbsAvail==nAbsAvail) ||
1732 (nAbsAvail<=m_nMin && m_nRelTabWidth==m_nMin) ||
1733 (!m_bPrcWidthOption && nAbsAvail>=m_nMax && m_nRelTabWidth==m_nMax) ) )
1734 return false;
1735
1736 if( nDelay==HTMLTABLE_RESIZE_NOW )
1737 {
1738 if( m_aResizeTimer.IsActive() )
1739 m_aResizeTimer.Stop();
1740 Resize_( nAbsAvail, bRecalc );
1741 }
1742 else if( nDelay > 0 )
1743 {
1744 m_nDelayedResizeAbsAvail = nAbsAvail;
1745 m_bDelayedResizeRecalc = bRecalc;
1746 m_aResizeTimer.SetTimeout( nDelay );
1747 m_aResizeTimer.Start();
1748 }
1749 else
1750 {
1751 Resize_( nAbsAvail, bRecalc );
1752 }
1753
1754 return true;
1755 }
1756
BordersChanged(sal_uInt16 nAbsAvail)1757 void SwHTMLTableLayout::BordersChanged( sal_uInt16 nAbsAvail )
1758 {
1759 m_bBordersChanged = true;
1760
1761 Resize( nAbsAvail, true/*bRecalc*/ );
1762 }
1763
1764 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1765