1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <sal/config.h>
21 #include <sal/log.hxx>
26 #include <fmtornt.hxx>
27 #include <fmtfsize.hxx>
31 #include <IDocumentLayoutAccess.hxx>
32 #include <swtable.hxx>
33 #include <rootfrm.hxx>
35 #include <poolfmt.hxx>
39 #include <viewopt.hxx>
40 #include <htmltbl.hxx>
42 #include <o3tl/numeric.hxx>
43 #include <osl/diagnose.h>
45 #include <tblrwcl.hxx>
48 using namespace ::com::sun::star
;
51 #define MAX_TABWIDTH (USHRT_MAX - 2001)
55 class SwHTMLTableLayoutConstraints
57 sal_uInt16 m_nRow
; // start row
58 sal_uInt16 m_nCol
; // start column
59 sal_uInt16 m_nColSpan
; // the column's COLSPAN
61 std::unique_ptr
<SwHTMLTableLayoutConstraints
> m_pNext
; // the next constraint
63 sal_uLong m_nMinNoAlign
, m_nMaxNoAlign
; // provisional result of AL-Pass 1
66 SwHTMLTableLayoutConstraints( sal_uLong nMin
, sal_uLong nMax
, sal_uInt16 nRow
,
67 sal_uInt16 nCol
, sal_uInt16 nColSp
);
69 sal_uLong
GetMinNoAlign() const { return m_nMinNoAlign
; }
70 sal_uLong
GetMaxNoAlign() const { return m_nMaxNoAlign
; }
72 SwHTMLTableLayoutConstraints
*InsertNext( SwHTMLTableLayoutConstraints
*pNxt
);
73 SwHTMLTableLayoutConstraints
* GetNext() const { return m_pNext
.get(); }
75 sal_uInt16
GetColSpan() const { return m_nColSpan
; }
76 sal_uInt16
GetColumn() const { return m_nCol
; }
81 SwHTMLTableLayoutCnts::SwHTMLTableLayoutCnts(const SwStartNode
*pSttNd
,
82 std::shared_ptr
<SwHTMLTableLayout
> xTab
,
84 std::shared_ptr
<SwHTMLTableLayoutCnts
> xNxt
) :
85 m_xNext( std::move(xNxt
) ), m_pBox( nullptr ), m_xTable( std::move(xTab
) ), m_pStartNode( pSttNd
),
86 m_nPass1Done( 0 ), m_nWidthSet( 0 ), m_bNoBreakTag( bNoBrTag
)
89 const SwStartNode
*SwHTMLTableLayoutCnts::GetStartNode() const
91 return m_pBox
? m_pBox
->GetSttNd() : m_pStartNode
;
94 SwHTMLTableLayoutCell::SwHTMLTableLayoutCell(std::shared_ptr
<SwHTMLTableLayoutCnts
> xCnts
,
95 sal_uInt16 nRSpan
, sal_uInt16 nCSpan
,
96 sal_uInt16 nWidth
, bool bPercentWidth
,
98 m_xContents(std::move(xCnts
)),
99 m_nRowSpan( nRSpan
), m_nColSpan( nCSpan
),
100 m_nWidthOption( nWidth
), m_bPercentWidthOption( bPercentWidth
),
101 m_bNoWrapOption( bNWrapOpt
)
104 SwHTMLTableLayoutColumn::SwHTMLTableLayoutColumn( sal_uInt16 nWidth
,
107 m_nMinNoAlign(MINLAY
), m_nMaxNoAlign(MINLAY
), m_nAbsMinNoAlign(MINLAY
),
108 m_nMin(0), m_nMax(0),
109 m_nAbsColWidth(0), m_nRelColWidth(0),
110 m_nWidthOption( nWidth
), m_bRelWidthOption( bRelWidth
),
111 m_bLeftBorder( bLBorder
)
114 SwHTMLTableLayoutConstraints::SwHTMLTableLayoutConstraints(sal_uLong nMin
, sal_uLong nMax
,
115 sal_uInt16 nRw
, sal_uInt16 nColumn
,
120 , m_nMinNoAlign(nMin
)
121 , m_nMaxNoAlign(nMax
)
124 SwHTMLTableLayoutConstraints
*SwHTMLTableLayoutConstraints::InsertNext(
125 SwHTMLTableLayoutConstraints
*pNxt
)
127 SwHTMLTableLayoutConstraints
*pPrev
= nullptr;
128 SwHTMLTableLayoutConstraints
*pConstr
= this;
131 if (pConstr
->m_nRow
> pNxt
->m_nRow
|| pConstr
->GetColumn() > pNxt
->GetColumn())
134 pConstr
= pConstr
->GetNext();
139 pNxt
->m_pNext
= std::move(pPrev
->m_pNext
);
140 pPrev
->m_pNext
.reset(pNxt
);
145 pNxt
->m_pNext
.reset(this);
152 SwHTMLTableLayout::SwHTMLTableLayout( const SwTable
* pTable
,
153 sal_uInt16 nRws
, sal_uInt16 nCls
,
154 bool bColsOpt
, bool bColTgs
,
155 sal_uInt16 nWdth
, bool bPercentWdth
,
156 sal_uInt16 nBorderOpt
, sal_uInt16 nCellPad
,
157 sal_uInt16 nCellSp
, SvxAdjust eAdjust
,
158 sal_uInt16 nLMargin
, sal_uInt16 nRMargin
,
159 sal_uInt16 nBWidth
, sal_uInt16 nLeftBWidth
,
160 sal_uInt16 nRightBWidth
)
161 : m_aResizeTimer("SwHTMLTableLayout m_aResizeTimer")
163 , m_aCells( static_cast<size_t>(nRws
)*nCls
)
164 , m_pSwTable( pTable
)
169 , m_nLeftMargin( nLMargin
)
170 , m_nRightMargin( nRMargin
)
171 , m_nInhAbsLeftSpace( 0 )
172 , m_nInhAbsRightSpace( 0 )
173 , m_nRelLeftFill( 0 )
174 , m_nRelRightFill( 0 )
175 , m_nRelTabWidth( 0 )
176 , m_nWidthOption( nWdth
)
177 , m_nCellPadding( nCellPad
)
178 , m_nCellSpacing( nCellSp
)
179 , m_nBorder( nBorderOpt
)
180 , m_nLeftBorderWidth( nLeftBWidth
)
181 , m_nRightBorderWidth( nRightBWidth
)
182 , m_nInhLeftBorderWidth( 0 )
183 , m_nInhRightBorderWidth( 0 )
184 , m_nBorderWidth( nBWidth
)
185 , m_nDelayedResizeAbsAvail( 0 )
186 , m_nLastResizeAbsAvail( 0 )
189 , m_eTableAdjust( eAdjust
)
190 , m_bColsOption( bColsOpt
)
191 , m_bColTags( bColTgs
)
192 , m_bPercentWidthOption( bPercentWdth
)
193 , m_bUseRelWidth( false )
194 , m_bMustResize( true )
195 , m_bExportable( true )
196 , m_bBordersChanged( false )
197 , m_bMayBeInFlyFrame( false )
198 , m_bDelayedResizeRecalc( false)
199 , m_bMustNotResize( false )
200 , m_bMustNotRecalc( false )
202 m_aResizeTimer
.SetInvokeHandler( LINK( this, SwHTMLTableLayout
,
203 DelayedResize_Impl
) );
206 SwHTMLTableLayout::~SwHTMLTableLayout()
210 /// The border widths are calculated like in Netscape:
211 /// Outer border: BORDER + CELLSPACING + CELLPADDING
212 /// Inner border: CELLSPACING + CELLPADDING
213 /// However, we respect the border widths in SW if bSwBorders is set,
214 /// so that we don't wrap wrongly.
215 /// We also need to respect the distance to the content. Even if
216 /// only the opposite side has a border.
217 sal_uInt16
SwHTMLTableLayout::GetLeftCellSpace( sal_uInt16 nCol
, sal_uInt16 nColSpan
,
218 bool bSwBorders
) const
220 sal_uInt16 nSpace
= m_nCellSpacing
+ m_nCellPadding
;
224 nSpace
= nSpace
+ m_nBorder
;
226 if( bSwBorders
&& nSpace
< m_nLeftBorderWidth
)
227 nSpace
= m_nLeftBorderWidth
;
229 else if( bSwBorders
)
231 if( GetColumn(nCol
)->HasLeftBorder() )
233 if( nSpace
< m_nBorderWidth
)
234 nSpace
= m_nBorderWidth
;
236 else if( nCol
+nColSpan
== m_nCols
&& m_nRightBorderWidth
&&
237 nSpace
< MIN_BORDER_DIST
)
239 OSL_ENSURE( !m_nCellPadding
, "GetLeftCellSpace: CELLPADDING!=0" );
240 // If the opposite side has a border we need to respect at
241 // least the minimum distance to the content.
242 // Additionally, we could also use nCellPadding for this.
243 nSpace
= MIN_BORDER_DIST
;
250 sal_uInt16
SwHTMLTableLayout::GetRightCellSpace( sal_uInt16 nCol
, sal_uInt16 nColSpan
,
251 bool bSwBorders
) const
253 sal_uInt16 nSpace
= m_nCellPadding
;
255 if( nCol
+nColSpan
== m_nCols
)
257 nSpace
+= m_nBorder
+ m_nCellSpacing
;
258 if( bSwBorders
&& nSpace
< m_nRightBorderWidth
)
259 nSpace
= m_nRightBorderWidth
;
261 else if( bSwBorders
&& GetColumn(nCol
)->HasLeftBorder() &&
262 nSpace
< MIN_BORDER_DIST
)
264 OSL_ENSURE( !m_nCellPadding
, "GetRightCellSpace: CELLPADDING!=0" );
265 // If the opposite side has a border we need to respect at
266 // least the minimum distance to the content.
267 // Additionally, we could also use nCellPadding for this.
268 nSpace
= MIN_BORDER_DIST
;
274 void SwHTMLTableLayout::AddBorderWidth( sal_uLong
&rMin
, sal_uLong
&rMax
,
276 sal_uInt16 nCol
, sal_uInt16 nColSpan
,
277 bool bSwBorders
) const
279 sal_uLong nAdd
= GetLeftCellSpace( nCol
, nColSpan
, bSwBorders
) +
280 GetRightCellSpace( nCol
, nColSpan
, bSwBorders
);
287 void SwHTMLTableLayout::SetBoxWidth( SwTableBox
*pBox
, sal_uInt16 nCol
,
288 sal_uInt16 nColSpan
) const
290 SwFrameFormat
*pFrameFormat
= pBox
->GetFrameFormat();
292 // calculate the box's width
293 SwTwips nFrameWidth
= 0;
295 nFrameWidth
+= GetColumn( nCol
++ )->GetRelColWidth();
298 pFrameFormat
->SetFormatAttr( SwFormatFrameSize( SwFrameSize::Variable
, nFrameWidth
, 0 ));
301 void SwHTMLTableLayout::GetAvail( sal_uInt16 nCol
, sal_uInt16 nColSpan
,
302 sal_uInt16
& rAbsAvail
, sal_uInt16
& rRelAvail
) const
306 for( sal_uInt16 i
=nCol
; i
<nCol
+nColSpan
;i
++ )
308 const SwHTMLTableLayoutColumn
*pColumn
= GetColumn(i
);
309 rAbsAvail
= rAbsAvail
+ pColumn
->GetAbsColWidth();
310 rRelAvail
= rRelAvail
+ pColumn
->GetRelColWidth();
314 sal_uInt16
SwHTMLTableLayout::GetBrowseWidthByVisArea( const SwDoc
& rDoc
)
316 SwViewShell
const *pVSh
= rDoc
.getIDocumentLayoutAccess().GetCurrentViewShell();
319 return o3tl::narrowing
<sal_uInt16
>(pVSh
->GetBrowseWidth());
325 sal_uInt16
SwHTMLTableLayout::GetBrowseWidth( const SwDoc
& rDoc
)
327 // If we have a layout, we can get the width from there.
328 const SwRootFrame
*pRootFrame
= rDoc
.getIDocumentLayoutAccess().GetCurrentLayout();
331 const SwFrame
*pPageFrame
= pRootFrame
->GetLower();
333 return o3tl::narrowing
<sal_uInt16
>(pPageFrame
->getFramePrintArea().Width());
337 // Assertion removed which state that no browse width is available.
338 // Investigation reveals that all calls can handle the case that no browse
339 // width is provided.
340 return GetBrowseWidthByVisArea( rDoc
);
343 sal_uInt16
SwHTMLTableLayout::GetBrowseWidthByTabFrame(
344 const SwTabFrame
& rTabFrame
) const
348 const SwFrame
*pUpper
= rTabFrame
.GetUpper();
349 if( MayBeInFlyFrame() && pUpper
->IsFlyFrame() &&
350 static_cast<const SwFlyFrame
*>(pUpper
)->GetAnchorFrame() )
352 // If the table is located within a self-created frame, the anchor's
353 // width is relevant not the frame's width.
354 // For paragraph-bound frames we don't respect paragraph indents.
355 const SwFrame
*pAnchor
= static_cast<const SwFlyFrame
*>(pUpper
)->GetAnchorFrame();
356 if( pAnchor
->IsTextFrame() )
357 nWidth
= pAnchor
->getFrameArea().Width();
359 nWidth
= pAnchor
->getFramePrintArea().Width();
363 nWidth
= pUpper
->getFramePrintArea().Width();
366 SwTwips nUpperDummy
= 0;
367 tools::Long nRightOffset
= 0,
369 rTabFrame
.CalcFlyOffsets(nUpperDummy
, nLeftOffset
, nRightOffset
, nullptr);
370 nWidth
-= (nLeftOffset
+ nRightOffset
);
372 return o3tl::narrowing
<sal_uInt16
>(std::min(nWidth
, SwTwips(SAL_MAX_UINT16
)));
375 sal_uInt16
SwHTMLTableLayout::GetBrowseWidthByTable( const SwDoc
& rDoc
) const
377 sal_uInt16 nBrowseWidth
= 0;
378 SwTabFrame
* pFrame
= SwIterator
<SwTabFrame
,SwFormat
>( *m_pSwTable
->GetFrameFormat() ).First();
381 nBrowseWidth
= GetBrowseWidthByTabFrame( *pFrame
);
385 nBrowseWidth
= SwHTMLTableLayout::GetBrowseWidth( rDoc
);
391 const SwStartNode
*SwHTMLTableLayout::GetAnyBoxStartNode() const
393 const SwStartNode
*pBoxSttNd
;
395 const SwTableBox
* pBox
= m_pSwTable
->GetTabLines()[0]->GetTabBoxes()[0];
396 while( nullptr == (pBoxSttNd
= pBox
->GetSttNd()) )
398 OSL_ENSURE( !pBox
->GetTabLines().empty(),
399 "Box without start node and lines" );
400 OSL_ENSURE( !pBox
->GetTabLines().front()->GetTabBoxes().empty(),
401 "Line without boxes" );
402 pBox
= pBox
->GetTabLines().front()->GetTabBoxes().front();
408 SwFrameFormat
*SwHTMLTableLayout::FindFlyFrameFormat() const
410 const SwTableNode
*pTableNd
= GetAnyBoxStartNode()->FindTableNode();
411 assert(pTableNd
&& "No Table-Node?");
412 return pTableNd
->GetFlyFormat();
415 static void lcl_GetMinMaxSize( sal_uLong
& rMinNoAlignCnts
, sal_uLong
& rMaxNoAlignCnts
,
416 sal_uLong
& rAbsMinNoAlignCnts
,
417 SwTextNode
const *pTextNd
, SwNodeOffset nIdx
, bool bNoBreak
)
419 pTextNd
->GetMinMaxSize( nIdx
, rMinNoAlignCnts
, rMaxNoAlignCnts
,
420 rAbsMinNoAlignCnts
);
421 OSL_ENSURE( rAbsMinNoAlignCnts
<= rMinNoAlignCnts
,
422 "GetMinMaxSize: absmin > min" );
423 OSL_ENSURE( rMinNoAlignCnts
<= rMaxNoAlignCnts
,
424 "GetMinMaxSize: max > min" );
426 // The maximal width for a <PRE> paragraph is the minimal width
427 const SwFormatColl
*pColl
= &pTextNd
->GetAnyFormatColl();
428 while( pColl
&& !pColl
->IsDefault() &&
429 (USER_FMT
& pColl
->GetPoolFormatId()) )
431 pColl
= static_cast<const SwFormatColl
*>(pColl
->DerivedFrom());
434 // <NOBR> in the whole cell apply to text but not to tables.
435 // Netscape only considers this for graphics.
436 if( (pColl
&& RES_POOLCOLL_HTML_PRE
==pColl
->GetPoolFormatId()) || bNoBreak
)
438 rMinNoAlignCnts
= rMaxNoAlignCnts
;
439 rAbsMinNoAlignCnts
= rMaxNoAlignCnts
;
443 void SwHTMLTableLayout::AutoLayoutPass1()
447 m_nMin
= m_nMax
= 0; // clear pass1 info
449 bool bFixRelWidths
= false;
452 std::unique_ptr
<SwHTMLTableLayoutConstraints
> xConstraints
;
454 for( i
=0; i
<m_nCols
; i
++ )
456 SwHTMLTableLayoutColumn
*pColumn
= GetColumn( i
);
457 pColumn
->ClearPass1Info( !HasColTags() );
458 sal_uInt16 nMinColSpan
= USHRT_MAX
; // Column count to which the calculated width refers to
459 sal_uInt16 nColSkip
= USHRT_MAX
; // How many columns need to be skipped
461 for( sal_uInt16 j
=0; j
<m_nRows
; j
++ )
463 SwHTMLTableLayoutCell
*pCell
= GetCell(j
,i
);
464 SwHTMLTableLayoutCnts
*pCnts
= pCell
->GetContents().get();
466 // We need to examine all rows in order to
467 // get the column that should be calculated next.
468 sal_uInt16 nColSpan
= pCell
->GetColSpan();
469 if( nColSpan
< nColSkip
)
472 if( !pCnts
|| !pCnts
->IsPass1Done(m_nPass1Done
) )
474 // The cell is empty or it's content was not edited
475 if( nColSpan
< nMinColSpan
)
476 nMinColSpan
= nColSpan
;
478 sal_uLong nMinNoAlignCell
= 0;
479 sal_uLong nMaxNoAlignCell
= 0;
480 sal_uLong nAbsMinNoAlignCell
= 0;
481 sal_uLong nMaxTableCell
= 0;
482 sal_uLong nAbsMinTableCell
= 0;
486 const SwStartNode
*pSttNd
= pCnts
->GetStartNode();
489 const SwDoc
& rDoc
= pSttNd
->GetDoc();
490 SwNodeOffset nIdx
= pSttNd
->GetIndex();
491 while (!rDoc
.GetNodes()[nIdx
]->IsEndNode())
493 SwTextNode
*pTextNd
= (rDoc
.GetNodes()[nIdx
])->GetTextNode();
496 sal_uLong nMinNoAlignCnts
= 0;
497 sal_uLong nMaxNoAlignCnts
= 0;
498 sal_uLong nAbsMinNoAlignCnts
= 0;
500 lcl_GetMinMaxSize( nMinNoAlignCnts
,
504 pCnts
->HasNoBreakTag() );
506 if( nMinNoAlignCnts
> nMinNoAlignCell
)
507 nMinNoAlignCell
= nMinNoAlignCnts
;
508 if( nMaxNoAlignCnts
> nMaxNoAlignCell
)
509 nMaxNoAlignCell
= nMaxNoAlignCnts
;
510 if( nAbsMinNoAlignCnts
> nAbsMinNoAlignCell
)
511 nAbsMinNoAlignCell
= nAbsMinNoAlignCnts
;
515 SwTableNode
*pTabNd
= (rDoc
.GetNodes()[nIdx
])->GetTableNode();
518 SwHTMLTableLayout
*pChild
= pTabNd
->GetTable().GetHTMLTableLayout();
521 pChild
->AutoLayoutPass1();
522 sal_uLong nMaxTableCnts
= pChild
->m_nMax
;
523 sal_uLong nAbsMinTableCnts
= pChild
->m_nMin
;
525 // A fixed table width is taken over as minimum and
526 // maximum at the same time
527 if( !pChild
->m_bPercentWidthOption
&& pChild
->m_nWidthOption
)
529 sal_uLong nTabWidth
= pChild
->m_nWidthOption
;
530 if( nTabWidth
>= nAbsMinTableCnts
)
532 nMaxTableCnts
= nTabWidth
;
533 nAbsMinTableCnts
= nTabWidth
;
537 nMaxTableCnts
= nAbsMinTableCnts
;
541 if( nMaxTableCnts
> nMaxTableCell
)
542 nMaxTableCell
= nMaxTableCnts
;
543 if( nAbsMinTableCnts
> nAbsMinTableCell
)
544 nAbsMinTableCell
= nAbsMinTableCnts
;
546 nIdx
= pTabNd
->EndOfSectionNode()->GetIndex();
552 else if (SwHTMLTableLayout
*pChild
= pCnts
->GetTable())
554 OSL_ENSURE( false, "Sub tables in HTML import?" );
555 pChild
->AutoLayoutPass1();
556 sal_uLong nMaxTableCnts
= pChild
->m_nMax
;
557 sal_uLong nAbsMinTableCnts
= pChild
->m_nMin
;
559 // A fixed table width is taken over as minimum and
560 // maximum at the same time
561 if( !pChild
->m_bPercentWidthOption
&& pChild
->m_nWidthOption
)
563 sal_uLong nTabWidth
= pChild
->m_nWidthOption
;
564 if( nTabWidth
>= nAbsMinTableCnts
)
566 nMaxTableCnts
= nTabWidth
;
567 nAbsMinTableCnts
= nTabWidth
;
571 nMaxTableCnts
= nAbsMinTableCnts
;
575 if( nMaxTableCnts
> nMaxTableCell
)
576 nMaxTableCell
= nMaxTableCnts
;
577 if( nAbsMinTableCnts
> nAbsMinTableCell
)
578 nAbsMinTableCell
= nAbsMinTableCnts
;
580 pCnts
->SetPass1Done( m_nPass1Done
);
581 pCnts
= pCnts
->GetNext().get();
584 // This code previously came after AddBorderWidth
585 // If a table's width is wider in a cell than what we've calculated
586 // for the other content we need to use the table's width.
587 if( nMaxTableCell
> nMaxNoAlignCell
)
588 nMaxNoAlignCell
= nMaxTableCell
;
589 if( nAbsMinTableCell
> nAbsMinNoAlignCell
)
591 nAbsMinNoAlignCell
= nAbsMinTableCell
;
592 if( nMinNoAlignCell
< nAbsMinNoAlignCell
)
593 nMinNoAlignCell
= nAbsMinNoAlignCell
;
594 if( nMaxNoAlignCell
< nMinNoAlignCell
)
595 nMaxNoAlignCell
= nMinNoAlignCell
;
597 // This code previously came after AddBorderWidth
599 bool bRelWidth
= pCell
->IsPercentWidthOption();
600 sal_uInt16 nWidth
= pCell
->GetWidthOption();
602 // A NOWRAP option applies to text and tables, but is
603 // not applied for fixed cell width.
604 // Instead, the stated cell width behaves like a minimal
606 if( pCell
->HasNoWrapOption() )
608 if( nWidth
==0 || bRelWidth
)
610 nMinNoAlignCell
= nMaxNoAlignCell
;
611 nAbsMinNoAlignCell
= nMaxNoAlignCell
;
615 if( nWidth
>nMinNoAlignCell
)
616 nMinNoAlignCell
= nWidth
;
617 if( nWidth
>nAbsMinNoAlignCell
)
618 nAbsMinNoAlignCell
= nWidth
;
622 // Respect minimum width for content
623 if( nMinNoAlignCell
< MINLAY
)
624 nMinNoAlignCell
= MINLAY
;
625 if( nMaxNoAlignCell
< MINLAY
)
626 nMaxNoAlignCell
= MINLAY
;
627 if( nAbsMinNoAlignCell
< MINLAY
)
628 nAbsMinNoAlignCell
= MINLAY
;
630 // Respect the border and distance to the content
631 AddBorderWidth( nMinNoAlignCell
, nMaxNoAlignCell
,
632 nAbsMinNoAlignCell
, i
, nColSpan
);
636 // take over the values directly
637 pColumn
->MergeMinMaxNoAlign( nMinNoAlignCell
,
639 nAbsMinNoAlignCell
);
641 // the widest WIDTH wins
643 pColumn
->MergeCellWidthOption( nWidth
, bRelWidth
);
647 // Process the data line by line from left to right at the end
649 // When which values is taken over will be explained further down.
650 if( !HasColTags() && nWidth
&& !bRelWidth
)
652 sal_uLong nAbsWidth
= nWidth
, nDummy
= 0, nDummy2
= 0;
653 AddBorderWidth( nAbsWidth
, nDummy
, nDummy2
,
654 i
, nColSpan
, false );
656 if( nAbsWidth
>= nMinNoAlignCell
)
658 nMaxNoAlignCell
= nAbsWidth
;
659 if( HasColsOption() )
660 nMinNoAlignCell
= nAbsWidth
;
662 else if( nAbsWidth
>= nAbsMinNoAlignCell
)
664 nMaxNoAlignCell
= nAbsWidth
;
665 nMinNoAlignCell
= nAbsWidth
;
669 nMaxNoAlignCell
= nAbsMinNoAlignCell
;
670 nMinNoAlignCell
= nAbsMinNoAlignCell
;
673 else if( HasColsOption() || HasColTags() )
674 nMinNoAlignCell
= nAbsMinNoAlignCell
;
676 SwHTMLTableLayoutConstraints
*pConstr
=
677 new SwHTMLTableLayoutConstraints( nMinNoAlignCell
,
678 nMaxNoAlignCell
, j
, i
, nColSpan
);
681 SwHTMLTableLayoutConstraints
* pConstraints
= xConstraints
->InsertNext(pConstr
);
682 // coverity[leaked_storage] - ownership transferred to pConstraints chain
683 xConstraints
.release();
684 xConstraints
.reset(pConstraints
);
687 xConstraints
.reset(pConstr
);
692 OSL_ENSURE( nMinColSpan
>0 && nColSkip
>0 && nColSkip
<= nMinColSpan
,
693 "Layout pass 1: Columns are being forgotten!" );
694 OSL_ENSURE( nMinColSpan
!=USHRT_MAX
,
695 "Layout pass 1: unnecessary pass through the loop or a bug" );
699 // There are cells with COLSPAN 1 and therefore also useful
702 // Take over values according to the following table (Netscape 4.0 pv 3):
704 // WIDTH: no COLS COLS
706 // none min = min min = absmin
707 // max = max max = max
709 // >= min min = min min = width
710 // max = width max = width
712 // >= absmin min = width(*) min = width
713 // max = width max = width
715 // < absmin min = absmin min = absmin
716 // max = absmin max = absmin
718 // (*) Netscape uses the minimum width without a break before
719 // the last graphic here. We don't have that (yet?),
720 // so we leave it set to width.
722 if( pColumn
->GetWidthOption() && !pColumn
->IsRelWidthOption() )
724 // Take over absolute widths as minimal and maximal widths.
725 sal_uLong nAbsWidth
= pColumn
->GetWidthOption();
726 sal_uLong nDummy
= 0, nDummy2
= 0;
727 AddBorderWidth( nAbsWidth
, nDummy
, nDummy2
, i
, 1, false );
729 if( nAbsWidth
>= pColumn
->GetMinNoAlign() )
731 pColumn
->SetMinMax( HasColsOption() ? nAbsWidth
732 : pColumn
->GetMinNoAlign(),
735 else if( nAbsWidth
>= pColumn
->GetAbsMinNoAlign() )
737 pColumn
->SetMinMax( nAbsWidth
, nAbsWidth
);
741 pColumn
->SetMinMax( pColumn
->GetAbsMinNoAlign(),
742 pColumn
->GetAbsMinNoAlign() );
747 pColumn
->SetMinMax( HasColsOption() ? pColumn
->GetAbsMinNoAlign()
748 : pColumn
->GetMinNoAlign(),
749 pColumn
->GetMaxNoAlign() );
752 else if( USHRT_MAX
!=nMinColSpan
)
754 // Can be anything != 0, because it is altered by the constraints.
755 pColumn
->SetMinMax( MINLAY
, MINLAY
);
757 // the next columns need not to be processed
761 m_nMin
+= pColumn
->GetMin();
762 m_nMax
+= pColumn
->GetMax();
763 if (pColumn
->IsRelWidthOption()) bFixRelWidths
= true;
766 // Now process the constraints
767 SwHTMLTableLayoutConstraints
*pConstr
= xConstraints
.get();
770 // At first we need to process the width in the same way
771 // as the column widths
772 sal_uInt16 nCol
= pConstr
->GetColumn();
773 sal_uInt16 nColSpan
= pConstr
->GetColSpan();
774 sal_uLong nConstrMin
= pConstr
->GetMinNoAlign();
775 sal_uLong nConstrMax
= pConstr
->GetMaxNoAlign();
777 // We get the hitherto width of the spanned columns
778 sal_uLong nColsMin
= 0;
779 sal_uLong nColsMax
= 0;
780 for( sal_uInt16 j
=nCol
; j
<nCol
+nColSpan
; j
++ )
782 SwHTMLTableLayoutColumn
*pColumn
= GetColumn( j
);
783 nColsMin
+= pColumn
->GetMin();
784 nColsMax
+= pColumn
->GetMax();
787 if( nColsMin
<nConstrMin
)
789 // Proportionately distribute the minimum value to the columns
790 sal_uLong nMinD
= nConstrMin
-nColsMin
;
792 if( nConstrMin
> nColsMax
)
794 // Proportional according to the minimum widths
795 sal_uInt16 nEndCol
= nCol
+nColSpan
;
796 sal_uLong nDiff
= nMinD
;
797 for( sal_uInt16 ic
=nCol
; ic
<nEndCol
; ic
++ )
799 SwHTMLTableLayoutColumn
*pColumn
= GetColumn( ic
);
801 sal_uLong nColMin
= pColumn
->GetMin();
802 sal_uLong nColMax
= pColumn
->GetMax();
809 throw o3tl::divide_by_zero();
810 nAdd
= (nColMin
* nMinD
) / nColsMin
;
818 OSL_ENSURE( nDiff
>= nAdd
, "Ooops: nDiff is not correct anymore" );
821 if( nColMax
< nColMin
)
830 pColumn
->SetMinMax( nColMin
, nColMax
);
835 // Proportional according to the difference of max and min
836 for( sal_uInt16 ic
=nCol
; ic
<nCol
+nColSpan
; ic
++ )
838 SwHTMLTableLayoutColumn
*pColumn
= GetColumn( ic
);
840 sal_uLong nDiff
= pColumn
->GetMax()-pColumn
->GetMin();
844 pColumn
->AddToMin( nDiff
);
846 OSL_ENSURE( pColumn
->GetMax() >= pColumn
->GetMin(),
847 "Why is the Column suddenly too narrow?" );
855 if( !HasColTags() && nColsMax
<nConstrMax
)
857 sal_uLong nMaxD
= nConstrMax
-nColsMax
;
859 for( sal_uInt16 ic
=nCol
; ic
<nCol
+nColSpan
; ic
++ )
861 SwHTMLTableLayoutColumn
*pColumn
= GetColumn( ic
);
863 m_nMax
-= pColumn
->GetMax();
865 pColumn
->AddToMax( (pColumn
->GetMax() * nMaxD
) / nColsMax
);
867 m_nMax
+= pColumn
->GetMax();
871 pConstr
= pConstr
->GetNext();
879 // To adapt the relative widths, in a first step we multiply the
880 // minimum width of all affected cells with the relative width
882 // Thus, the width ratio among the columns is correct.
884 // Furthermore, a factor is calculated that says by how much the
885 // cell has gotten wider than the minimum width.
887 // In the second step the calculated widths are divided by this
888 // factor. Thereby a cell's width is preserved and serves as a
889 // basis for the other cells.
890 // We only change the maximum widths here!
892 sal_uLong nAbsMin
= 0; // absolute minimum width of all widths with relative width
893 sal_uLong nRel
= 0; // sum of all relative widths of all columns
894 for( i
=0; i
<m_nCols
; i
++ )
896 SwHTMLTableLayoutColumn
*pColumn
= GetColumn( i
);
897 if( pColumn
->IsRelWidthOption() && pColumn
->GetWidthOption() )
899 nAbsMin
+= pColumn
->GetMin();
900 nRel
+= pColumn
->GetWidthOption();
904 sal_uLong nQuot
= ULONG_MAX
;
905 for( i
=0; i
<m_nCols
; i
++ )
907 SwHTMLTableLayoutColumn
*pColumn
= GetColumn( i
);
908 if( pColumn
->IsRelWidthOption() )
910 m_nMax
-= pColumn
->GetMax();
911 if( pColumn
->GetWidthOption() && pColumn
->GetMin() )
913 pColumn
->SetMax( nAbsMin
* pColumn
->GetWidthOption() );
914 sal_uLong nColQuot
= pColumn
->GetMax() / pColumn
->GetMin();
920 OSL_ENSURE( 0==nRel
|| nQuot
!=ULONG_MAX
,
921 "Where did the relative columns go?" );
922 for( i
=0; i
<m_nCols
; i
++ )
924 SwHTMLTableLayoutColumn
*pColumn
= GetColumn( i
);
925 if( pColumn
->IsRelWidthOption() )
927 if( pColumn
->GetWidthOption() )
928 pColumn
->SetMax( pColumn
->GetMax() / nQuot
);
930 pColumn
->SetMax( pColumn
->GetMin() );
931 OSL_ENSURE( pColumn
->GetMax() >= pColumn
->GetMin(),
932 "Maximum column width is lower than the minimum column width" );
933 m_nMax
+= pColumn
->GetMax();
939 sal_uInt16 nRel
= 0; // sum of the relative widths of all columns
940 sal_uInt16 nRelCols
= 0; // count of the columns with a relative setting
941 sal_uLong nRelMax
= 0; // fraction of the maximum of this column
942 for( i
=0; i
<m_nCols
; i
++ )
944 OSL_ENSURE( nRel
<=100, "relative width of all columns > 100%" );
945 SwHTMLTableLayoutColumn
*pColumn
= GetColumn( i
);
946 if( pColumn
->IsRelWidthOption() && pColumn
->GetWidthOption() )
948 // Make sure that the relative widths don't go above 100%
949 sal_uInt16 nColWidth
= pColumn
->GetWidthOption();
950 if( nRel
+nColWidth
> 100 )
952 nColWidth
= 100 - nRel
;
953 pColumn
->SetWidthOption( nColWidth
);
955 nRelMax
+= pColumn
->GetMax();
956 nRel
= nRel
+ nColWidth
;
959 else if( !pColumn
->GetMin() )
961 // The column is empty (so it was solely created by
962 // COLSPAN) and therefore must not be assigned a % width.
967 // If there are percentages left we distribute them to the columns
968 // that don't have a width setting. Like in Netscape we distribute
969 // the remaining percentages according to the ratio of the maximum
970 // width of the affected columns.
971 // For the maximum widths we also take the fixed-width columns
972 // into account. Is that correct?
973 sal_uLong nFixMax
= 0;
974 if( nRel
< 100 && nRelCols
< m_nCols
)
976 nFixMax
= m_nMax
- nRelMax
;
977 SAL_WARN_IF(!nFixMax
, "sw.core", "bad fixed width max");
981 sal_uInt16 nRelLeft
= 100 - nRel
;
982 for( i
=0; i
<m_nCols
; i
++ )
984 SwHTMLTableLayoutColumn
*pColumn
= GetColumn( i
);
985 if( !pColumn
->IsRelWidthOption() &&
986 !pColumn
->GetWidthOption() &&
989 // the next column gets the rest
990 sal_uInt16 nColWidth
=
991 o3tl::narrowing
<sal_uInt16
>((pColumn
->GetMax() * nRelLeft
) / nFixMax
);
992 pColumn
->SetWidthOption( nColWidth
);
997 // adjust the maximum widths now accordingly
998 sal_uLong nQuotMax
= ULONG_MAX
;
999 sal_uLong nOldMax
= m_nMax
;
1001 for( i
=0; i
<m_nCols
; i
++ )
1003 // Columns with a % setting are adapted accordingly.
1005 // - do not have a % setting and are located within a tables
1006 // with COLS and WIDTH, or
1007 // - their width is 0%
1008 // get set to the minimum width.
1009 SwHTMLTableLayoutColumn
*pColumn
= GetColumn( i
);
1010 if( pColumn
->IsRelWidthOption() && pColumn
->GetWidthOption() )
1013 sal_uLong nColQuotMax
;
1014 if( !m_nWidthOption
)
1016 nNewMax
= nOldMax
* pColumn
->GetWidthOption();
1017 nColQuotMax
= nNewMax
/ pColumn
->GetMax();
1021 nNewMax
= m_nMin
* pColumn
->GetWidthOption();
1022 nColQuotMax
= nNewMax
/ pColumn
->GetMin();
1024 pColumn
->SetMax( nNewMax
);
1025 if( nColQuotMax
< nQuotMax
)
1026 nQuotMax
= nColQuotMax
;
1028 else if( HasColsOption() || m_nWidthOption
||
1029 (pColumn
->IsRelWidthOption() &&
1030 !pColumn
->GetWidthOption()) )
1031 pColumn
->SetMax( pColumn
->GetMin() );
1033 // and divide by the quotient
1034 SAL_WARN_IF(!nQuotMax
, "sw.core", "Where did the relative columns go?");
1035 for (i
= 0; i
< m_nCols
; ++i
)
1037 SwHTMLTableLayoutColumn
*pColumn
= GetColumn( i
);
1038 if (pColumn
->IsRelWidthOption() && pColumn
->GetWidthOption() && nQuotMax
)
1040 pColumn
->SetMax( pColumn
->GetMax() / nQuotMax
);
1041 OSL_ENSURE( pColumn
->GetMax() >= pColumn
->GetMin(),
1042 "Minimum width is one column bigger than maximum" );
1043 if( pColumn
->GetMax() < pColumn
->GetMin() )
1044 pColumn
->SetMax( pColumn
->GetMin() );
1046 m_nMax
+= pColumn
->GetMax();
1051 //TODO: provide documentation
1054 @param nAbsAvail available space in TWIPS.
1055 @param nRelAvail available space related to USHRT_MAX or 0
1056 @param nAbsSpace fraction of nAbsAvail, which is reserved by the surrounding
1057 cell for the border and the distance to the paragraph.
1059 void SwHTMLTableLayout::AutoLayoutPass2( sal_uInt16 nAbsAvail
, sal_uInt16 nRelAvail
,
1060 sal_uInt16 nAbsLeftSpace
,
1061 sal_uInt16 nAbsRightSpace
,
1062 sal_uInt16 nParentInhAbsSpace
)
1064 // For a start we do a lot of plausibility tests
1066 // An absolute width always has to be passed
1067 OSL_ENSURE( nAbsAvail
, "AutoLayout pass 2: No absolute width given" );
1069 // A relative width must only be passed for tables within tables (?)
1070 OSL_ENSURE( IsTopTable() == (nRelAvail
==0),
1071 "AutoLayout pass 2: Relative width at table in table or the other way around" );
1073 // The table's minimum width must not be bigger than its maximum width
1074 OSL_ENSURE( m_nMin
<=m_nMax
, "AutoLayout pass 2: nMin > nMax" );
1076 // Remember the available width for which the table was calculated.
1077 // This is a good place as we pass by here for the initial calculation
1078 // of the table in the parser and for each Resize_ call.
1079 m_nLastResizeAbsAvail
= nAbsAvail
;
1081 // Step 1: The available space is readjusted for the left/right border,
1082 // possibly existing filler cells and distances.
1084 // Distance to the content and border
1085 sal_uInt16 nAbsLeftFill
= 0, nAbsRightFill
= 0;
1086 if( !IsTopTable() &&
1087 GetMin() + nAbsLeftSpace
+ nAbsRightSpace
<= nAbsAvail
)
1089 nAbsLeftFill
= nAbsLeftSpace
;
1090 nAbsRightFill
= nAbsRightSpace
;
1093 // Left and right distance
1094 if( m_nLeftMargin
|| m_nRightMargin
)
1098 // For the top table we always respect the borders, because we
1099 // never go below the table's minimum width.
1100 nAbsAvail
-= (m_nLeftMargin
+ m_nRightMargin
);
1102 else if( GetMin() + m_nLeftMargin
+ m_nRightMargin
<= nAbsAvail
)
1104 // Else, we only respect the borders if there's space available
1105 // for them (nMin has already been calculated!)
1106 nAbsLeftFill
= nAbsLeftFill
+ m_nLeftMargin
;
1107 nAbsRightFill
= nAbsRightFill
+ m_nRightMargin
;
1111 // Read just the available space
1113 m_nRelRightFill
= 0;
1114 if( !IsTopTable() && (nAbsLeftFill
>0 || nAbsRightFill
) )
1116 sal_uLong nAbsLeftFillL
= nAbsLeftFill
, nAbsRightFillL
= nAbsRightFill
;
1118 m_nRelLeftFill
= o3tl::narrowing
<sal_uInt16
>((nAbsLeftFillL
* nRelAvail
) / nAbsAvail
);
1119 m_nRelRightFill
= o3tl::narrowing
<sal_uInt16
>((nAbsRightFillL
* nRelAvail
) / nAbsAvail
);
1121 nAbsAvail
-= (nAbsLeftFill
+ nAbsRightFill
);
1123 nRelAvail
-= (m_nRelLeftFill
+ m_nRelRightFill
);
1126 // Step 2: Calculate the absolute table width.
1127 sal_uInt16 nAbsTabWidth
= 0;
1128 m_bUseRelWidth
= false;
1129 if( m_nWidthOption
)
1131 if( m_bPercentWidthOption
)
1133 OSL_ENSURE( m_nWidthOption
<=100, "Percentage value too high" );
1134 if( m_nWidthOption
> 100 )
1135 m_nWidthOption
= 100;
1137 // The absolute width is equal to the given percentage of
1138 // the available width.
1139 // Top tables only get a relative width if the available space
1140 // is *strictly larger* than the minimum width.
1142 // CAUTION: We need the "strictly larger" because changing from a
1143 // relative width to an absolute width by resizing would lead
1144 // to an infinite loop.
1146 // Because we do not call resize for tables in frames if the
1147 // frame has a non-relative width, we cannot play such games.
1149 // Let's play such games now anyway. We had a graphic in a 1% wide
1150 // table and it didn't fit in of course.
1151 nAbsTabWidth
= o3tl::narrowing
<sal_uInt16
>( (static_cast<sal_uLong
>(nAbsAvail
) * m_nWidthOption
) / 100 );
1153 ( /*MayBeInFlyFrame() ||*/ static_cast<sal_uLong
>(nAbsTabWidth
) > m_nMin
) )
1155 nRelAvail
= USHRT_MAX
;
1156 m_bUseRelWidth
= true;
1161 nAbsTabWidth
= m_nWidthOption
;
1162 if( nAbsTabWidth
> MAX_TABWIDTH
)
1163 nAbsTabWidth
= MAX_TABWIDTH
;
1165 // Tables within tables must never get wider than the available
1167 if( !IsTopTable() && nAbsTabWidth
> nAbsAvail
)
1168 nAbsTabWidth
= nAbsAvail
;
1172 OSL_ENSURE( IsTopTable() || nAbsTabWidth
<=nAbsAvail
,
1173 "AutoLayout pass 2: nAbsTabWidth > nAbsAvail for table in table" );
1174 OSL_ENSURE( !nRelAvail
|| nAbsTabWidth
<=nAbsAvail
,
1175 "AutoLayout pass 2: nAbsTabWidth > nAbsAvail for relative width" );
1177 // Catch for the two asserts above (we never know!)
1178 if( (!IsTopTable() || nRelAvail
>0) && nAbsTabWidth
>nAbsAvail
)
1179 nAbsTabWidth
= nAbsAvail
;
1181 // Step 3: Identify the column width and, if applicable, the absolute
1182 // and relative table widths.
1183 if( (!IsTopTable() && m_nMin
> static_cast<sal_uLong
>(nAbsAvail
)) ||
1184 m_nMin
> MAX_TABWIDTH
)
1187 // - an inner table's minimum is larger than the available space, or
1188 // - a top table's minimum is larger than USHORT_MAX the table
1189 // has to be adapted to the available space or USHORT_MAX.
1190 // We preserve the widths' ratio amongst themselves, however.
1192 nAbsTabWidth
= IsTopTable() ? MAX_TABWIDTH
: nAbsAvail
;
1193 m_nRelTabWidth
= (nRelAvail
? nRelAvail
: nAbsTabWidth
);
1195 // First of all, we check whether we can fit the layout constrains,
1196 // which are: Every cell's width excluding the borders must be at least
1199 sal_uLong nRealMin
= 0;
1200 for( sal_uInt16 i
=0; i
<m_nCols
; i
++ )
1202 sal_uLong nRealColMin
= MINLAY
, nDummy1
= 0, nDummy2
= 0;
1203 AddBorderWidth( nRealColMin
, nDummy1
, nDummy2
, i
, 1 );
1204 nRealMin
+= nRealColMin
;
1206 if( (nRealMin
>= nAbsTabWidth
) || (nRealMin
>= m_nMin
) )
1208 // "Rien ne va plus": we cannot get the minimum column widths
1209 // the layout wants to have.
1211 sal_uInt16 nAbs
= 0, nRel
= 0;
1212 SwHTMLTableLayoutColumn
*pColumn
;
1213 for( sal_uInt16 i
=0; i
<m_nCols
-1; i
++ )
1215 pColumn
= GetColumn( i
);
1216 sal_uLong nColMin
= pColumn
->GetMin();
1217 if( nColMin
<= USHRT_MAX
)
1219 pColumn
->SetAbsColWidth(
1220 o3tl::narrowing
<sal_uInt16
>((nColMin
* nAbsTabWidth
) / m_nMin
) );
1221 pColumn
->SetRelColWidth(
1222 o3tl::narrowing
<sal_uInt16
>((nColMin
* m_nRelTabWidth
) / m_nMin
) );
1226 double nColMinD
= nColMin
;
1227 pColumn
->SetAbsColWidth(
1228 o3tl::narrowing
<sal_uInt16
>((nColMinD
* nAbsTabWidth
) / m_nMin
) );
1229 pColumn
->SetRelColWidth(
1230 o3tl::narrowing
<sal_uInt16
>((nColMinD
* m_nRelTabWidth
) / m_nMin
) );
1233 nAbs
= nAbs
+ pColumn
->GetAbsColWidth();
1234 nRel
= nRel
+ pColumn
->GetRelColWidth();
1236 pColumn
= GetColumn( m_nCols
-1 );
1237 pColumn
->SetAbsColWidth( nAbsTabWidth
- nAbs
);
1238 pColumn
->SetRelColWidth( m_nRelTabWidth
- nRel
);
1242 sal_uLong nDistAbs
= nAbsTabWidth
- nRealMin
;
1243 sal_uLong nDistRel
= m_nRelTabWidth
- nRealMin
;
1244 sal_uLong nDistMin
= m_nMin
- nRealMin
;
1245 sal_uInt16 nAbs
= 0, nRel
= 0;
1246 SwHTMLTableLayoutColumn
*pColumn
;
1247 for( sal_uInt16 i
=0; i
<m_nCols
-1; i
++ )
1249 pColumn
= GetColumn( i
);
1250 sal_uLong nColMin
= pColumn
->GetMin();
1251 sal_uLong nRealColMin
= MINLAY
, nDummy1
= 0, nDummy2
= 0;
1252 AddBorderWidth( nRealColMin
, nDummy1
, nDummy2
, i
, 1 );
1254 if( nColMin
<= USHRT_MAX
)
1256 pColumn
->SetAbsColWidth(
1257 o3tl::narrowing
<sal_uInt16
>((((nColMin
-nRealColMin
) * nDistAbs
) / nDistMin
) + nRealColMin
) );
1258 pColumn
->SetRelColWidth(
1259 o3tl::narrowing
<sal_uInt16
>((((nColMin
-nRealColMin
) * nDistRel
) / nDistMin
) + nRealColMin
) );
1263 double nColMinD
= nColMin
;
1264 pColumn
->SetAbsColWidth(
1265 o3tl::narrowing
<sal_uInt16
>((((nColMinD
-nRealColMin
) * nDistAbs
) / nDistMin
) + nRealColMin
) );
1266 pColumn
->SetRelColWidth(
1267 o3tl::narrowing
<sal_uInt16
>((((nColMinD
-nRealColMin
) * nDistRel
) / nDistMin
) + nRealColMin
) );
1270 nAbs
= nAbs
+ pColumn
->GetAbsColWidth();
1271 nRel
= nRel
+ pColumn
->GetRelColWidth();
1273 pColumn
= GetColumn( m_nCols
-1 );
1274 pColumn
->SetAbsColWidth( nAbsTabWidth
- nAbs
);
1275 pColumn
->SetRelColWidth( m_nRelTabWidth
- nRel
);
1278 else if( m_nMax
<= static_cast<sal_uLong
>(nAbsTabWidth
? nAbsTabWidth
: nAbsAvail
) )
1281 // - the table has a fixed width and the table's maximum is
1283 //- the maximum is smaller than the available space,
1284 // we can take over the maximum as it is. Respectively
1285 // the table can only be adapted to the fixed width by
1286 // respecting the maximum.
1288 // No fixed width, use the maximum.
1290 nAbsTabWidth
= o3tl::narrowing
<sal_uInt16
>(m_nMax
);
1292 // A top table may also get wider then the available space.
1293 if( nAbsTabWidth
> nAbsAvail
)
1295 OSL_ENSURE( IsTopTable(),
1296 "Table in table should get wider than the surrounding cell." );
1297 nAbsAvail
= nAbsTabWidth
;
1300 // Only use the relative widths' fraction, that is used for the
1302 sal_uLong nAbsTabWidthL
= nAbsTabWidth
;
1306 throw o3tl::divide_by_zero();
1307 m_nRelTabWidth
= o3tl::narrowing
<sal_uInt16
>((nAbsTabWidthL
* nRelAvail
) / nAbsAvail
);
1310 m_nRelTabWidth
= nAbsTabWidth
;
1312 // Are there columns width a percentage setting and some without one?
1313 sal_uLong nFixMax
= m_nMax
;
1314 for( sal_uInt16 i
=0; i
<m_nCols
; i
++ )
1316 const SwHTMLTableLayoutColumn
*pColumn
= GetColumn( i
);
1317 if( pColumn
->IsRelWidthOption() && pColumn
->GetWidthOption()>0 )
1318 nFixMax
-= pColumn
->GetMax();
1321 if( nFixMax
> 0 && nFixMax
< m_nMax
)
1323 // Yes, distribute the to-be-distributed space only to the
1324 // columns with a percentage setting.
1326 // In this case (and in this case only) there are columns
1327 // that exactly keep their maximum width, that is they neither
1328 // get smaller nor wider. When calculating the absolute width
1329 // from the relative width we can get rounding errors.
1330 // To correct this, we first make the fixed widths compensate for
1331 // this error. We then fix the relative widths the same way.
1333 sal_uInt16 nAbs
= 0, nRel
= 0;
1334 sal_uInt16 nFixedCols
= 0;
1337 for( i
= 0; i
< m_nCols
; i
++ )
1339 SwHTMLTableLayoutColumn
*pColumn
= GetColumn( i
);
1340 if( !pColumn
->IsRelWidthOption() || !pColumn
->GetWidthOption() )
1342 // The column keeps its width.
1344 sal_uLong nColMax
= pColumn
->GetMax();
1345 pColumn
->SetAbsColWidth( o3tl::narrowing
<sal_uInt16
>(nColMax
) );
1347 sal_uLong nRelColWidth
=
1348 (nColMax
* m_nRelTabWidth
) / nAbsTabWidth
;
1349 sal_uLong nChkWidth
=
1350 (nRelColWidth
* nAbsTabWidth
) / m_nRelTabWidth
;
1351 if( nChkWidth
< nColMax
)
1353 else if( nChkWidth
> nColMax
)
1355 pColumn
->SetRelColWidth( o3tl::narrowing
<sal_uInt16
>(nRelColWidth
) );
1357 nAbs
= nAbs
+ o3tl::narrowing
<sal_uInt16
>(nColMax
);
1358 nRel
= nRel
+ o3tl::narrowing
<sal_uInt16
>(nRelColWidth
);
1362 // The to-be-distributed percentage of the maximum, the
1363 // relative and absolute widths. Here, nFixMax corresponds
1364 // to nAbs, so that we could've called it nAbs.
1365 // The code is, however, more readable like that.
1366 OSL_ENSURE( nFixMax
== nAbs
, "Two loops, two sums?" );
1367 sal_uLong nDistMax
= m_nMax
- nFixMax
;
1368 sal_uInt16 nDistAbsTabWidth
= nAbsTabWidth
- nAbs
;
1369 sal_uInt16 nDistRelTabWidth
= m_nRelTabWidth
- nRel
;
1371 for( i
=0; i
<m_nCols
; i
++ )
1373 SwHTMLTableLayoutColumn
*pColumn
= GetColumn( i
);
1374 if( pColumn
->IsRelWidthOption() && pColumn
->GetWidthOption() > 0 )
1376 // The column gets proportionately wider.
1378 if( nFixedCols
== m_nCols
)
1380 pColumn
->SetAbsColWidth( nAbsTabWidth
-nAbs
);
1381 pColumn
->SetRelColWidth( m_nRelTabWidth
-nRel
);
1385 sal_uLong nColMax
= pColumn
->GetMax();
1386 pColumn
->SetAbsColWidth(
1387 o3tl::narrowing
<sal_uInt16
>((nColMax
* nDistAbsTabWidth
) / nDistMax
) );
1388 pColumn
->SetRelColWidth(
1389 o3tl::narrowing
<sal_uInt16
>((nColMax
* nDistRelTabWidth
) / nDistMax
) );
1391 nAbs
= nAbs
+ pColumn
->GetAbsColWidth();
1392 nRel
= nRel
+ pColumn
->GetRelColWidth();
1395 OSL_ENSURE( m_nCols
==nFixedCols
, "Missed a column!" );
1397 else if (m_nCols
> 0)
1400 throw o3tl::divide_by_zero();
1401 // No. So distribute the space regularly among all columns.
1402 for (sal_uInt16 i
=0; i
< m_nCols
; ++i
)
1404 sal_uLong nColMax
= GetColumn( i
)->GetMax();
1405 GetColumn( i
)->SetAbsColWidth(
1406 o3tl::narrowing
<sal_uInt16
>((nColMax
* nAbsTabWidth
) / m_nMax
) );
1407 GetColumn( i
)->SetRelColWidth(
1408 o3tl::narrowing
<sal_uInt16
>((nColMax
* m_nRelTabWidth
) / m_nMax
) );
1414 // Proportionately distribute the space that extends over the minimum
1415 // width among the columns.
1417 nAbsTabWidth
= nAbsAvail
;
1418 if( nAbsTabWidth
< m_nMin
)
1419 nAbsTabWidth
= o3tl::narrowing
<sal_uInt16
>(m_nMin
);
1421 if( nAbsTabWidth
> nAbsAvail
)
1423 OSL_ENSURE( IsTopTable(),
1424 "A nested table should become wider than the available space." );
1425 nAbsAvail
= nAbsTabWidth
;
1428 sal_uLong nAbsTabWidthL
= nAbsTabWidth
;
1432 throw o3tl::divide_by_zero();
1433 m_nRelTabWidth
= o3tl::narrowing
<sal_uInt16
>((nAbsTabWidthL
* nRelAvail
) / nAbsAvail
);
1436 m_nRelTabWidth
= nAbsTabWidth
;
1437 double nW
= nAbsTabWidth
- m_nMin
;
1438 double nD
= (m_nMax
==m_nMin
? 1 : m_nMax
-m_nMin
);
1439 sal_uInt16 nAbs
= 0, nRel
= 0;
1440 for( sal_uInt16 i
=0; i
<m_nCols
-1; i
++ )
1442 double nd
= GetColumn( i
)->GetMax() - GetColumn( i
)->GetMin();
1443 sal_uLong nAbsColWidth
= GetColumn( i
)->GetMin() + static_cast<sal_uLong
>((nd
*nW
)/nD
);
1444 sal_uLong nRelColWidth
= nRelAvail
1445 ? (nAbsColWidth
* m_nRelTabWidth
) / nAbsTabWidth
1448 GetColumn( i
)->SetAbsColWidth( o3tl::narrowing
<sal_uInt16
>(nAbsColWidth
) );
1449 GetColumn( i
)->SetRelColWidth( o3tl::narrowing
<sal_uInt16
>(nRelColWidth
) );
1450 nAbs
= nAbs
+ o3tl::narrowing
<sal_uInt16
>(nAbsColWidth
);
1451 nRel
= nRel
+ o3tl::narrowing
<sal_uInt16
>(nRelColWidth
);
1453 GetColumn( m_nCols
-1 )->SetAbsColWidth( nAbsTabWidth
- nAbs
);
1454 GetColumn( m_nCols
-1 )->SetRelColWidth( m_nRelTabWidth
- nRel
);
1458 // Step 4: For nested tables we can have balancing cells on the
1459 // left or right. Here we calculate their width.
1460 m_nInhAbsLeftSpace
= 0;
1461 m_nInhAbsRightSpace
= 0;
1463 !(m_nRelLeftFill
>0 || m_nRelRightFill
>0 || nAbsTabWidth
<nAbsAvail
) )
1466 // Calculate the width of additional cells we use for
1467 // aligning inner tables.
1468 sal_uInt16 nAbsDist
= o3tl::narrowing
<sal_uInt16
>(nAbsAvail
-nAbsTabWidth
);
1469 sal_uInt16 nRelDist
= o3tl::narrowing
<sal_uInt16
>(nRelAvail
-m_nRelTabWidth
);
1470 sal_uInt16 nParentInhAbsLeftSpace
= 0, nParentInhAbsRightSpace
= 0;
1472 // Calculate the size and position of the additional cells.
1473 switch( m_eTableAdjust
)
1475 case SvxAdjust::Right
:
1476 nAbsLeftFill
= nAbsLeftFill
+ nAbsDist
;
1477 m_nRelLeftFill
= m_nRelLeftFill
+ nRelDist
;
1478 nParentInhAbsLeftSpace
= nParentInhAbsSpace
;
1480 case SvxAdjust::Center
:
1482 sal_uInt16 nAbsLeftDist
= nAbsDist
/ 2;
1483 nAbsLeftFill
= nAbsLeftFill
+ nAbsLeftDist
;
1484 nAbsRightFill
+= nAbsDist
- nAbsLeftDist
;
1485 sal_uInt16 nRelLeftDist
= nRelDist
/ 2;
1486 m_nRelLeftFill
= m_nRelLeftFill
+ nRelLeftDist
;
1487 m_nRelRightFill
+= nRelDist
- nRelLeftDist
;
1488 nParentInhAbsLeftSpace
= nParentInhAbsSpace
/ 2;
1489 nParentInhAbsRightSpace
= nParentInhAbsSpace
-
1490 nParentInhAbsLeftSpace
;
1493 case SvxAdjust::Left
:
1495 nAbsRightFill
= nAbsRightFill
+ nAbsDist
;
1496 m_nRelRightFill
= m_nRelRightFill
+ nRelDist
;
1497 nParentInhAbsRightSpace
= nParentInhAbsSpace
;
1501 // Filler widths are added to the outer columns, if there are no boxes
1502 // for them after the first pass (nWidth>0) or their width would become
1503 // too small or if there are COL tags and the filler width corresponds
1504 // to the border width.
1505 // In the last case we probably exported the table ourselves.
1506 if( m_nRelLeftFill
&&
1507 ( m_nWidthSet
>0 || nAbsLeftFill
<MINLAY
+m_nInhLeftBorderWidth
||
1508 (HasColTags() && nAbsLeftFill
< nAbsLeftSpace
+nParentInhAbsLeftSpace
+20) ) )
1510 SwHTMLTableLayoutColumn
*pColumn
= GetColumn( 0 );
1511 pColumn
->SetAbsColWidth( pColumn
->GetAbsColWidth()+nAbsLeftFill
);
1512 pColumn
->SetRelColWidth( pColumn
->GetRelColWidth()+m_nRelLeftFill
);
1514 m_nInhAbsLeftSpace
= nAbsLeftSpace
+ nParentInhAbsLeftSpace
;
1516 if( m_nRelRightFill
&&
1517 ( m_nWidthSet
>0 || nAbsRightFill
<MINLAY
+m_nInhRightBorderWidth
||
1518 (HasColTags() && nAbsRightFill
< nAbsRightSpace
+nParentInhAbsRightSpace
+20) ) )
1520 SwHTMLTableLayoutColumn
*pColumn
= GetColumn( m_nCols
-1 );
1521 pColumn
->SetAbsColWidth( pColumn
->GetAbsColWidth()+nAbsRightFill
);
1522 pColumn
->SetRelColWidth( pColumn
->GetRelColWidth()+m_nRelRightFill
);
1523 m_nRelRightFill
= 0;
1524 m_nInhAbsRightSpace
= nAbsRightSpace
+ nParentInhAbsRightSpace
;
1528 static void lcl_ResizeLine( const SwTableLine
* pLine
, SwTwips
*pWidth
);
1530 static void lcl_ResizeBox( const SwTableBox
* pBox
, SwTwips
* pWidth
)
1532 if( !pBox
->GetSttNd() )
1535 for( const SwTableLine
*pLine
: pBox
->GetTabLines() )
1536 lcl_ResizeLine( pLine
, &nWidth
);
1537 pBox
->GetFrameFormat()->SetFormatAttr( SwFormatFrameSize( SwFrameSize::Variable
, nWidth
, 0 ));
1538 *pWidth
= *pWidth
+ nWidth
;
1542 *pWidth
= *pWidth
+ pBox
->GetFrameFormat()->GetFrameSize().GetSize().Width();
1546 static void lcl_ResizeLine( const SwTableLine
* pLine
, SwTwips
*pWidth
)
1548 SwTwips nOldWidth
= *pWidth
;
1550 for( const SwTableBox
* pBox
: pLine
->GetTabBoxes() )
1551 lcl_ResizeBox(pBox
, pWidth
);
1553 SAL_WARN_IF( nOldWidth
&& std::abs(*pWidth
-nOldWidth
) >= COLFUZZY
, "sw.core",
1554 "A box's rows have all a different length" );
1557 void SwHTMLTableLayout::SetWidths( bool bCallPass2
, sal_uInt16 nAbsAvail
,
1558 sal_uInt16 nRelAvail
, sal_uInt16 nAbsLeftSpace
,
1559 sal_uInt16 nAbsRightSpace
,
1560 sal_uInt16 nParentInhAbsSpace
)
1562 // SetWidth must have been passed through once more for every cell in the
1566 // Step 0: If necessary, we call the layout algorithm of Pass2.
1568 AutoLayoutPass2( nAbsAvail
, nRelAvail
, nAbsLeftSpace
, nAbsRightSpace
,
1569 nParentInhAbsSpace
);
1571 // Step 1: Set the new width in all content boxes.
1572 // Because the boxes don't know anything about the HTML table structure,
1573 // we iterate over the HTML table structure.
1574 // For tables in tables in tables we call SetWidth recursively.
1575 for( sal_uInt16 i
=0; i
<m_nRows
; i
++ )
1577 for( sal_uInt16 j
=0; j
<m_nCols
; j
++ )
1579 SwHTMLTableLayoutCell
*pCell
= GetCell( i
, j
);
1581 SwHTMLTableLayoutCnts
* pContents
= pCell
->GetContents().get();
1582 while( pContents
&& !pContents
->IsWidthSet(m_nWidthSet
) )
1584 SwTableBox
*pBox
= pContents
->GetTableBox();
1587 SetBoxWidth( pBox
, j
, pCell
->GetColSpan() );
1589 else if (SwHTMLTableLayout
*pTable
= pContents
->GetTable())
1591 sal_uInt16 nAbs
= 0, nRel
= 0, nLSpace
= 0, nRSpace
= 0,
1595 sal_uInt16 nColSpan
= pCell
->GetColSpan();
1596 GetAvail( j
, nColSpan
, nAbs
, nRel
);
1597 nLSpace
= GetLeftCellSpace( j
, nColSpan
);
1598 nRSpace
= GetRightCellSpace( j
, nColSpan
);
1599 nInhSpace
= GetInhCellSpace( j
, nColSpan
);
1601 pTable
->SetWidths( bCallPass2
, nAbs
, nRel
,
1606 pContents
->SetWidthSet( m_nWidthSet
);
1607 pContents
= pContents
->GetNext().get();
1612 // Step 2: If we have a top table, we adapt the formats of the
1613 // non-content-boxes. Because they are not known in the HTML table
1614 // due to garbage collection there, we need the iterate over the
1616 // We also adapt the table frame format. For nested tables we set the
1617 // filler cell's width instead.
1621 SwTwips nCalcTabWidth
= 0;
1622 for( const SwTableLine
*pLine
: m_pSwTable
->GetTabLines() )
1623 lcl_ResizeLine( pLine
, &nCalcTabWidth
);
1624 SAL_WARN_IF( std::abs( m_nRelTabWidth
-nCalcTabWidth
) >= COLFUZZY
, "sw.core",
1625 "Table width is not equal to the row width" );
1627 // Lock the table format when altering it, or else the box formats
1628 // are altered again.
1629 // Also, we need to preserve a percent setting if it exists.
1630 SwFrameFormat
*pFrameFormat
= m_pSwTable
->GetFrameFormat();
1631 const_cast<SwTable
*>(m_pSwTable
)->LockModify();
1632 SwFormatFrameSize
aFrameSize( pFrameFormat
->GetFrameSize() );
1633 aFrameSize
.SetWidth( m_nRelTabWidth
);
1634 bool bRel
= m_bUseRelWidth
&&
1635 text::HoriOrientation::FULL
!=pFrameFormat
->GetHoriOrient().GetHoriOrient();
1636 aFrameSize
.SetWidthPercent( static_cast<sal_uInt8
>(bRel
? m_nWidthOption
: 0) );
1637 pFrameFormat
->SetFormatAttr( aFrameSize
);
1638 const_cast<SwTable
*>(m_pSwTable
)->UnlockModify();
1640 // If the table is located in a frame, we also need to adapt the
1642 if( MayBeInFlyFrame() )
1644 SwFrameFormat
*pFlyFrameFormat
= FindFlyFrameFormat();
1645 if( pFlyFrameFormat
)
1647 SwFormatFrameSize
aFlyFrameSize( SwFrameSize::Variable
, m_nRelTabWidth
, MINLAY
);
1649 if( m_bUseRelWidth
)
1651 // For percentage settings we set the width to the minimum.
1652 aFlyFrameSize
.SetWidth( m_nMin
> USHRT_MAX
? USHRT_MAX
1654 aFlyFrameSize
.SetWidthPercent( static_cast<sal_uInt8
>(m_nWidthOption
) );
1656 pFlyFrameFormat
->SetFormatAttr( aFlyFrameSize
);
1661 CheckBoxWidth(m_pSwTable
->GetTabLines(), *m_pSwTable
->GetFrameFormat());
1665 void SwHTMLTableLayout::Resize_( sal_uInt16 nAbsAvail
, bool bRecalc
)
1667 // If bRecalc is set, the table's content changed.
1668 // We need to execute pass 1 again.
1672 SwRootFrame
*pRoot
= GetDoc().getIDocumentLayoutAccess().GetCurrentViewShell()->GetLayout();
1673 if ( pRoot
&& pRoot
->IsCallbackActionEnabled() )
1674 pRoot
->StartAllAction();
1676 // Else we can set the widths, in which we have to run Pass 2 in each case.
1677 SetWidths( true, nAbsAvail
);
1679 if (pRoot
&& pRoot
->IsCallbackActionEnabled())
1680 pRoot
->EndAllAction();
1683 IMPL_LINK_NOARG( SwHTMLTableLayout
, DelayedResize_Impl
, Timer
*, void )
1685 m_aResizeTimer
.Stop();
1686 Resize_( m_nDelayedResizeAbsAvail
, m_bDelayedResizeRecalc
);
1689 bool SwHTMLTableLayout::Resize( sal_uInt16 nAbsAvail
, bool bRecalc
,
1690 bool bForce
, sal_uLong nDelay
)
1692 if( 0 == nAbsAvail
)
1694 OSL_ENSURE( IsTopTable(), "Resize must only be called for top tables!" );
1696 // May the table be resized at all? Or is it forced?
1697 if( m_bMustNotResize
&& !bForce
)
1700 // May the table be recalculated? Or is it forced?
1701 if( m_bMustNotRecalc
&& !bForce
)
1704 const SwDoc
& rDoc
= GetDoc();
1706 // If there is a layout, the root frame's size instead of the
1707 // VisArea's size was potentially passed.
1708 // If we're not in a frame we need to calculate the table for the VisArea,
1709 // because switching from relative to absolute wouldn't work.
1710 if( rDoc
.getIDocumentLayoutAccess().GetCurrentViewShell() && rDoc
.getIDocumentLayoutAccess().GetCurrentViewShell()->GetViewOptions()->getBrowseMode() )
1712 const sal_uInt16 nVisAreaWidth
= GetBrowseWidthByVisArea( rDoc
);
1713 if( nVisAreaWidth
< nAbsAvail
&& !FindFlyFrameFormat() )
1714 nAbsAvail
= nVisAreaWidth
;
1717 if( nDelay
==0 && m_aResizeTimer
.IsActive() )
1719 m_nDelayedResizeAbsAvail
= nAbsAvail
;
1724 // If the minimum or maximum should not be recalculated and
1725 // - the table's width never needs to be recalculated, or
1726 // - the table was already calculated for the passed width, or
1727 // - the available space is less or equal to the minimum width
1728 // and the table already has the minimum width, or
1729 // - the available space is larger than the maximum width and
1730 // the table already has the maximum width
1731 // nothing will happen to the table.
1732 if( !bRecalc
&& ( !m_bMustResize
||
1733 (m_nLastResizeAbsAvail
==nAbsAvail
) ||
1734 (nAbsAvail
<=m_nMin
&& m_nRelTabWidth
==m_nMin
) ||
1735 (!m_bPercentWidthOption
&& nAbsAvail
>=m_nMax
&& m_nRelTabWidth
==m_nMax
) ) )
1738 if( nDelay
==HTMLTABLE_RESIZE_NOW
)
1740 if( m_aResizeTimer
.IsActive() )
1741 m_aResizeTimer
.Stop();
1742 Resize_( nAbsAvail
, bRecalc
);
1744 else if( nDelay
> 0 )
1746 m_nDelayedResizeAbsAvail
= nAbsAvail
;
1747 m_bDelayedResizeRecalc
= bRecalc
;
1748 m_aResizeTimer
.SetTimeout( nDelay
);
1749 m_aResizeTimer
.Start();
1753 Resize_( nAbsAvail
, bRecalc
);
1759 void SwHTMLTableLayout::BordersChanged( sal_uInt16 nAbsAvail
)
1761 m_bBordersChanged
= true;
1763 Resize( nAbsAvail
, true/*bRecalc*/ );
1766 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */