diff options
Diffstat (limited to 'sw/source/core/doc/htmltbl.cxx')
-rw-r--r-- | sw/source/core/doc/htmltbl.cxx | 1772 |
1 files changed, 1772 insertions, 0 deletions
diff --git a/sw/source/core/doc/htmltbl.cxx b/sw/source/core/doc/htmltbl.cxx new file mode 100644 index 000000000..19f8e3305 --- /dev/null +++ b/sw/source/core/doc/htmltbl.cxx @@ -0,0 +1,1772 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/config.h> +#include <sal/log.hxx> + +#include <algorithm> +#include <memory> + +#include <fmtornt.hxx> +#include <fmtfsize.hxx> +#include <frmfmt.hxx> +#include <ndtxt.hxx> +#include <doc.hxx> +#include <IDocumentLayoutAccess.hxx> +#include <swtable.hxx> +#include <rootfrm.hxx> +#include <flyfrm.hxx> +#include <poolfmt.hxx> +#include <viewsh.hxx> +#include <tabfrm.hxx> +#include <viewopt.hxx> +#include <htmltbl.hxx> +#include <calbck.hxx> +#include <o3tl/numeric.hxx> +#include <osl/diagnose.h> +#ifdef DBG_UTIL +#include <tblrwcl.hxx> +#endif + +using namespace ::com::sun::star; + +#define COLFUZZY 20 +#define MAX_TABWIDTH (USHRT_MAX - 2001) + +namespace { + +class SwHTMLTableLayoutConstraints +{ + sal_uInt16 m_nRow; // start row + sal_uInt16 m_nCol; // start column + sal_uInt16 m_nColSpan; // the column's COLSPAN + + std::unique_ptr<SwHTMLTableLayoutConstraints> m_pNext; // the next constraint + + sal_uLong m_nMinNoAlign, m_nMaxNoAlign; // provisional result of AL-Pass 1 + +public: + SwHTMLTableLayoutConstraints( sal_uLong nMin, sal_uLong nMax, sal_uInt16 nRow, + sal_uInt16 nCol, sal_uInt16 nColSp ); + + sal_uLong GetMinNoAlign() const { return m_nMinNoAlign; } + sal_uLong GetMaxNoAlign() const { return m_nMaxNoAlign; } + + SwHTMLTableLayoutConstraints *InsertNext( SwHTMLTableLayoutConstraints *pNxt ); + SwHTMLTableLayoutConstraints* GetNext() const { return m_pNext.get(); } + + sal_uInt16 GetColSpan() const { return m_nColSpan; } + sal_uInt16 GetColumn() const { return m_nCol; } +}; + +} + +SwHTMLTableLayoutCnts::SwHTMLTableLayoutCnts(const SwStartNode *pSttNd, + std::shared_ptr<SwHTMLTableLayout> const& rTab, + bool bNoBrTag, + std::shared_ptr<SwHTMLTableLayoutCnts> const& rNxt ) : + m_xNext( rNxt ), m_pBox( nullptr ), m_xTable( rTab ), m_pStartNode( pSttNd ), + m_nPass1Done( 0 ), m_nWidthSet( 0 ), m_bNoBreakTag( bNoBrTag ) +{} + +const SwStartNode *SwHTMLTableLayoutCnts::GetStartNode() const +{ + return m_pBox ? m_pBox->GetSttNd() : m_pStartNode; +} + +SwHTMLTableLayoutCell::SwHTMLTableLayoutCell(std::shared_ptr<SwHTMLTableLayoutCnts> const& rCnts, + sal_uInt16 nRSpan, sal_uInt16 nCSpan, + sal_uInt16 nWidth, bool bPercentWidth, + bool bNWrapOpt ) : + m_xContents(rCnts), + m_nRowSpan( nRSpan ), m_nColSpan( nCSpan ), + m_nWidthOption( nWidth ), m_bPercentWidthOption( bPercentWidth ), + m_bNoWrapOption( bNWrapOpt ) +{} + +SwHTMLTableLayoutColumn::SwHTMLTableLayoutColumn( sal_uInt16 nWidth, + bool bRelWidth, + bool bLBorder ) : + m_nMinNoAlign(MINLAY), m_nMaxNoAlign(MINLAY), m_nAbsMinNoAlign(MINLAY), + m_nMin(0), m_nMax(0), + m_nAbsColWidth(0), m_nRelColWidth(0), + m_nWidthOption( nWidth ), m_bRelWidthOption( bRelWidth ), + m_bLeftBorder( bLBorder ) +{} + +SwHTMLTableLayoutConstraints::SwHTMLTableLayoutConstraints(sal_uLong nMin, sal_uLong nMax, + sal_uInt16 nRw, sal_uInt16 nColumn, + sal_uInt16 nColSp) + : m_nRow(nRw) + , m_nCol(nColumn) + , m_nColSpan(nColSp) + , m_nMinNoAlign(nMin) + , m_nMaxNoAlign(nMax) +{} + +SwHTMLTableLayoutConstraints *SwHTMLTableLayoutConstraints::InsertNext( + SwHTMLTableLayoutConstraints *pNxt ) +{ + SwHTMLTableLayoutConstraints *pPrev = nullptr; + SwHTMLTableLayoutConstraints *pConstr = this; + while( pConstr ) + { + if (pConstr->m_nRow > pNxt->m_nRow || pConstr->GetColumn() > pNxt->GetColumn()) + break; + pPrev = pConstr; + pConstr = pConstr->GetNext(); + } + + if( pPrev ) + { + pNxt->m_pNext = std::move(pPrev->m_pNext); + pPrev->m_pNext.reset(pNxt); + pConstr = this; + } + else + { + pNxt->m_pNext.reset(this); + pConstr = pNxt; + } + + return pConstr; +} + +SwHTMLTableLayout::SwHTMLTableLayout( const SwTable * pTable, + sal_uInt16 nRws, sal_uInt16 nCls, + bool bColsOpt, bool bColTgs, + sal_uInt16 nWdth, bool bPercentWdth, + 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 ) + : m_aResizeTimer("SwHTMLTableLayout m_aResizeTimer") + , m_aColumns( nCls ) + , m_aCells( static_cast<size_t>(nRws)*nCls ) + , m_pSwTable( pTable ) + , m_nMin( 0 ) + , m_nMax( 0 ) + , m_nRows( nRws ) + , m_nCols( nCls ) + , m_nLeftMargin( nLMargin ) + , m_nRightMargin( nRMargin ) + , m_nInhAbsLeftSpace( 0 ) + , m_nInhAbsRightSpace( 0 ) + , m_nRelLeftFill( 0 ) + , m_nRelRightFill( 0 ) + , m_nRelTabWidth( 0 ) + , m_nWidthOption( nWdth ) + , m_nCellPadding( nCellPad ) + , m_nCellSpacing( nCellSp ) + , m_nBorder( nBorderOpt ) + , m_nLeftBorderWidth( nLeftBWidth ) + , m_nRightBorderWidth( nRightBWidth ) + , m_nInhLeftBorderWidth( 0 ) + , m_nInhRightBorderWidth( 0 ) + , m_nBorderWidth( nBWidth ) + , m_nDelayedResizeAbsAvail( 0 ) + , m_nLastResizeAbsAvail( 0 ) + , m_nPass1Done( 0 ) + , m_nWidthSet( 0 ) + , m_eTableAdjust( eAdjust ) + , m_bColsOption( bColsOpt ) + , m_bColTags( bColTgs ) + , m_bPercentWidthOption( bPercentWdth ) + , m_bUseRelWidth( false ) + , m_bMustResize( true ) + , m_bExportable( true ) + , m_bBordersChanged( false ) + , m_bMayBeInFlyFrame( false ) + , m_bDelayedResizeRecalc( false) + , m_bMustNotResize( false ) + , m_bMustNotRecalc( false ) +{ + m_aResizeTimer.SetInvokeHandler( LINK( this, SwHTMLTableLayout, + DelayedResize_Impl ) ); +} + +SwHTMLTableLayout::~SwHTMLTableLayout() +{ +} + +/// The border widths are calculated like in Netscape: +/// Outer border: BORDER + CELLSPACING + CELLPADDING +/// Inner border: CELLSPACING + CELLPADDING +/// However, we respect the border widths in SW if bSwBorders is set, +/// so that we don't wrap wrongly. +/// We also need to respect the distance to the content. Even if +/// only the opposite side has a border. +sal_uInt16 SwHTMLTableLayout::GetLeftCellSpace( sal_uInt16 nCol, sal_uInt16 nColSpan, + bool bSwBorders ) const +{ + sal_uInt16 nSpace = m_nCellSpacing + m_nCellPadding; + + if( nCol == 0 ) + { + nSpace = nSpace + m_nBorder; + + if( bSwBorders && nSpace < m_nLeftBorderWidth ) + nSpace = m_nLeftBorderWidth; + } + else if( bSwBorders ) + { + if( GetColumn(nCol)->HasLeftBorder() ) + { + if( nSpace < m_nBorderWidth ) + nSpace = m_nBorderWidth; + } + else if( nCol+nColSpan == m_nCols && m_nRightBorderWidth && + nSpace < MIN_BORDER_DIST ) + { + OSL_ENSURE( !m_nCellPadding, "GetLeftCellSpace: CELLPADDING!=0" ); + // If the opposite side has a border we need to respect at + // least the minimum distance to the content. + // Additionally, we could also use nCellPadding for this. + nSpace = MIN_BORDER_DIST; + } + } + + return nSpace; +} + +sal_uInt16 SwHTMLTableLayout::GetRightCellSpace( sal_uInt16 nCol, sal_uInt16 nColSpan, + bool bSwBorders ) const +{ + sal_uInt16 nSpace = m_nCellPadding; + + if( nCol+nColSpan == m_nCols ) + { + nSpace += m_nBorder + m_nCellSpacing; + if( bSwBorders && nSpace < m_nRightBorderWidth ) + nSpace = m_nRightBorderWidth; + } + else if( bSwBorders && GetColumn(nCol)->HasLeftBorder() && + nSpace < MIN_BORDER_DIST ) + { + OSL_ENSURE( !m_nCellPadding, "GetRightCellSpace: CELLPADDING!=0" ); + // If the opposite side has a border we need to respect at + // least the minimum distance to the content. + // Additionally, we could also use nCellPadding for this. + nSpace = MIN_BORDER_DIST; + } + + return nSpace; +} + +void SwHTMLTableLayout::AddBorderWidth( sal_uLong &rMin, sal_uLong &rMax, + sal_uLong &rAbsMin, + sal_uInt16 nCol, sal_uInt16 nColSpan, + bool bSwBorders ) const +{ + sal_uLong nAdd = GetLeftCellSpace( nCol, nColSpan, bSwBorders ) + + GetRightCellSpace( nCol, nColSpan, bSwBorders ); + + rMin += nAdd; + rMax += nAdd; + rAbsMin += nAdd; +} + +void SwHTMLTableLayout::SetBoxWidth( SwTableBox *pBox, sal_uInt16 nCol, + sal_uInt16 nColSpan ) const +{ + SwFrameFormat *pFrameFormat = pBox->GetFrameFormat(); + + // calculate the box's width + SwTwips nFrameWidth = 0; + while( nColSpan-- ) + nFrameWidth += GetColumn( nCol++ )->GetRelColWidth(); + + // and reset + pFrameFormat->SetFormatAttr( SwFormatFrameSize( SwFrameSize::Variable, nFrameWidth, 0 )); +} + +void SwHTMLTableLayout::GetAvail( sal_uInt16 nCol, sal_uInt16 nColSpan, + sal_uInt16& rAbsAvail, sal_uInt16& rRelAvail ) const +{ + rAbsAvail = 0; + rRelAvail = 0; + for( sal_uInt16 i=nCol; i<nCol+nColSpan;i++ ) + { + const SwHTMLTableLayoutColumn *pColumn = GetColumn(i); + rAbsAvail = rAbsAvail + pColumn->GetAbsColWidth(); + rRelAvail = rRelAvail + pColumn->GetRelColWidth(); + } +} + +sal_uInt16 SwHTMLTableLayout::GetBrowseWidthByVisArea( const SwDoc& rDoc ) +{ + SwViewShell const *pVSh = rDoc.getIDocumentLayoutAccess().GetCurrentViewShell(); + if( pVSh ) + { + return o3tl::narrowing<sal_uInt16>(pVSh->GetBrowseWidth()); + } + + return 0; +} + +sal_uInt16 SwHTMLTableLayout::GetBrowseWidth( const SwDoc& rDoc ) +{ + // If we have a layout, we can get the width from there. + const SwRootFrame *pRootFrame = rDoc.getIDocumentLayoutAccess().GetCurrentLayout(); + if( pRootFrame ) + { + const SwFrame *pPageFrame = pRootFrame->GetLower(); + if( pPageFrame ) + return o3tl::narrowing<sal_uInt16>(pPageFrame->getFramePrintArea().Width()); + } + + // #i91658# + // Assertion removed which state that no browse width is available. + // Investigation reveals that all calls can handle the case that no browse + // width is provided. + return GetBrowseWidthByVisArea( rDoc ); +} + +sal_uInt16 SwHTMLTableLayout::GetBrowseWidthByTabFrame( + const SwTabFrame& rTabFrame ) const +{ + SwTwips nWidth = 0; + + const SwFrame *pUpper = rTabFrame.GetUpper(); + if( MayBeInFlyFrame() && pUpper->IsFlyFrame() && + static_cast<const SwFlyFrame *>(pUpper)->GetAnchorFrame() ) + { + // If the table is located within a self-created frame, the anchor's + // width is relevant not the frame's width. + // For paragraph-bound frames we don't respect paragraph indents. + const SwFrame *pAnchor = static_cast<const SwFlyFrame *>(pUpper)->GetAnchorFrame(); + if( pAnchor->IsTextFrame() ) + nWidth = pAnchor->getFrameArea().Width(); + else + nWidth = pAnchor->getFramePrintArea().Width(); + } + else + { + nWidth = pUpper->getFramePrintArea().Width(); + } + + SwTwips nUpperDummy = 0; + tools::Long nRightOffset = 0, + nLeftOffset = 0; + rTabFrame.CalcFlyOffsets(nUpperDummy, nLeftOffset, nRightOffset, nullptr); + nWidth -= (nLeftOffset + nRightOffset); + + return o3tl::narrowing<sal_uInt16>(std::min(nWidth, SwTwips(SAL_MAX_UINT16))); +} + +sal_uInt16 SwHTMLTableLayout::GetBrowseWidthByTable( const SwDoc& rDoc ) const +{ + sal_uInt16 nBrowseWidth = 0; + SwTabFrame* pFrame = SwIterator<SwTabFrame,SwFormat>( *m_pSwTable->GetFrameFormat() ).First(); + if( pFrame ) + { + nBrowseWidth = GetBrowseWidthByTabFrame( *pFrame ); + } + else + { + nBrowseWidth = SwHTMLTableLayout::GetBrowseWidth( rDoc ); + } + + return nBrowseWidth; +} + +const SwStartNode *SwHTMLTableLayout::GetAnyBoxStartNode() const +{ + const SwStartNode *pBoxSttNd; + + const SwTableBox* pBox = m_pSwTable->GetTabLines()[0]->GetTabBoxes()[0]; + while( nullptr == (pBoxSttNd = pBox->GetSttNd()) ) + { + OSL_ENSURE( !pBox->GetTabLines().empty(), + "Box without start node and lines" ); + OSL_ENSURE( !pBox->GetTabLines().front()->GetTabBoxes().empty(), + "Line without boxes" ); + pBox = pBox->GetTabLines().front()->GetTabBoxes().front(); + } + + return pBoxSttNd; +} + +SwFrameFormat *SwHTMLTableLayout::FindFlyFrameFormat() const +{ + const SwTableNode *pTableNd = GetAnyBoxStartNode()->FindTableNode(); + OSL_ENSURE( pTableNd, "No Table-Node?" ); + return pTableNd->GetFlyFormat(); +} + +static void lcl_GetMinMaxSize( sal_uLong& rMinNoAlignCnts, sal_uLong& rMaxNoAlignCnts, + sal_uLong& rAbsMinNoAlignCnts, + SwTextNode const *pTextNd, SwNodeOffset nIdx, bool bNoBreak ) +{ + pTextNd->GetMinMaxSize( nIdx, rMinNoAlignCnts, rMaxNoAlignCnts, + rAbsMinNoAlignCnts ); + OSL_ENSURE( rAbsMinNoAlignCnts <= rMinNoAlignCnts, + "GetMinMaxSize: absmin > min" ); + OSL_ENSURE( rMinNoAlignCnts <= rMaxNoAlignCnts, + "GetMinMaxSize: max > min" ); + + // The maximal width for a <PRE> paragraph is the minimal width + const SwFormatColl *pColl = &pTextNd->GetAnyFormatColl(); + while( pColl && !pColl->IsDefault() && + (USER_FMT & pColl->GetPoolFormatId()) ) + { + pColl = static_cast<const SwFormatColl *>(pColl->DerivedFrom()); + } + + // <NOBR> in the whole cell apply to text but not to tables. + // Netscape only considers this for graphics. + if( (pColl && RES_POOLCOLL_HTML_PRE==pColl->GetPoolFormatId()) || bNoBreak ) + { + rMinNoAlignCnts = rMaxNoAlignCnts; + rAbsMinNoAlignCnts = rMaxNoAlignCnts; + } +} + +void SwHTMLTableLayout::AutoLayoutPass1() +{ + m_nPass1Done++; + + m_nMin = m_nMax = 0; // clear pass1 info + + bool bFixRelWidths = false; + sal_uInt16 i; + + std::unique_ptr<SwHTMLTableLayoutConstraints> xConstraints; + + for( i=0; i<m_nCols; i++ ) + { + SwHTMLTableLayoutColumn *pColumn = GetColumn( i ); + pColumn->ClearPass1Info( !HasColTags() ); + sal_uInt16 nMinColSpan = USHRT_MAX; // Column count to which the calculated width refers to + sal_uInt16 nColSkip = USHRT_MAX; // How many columns need to be skipped + + for( sal_uInt16 j=0; j<m_nRows; j++ ) + { + SwHTMLTableLayoutCell *pCell = GetCell(j,i); + SwHTMLTableLayoutCnts *pCnts = pCell->GetContents().get(); + + // We need to examine all rows in order to + // get the column that should be calculated next. + sal_uInt16 nColSpan = pCell->GetColSpan(); + if( nColSpan < nColSkip ) + nColSkip = nColSpan; + + if( !pCnts || !pCnts->IsPass1Done(m_nPass1Done) ) + { + // The cell is empty or it's content was not edited + if( nColSpan < nMinColSpan ) + nMinColSpan = nColSpan; + + sal_uLong nMinNoAlignCell = 0; + sal_uLong nMaxNoAlignCell = 0; + sal_uLong nAbsMinNoAlignCell = 0; + sal_uLong nMaxTableCell = 0; + sal_uLong nAbsMinTableCell = 0; + + while( pCnts ) + { + const SwStartNode *pSttNd = pCnts->GetStartNode(); + if( pSttNd ) + { + const SwDoc& rDoc = pSttNd->GetDoc(); + SwNodeOffset nIdx = pSttNd->GetIndex(); + while (!rDoc.GetNodes()[nIdx]->IsEndNode()) + { + SwTextNode *pTextNd = (rDoc.GetNodes()[nIdx])->GetTextNode(); + if( pTextNd ) + { + sal_uLong nMinNoAlignCnts = 0; + sal_uLong nMaxNoAlignCnts = 0; + sal_uLong nAbsMinNoAlignCnts = 0; + + lcl_GetMinMaxSize( nMinNoAlignCnts, + nMaxNoAlignCnts, + nAbsMinNoAlignCnts, + pTextNd, nIdx, + pCnts->HasNoBreakTag() ); + + if( nMinNoAlignCnts > nMinNoAlignCell ) + nMinNoAlignCell = nMinNoAlignCnts; + if( nMaxNoAlignCnts > nMaxNoAlignCell ) + nMaxNoAlignCell = nMaxNoAlignCnts; + if( nAbsMinNoAlignCnts > nAbsMinNoAlignCell ) + nAbsMinNoAlignCell = nAbsMinNoAlignCnts; + } + else + { + SwTableNode *pTabNd = (rDoc.GetNodes()[nIdx])->GetTableNode(); + if( pTabNd ) + { + SwHTMLTableLayout *pChild = pTabNd->GetTable().GetHTMLTableLayout(); + if( pChild ) + { + pChild->AutoLayoutPass1(); + sal_uLong nMaxTableCnts = pChild->m_nMax; + sal_uLong nAbsMinTableCnts = pChild->m_nMin; + + // A fixed table width is taken over as minimum and + // maximum at the same time + if( !pChild->m_bPercentWidthOption && pChild->m_nWidthOption ) + { + sal_uLong nTabWidth = pChild->m_nWidthOption; + if( nTabWidth >= nAbsMinTableCnts ) + { + nMaxTableCnts = nTabWidth; + nAbsMinTableCnts = nTabWidth; + } + else + { + nMaxTableCnts = nAbsMinTableCnts; + } + } + + if( nMaxTableCnts > nMaxTableCell ) + nMaxTableCell = nMaxTableCnts; + if( nAbsMinTableCnts > nAbsMinTableCell ) + nAbsMinTableCell = nAbsMinTableCnts; + } + nIdx = pTabNd->EndOfSectionNode()->GetIndex(); + } + } + nIdx++; + } + } + else if (SwHTMLTableLayout *pChild = pCnts->GetTable()) + { + OSL_ENSURE( false, "Sub tables in HTML import?" ); + pChild->AutoLayoutPass1(); + sal_uLong nMaxTableCnts = pChild->m_nMax; + sal_uLong nAbsMinTableCnts = pChild->m_nMin; + + // A fixed table width is taken over as minimum and + // maximum at the same time + if( !pChild->m_bPercentWidthOption && pChild->m_nWidthOption ) + { + sal_uLong nTabWidth = pChild->m_nWidthOption; + if( nTabWidth >= nAbsMinTableCnts ) + { + nMaxTableCnts = nTabWidth; + nAbsMinTableCnts = nTabWidth; + } + else + { + nMaxTableCnts = nAbsMinTableCnts; + } + } + + if( nMaxTableCnts > nMaxTableCell ) + nMaxTableCell = nMaxTableCnts; + if( nAbsMinTableCnts > nAbsMinTableCell ) + nAbsMinTableCell = nAbsMinTableCnts; + } + pCnts->SetPass1Done( m_nPass1Done ); + pCnts = pCnts->GetNext().get(); + } + +// This code previously came after AddBorderWidth + // If a table's width is wider in a cell than what we've calculated + // for the other content we need to use the table's width. + if( nMaxTableCell > nMaxNoAlignCell ) + nMaxNoAlignCell = nMaxTableCell; + if( nAbsMinTableCell > nAbsMinNoAlignCell ) + { + nAbsMinNoAlignCell = nAbsMinTableCell; + if( nMinNoAlignCell < nAbsMinNoAlignCell ) + nMinNoAlignCell = nAbsMinNoAlignCell; + if( nMaxNoAlignCell < nMinNoAlignCell ) + nMaxNoAlignCell = nMinNoAlignCell; + } +// This code previously came after AddBorderWidth + + bool bRelWidth = pCell->IsPercentWidthOption(); + sal_uInt16 nWidth = pCell->GetWidthOption(); + + // A NOWRAP option applies to text and tables, but is + // not applied for fixed cell width. + // Instead, the stated cell width behaves like a minimal + // width. + if( pCell->HasNoWrapOption() ) + { + if( nWidth==0 || bRelWidth ) + { + nMinNoAlignCell = nMaxNoAlignCell; + nAbsMinNoAlignCell = nMaxNoAlignCell; + } + else + { + if( nWidth>nMinNoAlignCell ) + nMinNoAlignCell = nWidth; + if( nWidth>nAbsMinNoAlignCell ) + nAbsMinNoAlignCell = nWidth; + } + } + + // Respect minimum width for content + if( nMinNoAlignCell < MINLAY ) + nMinNoAlignCell = MINLAY; + if( nMaxNoAlignCell < MINLAY ) + nMaxNoAlignCell = MINLAY; + if( nAbsMinNoAlignCell < MINLAY ) + nAbsMinNoAlignCell = MINLAY; + + // Respect the border and distance to the content + AddBorderWidth( nMinNoAlignCell, nMaxNoAlignCell, + nAbsMinNoAlignCell, i, nColSpan ); + + if( 1==nColSpan ) + { + // take over the values directly + pColumn->MergeMinMaxNoAlign( nMinNoAlignCell, + nMaxNoAlignCell, + nAbsMinNoAlignCell ); + + // the widest WIDTH wins + if( !HasColTags() ) + pColumn->MergeCellWidthOption( nWidth, bRelWidth ); + } + else + { + // Process the data line by line from left to right at the end + + // When which values is taken over will be explained further down. + if( !HasColTags() && nWidth && !bRelWidth ) + { + sal_uLong nAbsWidth = nWidth, nDummy = 0, nDummy2 = 0; + AddBorderWidth( nAbsWidth, nDummy, nDummy2, + i, nColSpan, false ); + + if( nAbsWidth >= nMinNoAlignCell ) + { + nMaxNoAlignCell = nAbsWidth; + if( HasColsOption() ) + nMinNoAlignCell = nAbsWidth; + } + else if( nAbsWidth >= nAbsMinNoAlignCell ) + { + nMaxNoAlignCell = nAbsWidth; + nMinNoAlignCell = nAbsWidth; + } + else + { + nMaxNoAlignCell = nAbsMinNoAlignCell; + nMinNoAlignCell = nAbsMinNoAlignCell; + } + } + else if( HasColsOption() || HasColTags() ) + nMinNoAlignCell = nAbsMinNoAlignCell; + + SwHTMLTableLayoutConstraints *pConstr = + new SwHTMLTableLayoutConstraints( nMinNoAlignCell, + nMaxNoAlignCell, j, i, nColSpan ); + if (xConstraints) + { + SwHTMLTableLayoutConstraints* pConstraints = xConstraints->InsertNext(pConstr); + xConstraints.release(); + xConstraints.reset(pConstraints); + } + else + xConstraints.reset(pConstr); + } + } + } + + OSL_ENSURE( nMinColSpan>0 && nColSkip>0 && nColSkip <= nMinColSpan, + "Layout pass 1: Columns are being forgotten!" ); + OSL_ENSURE( nMinColSpan!=USHRT_MAX, + "Layout pass 1: unnecessary pass through the loop or a bug" ); + + if( 1==nMinColSpan ) + { + // There are cells with COLSPAN 1 and therefore also useful + // values in pColumn + + // Take over values according to the following table (Netscape 4.0 pv 3): + + // WIDTH: no COLS COLS + + // none min = min min = absmin + // max = max max = max + + // >= min min = min min = width + // max = width max = width + + // >= absmin min = width(*) min = width + // max = width max = width + + // < absmin min = absmin min = absmin + // max = absmin max = absmin + + // (*) Netscape uses the minimum width without a break before + // the last graphic here. We don't have that (yet?), + // so we leave it set to width. + + if( pColumn->GetWidthOption() && !pColumn->IsRelWidthOption() ) + { + // Take over absolute widths as minimal and maximal widths. + sal_uLong nAbsWidth = pColumn->GetWidthOption(); + sal_uLong nDummy = 0, nDummy2 = 0; + AddBorderWidth( nAbsWidth, nDummy, nDummy2, i, 1, false ); + + if( nAbsWidth >= pColumn->GetMinNoAlign() ) + { + pColumn->SetMinMax( HasColsOption() ? nAbsWidth + : pColumn->GetMinNoAlign(), + nAbsWidth ); + } + else if( nAbsWidth >= pColumn->GetAbsMinNoAlign() ) + { + pColumn->SetMinMax( nAbsWidth, nAbsWidth ); + } + else + { + pColumn->SetMinMax( pColumn->GetAbsMinNoAlign(), + pColumn->GetAbsMinNoAlign() ); + } + } + else + { + pColumn->SetMinMax( HasColsOption() ? pColumn->GetAbsMinNoAlign() + : pColumn->GetMinNoAlign(), + pColumn->GetMaxNoAlign() ); + } + } + else if( USHRT_MAX!=nMinColSpan ) + { + // Can be anything != 0, because it is altered by the constraints. + pColumn->SetMinMax( MINLAY, MINLAY ); + + // the next columns need not to be processed + i += (nColSkip-1); + } + + m_nMin += pColumn->GetMin(); + m_nMax += pColumn->GetMax(); + if (pColumn->IsRelWidthOption()) bFixRelWidths = true; + } + + // Now process the constraints + SwHTMLTableLayoutConstraints *pConstr = xConstraints.get(); + while( pConstr ) + { + // At first we need to process the width in the same way + // as the column widths + sal_uInt16 nCol = pConstr->GetColumn(); + sal_uInt16 nColSpan = pConstr->GetColSpan(); + sal_uLong nConstrMin = pConstr->GetMinNoAlign(); + sal_uLong nConstrMax = pConstr->GetMaxNoAlign(); + + // We get the hitherto width of the spanned columns + sal_uLong nColsMin = 0; + sal_uLong nColsMax = 0; + for( sal_uInt16 j=nCol; j<nCol+nColSpan; j++ ) + { + SwHTMLTableLayoutColumn *pColumn = GetColumn( j ); + nColsMin += pColumn->GetMin(); + nColsMax += pColumn->GetMax(); + } + + if( nColsMin<nConstrMin ) + { + // Proportionately distribute the minimum value to the columns + sal_uLong nMinD = nConstrMin-nColsMin; + + if( nConstrMin > nColsMax ) + { + // Proportional according to the minimum widths + sal_uInt16 nEndCol = nCol+nColSpan; + sal_uLong nDiff = nMinD; + for( sal_uInt16 ic=nCol; ic<nEndCol; ic++ ) + { + SwHTMLTableLayoutColumn *pColumn = GetColumn( ic ); + + sal_uLong nColMin = pColumn->GetMin(); + sal_uLong nColMax = pColumn->GetMax(); + + m_nMin -= nColMin; + sal_uLong nAdd; + if (ic < nEndCol-1) + { + if (nColsMin == 0) + throw o3tl::divide_by_zero(); + nAdd = (nColMin * nMinD) / nColsMin; + } + else + { + nAdd = nDiff; + } + nColMin += nAdd; + m_nMin += nColMin; + OSL_ENSURE( nDiff >= nAdd, "Ooops: nDiff is not correct anymore" ); + nDiff -= nAdd; + + if( nColMax < nColMin ) + { + m_nMax -= nColMax; + nColsMax -= nColMax; + nColMax = nColMin; + m_nMax += nColMax; + nColsMax += nColMax; + } + + pColumn->SetMinMax( nColMin, nColMax ); + } + } + else + { + // Proportional according to the difference of max and min + for( sal_uInt16 ic=nCol; ic<nCol+nColSpan; ic++ ) + { + SwHTMLTableLayoutColumn *pColumn = GetColumn( ic ); + + sal_uLong nDiff = pColumn->GetMax()-pColumn->GetMin(); + if( nMinD < nDiff ) + nDiff = nMinD; + + pColumn->AddToMin( nDiff ); + + OSL_ENSURE( pColumn->GetMax() >= pColumn->GetMin(), + "Why is the Column suddenly too narrow?" ); + + m_nMin += nDiff; + nMinD -= nDiff; + } + } + } + + if( !HasColTags() && nColsMax<nConstrMax ) + { + sal_uLong nMaxD = nConstrMax-nColsMax; + + for( sal_uInt16 ic=nCol; ic<nCol+nColSpan; ic++ ) + { + SwHTMLTableLayoutColumn *pColumn = GetColumn( ic ); + + m_nMax -= pColumn->GetMax(); + + pColumn->AddToMax( (pColumn->GetMax() * nMaxD) / nColsMax ); + + m_nMax += pColumn->GetMax(); + } + } + + pConstr = pConstr->GetNext(); + } + + if( !bFixRelWidths ) + return; + + if( HasColTags() ) + { + // To adapt the relative widths, in a first step we multiply the + // minimum width of all affected cells with the relative width + // of the column. + // Thus, the width ratio among the columns is correct. + + // Furthermore, a factor is calculated that says by how much the + // cell has gotten wider than the minimum width. + + // In the second step the calculated widths are divided by this + // factor. Thereby a cell's width is preserved and serves as a + // basis for the other cells. + // We only change the maximum widths here! + + sal_uLong nAbsMin = 0; // absolute minimum width of all widths with relative width + sal_uLong nRel = 0; // sum of all relative widths of all columns + for( i=0; i<m_nCols; i++ ) + { + SwHTMLTableLayoutColumn *pColumn = GetColumn( i ); + if( pColumn->IsRelWidthOption() && pColumn->GetWidthOption() ) + { + nAbsMin += pColumn->GetMin(); + nRel += pColumn->GetWidthOption(); + } + } + + sal_uLong nQuot = ULONG_MAX; + for( i=0; i<m_nCols; i++ ) + { + SwHTMLTableLayoutColumn *pColumn = GetColumn( i ); + if( pColumn->IsRelWidthOption() ) + { + m_nMax -= pColumn->GetMax(); + if( pColumn->GetWidthOption() && pColumn->GetMin() ) + { + pColumn->SetMax( nAbsMin * pColumn->GetWidthOption() ); + sal_uLong nColQuot = pColumn->GetMax() / pColumn->GetMin(); + if( nColQuot<nQuot ) + nQuot = nColQuot; + } + } + } + OSL_ENSURE( 0==nRel || nQuot!=ULONG_MAX, + "Where did the relative columns go?" ); + for( i=0; i<m_nCols; i++ ) + { + SwHTMLTableLayoutColumn *pColumn = GetColumn( i ); + if( pColumn->IsRelWidthOption() ) + { + if( pColumn->GetWidthOption() ) + pColumn->SetMax( pColumn->GetMax() / nQuot ); + else + pColumn->SetMax( pColumn->GetMin() ); + OSL_ENSURE( pColumn->GetMax() >= pColumn->GetMin(), + "Maximum column width is lower than the minimum column width" ); + m_nMax += pColumn->GetMax(); + } + } + } + else + { + sal_uInt16 nRel = 0; // sum of the relative widths of all columns + sal_uInt16 nRelCols = 0; // count of the columns with a relative setting + sal_uLong nRelMax = 0; // fraction of the maximum of this column + for( i=0; i<m_nCols; i++ ) + { + OSL_ENSURE( nRel<=100, "relative width of all columns > 100%" ); + SwHTMLTableLayoutColumn *pColumn = GetColumn( i ); + if( pColumn->IsRelWidthOption() && pColumn->GetWidthOption() ) + { + // Make sure that the relative widths don't go above 100% + sal_uInt16 nColWidth = pColumn->GetWidthOption(); + if( nRel+nColWidth > 100 ) + { + nColWidth = 100 - nRel; + pColumn->SetWidthOption( nColWidth ); + } + nRelMax += pColumn->GetMax(); + nRel = nRel + nColWidth; + nRelCols++; + } + else if( !pColumn->GetMin() ) + { + // The column is empty (so it was solely created by + // COLSPAN) and therefore must not be assigned a % width. + nRelCols++; + } + } + + // If there are percentages left we distribute them to the columns + // that don't have a width setting. Like in Netscape we distribute + // the remaining percentages according to the ratio of the maximum + // width of the affected columns. + // For the maximum widths we also take the fixed-width columns + // into account. Is that correct? + sal_uLong nFixMax = 0; + if( nRel < 100 && nRelCols < m_nCols ) + { + nFixMax = m_nMax - nRelMax; + SAL_WARN_IF(!nFixMax, "sw.core", "bad fixed width max"); + } + if (nFixMax) + { + sal_uInt16 nRelLeft = 100 - nRel; + for( i=0; i<m_nCols; i++ ) + { + SwHTMLTableLayoutColumn *pColumn = GetColumn( i ); + if( !pColumn->IsRelWidthOption() && + !pColumn->GetWidthOption() && + pColumn->GetMin() ) + { + // the next column gets the rest + sal_uInt16 nColWidth = + o3tl::narrowing<sal_uInt16>((pColumn->GetMax() * nRelLeft) / nFixMax); + pColumn->SetWidthOption( nColWidth ); + } + } + } + + // adjust the maximum widths now accordingly + sal_uLong nQuotMax = ULONG_MAX; + sal_uLong nOldMax = m_nMax; + m_nMax = 0; + for( i=0; i<m_nCols; i++ ) + { + // Columns with a % setting are adapted accordingly. + // Columns, that + // - do not have a % setting and are located within a tables + // with COLS and WIDTH, or + // - their width is 0% + // get set to the minimum width. + SwHTMLTableLayoutColumn *pColumn = GetColumn( i ); + if( pColumn->IsRelWidthOption() && pColumn->GetWidthOption() ) + { + sal_uLong nNewMax; + sal_uLong nColQuotMax; + if( !m_nWidthOption ) + { + nNewMax = nOldMax * pColumn->GetWidthOption(); + nColQuotMax = nNewMax / pColumn->GetMax(); + } + else + { + nNewMax = m_nMin * pColumn->GetWidthOption(); + nColQuotMax = nNewMax / pColumn->GetMin(); + } + pColumn->SetMax( nNewMax ); + if( nColQuotMax < nQuotMax ) + nQuotMax = nColQuotMax; + } + else if( HasColsOption() || m_nWidthOption || + (pColumn->IsRelWidthOption() && + !pColumn->GetWidthOption()) ) + pColumn->SetMax( pColumn->GetMin() ); + } + // and divide by the quotient + SAL_WARN_IF(!nQuotMax, "sw.core", "Where did the relative columns go?"); + for (i = 0; i < m_nCols; ++i) + { + SwHTMLTableLayoutColumn *pColumn = GetColumn( i ); + if (pColumn->IsRelWidthOption() && pColumn->GetWidthOption() && nQuotMax) + { + pColumn->SetMax( pColumn->GetMax() / nQuotMax ); + OSL_ENSURE( pColumn->GetMax() >= pColumn->GetMin(), + "Minimum width is one column bigger than maximum" ); + if( pColumn->GetMax() < pColumn->GetMin() ) + pColumn->SetMax( pColumn->GetMin() ); + } + m_nMax += pColumn->GetMax(); + } + } +} + +//TODO: provide documentation +/** + + @param nAbsAvail available space in TWIPS. + @param nRelAvail available space related to USHRT_MAX or 0 + @param nAbsSpace fraction of nAbsAvail, which is reserved by the surrounding + cell for the border and the distance to the paragraph. +*/ +void SwHTMLTableLayout::AutoLayoutPass2( sal_uInt16 nAbsAvail, sal_uInt16 nRelAvail, + sal_uInt16 nAbsLeftSpace, + sal_uInt16 nAbsRightSpace, + sal_uInt16 nParentInhAbsSpace ) +{ + // For a start we do a lot of plausibility tests + + // An absolute width always has to be passed + OSL_ENSURE( nAbsAvail, "AutoLayout pass 2: No absolute width given" ); + + // A relative width must only be passed for tables within tables (?) + OSL_ENSURE( IsTopTable() == (nRelAvail==0), + "AutoLayout pass 2: Relative width at table in table or the other way around" ); + + // The table's minimum width must not be bigger than its maximum width + OSL_ENSURE( m_nMin<=m_nMax, "AutoLayout pass 2: nMin > nMax" ); + + // Remember the available width for which the table was calculated. + // This is a good place as we pass by here for the initial calculation + // of the table in the parser and for each Resize_ call. + m_nLastResizeAbsAvail = nAbsAvail; + + // Step 1: The available space is readjusted for the left/right border, + // possibly existing filler cells and distances. + + // Distance to the content and border + sal_uInt16 nAbsLeftFill = 0, nAbsRightFill = 0; + if( !IsTopTable() && + GetMin() + nAbsLeftSpace + nAbsRightSpace <= nAbsAvail ) + { + nAbsLeftFill = nAbsLeftSpace; + nAbsRightFill = nAbsRightSpace; + } + + // Left and right distance + if( m_nLeftMargin || m_nRightMargin ) + { + if( IsTopTable() ) + { + // For the top table we always respect the borders, because we + // never go below the table's minimum width. + nAbsAvail -= (m_nLeftMargin + m_nRightMargin); + } + else if( GetMin() + m_nLeftMargin + m_nRightMargin <= nAbsAvail ) + { + // Else, we only respect the borders if there's space available + // for them (nMin has already been calculated!) + nAbsLeftFill = nAbsLeftFill + m_nLeftMargin; + nAbsRightFill = nAbsRightFill + m_nRightMargin; + } + } + + // Read just the available space + m_nRelLeftFill = 0; + m_nRelRightFill = 0; + if( !IsTopTable() && (nAbsLeftFill>0 || nAbsRightFill) ) + { + sal_uLong nAbsLeftFillL = nAbsLeftFill, nAbsRightFillL = nAbsRightFill; + + m_nRelLeftFill = o3tl::narrowing<sal_uInt16>((nAbsLeftFillL * nRelAvail) / nAbsAvail); + m_nRelRightFill = o3tl::narrowing<sal_uInt16>((nAbsRightFillL * nRelAvail) / nAbsAvail); + + nAbsAvail -= (nAbsLeftFill + nAbsRightFill); + if( nRelAvail ) + nRelAvail -= (m_nRelLeftFill + m_nRelRightFill); + } + + // Step 2: Calculate the absolute table width. + sal_uInt16 nAbsTabWidth = 0; + m_bUseRelWidth = false; + if( m_nWidthOption ) + { + if( m_bPercentWidthOption ) + { + OSL_ENSURE( m_nWidthOption<=100, "Percentage value too high" ); + if( m_nWidthOption > 100 ) + m_nWidthOption = 100; + + // The absolute width is equal to the given percentage of + // the available width. + // Top tables only get a relative width if the available space + // is *strictly larger* than the minimum width. + + // CAUTION: We need the "strictly larger" because changing from a + // relative width to an absolute width by resizing would lead + // to an infinite loop. + + // Because we do not call resize for tables in frames if the + // frame has a non-relative width, we cannot play such games. + + // Let's play such games now anyway. We had a graphic in a 1% wide + // table and it didn't fit in of course. + nAbsTabWidth = o3tl::narrowing<sal_uInt16>( (static_cast<sal_uLong>(nAbsAvail) * m_nWidthOption) / 100 ); + if( IsTopTable() && + ( /*MayBeInFlyFrame() ||*/ static_cast<sal_uLong>(nAbsTabWidth) > m_nMin ) ) + { + nRelAvail = USHRT_MAX; + m_bUseRelWidth = true; + } + } + else + { + nAbsTabWidth = m_nWidthOption; + if( nAbsTabWidth > MAX_TABWIDTH ) + nAbsTabWidth = MAX_TABWIDTH; + + // Tables within tables must never get wider than the available + // space. + if( !IsTopTable() && nAbsTabWidth > nAbsAvail ) + nAbsTabWidth = nAbsAvail; + } + } + + OSL_ENSURE( IsTopTable() || nAbsTabWidth<=nAbsAvail, + "AutoLayout pass 2: nAbsTabWidth > nAbsAvail for table in table" ); + OSL_ENSURE( !nRelAvail || nAbsTabWidth<=nAbsAvail, + "AutoLayout pass 2: nAbsTabWidth > nAbsAvail for relative width" ); + + // Catch for the two asserts above (we never know!) + if( (!IsTopTable() || nRelAvail>0) && nAbsTabWidth>nAbsAvail ) + nAbsTabWidth = nAbsAvail; + + // Step 3: Identify the column width and, if applicable, the absolute + // and relative table widths. + if( (!IsTopTable() && m_nMin > static_cast<sal_uLong>(nAbsAvail)) || + m_nMin > MAX_TABWIDTH ) + { + // If + // - an inner table's minimum is larger than the available space, or + // - a top table's minimum is larger than USHORT_MAX the table + // has to be adapted to the available space or USHORT_MAX. + // We preserve the widths' ratio amongst themselves, however. + + nAbsTabWidth = IsTopTable() ? MAX_TABWIDTH : nAbsAvail; + m_nRelTabWidth = (nRelAvail ? nRelAvail : nAbsTabWidth ); + + // First of all, we check whether we can fit the layout constrains, + // which are: Every cell's width excluding the borders must be at least + // MINLAY: + + sal_uLong nRealMin = 0; + for( sal_uInt16 i=0; i<m_nCols; i++ ) + { + sal_uLong nRealColMin = MINLAY, nDummy1 = 0, nDummy2 = 0; + AddBorderWidth( nRealColMin, nDummy1, nDummy2, i, 1 ); + nRealMin += nRealColMin; + } + if( (nRealMin >= nAbsTabWidth) || (nRealMin >= m_nMin) ) + { + // "Rien ne va plus": we cannot get the minimum column widths + // the layout wants to have. + + sal_uInt16 nAbs = 0, nRel = 0; + SwHTMLTableLayoutColumn *pColumn; + for( sal_uInt16 i=0; i<m_nCols-1; i++ ) + { + pColumn = GetColumn( i ); + sal_uLong nColMin = pColumn->GetMin(); + if( nColMin <= USHRT_MAX ) + { + pColumn->SetAbsColWidth( + o3tl::narrowing<sal_uInt16>((nColMin * nAbsTabWidth) / m_nMin) ); + pColumn->SetRelColWidth( + o3tl::narrowing<sal_uInt16>((nColMin * m_nRelTabWidth) / m_nMin) ); + } + else + { + double nColMinD = nColMin; + pColumn->SetAbsColWidth( + o3tl::narrowing<sal_uInt16>((nColMinD * nAbsTabWidth) / m_nMin) ); + pColumn->SetRelColWidth( + o3tl::narrowing<sal_uInt16>((nColMinD * m_nRelTabWidth) / m_nMin) ); + } + + nAbs = nAbs + pColumn->GetAbsColWidth(); + nRel = nRel + pColumn->GetRelColWidth(); + } + pColumn = GetColumn( m_nCols-1 ); + pColumn->SetAbsColWidth( nAbsTabWidth - nAbs ); + pColumn->SetRelColWidth( m_nRelTabWidth - nRel ); + } + else + { + sal_uLong nDistAbs = nAbsTabWidth - nRealMin; + sal_uLong nDistRel = m_nRelTabWidth - nRealMin; + sal_uLong nDistMin = m_nMin - nRealMin; + sal_uInt16 nAbs = 0, nRel = 0; + SwHTMLTableLayoutColumn *pColumn; + for( sal_uInt16 i=0; i<m_nCols-1; i++ ) + { + pColumn = GetColumn( i ); + sal_uLong nColMin = pColumn->GetMin(); + sal_uLong nRealColMin = MINLAY, nDummy1 = 0, nDummy2 = 0; + AddBorderWidth( nRealColMin, nDummy1, nDummy2, i, 1 ); + + if( nColMin <= USHRT_MAX ) + { + pColumn->SetAbsColWidth( + o3tl::narrowing<sal_uInt16>((((nColMin-nRealColMin) * nDistAbs) / nDistMin) + nRealColMin) ); + pColumn->SetRelColWidth( + o3tl::narrowing<sal_uInt16>((((nColMin-nRealColMin) * nDistRel) / nDistMin) + nRealColMin) ); + } + else + { + double nColMinD = nColMin; + pColumn->SetAbsColWidth( + o3tl::narrowing<sal_uInt16>((((nColMinD-nRealColMin) * nDistAbs) / nDistMin) + nRealColMin) ); + pColumn->SetRelColWidth( + o3tl::narrowing<sal_uInt16>((((nColMinD-nRealColMin) * nDistRel) / nDistMin) + nRealColMin) ); + } + + nAbs = nAbs + pColumn->GetAbsColWidth(); + nRel = nRel + pColumn->GetRelColWidth(); + } + pColumn = GetColumn( m_nCols-1 ); + pColumn->SetAbsColWidth( nAbsTabWidth - nAbs ); + pColumn->SetRelColWidth( m_nRelTabWidth - nRel ); + } + } + else if( m_nMax <= static_cast<sal_uLong>(nAbsTabWidth ? nAbsTabWidth : nAbsAvail) ) + { + // If + // - the table has a fixed width and the table's maximum is + // smaller, or + //- the maximum is smaller than the available space, + // we can take over the maximum as it is. Respectively + // the table can only be adapted to the fixed width by + // respecting the maximum. + + // No fixed width, use the maximum. + if( !nAbsTabWidth ) + nAbsTabWidth = o3tl::narrowing<sal_uInt16>(m_nMax); + + // A top table may also get wider then the available space. + if( nAbsTabWidth > nAbsAvail ) + { + OSL_ENSURE( IsTopTable(), + "Table in table should get wider than the surrounding cell." ); + nAbsAvail = nAbsTabWidth; + } + + // Only use the relative widths' fraction, that is used for the + // absolute width. + sal_uLong nAbsTabWidthL = nAbsTabWidth; + if (nRelAvail) + { + if (nAbsAvail == 0) + throw o3tl::divide_by_zero(); + m_nRelTabWidth = o3tl::narrowing<sal_uInt16>((nAbsTabWidthL * nRelAvail) / nAbsAvail); + } + else + m_nRelTabWidth = nAbsTabWidth; + + // Are there columns width a percentage setting and some without one? + sal_uLong nFixMax = m_nMax; + for( sal_uInt16 i=0; i<m_nCols; i++ ) + { + const SwHTMLTableLayoutColumn *pColumn = GetColumn( i ); + if( pColumn->IsRelWidthOption() && pColumn->GetWidthOption()>0 ) + nFixMax -= pColumn->GetMax(); + } + + if( nFixMax > 0 && nFixMax < m_nMax ) + { + // Yes, distribute the to-be-distributed space only to the + // columns with a percentage setting. + + // In this case (and in this case only) there are columns + // that exactly keep their maximum width, that is they neither + // get smaller nor wider. When calculating the absolute width + // from the relative width we can get rounding errors. + // To correct this, we first make the fixed widths compensate for + // this error. We then fix the relative widths the same way. + + sal_uInt16 nAbs = 0, nRel = 0; + sal_uInt16 nFixedCols = 0; + sal_uInt16 i; + + for( i = 0; i < m_nCols; i++ ) + { + SwHTMLTableLayoutColumn *pColumn = GetColumn( i ); + if( !pColumn->IsRelWidthOption() || !pColumn->GetWidthOption() ) + { + // The column keeps its width. + nFixedCols++; + sal_uLong nColMax = pColumn->GetMax(); + pColumn->SetAbsColWidth( o3tl::narrowing<sal_uInt16>(nColMax) ); + + sal_uLong nRelColWidth = + (nColMax * m_nRelTabWidth) / nAbsTabWidth; + sal_uLong nChkWidth = + (nRelColWidth * nAbsTabWidth) / m_nRelTabWidth; + if( nChkWidth < nColMax ) + nRelColWidth++; + else if( nChkWidth > nColMax ) + nRelColWidth--; + pColumn->SetRelColWidth( o3tl::narrowing<sal_uInt16>(nRelColWidth) ); + + nAbs = nAbs + o3tl::narrowing<sal_uInt16>(nColMax); + nRel = nRel + o3tl::narrowing<sal_uInt16>(nRelColWidth); + } + } + + // The to-be-distributed percentage of the maximum, the + // relative and absolute widths. Here, nFixMax corresponds + // to nAbs, so that we could've called it nAbs. + // The code is, however, more readable like that. + OSL_ENSURE( nFixMax == nAbs, "Two loops, two sums?" ); + sal_uLong nDistMax = m_nMax - nFixMax; + sal_uInt16 nDistAbsTabWidth = nAbsTabWidth - nAbs; + sal_uInt16 nDistRelTabWidth = m_nRelTabWidth - nRel; + + for( i=0; i<m_nCols; i++ ) + { + SwHTMLTableLayoutColumn *pColumn = GetColumn( i ); + if( pColumn->IsRelWidthOption() && pColumn->GetWidthOption() > 0 ) + { + // The column gets proportionately wider. + nFixedCols++; + if( nFixedCols == m_nCols ) + { + pColumn->SetAbsColWidth( nAbsTabWidth-nAbs ); + pColumn->SetRelColWidth( m_nRelTabWidth-nRel ); + } + else + { + sal_uLong nColMax = pColumn->GetMax(); + pColumn->SetAbsColWidth( + o3tl::narrowing<sal_uInt16>((nColMax * nDistAbsTabWidth) / nDistMax) ); + pColumn->SetRelColWidth( + o3tl::narrowing<sal_uInt16>((nColMax * nDistRelTabWidth) / nDistMax) ); + } + nAbs = nAbs + pColumn->GetAbsColWidth(); + nRel = nRel + pColumn->GetRelColWidth(); + } + } + OSL_ENSURE( m_nCols==nFixedCols, "Missed a column!" ); + } + else if (m_nCols > 0) + { + if (m_nMax == 0) + throw o3tl::divide_by_zero(); + // No. So distribute the space regularly among all columns. + for (sal_uInt16 i=0; i < m_nCols; ++i) + { + sal_uLong nColMax = GetColumn( i )->GetMax(); + GetColumn( i )->SetAbsColWidth( + o3tl::narrowing<sal_uInt16>((nColMax * nAbsTabWidth) / m_nMax) ); + GetColumn( i )->SetRelColWidth( + o3tl::narrowing<sal_uInt16>((nColMax * m_nRelTabWidth) / m_nMax) ); + } + } + } + else + { + // Proportionately distribute the space that extends over the minimum + // width among the columns. + if( !nAbsTabWidth ) + nAbsTabWidth = nAbsAvail; + if( nAbsTabWidth < m_nMin ) + nAbsTabWidth = o3tl::narrowing<sal_uInt16>(m_nMin); + + if( nAbsTabWidth > nAbsAvail ) + { + OSL_ENSURE( IsTopTable(), + "A nested table should become wider than the available space." ); + nAbsAvail = nAbsTabWidth; + } + + sal_uLong nAbsTabWidthL = nAbsTabWidth; + if (nRelAvail) + { + if (nAbsAvail == 0) + throw o3tl::divide_by_zero(); + m_nRelTabWidth = o3tl::narrowing<sal_uInt16>((nAbsTabWidthL * nRelAvail) / nAbsAvail); + } + else + m_nRelTabWidth = nAbsTabWidth; + double nW = nAbsTabWidth - m_nMin; + double nD = (m_nMax==m_nMin ? 1 : m_nMax-m_nMin); + sal_uInt16 nAbs = 0, nRel = 0; + for( sal_uInt16 i=0; i<m_nCols-1; i++ ) + { + double nd = GetColumn( i )->GetMax() - GetColumn( i )->GetMin(); + sal_uLong nAbsColWidth = GetColumn( i )->GetMin() + static_cast<sal_uLong>((nd*nW)/nD); + sal_uLong nRelColWidth = nRelAvail + ? (nAbsColWidth * m_nRelTabWidth) / nAbsTabWidth + : nAbsColWidth; + + GetColumn( i )->SetAbsColWidth( o3tl::narrowing<sal_uInt16>(nAbsColWidth) ); + GetColumn( i )->SetRelColWidth( o3tl::narrowing<sal_uInt16>(nRelColWidth) ); + nAbs = nAbs + o3tl::narrowing<sal_uInt16>(nAbsColWidth); + nRel = nRel + o3tl::narrowing<sal_uInt16>(nRelColWidth); + } + GetColumn( m_nCols-1 )->SetAbsColWidth( nAbsTabWidth - nAbs ); + GetColumn( m_nCols-1 )->SetRelColWidth( m_nRelTabWidth - nRel ); + + } + + // Step 4: For nested tables we can have balancing cells on the + // left or right. Here we calculate their width. + m_nInhAbsLeftSpace = 0; + m_nInhAbsRightSpace = 0; + if( IsTopTable() || + !(m_nRelLeftFill>0 || m_nRelRightFill>0 || nAbsTabWidth<nAbsAvail) ) + return; + + // Calculate the width of additional cells we use for + // aligning inner tables. + sal_uInt16 nAbsDist = o3tl::narrowing<sal_uInt16>(nAbsAvail-nAbsTabWidth); + sal_uInt16 nRelDist = o3tl::narrowing<sal_uInt16>(nRelAvail-m_nRelTabWidth); + sal_uInt16 nParentInhAbsLeftSpace = 0, nParentInhAbsRightSpace = 0; + + // Calculate the size and position of the additional cells. + switch( m_eTableAdjust ) + { + case SvxAdjust::Right: + nAbsLeftFill = nAbsLeftFill + nAbsDist; + m_nRelLeftFill = m_nRelLeftFill + nRelDist; + nParentInhAbsLeftSpace = nParentInhAbsSpace; + break; + case SvxAdjust::Center: + { + sal_uInt16 nAbsLeftDist = nAbsDist / 2; + nAbsLeftFill = nAbsLeftFill + nAbsLeftDist; + nAbsRightFill += nAbsDist - nAbsLeftDist; + sal_uInt16 nRelLeftDist = nRelDist / 2; + m_nRelLeftFill = m_nRelLeftFill + nRelLeftDist; + m_nRelRightFill += nRelDist - nRelLeftDist; + nParentInhAbsLeftSpace = nParentInhAbsSpace / 2; + nParentInhAbsRightSpace = nParentInhAbsSpace - + nParentInhAbsLeftSpace; + } + break; + case SvxAdjust::Left: + default: + nAbsRightFill = nAbsRightFill + nAbsDist; + m_nRelRightFill = m_nRelRightFill + nRelDist; + nParentInhAbsRightSpace = nParentInhAbsSpace; + break; + } + + // Filler widths are added to the outer columns, if there are no boxes + // for them after the first pass (nWidth>0) or their width would become + // too small or if there are COL tags and the filler width corresponds + // to the border width. + // In the last case we probably exported the table ourselves. + if( m_nRelLeftFill && + ( m_nWidthSet>0 || nAbsLeftFill<MINLAY+m_nInhLeftBorderWidth || + (HasColTags() && nAbsLeftFill < nAbsLeftSpace+nParentInhAbsLeftSpace+20) ) ) + { + SwHTMLTableLayoutColumn *pColumn = GetColumn( 0 ); + pColumn->SetAbsColWidth( pColumn->GetAbsColWidth()+nAbsLeftFill ); + pColumn->SetRelColWidth( pColumn->GetRelColWidth()+m_nRelLeftFill ); + m_nRelLeftFill = 0; + m_nInhAbsLeftSpace = nAbsLeftSpace + nParentInhAbsLeftSpace; + } + if( m_nRelRightFill && + ( m_nWidthSet>0 || nAbsRightFill<MINLAY+m_nInhRightBorderWidth || + (HasColTags() && nAbsRightFill < nAbsRightSpace+nParentInhAbsRightSpace+20) ) ) + { + SwHTMLTableLayoutColumn *pColumn = GetColumn( m_nCols-1 ); + pColumn->SetAbsColWidth( pColumn->GetAbsColWidth()+nAbsRightFill ); + pColumn->SetRelColWidth( pColumn->GetRelColWidth()+m_nRelRightFill ); + m_nRelRightFill = 0; + m_nInhAbsRightSpace = nAbsRightSpace + nParentInhAbsRightSpace; + } +} + +static void lcl_ResizeLine( const SwTableLine* pLine, SwTwips *pWidth ); + +static void lcl_ResizeBox( const SwTableBox* pBox, SwTwips* pWidth ) +{ + if( !pBox->GetSttNd() ) + { + SwTwips nWidth = 0; + for( const SwTableLine *pLine : pBox->GetTabLines() ) + lcl_ResizeLine( pLine, &nWidth ); + pBox->GetFrameFormat()->SetFormatAttr( SwFormatFrameSize( SwFrameSize::Variable, nWidth, 0 )); + *pWidth = *pWidth + nWidth; + } + else + { + *pWidth = *pWidth + pBox->GetFrameFormat()->GetFrameSize().GetSize().Width(); + } +} + +static void lcl_ResizeLine( const SwTableLine* pLine, SwTwips *pWidth ) +{ + SwTwips nOldWidth = *pWidth; + *pWidth = 0; + for( const SwTableBox* pBox : pLine->GetTabBoxes() ) + lcl_ResizeBox(pBox, pWidth ); + + SAL_WARN_IF( nOldWidth && std::abs(*pWidth-nOldWidth) >= COLFUZZY, "sw.core", + "A box's rows have all a different length" ); +} + +void SwHTMLTableLayout::SetWidths( bool bCallPass2, sal_uInt16 nAbsAvail, + sal_uInt16 nRelAvail, sal_uInt16 nAbsLeftSpace, + sal_uInt16 nAbsRightSpace, + sal_uInt16 nParentInhAbsSpace ) +{ + // SetWidth must have been passed through once more for every cell in the + // end. + m_nWidthSet++; + + // Step 0: If necessary, we call the layout algorithm of Pass2. + if( bCallPass2 ) + AutoLayoutPass2( nAbsAvail, nRelAvail, nAbsLeftSpace, nAbsRightSpace, + nParentInhAbsSpace ); + + // Step 1: Set the new width in all content boxes. + // Because the boxes don't know anything about the HTML table structure, + // we iterate over the HTML table structure. + // For tables in tables in tables we call SetWidth recursively. + for( sal_uInt16 i=0; i<m_nRows; i++ ) + { + for( sal_uInt16 j=0; j<m_nCols; j++ ) + { + SwHTMLTableLayoutCell *pCell = GetCell( i, j ); + + SwHTMLTableLayoutCnts* pContents = pCell->GetContents().get(); + while( pContents && !pContents->IsWidthSet(m_nWidthSet) ) + { + SwTableBox *pBox = pContents->GetTableBox(); + if( pBox ) + { + SetBoxWidth( pBox, j, pCell->GetColSpan() ); + } + else if (SwHTMLTableLayout *pTable = pContents->GetTable()) + { + sal_uInt16 nAbs = 0, nRel = 0, nLSpace = 0, nRSpace = 0, + nInhSpace = 0; + if( bCallPass2 ) + { + sal_uInt16 nColSpan = pCell->GetColSpan(); + GetAvail( j, nColSpan, nAbs, nRel ); + nLSpace = GetLeftCellSpace( j, nColSpan ); + nRSpace = GetRightCellSpace( j, nColSpan ); + nInhSpace = GetInhCellSpace( j, nColSpan ); + } + pTable->SetWidths( bCallPass2, nAbs, nRel, + nLSpace, nRSpace, + nInhSpace ); + } + + pContents->SetWidthSet( m_nWidthSet ); + pContents = pContents->GetNext().get(); + } + } + } + + // Step 2: If we have a top table, we adapt the formats of the + // non-content-boxes. Because they are not known in the HTML table + // due to garbage collection there, we need the iterate over the + // whole table. + // We also adapt the table frame format. For nested tables we set the + // filler cell's width instead. + if( !IsTopTable() ) + return; + + SwTwips nCalcTabWidth = 0; + for( const SwTableLine *pLine : m_pSwTable->GetTabLines() ) + lcl_ResizeLine( pLine, &nCalcTabWidth ); + SAL_WARN_IF( std::abs( m_nRelTabWidth-nCalcTabWidth ) >= COLFUZZY, "sw.core", + "Table width is not equal to the row width" ); + + // Lock the table format when altering it, or else the box formats + // are altered again. + // Also, we need to preserve a percent setting if it exists. + SwFrameFormat *pFrameFormat = m_pSwTable->GetFrameFormat(); + const_cast<SwTable *>(m_pSwTable)->LockModify(); + SwFormatFrameSize aFrameSize( pFrameFormat->GetFrameSize() ); + aFrameSize.SetWidth( m_nRelTabWidth ); + bool bRel = m_bUseRelWidth && + text::HoriOrientation::FULL!=pFrameFormat->GetHoriOrient().GetHoriOrient(); + aFrameSize.SetWidthPercent( static_cast<sal_uInt8>(bRel ? m_nWidthOption : 0) ); + pFrameFormat->SetFormatAttr( aFrameSize ); + const_cast<SwTable *>(m_pSwTable)->UnlockModify(); + + // If the table is located in a frame, we also need to adapt the + // frame's width. + if( MayBeInFlyFrame() ) + { + SwFrameFormat *pFlyFrameFormat = FindFlyFrameFormat(); + if( pFlyFrameFormat ) + { + SwFormatFrameSize aFlyFrameSize( SwFrameSize::Variable, m_nRelTabWidth, MINLAY ); + + if( m_bUseRelWidth ) + { + // For percentage settings we set the width to the minimum. + aFlyFrameSize.SetWidth( m_nMin > USHRT_MAX ? USHRT_MAX + : m_nMin ); + aFlyFrameSize.SetWidthPercent( static_cast<sal_uInt8>(m_nWidthOption) ); + } + pFlyFrameFormat->SetFormatAttr( aFlyFrameSize ); + } + } + +#ifdef DBG_UTIL + { + // check if the tables have correct widths + SwTwips nSize = m_pSwTable->GetFrameFormat()->GetFrameSize().GetWidth(); + const SwTableLines& rLines = m_pSwTable->GetTabLines(); + for (size_t n = 0; n < rLines.size(); ++n) + { + CheckBoxWidth( *rLines[ n ], nSize ); + } + } +#endif +} + +void SwHTMLTableLayout::Resize_( sal_uInt16 nAbsAvail, bool bRecalc ) +{ + // If bRecalc is set, the table's content changed. + // We need to execute pass 1 again. + if( bRecalc ) + AutoLayoutPass1(); + + SwRootFrame *pRoot = GetDoc().getIDocumentLayoutAccess().GetCurrentViewShell()->GetLayout(); + if ( pRoot && pRoot->IsCallbackActionEnabled() ) + pRoot->StartAllAction(); + + // Else we can set the widths, in which we have to run Pass 2 in each case. + SetWidths( true, nAbsAvail ); + + if ( pRoot && pRoot->IsCallbackActionEnabled() ) + pRoot->EndAllAction( true ); //True per VirDev (browsing is calmer) +} + +IMPL_LINK_NOARG( SwHTMLTableLayout, DelayedResize_Impl, Timer*, void ) +{ + m_aResizeTimer.Stop(); + Resize_( m_nDelayedResizeAbsAvail, m_bDelayedResizeRecalc ); +} + +bool SwHTMLTableLayout::Resize( sal_uInt16 nAbsAvail, bool bRecalc, + bool bForce, sal_uLong nDelay ) +{ + if( 0 == nAbsAvail ) + return false; + OSL_ENSURE( IsTopTable(), "Resize must only be called for top tables!" ); + + // May the table be resized at all? Or is it forced? + if( m_bMustNotResize && !bForce ) + return false; + + // May the table be recalculated? Or is it forced? + if( m_bMustNotRecalc && !bForce ) + bRecalc = false; + + const SwDoc& rDoc = GetDoc(); + + // If there is a layout, the root frame's size instead of the + // VisArea's size was potentially passed. + // If we're not in a frame we need to calculate the table for the VisArea, + // because switching from relative to absolute wouldn't work. + if( rDoc.getIDocumentLayoutAccess().GetCurrentViewShell() && rDoc.getIDocumentLayoutAccess().GetCurrentViewShell()->GetViewOptions()->getBrowseMode() ) + { + const sal_uInt16 nVisAreaWidth = GetBrowseWidthByVisArea( rDoc ); + if( nVisAreaWidth < nAbsAvail && !FindFlyFrameFormat() ) + nAbsAvail = nVisAreaWidth; + } + + if( nDelay==0 && m_aResizeTimer.IsActive() ) + { + m_nDelayedResizeAbsAvail = nAbsAvail; + return false; + } + + // Optimisation: + // If the minimum or maximum should not be recalculated and + // - the table's width never needs to be recalculated, or + // - the table was already calculated for the passed width, or + // - the available space is less or equal to the minimum width + // and the table already has the minimum width, or + // - the available space is larger than the maximum width and + // the table already has the maximum width + // nothing will happen to the table. + if( !bRecalc && ( !m_bMustResize || + (m_nLastResizeAbsAvail==nAbsAvail) || + (nAbsAvail<=m_nMin && m_nRelTabWidth==m_nMin) || + (!m_bPercentWidthOption && nAbsAvail>=m_nMax && m_nRelTabWidth==m_nMax) ) ) + return false; + + if( nDelay==HTMLTABLE_RESIZE_NOW ) + { + if( m_aResizeTimer.IsActive() ) + m_aResizeTimer.Stop(); + Resize_( nAbsAvail, bRecalc ); + } + else if( nDelay > 0 ) + { + m_nDelayedResizeAbsAvail = nAbsAvail; + m_bDelayedResizeRecalc = bRecalc; + m_aResizeTimer.SetTimeout( nDelay ); + m_aResizeTimer.Start(); + } + else + { + Resize_( nAbsAvail, bRecalc ); + } + + return true; +} + +void SwHTMLTableLayout::BordersChanged( sal_uInt16 nAbsAvail ) +{ + m_bBordersChanged = true; + + Resize( nAbsAvail, true/*bRecalc*/ ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |