/************************************************************** * * 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 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * *************************************************************/ // MARKER(update_precomp.py): autogen include statement, do not remove #include "precompiled_sw.hxx" #include "hintids.hxx" //#define TEST_DELAYED_RESIZE #ifdef TEST_DELAYED_RESIZE #include <vcl/sound.hxx> #endif #include <vcl/wrkwin.hxx> #include <vcl/svapp.hxx> #include <sot/storage.hxx> #include <fmtornt.hxx> #include <fmtfsize.hxx> #include <frmfmt.hxx> #include <docary.hxx> #include "ndtxt.hxx" #include "doc.hxx" #include "swtable.hxx" #include "rootfrm.hxx" #include "docsh.hxx" #include "flyfrm.hxx" #include "poolfmt.hxx" #include "viewsh.hxx" #include "tabfrm.hxx" #include "viewopt.hxx" #include "htmltbl.hxx" #include "ndindex.hxx" #include "switerator.hxx" using namespace ::com::sun::star; #define COLFUZZY 20 #define MAX_TABWIDTH (USHRT_MAX - 2001) class SwHTMLTableLayoutConstraints { sal_uInt16 nRow; // Start-Zeile sal_uInt16 nCol; // Start-Spalte sal_uInt16 nColSpan; // COLSPAN der Zelle SwHTMLTableLayoutConstraints *pNext; // die naechste Bedingung sal_uLong nMinNoAlign, nMaxNoAlign; // Zwischenergebnisse AL-Pass 1 public: SwHTMLTableLayoutConstraints( sal_uLong nMin, sal_uLong nMax, sal_uInt16 nRow, sal_uInt16 nCol, sal_uInt16 nColSp ); ~SwHTMLTableLayoutConstraints(); sal_uLong GetMinNoAlign() const { return nMinNoAlign; } sal_uLong GetMaxNoAlign() const { return nMaxNoAlign; } SwHTMLTableLayoutConstraints *InsertNext( SwHTMLTableLayoutConstraints *pNxt ); SwHTMLTableLayoutConstraints* GetNext() const { return pNext; } sal_uInt16 GetRow() const { return nRow; } sal_uInt16 GetColSpan() const { return nColSpan; } sal_uInt16 GetColumn() const { return nCol; } }; /* */ SwHTMLTableLayoutCnts::SwHTMLTableLayoutCnts( const SwStartNode *pSttNd, SwHTMLTableLayout* pTab, sal_Bool bNoBrTag, SwHTMLTableLayoutCnts* pNxt ) : pNext( pNxt ), pBox( 0 ), pTable( pTab ), pStartNode( pSttNd ), nPass1Done( 0 ), nWidthSet( 0 ), bNoBreakTag( bNoBrTag ) {} SwHTMLTableLayoutCnts::~SwHTMLTableLayoutCnts() { delete pNext; delete pTable; } const SwStartNode *SwHTMLTableLayoutCnts::GetStartNode() const { return pBox ? pBox->GetSttNd() : pStartNode; } /* */ SwHTMLTableLayoutCell::SwHTMLTableLayoutCell( SwHTMLTableLayoutCnts *pCnts, sal_uInt16 nRSpan, sal_uInt16 nCSpan, sal_uInt16 nWidth, sal_Bool bPrcWidth, sal_Bool bNWrapOpt ) : pContents( pCnts ), nRowSpan( nRSpan ), nColSpan( nCSpan ), nWidthOption( nWidth ), bPrcWidthOption( bPrcWidth ), bNoWrapOption( bNWrapOpt ) {} SwHTMLTableLayoutCell::~SwHTMLTableLayoutCell() { if( nRowSpan==1 && nColSpan==1 ) { delete pContents; } } /* */ SwHTMLTableLayoutColumn::SwHTMLTableLayoutColumn( sal_uInt16 nWidth, sal_Bool bRelWidth, sal_Bool bLBorder ) : nMinNoAlign(MINLAY), nMaxNoAlign(MINLAY), nAbsMinNoAlign(MINLAY), nMin(0), nMax(0), nAbsColWidth(0), nRelColWidth(0), nWidthOption( nWidth ), bRelWidthOption( bRelWidth ), bLeftBorder( bLBorder ) {} /* */ SwHTMLTableLayoutConstraints::SwHTMLTableLayoutConstraints( sal_uLong nMin, sal_uLong nMax, sal_uInt16 nRw, sal_uInt16 nColumn, sal_uInt16 nColSp ): nRow( nRw ), nCol( nColumn ), nColSpan( nColSp ), pNext( 0 ), nMinNoAlign( nMin ), nMaxNoAlign( nMax ) {} SwHTMLTableLayoutConstraints::~SwHTMLTableLayoutConstraints() { delete pNext; } SwHTMLTableLayoutConstraints *SwHTMLTableLayoutConstraints::InsertNext( SwHTMLTableLayoutConstraints *pNxt ) { SwHTMLTableLayoutConstraints *pPrev = 0; SwHTMLTableLayoutConstraints *pConstr = this; while( pConstr ) { if( pConstr->GetRow() > pNxt->GetRow() || pConstr->GetColumn() > pNxt->GetColumn() ) break; pPrev = pConstr; pConstr = pConstr->GetNext(); } if( pPrev ) { pNxt->pNext = pPrev->GetNext(); pPrev->pNext = pNxt; pConstr = this; } else { pNxt->pNext = this; pConstr = pNxt; } return pConstr; } /* */ typedef SwHTMLTableLayoutColumn *SwHTMLTableLayoutColumnPtr; typedef SwHTMLTableLayoutCell *SwHTMLTableLayoutCellPtr; SwHTMLTableLayout::SwHTMLTableLayout( const SwTable * pSwTbl, sal_uInt16 nRws, sal_uInt16 nCls, sal_Bool bColsOpt, sal_Bool bColTgs, sal_uInt16 nWdth, sal_Bool bPrcWdth, sal_uInt16 nBorderOpt, sal_uInt16 nCellPad, sal_uInt16 nCellSp, SvxAdjust eAdjust, sal_uInt16 nLMargin, sal_uInt16 nRMargin, sal_uInt16 nBWidth, sal_uInt16 nLeftBWidth, sal_uInt16 nRightBWidth, sal_uInt16 nInhLeftBWidth, sal_uInt16 nInhRightBWidth ) : aColumns( new SwHTMLTableLayoutColumnPtr[nCls] ), aCells( new SwHTMLTableLayoutCellPtr[nRws*nCls] ), pSwTable( pSwTbl ), pLeftFillerBox( 0 ), pRightFillerBox( 0 ), nMin( 0 ), nMax( 0 ), nRows( nRws ), nCols( nCls ), nLeftMargin( nLMargin ), nRightMargin( nRMargin ), nInhAbsLeftSpace( 0 ), nInhAbsRightSpace( 0 ), nRelLeftFill( 0 ), nRelRightFill( 0 ), nRelTabWidth( 0 ), nWidthOption( nWdth ), nCellPadding( nCellPad ), nCellSpacing( nCellSp ), nBorder( nBorderOpt ), nLeftBorderWidth( nLeftBWidth ), nRightBorderWidth( nRightBWidth ), nInhLeftBorderWidth( nInhLeftBWidth ), nInhRightBorderWidth( nInhRightBWidth ), nBorderWidth( nBWidth ), nDelayedResizeAbsAvail( 0 ), nLastResizeAbsAvail( 0 ), nPass1Done( 0 ), nWidthSet( 0 ), eTableAdjust( eAdjust ), bColsOption( bColsOpt ), bColTags( bColTgs ), bPrcWidthOption( bPrcWdth ), bUseRelWidth( sal_False ), bMustResize( sal_True ), bExportable( sal_True ), bBordersChanged( sal_False ), bMustNotResize( sal_False ), bMustNotRecalc( sal_False ) { aResizeTimer.SetTimeoutHdl( STATIC_LINK( this, SwHTMLTableLayout, DelayedResize_Impl ) ); } SwHTMLTableLayout::~SwHTMLTableLayout() { sal_uInt16 i; for( i = 0; i < nCols; i++ ) delete aColumns[i]; delete[] aColumns; sal_uInt16 nCount = nRows*nCols; for( i=0; i<nCount; i++ ) delete aCells[i]; delete[] aCells; } // Die Breiten der Umrandung werden zunaechst wie in Netscape berechnet: // Aeussere Umrandung: BORDER + CELLSPACING + CELLPADDING // Innere Umrandung: CELLSPACING + CELLPADDING // Allerdings wird die Breite der Umrandung im SW trotzdem beachtet, wenn // bSwBorders gesetzt ist, damit nicht faellschlich umgebrochen wird. // MIB 27.6.97: Dabei muss auch der Abstand zum Inhalt beruecksichtigt werden, // und zwar auch dann, wenn wenn nur die gegenueberliegende Seite // eine Umrandung hat. sal_uInt16 SwHTMLTableLayout::GetLeftCellSpace( sal_uInt16 nCol, sal_uInt16 nColSpan, sal_Bool bSwBorders ) const { sal_uInt16 nSpace = nCellSpacing + nCellPadding; if( nCol == 0 ) { nSpace = nSpace + nBorder; if( bSwBorders && nSpace < nLeftBorderWidth ) nSpace = nLeftBorderWidth; } else if( bSwBorders ) { if( GetColumn(nCol)->HasLeftBorder() ) { if( nSpace < nBorderWidth ) nSpace = nBorderWidth; } else if( nCol+nColSpan == nCols && nRightBorderWidth && nSpace < MIN_BORDER_DIST ) { ASSERT( !nCellPadding, "GetLeftCellSpace: CELLPADDING!=0" ); // Wenn die Gegenueberliegende Seite umrandet ist muessen // wir zumindest den minimalen Abstand zum Inhalt // beruecksichtigen. (Koennte man zusaetzlich auch an // nCellPadding festmachen.) nSpace = MIN_BORDER_DIST; } } return nSpace; } sal_uInt16 SwHTMLTableLayout::GetRightCellSpace( sal_uInt16 nCol, sal_uInt16 nColSpan, sal_Bool bSwBorders ) const { sal_uInt16 nSpace = nCellPadding; if( nCol+nColSpan == nCols ) { nSpace += nBorder + nCellSpacing; if( bSwBorders && nSpace < nRightBorderWidth ) nSpace = nRightBorderWidth; } else if( bSwBorders && GetColumn(nCol)->HasLeftBorder() && nSpace < MIN_BORDER_DIST ) { ASSERT( !nCellPadding, "GetRightCellSpace: CELLPADDING!=0" ); // Wenn die Gegenueberliegende Seite umrandet ist muessen // wir zumindest den minimalen Abstand zum Inhalt // beruecksichtigen. (Koennte man zusaetzlich auch an // nCellPadding festmachen.) nSpace = MIN_BORDER_DIST; } return nSpace; } void SwHTMLTableLayout::AddBorderWidth( sal_uLong &rMin, sal_uLong &rMax, sal_uLong &rAbsMin, sal_uInt16 nCol, sal_uInt16 nColSpan, sal_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 { SwFrmFmt *pFrmFmt = pBox->GetFrmFmt(); // die Breite der Box berechnen SwTwips nFrmWidth = 0; while( nColSpan-- ) nFrmWidth += GetColumn( nCol++ )->GetRelColWidth(); // und neu setzen pFrmFmt->SetFmtAttr( SwFmtFrmSize( ATT_VAR_SIZE, nFrmWidth, 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 ) { ViewShell *pVSh = 0; rDoc.GetEditShell( &pVSh ); if( pVSh ) { return (sal_uInt16)pVSh->GetBrowseWidth(); } return 0; } sal_uInt16 SwHTMLTableLayout::GetBrowseWidth( const SwDoc& rDoc ) { // Wenn ein Layout da ist, koennen wir die Breite dort herholen. const SwRootFrm *pRootFrm = rDoc.GetCurrentLayout(); //swmod 080218 if( pRootFrm ) { const SwFrm *pPageFrm = pRootFrm->GetLower(); if( pPageFrm ) return (sal_uInt16)pPageFrm->Prt().Width(); } // --> OD 2010-05-12 #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::GetBrowseWidthByTabFrm( const SwTabFrm& rTabFrm ) const { SwTwips nWidth = 0; const SwFrm *pUpper = rTabFrm.GetUpper(); if( MayBeInFlyFrame() && pUpper->IsFlyFrm() && ((const SwFlyFrm *)pUpper)->GetAnchorFrm() ) { // Wenn die Tabelle in einem selbst angelegten Rahmen steht, dann ist // die Breite Ankers und nicht die Breite Rahmens von Bedeutung. // Bei Absatz-gebundenen Rahmen werden Absatz-Einzuege nicht beachtet. const SwFrm *pAnchor = ((const SwFlyFrm *)pUpper)->GetAnchorFrm(); if( pAnchor->IsTxtFrm() ) nWidth = pAnchor->Frm().Width(); else nWidth = pAnchor->Prt().Width(); } else { nWidth = pUpper->Prt().Width(); } SwTwips nUpperDummy = 0; long nRightOffset = 0, nLeftOffset = 0; rTabFrm.CalcFlyOffsets( nUpperDummy, nLeftOffset, nRightOffset ); nWidth -= (nLeftOffset + nRightOffset); return nWidth < USHRT_MAX ? static_cast<sal_uInt16>(nWidth) : USHRT_MAX; } sal_uInt16 SwHTMLTableLayout::GetBrowseWidthByTable( const SwDoc& rDoc ) const { sal_uInt16 nBrowseWidth = 0; SwTabFrm* pFrm = SwIterator<SwTabFrm,SwFmt>::FirstElement( *pSwTable->GetFrmFmt() ); if( pFrm ) { nBrowseWidth = GetBrowseWidthByTabFrm( *pFrm ); } else { nBrowseWidth = SwHTMLTableLayout::GetBrowseWidth( rDoc ); } return nBrowseWidth; } const SwStartNode *SwHTMLTableLayout::GetAnyBoxStartNode() const { const SwStartNode *pBoxSttNd; const SwTableBox* pBox = pSwTable->GetTabLines()[0]->GetTabBoxes()[0]; while( 0 == (pBoxSttNd = pBox->GetSttNd()) ) { ASSERT( pBox->GetTabLines().Count() > 0, "Box ohne Start-Node und Lines" ); ASSERT( pBox->GetTabLines()[0]->GetTabBoxes().Count() > 0, "Line ohne Boxen" ); pBox = pBox->GetTabLines()[0]->GetTabBoxes()[0]; } return pBoxSttNd; } SwFrmFmt *SwHTMLTableLayout::FindFlyFrmFmt() const { const SwTableNode *pTblNd = GetAnyBoxStartNode()->FindTableNode(); ASSERT( pTblNd, "Kein Table-Node?" ); return pTblNd->GetFlyFmt(); } static void lcl_GetMinMaxSize( sal_uLong& rMinNoAlignCnts, sal_uLong& rMaxNoAlignCnts, sal_uLong& rAbsMinNoAlignCnts, #ifdef FIX41370 sal_Bool& rHR, #endif SwTxtNode *pTxtNd, sal_uLong nIdx, sal_Bool bNoBreak ) { pTxtNd->GetMinMaxSize( nIdx, rMinNoAlignCnts, rMaxNoAlignCnts, rAbsMinNoAlignCnts ); ASSERT( rAbsMinNoAlignCnts <= rMinNoAlignCnts, "GetMinMaxSize: absmin > min" ); ASSERT( rMinNoAlignCnts <= rMaxNoAlignCnts, "GetMinMaxSize: max > min" ); // Bei einen <PRE>-Absatz entspricht die maximale Breite der // minimalen breite const SwFmtColl *pColl = &pTxtNd->GetAnyFmtColl(); while( pColl && !pColl->IsDefault() && (USER_FMT & pColl->GetPoolFmtId()) ) { pColl = (const SwFmtColl *)pColl->DerivedFrom(); } // <NOBR> in der gesamten Zelle bezieht sich auf Text, aber nicht // auf Tabellen. Netscape beruecksichtigt dies nur fuer Grafiken. if( (pColl && RES_POOLCOLL_HTML_PRE==pColl->GetPoolFmtId()) || bNoBreak ) { rMinNoAlignCnts = rMaxNoAlignCnts; rAbsMinNoAlignCnts = rMaxNoAlignCnts; } #ifdef FIX41370 else if( pColl && RES_POOLCOLL_HTML_HR==pColl->GetPoolFmtId() ) { rHR |= !pTxtNd->HasSwAttrSet() || SFX_ITEM_SET != pTxtNd->GetpSwAttrSet() ->GetItemState( RES_LR_SPACE, sal_False ); } #endif } void SwHTMLTableLayout::AutoLayoutPass1() { nPass1Done++; ClearPass1Info(); sal_Bool bFixRelWidths = sal_False; sal_uInt16 i; SwHTMLTableLayoutConstraints *pConstraints = 0; for( i=0; i<nCols; i++ ) { SwHTMLTableLayoutColumn *pColumn = GetColumn( i ); pColumn->ClearPass1Info( !HasColTags() ); sal_uInt16 nMinColSpan = USHRT_MAX; // Spaltenzahl, auf die sich dir // berechnete Breite bezieht sal_uInt16 nColSkip = USHRT_MAX; // Wie viele Spalten muessen // uebersprungen werden for( sal_uInt16 j=0; j<nRows; j++ ) { SwHTMLTableLayoutCell *pCell = GetCell(j,i); SwHTMLTableLayoutCnts *pCnts = pCell->GetContents(); // fix #31488#: Zum Ermitteln der naechsten zu berechnenden // Spalte muessen alle Zeilen herangezogen werden sal_uInt16 nColSpan = pCell->GetColSpan(); if( nColSpan < nColSkip ) nColSkip = nColSpan; if( !pCnts || (pCnts && !pCnts->IsPass1Done(nPass1Done)) ) { // die Zelle ist leer oder ihr Inhalt wurde noch nicht // bearbeitet 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; #ifdef FIX41370 sal_Bool bHR = sal_False; #endif while( pCnts ) { const SwStartNode *pSttNd = pCnts->GetStartNode(); if( pSttNd ) { const SwDoc *pDoc = pSttNd->GetDoc(); sal_uLong nIdx = pSttNd->GetIndex(); while( !(pDoc->GetNodes()[nIdx])->IsEndNode() ) { SwTxtNode *pTxtNd = (pDoc->GetNodes()[nIdx])->GetTxtNode(); if( pTxtNd ) { sal_uLong nMinNoAlignCnts = 0; sal_uLong nMaxNoAlignCnts = 0; sal_uLong nAbsMinNoAlignCnts = 0; lcl_GetMinMaxSize( nMinNoAlignCnts, nMaxNoAlignCnts, nAbsMinNoAlignCnts, #ifdef FIX41370 bHR, #endif pTxtNd, nIdx, pCnts->HasNoBreakTag() ); if( nMinNoAlignCnts > nMinNoAlignCell ) nMinNoAlignCell = nMinNoAlignCnts; if( nMaxNoAlignCnts > nMaxNoAlignCell ) nMaxNoAlignCell = nMaxNoAlignCnts; if( nAbsMinNoAlignCnts > nAbsMinNoAlignCell ) nAbsMinNoAlignCell = nAbsMinNoAlignCnts; } else { SwTableNode *pTabNd = (pDoc->GetNodes()[nIdx])->GetTableNode(); if( pTabNd ) { SwHTMLTableLayout *pChild = pTabNd->GetTable().GetHTMLTableLayout(); if( pChild ) { pChild->AutoLayoutPass1(); sal_uLong nMaxTableCnts = pChild->nMax; sal_uLong nAbsMinTableCnts = pChild->nMin; // Eine feste Tabellen-Breite wird als Minimum // und Maximum gleichzeitig uebernommen if( !pChild->bPrcWidthOption && pChild->nWidthOption ) { sal_uLong nTabWidth = pChild->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 { ASSERT( sal_False, "Sub tables in HTML import?" ) SwHTMLTableLayout *pChild = pCnts->GetTable(); pChild->AutoLayoutPass1(); sal_uLong nMaxTableCnts = pChild->nMax; sal_uLong nAbsMinTableCnts = pChild->nMin; // Eine feste Tabellen-Breite wird als Minimum // und Maximum gleichzeitig uebernommen if( !pChild->bPrcWidthOption && pChild->nWidthOption ) { sal_uLong nTabWidth = pChild->nWidthOption; if( nTabWidth >= nAbsMinTableCnts ) { nMaxTableCnts = nTabWidth; nAbsMinTableCnts = nTabWidth; } else { nMaxTableCnts = nAbsMinTableCnts; } } if( nMaxTableCnts > nMaxTableCell ) nMaxTableCell = nMaxTableCnts; if( nAbsMinTableCnts > nAbsMinTableCell ) nAbsMinTableCell = nAbsMinTableCnts; } pCnts->SetPass1Done( nPass1Done ); pCnts = pCnts->GetNext(); } // War frueher hinter AddBorderWidth // Wenn die Breite einer Tabelle in der Zelle breiter ist als // das, was wir fuer sonstigen Inhalt berechnet haben, muessen // wir die Breite der Tabelle nutzen if( nMaxTableCell > nMaxNoAlignCell ) nMaxNoAlignCell = nMaxTableCell; if( nAbsMinTableCell > nAbsMinNoAlignCell ) { nAbsMinNoAlignCell = nAbsMinTableCell; if( nMinNoAlignCell < nAbsMinNoAlignCell ) nMinNoAlignCell = nAbsMinNoAlignCell; if( nMaxNoAlignCell < nMinNoAlignCell ) nMaxNoAlignCell = nMinNoAlignCell; } // War frueher hinter AddBorderWidth sal_Bool bRelWidth = pCell->IsPrcWidthOption(); sal_uInt16 nWidth = pCell->GetWidthOption(); // Eine NOWRAP-Option bezieht sich auf Text und auf // Tabellen, wird aber bei fester Zellenbreite // nicht uebernommen. Stattdessen wirkt die angegebene // Zellenbreite wie eine Mindestbreite. if( pCell->HasNoWrapOption() ) { if( nWidth==0 || bRelWidth ) { nMinNoAlignCell = nMaxNoAlignCell; nAbsMinNoAlignCell = nMaxNoAlignCell; } else { if( nWidth>nMinNoAlignCell ) nMinNoAlignCell = nWidth; if( nWidth>nAbsMinNoAlignCell ) nAbsMinNoAlignCell = nWidth; } } #ifdef FIX41370 else if( bHR && nWidth>0 && !bRelWidth ) { // Ein kleiner Hack, um einen Bug in Netscape 4.0 // nachzubilden (siehe #41370#). Wenn eine Zelle eine // fixe Breite besitzt und gleichzeitig ein HR, wird // sie nie schmaler als die angegebene Breite. // (Genaugenomen scheint die Zelle nie schmaler zu werden // als die HR-Linie, denn wenn man fuer die Linie eine // Breite angibt, die breiter ist als die der Zelle, dann // wird die Zelle so breit wie die Linie. Das bekommen wir // natuerlich nicht hin.) if( nWidth>nMinNoAlignCell ) nMinNoAlignCell = nWidth; if( nWidth>nAbsMinNoAlignCell ) nAbsMinNoAlignCell = nWidth; } #endif // Mindestbreite fuer Inhalt einhalten if( nMinNoAlignCell < MINLAY ) nMinNoAlignCell = MINLAY; if( nMaxNoAlignCell < MINLAY ) nMaxNoAlignCell = MINLAY; if( nAbsMinNoAlignCell < MINLAY ) nAbsMinNoAlignCell = MINLAY; // Umrandung und Abstand zum Inhalt beachten. AddBorderWidth( nMinNoAlignCell, nMaxNoAlignCell, nAbsMinNoAlignCell, i, nColSpan ); if( 1==nColSpan ) { // die Werte direkt uebernehmen pColumn->MergeMinMaxNoAlign( nMinNoAlignCell, nMaxNoAlignCell, nAbsMinNoAlignCell ); // bei den WIDTH angaben gewinnt die breiteste if( !HasColTags() ) pColumn->MergeCellWidthOption( nWidth, bRelWidth ); } else { // die Angaben erst am Ende, und zwar zeilenweise von // links nach rechts bearbeiten // Wann welche Werte wie uebernommen werden ist weiter // unten erklaert. if( !HasColTags() && nWidth && !bRelWidth ) { sal_uLong nAbsWidth = nWidth, nDummy = 0, nDummy2 = 0; AddBorderWidth( nAbsWidth, nDummy, nDummy2, i, nColSpan, sal_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( pConstraints ) pConstraints = pConstraints->InsertNext( pConstr ); else pConstraints = pConstr; } } } ASSERT( nMinColSpan>0 && nColSkip>0 && nColSkip <= nMinColSpan, "Layout Pass 1: Da werden Spalten vergessen!" ); ASSERT( nMinColSpan!=USHRT_MAX, "Layout Pass 1: unnoetiger Schleifendurchlauf oder Bug" ); if( 1==nMinColSpan ) { // es gibt Zellen mit COLSPAN 1 und demnach auch sinnvolle // Werte in pColumn // Werte anhand folgender Tabelle (Netscape 4.0 pv 3) uebernehmen: // // WIDTH: kein COLS COLS // // keine min = min min = absmin // max = max max = max // // >= min min = min min = width // max = width max = width // // >= absmin min = wdith(*) min = width // max = width max = width // // < absmin min = absmin min = absmin // max = absmin max = absmin // // (*) Netscape benutzt hier die Mindestbreite ohne einen // Umbruch vor der letzten Grafik. Haben wir (noch?) nicht, // also belassen wir es bei width.^ if( pColumn->GetWidthOption() && !pColumn->IsRelWidthOption() ) { // absolute Breiten als Minimal- und Maximalbreite // uebernehmen. sal_uLong nAbsWidth = pColumn->GetWidthOption(); sal_uLong nDummy = 0, nDummy2 = 0; AddBorderWidth( nAbsWidth, nDummy, nDummy2, i, 1, sal_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 ) { // kann irgendwas !=0 sein, weil es durch die Constraints // angepasst wird. pColumn->SetMinMax( MINLAY, MINLAY ); // die naechsten Spalten muessen nicht bearbeitet werden i += (nColSkip-1); } nMin += pColumn->GetMin(); nMax += pColumn->GetMax(); bFixRelWidths |= pColumn->IsRelWidthOption(); } // jetzt noch die Constrains verarbeiten SwHTMLTableLayoutConstraints *pConstr = pConstraints; while( pConstr ) { // Erstmal muss die Breite analog zu den den Spaltenbreiten // aufbereitet werden sal_uInt16 nCol = pConstr->GetColumn(); sal_uInt16 nColSpan = pConstr->GetColSpan(); sal_uLong nConstrMin = pConstr->GetMinNoAlign(); sal_uLong nConstrMax = pConstr->GetMaxNoAlign(); // jetzt holen wir uns die bisherige Breite der ueberspannten // Spalten 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 ) { // den Minimalwert anteilig auf die Spalten verteilen sal_uLong nMinD = nConstrMin-nColsMin; if( nConstrMin > nColsMax ) { // Anteilig anhand der Mindestbreiten 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(); nMin -= nColMin; sal_uLong nAdd = ic<nEndCol-1 ? (nColMin * nMinD) / nColsMin : nDiff; nColMin += nAdd; nMin += nColMin; ASSERT( nDiff >= nAdd, "Ooops: nDiff stimmt nicht mehr" ); nDiff -= nAdd; if( nColMax < nColMin ) { nMax -= nColMax; nColsMax -= nColMax; nColMax = nColMin; nMax += nColMax; nColsMax += nColMax; } pColumn->SetMinMax( nColMin, nColMax ); } } else { // Anteilig anhand der Differenz zwischen Max und 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 ); ASSERT( pColumn->GetMax() >= pColumn->GetMin(), "Wieso ist die SPalte auf einmal zu schmal?" ) 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 ); nMax -= pColumn->GetMax(); pColumn->AddToMax( (pColumn->GetMax() * nMaxD) / nColsMax ); nMax += pColumn->GetMax(); } } pConstr = pConstr->GetNext(); } if( bFixRelWidths ) { if( HasColTags() ) { // Zum Anpassen der relativen Breiten werden im 1. Schritt die // Minimalbreiten aller anzupassenden Zellen jeweils mit der // relativen Breite einer Spalte multipliziert. Dadurch stimmen // dann die Breitenverhaeltnisse der Spalten untereinander. // Ausserdem wird der Faktor berechnet, um den die Zelle dadurch // breiter geworden ist als die Minimalbreite. // Im 2. Schritt werden dann die berechneten Breiten durch diesen // Faktor geteilt. Dadurch bleibt die Breite (mind.) einer Zelle // erhalten und dient als Ausgangsbasis fuer die andern Breiten. // Es werden auch hier nur die Maximalbreiten beeinflusst! sal_uLong nAbsMin = 0; // absolute Min-Breite alter Spalten mit // relativer Breite sal_uLong nRel = 0; // Summe der relativen Breiten aller Spalten for( i=0; i<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<nCols; i++ ) { SwHTMLTableLayoutColumn *pColumn = GetColumn( i ); if( pColumn->IsRelWidthOption() ) { 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; } } } ASSERT( 0==nRel || nQuot!=ULONG_MAX, "Wo sind die relativen Spalten geblieben?" ); for( i=0; i<nCols; i++ ) { SwHTMLTableLayoutColumn *pColumn = GetColumn( i ); if( pColumn->IsRelWidthOption() ) { if( pColumn->GetWidthOption() ) pColumn->SetMax( pColumn->GetMax() / nQuot ); else pColumn->SetMax( pColumn->GetMin() ); ASSERT( pColumn->GetMax() >= pColumn->GetMin(), "Maximale Spaltenbreite kleiner als Minimale" ); nMax += pColumn->GetMax(); } } } else { sal_uInt16 nRel = 0; // Summe der relativen Breiten aller Spalten sal_uInt16 nRelCols = 0; // Anzahl Spalten mit relativer Angabe sal_uLong nRelMax = 0; // Anteil am Maximum dieser Spalten for( i=0; i<nCols; i++ ) { ASSERT( nRel<=100, "relative Breite aller Spalten>100%" ); SwHTMLTableLayoutColumn *pColumn = GetColumn( i ); if( pColumn->IsRelWidthOption() && pColumn->GetWidthOption() ) { // Sicherstellen, dass die relativen breiten nicht // ueber 100% landen sal_uInt16 nColWidth = pColumn->GetWidthOption(); if( nRel+nColWidth > 100 ) { nColWidth = 100 - nRel; pColumn->SetWidthOption( nColWidth, sal_True, sal_False ); } nRelMax += pColumn->GetMax(); nRel = nRel + nColWidth; nRelCols++; } else if( !pColumn->GetMin() ) { // Die Spalte ist leer (wurde also ausschliesslich // durch COLSPAN erzeugt) und darf deshalb auch // keine %-Breite zugewiesen bekommen. nRelCols++; } } // Eventuell noch vorhandene Prozente werden auf die Spalten ohne // eine Breiten-Angabe verteilt. Wie in Netscape werden die // verbleibenden Prozente entsprechend der Verhaeltnisse // der Maximalbreiten der in Frage kommenden Spalten // untereinander verteilt. // ??? Wie beruecksichtigen bei den Maximalbreiten auch Spalten // mit fester Breite. Ist das richtig??? if( nRel < 100 && nRelCols < nCols ) { sal_uInt16 nRelLeft = 100 - nRel; sal_uLong nFixMax = nMax - nRelMax; for( i=0; i<nCols; i++ ) { SwHTMLTableLayoutColumn *pColumn = GetColumn( i ); if( !pColumn->IsRelWidthOption() && !pColumn->GetWidthOption() && pColumn->GetMin() ) { // den Rest bekommt die naechste Spalte sal_uInt16 nColWidth = (sal_uInt16)((pColumn->GetMax() * nRelLeft) / nFixMax); pColumn->SetWidthOption( nColWidth, sal_True, sal_False ); } } } // nun die Maximalbreiten entsprechend anpassen sal_uLong nQuotMax = ULONG_MAX; sal_uLong nOldMax = nMax; nMax = 0; for( i=0; i<nCols; i++ ) { // Spalten mit %-Angaben werden entsprechend angepasst. // Spalten, die // - keine %-Angabe besitzen und in einer Tabelle mit COLS // oder WIDTH vorkommen, oder // - als Breite 0% angegeben haben erhalten die Minimalbreite SwHTMLTableLayoutColumn *pColumn = GetColumn( i ); if( pColumn->IsRelWidthOption() && pColumn->GetWidthOption() ) { sal_uLong nNewMax; sal_uLong nColQuotMax; if( !nWidthOption ) { nNewMax = nOldMax * pColumn->GetWidthOption(); nColQuotMax = nNewMax / pColumn->GetMax(); } else { nNewMax = nMin * pColumn->GetWidthOption(); nColQuotMax = nNewMax / pColumn->GetMin(); } pColumn->SetMax( nNewMax ); if( nColQuotMax < nQuotMax ) nQuotMax = nColQuotMax; } else if( HasColsOption() || nWidthOption || (pColumn->IsRelWidthOption() && !pColumn->GetWidthOption()) ) pColumn->SetMax( pColumn->GetMin() ); } // und durch den Quotienten teilen ASSERT( nQuotMax!=ULONG_MAX, "Wo sind die relativen Spalten geblieben?" ); for( i=0; i<nCols; i++ ) { SwHTMLTableLayoutColumn *pColumn = GetColumn( i ); if( pColumn->IsRelWidthOption() && pColumn->GetWidthOption() ) { if( pColumn->GetWidthOption() ) { pColumn->SetMax( pColumn->GetMax() / nQuotMax ); ASSERT( pColumn->GetMax() >= pColumn->GetMin(), "Minimalbreite ein Spalte Groesser Maximum" ); if( pColumn->GetMax() < pColumn->GetMin() ) pColumn->SetMax( pColumn->GetMin() ); } } nMax += pColumn->GetMax(); } } } delete pConstraints; } // nAbsAvail ist der verfuegbare Platz in TWIPS. // nRelAvail ist der auf USHRT_MAX bezogene verfuegbare Platz oder 0 // nAbsSpace ist der Anteil von nAbsAvail, der durch der umgebende Zelle // fur die Umrandung und den Abstand zum Inhalt reserviert ist. void SwHTMLTableLayout::AutoLayoutPass2( sal_uInt16 nAbsAvail, sal_uInt16 nRelAvail, sal_uInt16 nAbsLeftSpace, sal_uInt16 nAbsRightSpace, sal_uInt16 nParentInhAbsSpace ) { // Erstmal fuehren wie jede Menge Plausibilitaets-Test durch // Eine absolute zur Verfuegung stehende Breite muss immer uebergeben // werden. ASSERT( nAbsAvail, "AutoLayout Pass 2: Keine absolute Breite gegeben" ); // Eine realtive zur Verfuegung stehende Breite darf nur und muss fuer // Tabellen in Tabellen uebergeben ASSERT( IsTopTable() == (nRelAvail==0), "AutoLayout Pass 2: Rel. Breite bei Tab in Tab oder umgekehrt" ); // Die Minimalbreite der Tabelle darf natuerlich nie groesser sein // als das die Maximalbreite. ASSERT( nMin<=nMax, "AutoLayout Pass2: nMin > nMax" ); // Die verfuegbare Breite, fuer die die Tabelle berechnet wurde, merken. // (Dies ist ein guter Ort, denn hier kommen wir bei der Erstberechnung // der Tabelle aus dem Parser und bei jedem _Resize-Aufruf vorbei.) nLastResizeAbsAvail = nAbsAvail; // Schritt 1: Der verfuegbare Platz wird an linke/rechte Raender, // vorhandene Filler-Zellen und Abstaende angepasst // Abstand zum Inhalt und Unrandung sal_uInt16 nAbsLeftFill = 0, nAbsRightFill = 0; if( !IsTopTable() && GetMin() + nAbsLeftSpace + nAbsRightSpace <= nAbsAvail ) { nAbsLeftFill = nAbsLeftSpace; nAbsRightFill = nAbsRightSpace; } // Linker und rechter Abstand if( nLeftMargin || nRightMargin ) { if( IsTopTable() ) { // fuer die Top-Table beruecksichtigen wir die Raender immer, // den die Minimalbreite der Tabelle wird hier nie unterschritten nAbsAvail -= (nLeftMargin + nRightMargin); } else if( GetMin() + nLeftMargin + nRightMargin <= nAbsAvail ) { // sonst beruecksichtigen wir die Raender nur, wenn auch Platz // fuer sie da ist (nMin ist hier bereits berechnet!) nAbsLeftFill = nAbsLeftFill + nLeftMargin; nAbsRightFill = nAbsRightFill + nRightMargin; } } // Filler-Zellen if( !IsTopTable() ) { if( pLeftFillerBox && nAbsLeftFill<MINLAY+nInhLeftBorderWidth ) nAbsLeftFill = MINLAY+nInhLeftBorderWidth; if( pRightFillerBox && nAbsRightFill<MINLAY+nInhRightBorderWidth ) nAbsRightFill = MINLAY+nInhRightBorderWidth; } // Anpassen des verfuegbaren Platzes. nRelLeftFill = 0; nRelRightFill = 0; if( !IsTopTable() && (nAbsLeftFill>0 || nAbsRightFill) ) { sal_uLong nAbsLeftFillL = nAbsLeftFill, nAbsRightFillL = nAbsRightFill; nRelLeftFill = (sal_uInt16)((nAbsLeftFillL * nRelAvail) / nAbsAvail); nRelRightFill = (sal_uInt16)((nAbsRightFillL * nRelAvail) / nAbsAvail); nAbsAvail -= (nAbsLeftFill + nAbsRightFill); if( nRelAvail ) nRelAvail -= (nRelLeftFill + nRelRightFill); } // Schritt 2: Die absolute Tabellenbreite wird berechnet. sal_uInt16 nAbsTabWidth = 0; bUseRelWidth = sal_False; if( nWidthOption ) { if( bPrcWidthOption ) { ASSERT( nWidthOption<=100, "Prozentangabe zu gross" ); if( nWidthOption > 100 ) nWidthOption = 100; // Die absolute Breite entspricht den angegeben Prozent der // zur Verfuegung stehenden Breite. // Top-Tabellen bekommen nur eine relative Breite, wenn der // verfuegbare Platz *echt groesser* ist als die Minimalbreite. // ACHTUNG: Das "echte groesser" ist noetig, weil der Wechsel // von einer relativen Breite zu einer absoluten Breite durch // Resize sonst zu einer Endlosschleife fuehrt. // Weil bei Tabellen in Rahmen kein Resize aufgerufen wird, // wenn der Rahmen eine nicht-relative Breite besitzt, koennen // wir da solche Spielchen nicht spielen // MIB 19.2.98: Wegen fix #47394# spielen wir solche Spielchen // jetzt doch. Dort war eine Grafik in einer 1%-breiten // Tabelle und hat da natuerlich nicht hineingepasst. nAbsTabWidth = (sal_uInt16)( ((sal_uLong)nAbsAvail * nWidthOption) / 100 ); if( IsTopTable() && ( /*MayBeInFlyFrame() ||*/ (sal_uLong)nAbsTabWidth > nMin ) ) { nRelAvail = USHRT_MAX; bUseRelWidth = sal_True; } } else { nAbsTabWidth = nWidthOption; if( nAbsTabWidth > MAX_TABWIDTH ) nAbsTabWidth = MAX_TABWIDTH; // Tabellen in Tabellen duerfen niemals breiter werden als der // verfuegbare Platz. if( !IsTopTable() && nAbsTabWidth > nAbsAvail ) nAbsTabWidth = nAbsAvail; } } ASSERT( IsTopTable() || nAbsTabWidth<=nAbsAvail, "AutoLayout Pass2: nAbsTabWidth > nAbsAvail fuer Tab in Tab" ); ASSERT( !nRelAvail || nAbsTabWidth<=nAbsAvail, "AutoLayout Pass2: nAbsTabWidth > nAbsAvail fuer relative Breite" ); // Catch fuer die beiden Asserts von oben (man weiss ja nie!) if( (!IsTopTable() || nRelAvail>0) && nAbsTabWidth>nAbsAvail ) nAbsTabWidth = nAbsAvail; // Schritt 3: Bestimmen der Spaltenbreiten und ggf. auch der // absoluten und relativen Tabellenbreiten. if( (!IsTopTable() && nMin > (sal_uLong)nAbsAvail) || nMin > MAX_TABWIDTH ) { // Wenn // - das Minimum einer inneren Tabelle groesser ist als der // verfuegbare Platz, oder // - das Minimum einer Top-Table groesser ist als USHRT_MAX // muss die Tabelle an den verfuegbaren Platz bzw. USHRT_MAX // abgepasst werden. Dabei bleiben die Verhaeltnisse der Breiten // untereinander erhalten. nAbsTabWidth = IsTopTable() ? MAX_TABWIDTH : nAbsAvail; nRelTabWidth = (nRelAvail ? nRelAvail : nAbsTabWidth ); // First of all, we check whether we can fit the layout constrains, // that are: Every cell's width excluding the borders must be at least // MINLAY: sal_uLong nRealMin = 0; for( sal_uInt16 i=0; i<nCols; i++ ) { sal_uLong nRealColMin = MINLAY, nDummy1, nDummy2; AddBorderWidth( nRealColMin, nDummy1, nDummy2, i, 1 ); nRealMin += nRealColMin; } if( (nRealMin >= nAbsTabWidth) || (nRealMin >= nMin) ) { // "Nichts geht mehr". 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<nCols-1; i++ ) { pColumn = GetColumn( i ); sal_uLong nColMin = pColumn->GetMin(); if( nColMin <= USHRT_MAX ) { pColumn->SetAbsColWidth( (sal_uInt16)((nColMin * nAbsTabWidth) / nMin) ); pColumn->SetRelColWidth( (sal_uInt16)((nColMin * nRelTabWidth) / nMin) ); } else { double nColMinD = nColMin; pColumn->SetAbsColWidth( (sal_uInt16)((nColMinD * nAbsTabWidth) / nMin) ); pColumn->SetRelColWidth( (sal_uInt16)((nColMinD * nRelTabWidth) / nMin) ); } nAbs = nAbs + (sal_uInt16)pColumn->GetAbsColWidth(); nRel = nRel + (sal_uInt16)pColumn->GetRelColWidth(); } pColumn = GetColumn( nCols-1 ); pColumn->SetAbsColWidth( nAbsTabWidth - nAbs ); pColumn->SetRelColWidth( nRelTabWidth - nRel ); } else { sal_uLong nDistAbs = nAbsTabWidth - nRealMin; sal_uLong nDistRel = nRelTabWidth - nRealMin; sal_uLong nDistMin = nMin - nRealMin; sal_uInt16 nAbs = 0, nRel = 0; SwHTMLTableLayoutColumn *pColumn; for( sal_uInt16 i=0; i<nCols-1; i++ ) { pColumn = GetColumn( i ); sal_uLong nColMin = pColumn->GetMin(); sal_uLong nRealColMin = MINLAY, nDummy1, nDummy2; AddBorderWidth( nRealColMin, nDummy1, nDummy2, i, 1 ); if( nColMin <= USHRT_MAX ) { pColumn->SetAbsColWidth( (sal_uInt16)((((nColMin-nRealColMin) * nDistAbs) / nDistMin) + nRealColMin) ); pColumn->SetRelColWidth( (sal_uInt16)((((nColMin-nRealColMin) * nDistRel) / nDistMin) + nRealColMin) ); } else { double nColMinD = nColMin; pColumn->SetAbsColWidth( (sal_uInt16)((((nColMinD-nRealColMin) * nDistAbs) / nDistMin) + nRealColMin) ); pColumn->SetRelColWidth( (sal_uInt16)((((nColMinD-nRealColMin) * nDistRel) / nDistMin) + nRealColMin) ); } nAbs = nAbs + (sal_uInt16)pColumn->GetAbsColWidth(); nRel = nRel + (sal_uInt16)pColumn->GetRelColWidth(); } pColumn = GetColumn( nCols-1 ); pColumn->SetAbsColWidth( nAbsTabWidth - nAbs ); pColumn->SetRelColWidth( nRelTabWidth - nRel ); } } else if( nMax <= (sal_uLong)(nAbsTabWidth ? nAbsTabWidth : nAbsAvail) ) { // Wenn // - die Tabelle eine fixe Breite besitzt und das Maximum der // Tabelle kleiner ist, oder // - das Maximum kleiner ist als der verfuegbare Platz // kann das Maximum direkt uebernommen werden bzw. die Tabelle nur // unter Beruecksichtigung des Maximums an die fixe Breite // angepasst werden. // Keine fixe Breite, dann das Maximum nehmen. if( !nAbsTabWidth ) nAbsTabWidth = (sal_uInt16)nMax; // Eine Top-Table darf auch breiter werden als der verfuegbare Platz. if( nAbsTabWidth > nAbsAvail ) { ASSERT( IsTopTable(), "Tabelle in Tabelle soll breiter werden als umgebende Zelle" ); nAbsAvail = nAbsTabWidth; } // Nur den Anteil der relativen Breite verwenden, der auch fuer // die absolute Breite verwendet wuerde. sal_uLong nAbsTabWidthL = nAbsTabWidth; nRelTabWidth = ( nRelAvail ? (sal_uInt16)((nAbsTabWidthL * nRelAvail) / nAbsAvail) : nAbsTabWidth ); // Gibt es Spalten mit und Spalten ohne %-Angabe? sal_uLong nFixMax = nMax; for( sal_uInt16 i=0; i<nCols; i++ ) { const SwHTMLTableLayoutColumn *pColumn = GetColumn( i ); if( pColumn->IsRelWidthOption() && pColumn->GetWidthOption()>0 ) nFixMax -= pColumn->GetMax(); } if( nFixMax > 0 && nFixMax < nMax ) { // ja, dann den zu verteilenden Platz nur auf die Spalten // mit %-Angabe verteilen. // In diesem (und nur in diesem) Fall gibt es Spalten, // die ihre Maximalbreite genau einhalten, also weder // schmaler noch breiter werden. Beim zurueckrechnen der // absoluten Breite aus der relativen Breite kann es // zu Rundungsfehlern kommen (bug #45598#). Um die auszugleichen // werden zuerst die fixen Breiten entsprechend korrigiert // eingestellt und erst danach die relativen. sal_uInt16 nAbs = 0, nRel = 0; sal_uInt16 nFixedCols = 0; sal_uInt16 i; for( i = 0; i < nCols; i++ ) { SwHTMLTableLayoutColumn *pColumn = GetColumn( i ); if( !pColumn->IsRelWidthOption() || !pColumn->GetWidthOption() ) { // Die Spalte behaelt ihre Breite bei. nFixedCols++; sal_uLong nColMax = pColumn->GetMax(); pColumn->SetAbsColWidth( (sal_uInt16)nColMax ); sal_uLong nRelColWidth = (nColMax * nRelTabWidth) / nAbsTabWidth; sal_uLong nChkWidth = (nRelColWidth * nAbsTabWidth) / nRelTabWidth; if( nChkWidth < nColMax ) nRelColWidth++; else if( nChkWidth > nColMax ) nRelColWidth--; pColumn->SetRelColWidth( (sal_uInt16)nRelColWidth ); nAbs = nAbs + (sal_uInt16)nColMax; nRel = nRel + (sal_uInt16)nRelColWidth; } } // Zu verteilende Anteile des Maximums und der relativen und // absoluten Breiten. nFixMax entspricht an dieser Stelle // nAbs, so dass man gleich nFixMax haette nehmen koennen. // Der Code ist so aber verstaendlicher. ASSERT( nFixMax == nAbs, "Zwei Schleifen, zwei Summen?" ) sal_uLong nDistMax = nMax - nFixMax; sal_uInt16 nDistAbsTabWidth = nAbsTabWidth - nAbs; sal_uInt16 nDistRelTabWidth = nRelTabWidth - nRel; for( i=0; i<nCols; i++ ) { SwHTMLTableLayoutColumn *pColumn = GetColumn( i ); if( pColumn->IsRelWidthOption() && pColumn->GetWidthOption() > 0 ) { // Die Spalte wird anteilig breiter. nFixedCols++; if( nFixedCols == nCols ) { pColumn->SetAbsColWidth( nAbsTabWidth-nAbs ); pColumn->SetRelColWidth( nRelTabWidth-nRel ); } else { sal_uLong nColMax = pColumn->GetMax(); pColumn->SetAbsColWidth( (sal_uInt16)((nColMax * nDistAbsTabWidth) / nDistMax) ); pColumn->SetRelColWidth( (sal_uInt16)((nColMax * nDistRelTabWidth) / nDistMax) ); } nAbs = nAbs + pColumn->GetAbsColWidth(); nRel = nRel + pColumn->GetRelColWidth(); } } ASSERT( nCols==nFixedCols, "Spalte vergessen!" ); } else { // nein, dann den zu verteilenden Platz auf alle Spalten // gleichmaessig verteilen. for( sal_uInt16 i=0; i<nCols; i++ ) { sal_uLong nColMax = GetColumn( i )->GetMax(); GetColumn( i )->SetAbsColWidth( (sal_uInt16)((nColMax * nAbsTabWidth) / nMax) ); GetColumn( i )->SetRelColWidth( (sal_uInt16)((nColMax * nRelTabWidth) / nMax) ); } } } else { // den ueber die Minimalbreite herausgehenden Platz entsprechend // den einzelnen Spalten anteilig zuschlagen if( !nAbsTabWidth ) nAbsTabWidth = nAbsAvail; if( nAbsTabWidth < nMin ) nAbsTabWidth = (sal_uInt16)nMin; if( nAbsTabWidth > nAbsAvail ) { ASSERT( IsTopTable(), "Tabelle in Tabelle soll breiter werden als Platz da ist" ); nAbsAvail = nAbsTabWidth; } sal_uLong nAbsTabWidthL = nAbsTabWidth; nRelTabWidth = ( nRelAvail ? (sal_uInt16)((nAbsTabWidthL * nRelAvail) / nAbsAvail) : nAbsTabWidth ); double nW = nAbsTabWidth - nMin; double nD = (nMax==nMin ? 1 : nMax-nMin); sal_uInt16 nAbs = 0, nRel = 0; for( sal_uInt16 i=0; i<nCols-1; i++ ) { double nd = GetColumn( i )->GetMax() - GetColumn( i )->GetMin(); sal_uLong nAbsColWidth = GetColumn( i )->GetMin() + (sal_uLong)((nd*nW)/nD); sal_uLong nRelColWidth = nRelAvail ? (nAbsColWidth * nRelTabWidth) / nAbsTabWidth : nAbsColWidth; GetColumn( i )->SetAbsColWidth( (sal_uInt16)nAbsColWidth ); GetColumn( i )->SetRelColWidth( (sal_uInt16)nRelColWidth ); nAbs = nAbs + (sal_uInt16)nAbsColWidth; nRel = nRel + (sal_uInt16)nRelColWidth; } GetColumn( nCols-1 )->SetAbsColWidth( nAbsTabWidth - nAbs ); GetColumn( nCols-1 )->SetRelColWidth( nRelTabWidth - nRel ); } // Schritt 4: Fuer Tabellen in Tabellen kann es links und/oder rechts // noch Ausgleichzellen geben. Deren Breite wird jetzt berechnet. nInhAbsLeftSpace = 0; nInhAbsRightSpace = 0; if( !IsTopTable() && (nRelLeftFill>0 || nRelRightFill>0 || nAbsTabWidth<nAbsAvail) ) { // Die Breite von zusaetzlichen Zellen zur Ausrichtung der // inneren Tabelle bestimmen sal_uInt16 nAbsDist = (sal_uInt16)(nAbsAvail-nAbsTabWidth); sal_uInt16 nRelDist = (sal_uInt16)(nRelAvail-nRelTabWidth); sal_uInt16 nParentInhAbsLeftSpace = 0, nParentInhAbsRightSpace = 0; // Groesse und Position der zusaetzlichen Zellen bestimmen switch( eTableAdjust ) { case SVX_ADJUST_RIGHT: nAbsLeftFill = nAbsLeftFill + nAbsDist; nRelLeftFill = nRelLeftFill + nRelDist; nParentInhAbsLeftSpace = nParentInhAbsSpace; break; case SVX_ADJUST_CENTER: { sal_uInt16 nAbsLeftDist = nAbsDist / 2; nAbsLeftFill = nAbsLeftFill + nAbsLeftDist; nAbsRightFill += nAbsDist - nAbsLeftDist; sal_uInt16 nRelLeftDist = nRelDist / 2; nRelLeftFill = nRelLeftFill + nRelLeftDist; nRelRightFill += nRelDist - nRelLeftDist; nParentInhAbsLeftSpace = nParentInhAbsSpace / 2; nParentInhAbsRightSpace = nParentInhAbsSpace - nParentInhAbsLeftSpace; } break; case SVX_ADJUST_LEFT: default: nAbsRightFill = nAbsRightFill + nAbsDist; nRelRightFill = nRelRightFill + nRelDist; nParentInhAbsRightSpace = nParentInhAbsSpace; break; } ASSERT( !pLeftFillerBox || nRelLeftFill>0, "Fuer linke Filler-Box ist keine Breite da!" ); ASSERT( !pRightFillerBox || nRelRightFill>0, "Fuer rechte Filler-Box ist keine Breite da!" ); // Filler-Breiten werden auf die aeusseren Spalten geschlagen, wenn // es nach dem ersten Durchlauf keine Boxen fuer sie gibt (nWidth>0) // oder ihre Breite zu klein wuerde oder wenn es COL-Tags gibt und // die Filler-Breite der Umrandung-Breite entspricht (dann haben wir // die Tabelle wahrscheinlich selbst exportiert) if( nRelLeftFill && !pLeftFillerBox && ( nWidthSet>0 || nAbsLeftFill<MINLAY+nInhLeftBorderWidth || (HasColTags() && nAbsLeftFill < nAbsLeftSpace+nParentInhAbsLeftSpace+20) ) ) // (nAbsLeftFill<MINLAY || nAbsLeftFill<=nAbsLeftSpace) ) { SwHTMLTableLayoutColumn *pColumn = GetColumn( 0 ); pColumn->SetAbsColWidth( pColumn->GetAbsColWidth()+nAbsLeftFill ); pColumn->SetRelColWidth( pColumn->GetRelColWidth()+nRelLeftFill ); nRelLeftFill = 0; nInhAbsLeftSpace = nAbsLeftSpace + nParentInhAbsLeftSpace; } if( nRelRightFill && !pRightFillerBox && ( nWidthSet>0 || nAbsRightFill<MINLAY+nInhRightBorderWidth || (HasColTags() && nAbsRightFill < nAbsRightSpace+nParentInhAbsRightSpace+20) ) ) // (nAbsRightFill<MINLAY || nAbsRightFill<=nAbsRightSpace) ) { SwHTMLTableLayoutColumn *pColumn = GetColumn( nCols-1 ); pColumn->SetAbsColWidth( pColumn->GetAbsColWidth()+nAbsRightFill ); pColumn->SetRelColWidth( pColumn->GetRelColWidth()+nRelRightFill ); nRelRightFill = 0; nInhAbsRightSpace = nAbsRightSpace + nParentInhAbsRightSpace; } } } static sal_Bool lcl_ResizeLine( const SwTableLine*& rpLine, void* pPara ); static sal_Bool lcl_ResizeBox( const SwTableBox*& rpBox, void* pPara ) { sal_uInt16 *pWidth = (sal_uInt16 *)pPara; if( !rpBox->GetSttNd() ) { sal_uInt16 nWidth = 0; ((SwTableBox *)rpBox)->GetTabLines().ForEach( &lcl_ResizeLine, &nWidth ); rpBox->GetFrmFmt()->SetFmtAttr( SwFmtFrmSize( ATT_VAR_SIZE, nWidth, 0 )); *pWidth = *pWidth + nWidth; } else { *pWidth = *pWidth + (sal_uInt16)rpBox->GetFrmFmt()->GetFrmSize().GetSize().Width(); } return sal_True; } static sal_Bool lcl_ResizeLine( const SwTableLine*& rpLine, void* pPara ) { sal_uInt16 *pWidth = (sal_uInt16 *)pPara; #ifdef DBG_UTIL sal_uInt16 nOldWidth = *pWidth; #endif *pWidth = 0; ((SwTableLine *)rpLine)->GetTabBoxes().ForEach( &lcl_ResizeBox, pWidth ); #ifdef DBG_UTIL ASSERT( !nOldWidth || Abs(*pWidth-nOldWidth) < COLFUZZY, "Zeilen einer Box sind unterschiedlich lang" ); #endif return sal_True; } void SwHTMLTableLayout::SetWidths( sal_Bool bCallPass2, sal_uInt16 nAbsAvail, sal_uInt16 nRelAvail, sal_uInt16 nAbsLeftSpace, sal_uInt16 nAbsRightSpace, sal_uInt16 nParentInhAbsSpace ) { // SetWidth muss am Ende einmal mehr fuer jede Zelle durchlaufen // worden sein. nWidthSet++; // Schritt 0: Wenn noetig, wird hier noch der Pass2 des Layout-Algorithmus // aufgerufen. if( bCallPass2 ) AutoLayoutPass2( nAbsAvail, nRelAvail, nAbsLeftSpace, nAbsRightSpace, nParentInhAbsSpace ); // Schritt 1: Setzten der neuen Breite an allen Content-Boxen. // Da die Boxen nichts von der HTML-Tabellen-Struktur wissen, wird // ueber die HTML-Tabellen-Struktur iteriert. Fuer Tabellen in Tabellen // in Tabellen wird rekursiv SetWidth aufgerufen. for( sal_uInt16 i=0; i<nRows; i++ ) { for( sal_uInt16 j=0; j<nCols; j++ ) { SwHTMLTableLayoutCell *pCell = GetCell( i, j ); SwHTMLTableLayoutCnts* pCntnts = pCell->GetContents(); while( pCntnts && !pCntnts->IsWidthSet(nWidthSet) ) { SwTableBox *pBox = pCntnts->GetTableBox(); if( pBox ) { SetBoxWidth( pBox, j, pCell->GetColSpan() ); } else { 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 ); } pCntnts->GetTable()->SetWidths( bCallPass2, nAbs, nRel, nLSpace, nRSpace, nInhSpace ); } pCntnts->SetWidthSet( nWidthSet ); pCntnts = pCntnts->GetNext(); } } } // Schritt 2: Wenn eine Top-Tabelle vorliegt, werden jetzt die Formate // der Nicht-Content-Boxen angepasst. Da diese aufgrund der // Garbage-Collection in der HTML-Tabelle nicht bekannt sind, muessen // wir hier ueber die Tabelle iterieren. Bei der Gelegenheit wird auch // das Tabellen-Frameformat angepasst. Fuer Tabellen in Tabellen werden // stattdessen die Breiten der Filler-Zellen gesetzt. if( IsTopTable() ) { sal_uInt16 nCalcTabWidth = 0; ((SwTable *)pSwTable)->GetTabLines().ForEach( &lcl_ResizeLine, &nCalcTabWidth ); ASSERT( Abs( nRelTabWidth-nCalcTabWidth ) < COLFUZZY, "Tabellebreite stimmt nicht mit Zeilenbreite ueberein." ); // Beim Anpassen des Tabellen-Formats dieses locken, weil sonst // die Boxformate erneut angepasst werden. Ausserdem muss eine // evtl. vorhandene %-Angabe in jedem Fall erhalten bleiben. SwFrmFmt *pFrmFmt = pSwTable->GetFrmFmt(); ((SwTable *)pSwTable)->LockModify(); SwFmtFrmSize aFrmSize( pFrmFmt->GetFrmSize() ); aFrmSize.SetWidth( nRelTabWidth ); sal_Bool bRel = bUseRelWidth && text::HoriOrientation::FULL!=pFrmFmt->GetHoriOrient().GetHoriOrient(); aFrmSize.SetWidthPercent( (sal_uInt8)(bRel ? nWidthOption : 0) ); pFrmFmt->SetFmtAttr( aFrmSize ); ((SwTable *)pSwTable)->UnlockModify(); // Wenn die Tabelle in einem Rahmen steht, muss auch noch dessen // Breite angepasst werden. if( MayBeInFlyFrame() ) { SwFrmFmt *pFlyFrmFmt = FindFlyFrmFmt(); if( pFlyFrmFmt ) { SwFmtFrmSize aFlyFrmSize( ATT_VAR_SIZE, nRelTabWidth, MINLAY ); if( bUseRelWidth ) { // Bei %-Angaben wird die Breite auf das Minimum gesetzt. aFlyFrmSize.SetWidth( nMin > USHRT_MAX ? USHRT_MAX : nMin ); aFlyFrmSize.SetWidthPercent( (sal_uInt8)nWidthOption ); } pFlyFrmFmt->SetFmtAttr( aFlyFrmSize ); } } #ifdef DBG_UTIL { // steht im tblrwcl.cxx extern void _CheckBoxWidth( const SwTableLine&, SwTwips ); // checke doch mal ob die Tabellen korrekte Breiten haben SwTwips nSize = pSwTable->GetFrmFmt()->GetFrmSize().GetWidth(); const SwTableLines& rLines = pSwTable->GetTabLines(); for( sal_uInt16 n = 0; n < rLines.Count(); ++n ) _CheckBoxWidth( *rLines[ n ], nSize ); } #endif } else { if( pLeftFillerBox ) { pLeftFillerBox->GetFrmFmt()->SetFmtAttr( SwFmtFrmSize( ATT_VAR_SIZE, nRelLeftFill, 0 )); } if( pRightFillerBox ) { pRightFillerBox->GetFrmFmt()->SetFmtAttr( SwFmtFrmSize( ATT_VAR_SIZE, nRelRightFill, 0 )); } } } void SwHTMLTableLayout::_Resize( sal_uInt16 nAbsAvail, sal_Bool bRecalc ) { // Wenn bRecalc gesetzt ist, hat sich am Inhalt der Tabelle etwas // geaendert. Es muss dann der erste Pass noch einmal durchgefuehrt // werden. if( bRecalc ) AutoLayoutPass1(); SwRootFrm *pRoot = (SwRootFrm*)GetDoc()->GetCurrentViewShell()->GetLayout(); if ( pRoot && pRoot->IsCallbackActionEnabled() ) pRoot->StartAllAction(); //swmod 071108//swmod 071225 // Sonst koennen die Breiten gesetzt werden, wobei zuvor aber jeweils // noch der Pass 2 laufen muss. SetWidths( sal_True, nAbsAvail ); if ( pRoot && pRoot->IsCallbackActionEnabled() ) pRoot->EndAllAction( sal_True ); //True per VirDev (Browsen ruhiger) //swmod 071108//swmod 071225 } IMPL_STATIC_LINK( SwHTMLTableLayout, DelayedResize_Impl, void*, EMPTYARG ) { #ifdef TEST_DELAYED_RESIZE Sound::Beep( SOUND_WARNING ); #endif pThis->aResizeTimer.Stop(); pThis->_Resize( pThis->nDelayedResizeAbsAvail, pThis->bDelayedResizeRecalc ); return 0; } sal_Bool SwHTMLTableLayout::Resize( sal_uInt16 nAbsAvail, sal_Bool bRecalc, sal_Bool bForce, sal_uLong nDelay ) { if( 0 == nAbsAvail ) return sal_False; ASSERT( IsTopTable(), "Resize darf nur an Top-Tabellen aufgerufen werden" ); // Darf die Tabelle ueberhaupt Resized werden oder soll sie es trotzdem? if( bMustNotResize && !bForce ) return sal_False; // Darf ein Recalc der Tabelle durchgefuehrt werden? if( bMustNotRecalc && !bForce ) bRecalc = sal_False; const SwDoc *pDoc = GetDoc(); // Wenn es ein Layout gibt, wurde evtl. die Groesse der Root-Frames // und nicht die der VisArea uebergeben. Wenn wir nicht in einem Rahmen // stehen, muss die Tabelle allerdings fuer die VisArea berechnet werden, // weil sonst die Umschaltung von relativ nach absolut nicht funktioniert. if( pDoc->GetCurrentViewShell() && pDoc->GetCurrentViewShell()->GetViewOptions()->getBrowseMode() ) { const sal_uInt16 nVisAreaWidth = GetBrowseWidthByVisArea( *pDoc ); if( nVisAreaWidth < nAbsAvail && !FindFlyFrmFmt() ) nAbsAvail = nVisAreaWidth; } if( nDelay==0 && aResizeTimer.IsActive() ) { // Wenn beim Aufruf eines synchronen Resize noch ein asynchrones // Resize aussteht, dann werden nur die neuen Werte uebernommen. bRecalc |= bDelayedResizeRecalc; nDelayedResizeAbsAvail = nAbsAvail; return sal_False; } // Optimierung: // Wenn die Minima/Maxima nicht neu berechnet werden sollen und // - die Breite der Tabelle nie neu berechnet werden muss, oder // - die Tabelle schon fuer die uebergebene Breite berechnet wurde, oder // - der verfuegbare Platz kleiner oder gleich der Minimalbreite ist // und die Tabelle bereits die Minimalbreite besitzt, oder // - der verfuegbare Platz groesser ist als die Maximalbreite und // die Tabelle bereits die Maximalbreite besitzt // wird sich an der Tabelle nichts aendern. if( !bRecalc && ( !bMustResize || (nLastResizeAbsAvail==nAbsAvail) || (nAbsAvail<=nMin && nRelTabWidth==nMin) || (!bPrcWidthOption && nAbsAvail>=nMax && nRelTabWidth==nMax) ) ) return sal_False; if( nDelay==HTMLTABLE_RESIZE_NOW ) { if( aResizeTimer.IsActive() ) aResizeTimer.Stop(); _Resize( nAbsAvail, bRecalc ); } else if( nDelay > 0 ) { nDelayedResizeAbsAvail = nAbsAvail; bDelayedResizeRecalc = bRecalc; aResizeTimer.SetTimeout( nDelay ); aResizeTimer.Start(); #ifdef TEST_DELAYED_RESIZE Sound::Beep( SOUND_DEFAULT ); #endif } else { _Resize( nAbsAvail, bRecalc ); } return sal_True; } void SwHTMLTableLayout::BordersChanged( sal_uInt16 nAbsAvail, sal_Bool bRecalc ) { bBordersChanged = sal_True; Resize( nAbsAvail, bRecalc ); }