/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /************************************************************************* * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * Copyright 2000, 2010 Oracle and/or its affiliates. * * OpenOffice.org - a multi-platform office productivity suite * * This file is part of OpenOffice.org. * * OpenOffice.org is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 3 * only, as published by the Free Software Foundation. * * OpenOffice.org is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License version 3 for more details * (a copy is included in the LICENSE file that accompanied this code). * * You should have received a copy of the GNU Lesser General Public License * version 3 along with OpenOffice.org. If not, see * * for a copy of the LGPLv3 License. * ************************************************************************/ // MARKER(update_precomp.py): autogen include statement, do not remove #include "precompiled_sw.hxx" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "viewimp.hxx" #include "viewopt.hxx" #include "pagefrm.hxx" #include "rootfrm.hxx" #include "cntfrm.hxx" #include "flyfrm.hxx" #include "doc.hxx" #include "fesh.hxx" #include "dview.hxx" #include "dflyobj.hxx" #include "dcontact.hxx" #include "frmtool.hxx" #include "fldbas.hxx" #include "hints.hxx" #include "swtable.hxx" #include "ftnidx.hxx" #include "bodyfrm.hxx" #include "ftnfrm.hxx" #include "tabfrm.hxx" #include "txtfrm.hxx" #include "layact.hxx" #include "flyfrms.hxx" #include "htmltbl.hxx" #include "pagedesc.hxx" #include "poolfmt.hxx" #include #include // SwFontAccess #include #include #include using namespace ::com::sun::star; /************************************************************************* |* |* SwBodyFrm::SwBodyFrm() |* |*************************************************************************/ SwBodyFrm::SwBodyFrm( SwFrmFmt *pFmt, SwFrm* pSib ): SwLayoutFrm( pFmt, pSib ) { nType = FRMC_BODY; } /************************************************************************* |* |* SwBodyFrm::Format() |* |*************************************************************************/ void SwBodyFrm::Format( const SwBorderAttrs * ) { //Formatieren des Body ist zu einfach, deshalb bekommt er ein eigenes //Format; Umrandungen und dergl. sind hier nicht zu beruecksichtigen. //Breite ist die der PrtArea des Uppers, Hoehe ist die Hoehe der PrtArea //des Uppers abzueglich der Nachbarn (Wird eigentlich eingestellt aber //Vorsicht ist die Mutter der Robustheit). //Die PrtArea ist stets so gross wie der Frm itself. if ( !bValidSize ) { SwTwips nHeight = GetUpper()->Prt().Height(); SwTwips nWidth = GetUpper()->Prt().Width(); const SwFrm *pFrm = GetUpper()->Lower(); do { if ( pFrm != this ) { if( pFrm->IsVertical() ) nWidth -= pFrm->Frm().Width(); else nHeight -= pFrm->Frm().Height(); } pFrm = pFrm->GetNext(); } while ( pFrm ); if ( nHeight < 0 ) nHeight = 0; Frm().Height( nHeight ); //Badaa: 2008-04-18 * Support for Classical Mongolian Script (SCMS) joint with Jiayanmin if( IsVertical() && !IsVertLR() && !IsReverse() && nWidth != Frm().Width() ) Frm().Pos().X() += Frm().Width() - nWidth; Frm().Width( nWidth ); } sal_Bool bNoGrid = sal_True; if( GetUpper()->IsPageFrm() && ((SwPageFrm*)GetUpper())->HasGrid() ) { GETGRID( ((SwPageFrm*)GetUpper()) ) if( pGrid ) { bNoGrid = sal_False; long nSum = pGrid->GetBaseHeight() + pGrid->GetRubyHeight(); SWRECTFN( this ) long nSize = (Frm().*fnRect->fnGetWidth)(); long nBorder = 0; if( GRID_LINES_CHARS == pGrid->GetGridType() ) { //for textgrid refactor SwDoc *pDoc = GetFmt()->GetDoc(); nBorder = nSize % (GETGRIDWIDTH(pGrid, pDoc)); nSize -= nBorder; nBorder /= 2; } (Prt().*fnRect->fnSetPosX)( nBorder ); (Prt().*fnRect->fnSetWidth)( nSize ); // Height of body frame: nBorder = (Frm().*fnRect->fnGetHeight)(); // Number of possible lines in area of body frame: long nNumberOfLines = nBorder / nSum; if( nNumberOfLines > pGrid->GetLines() ) nNumberOfLines = pGrid->GetLines(); // Space required for nNumberOfLines lines: nSize = nNumberOfLines * nSum; nBorder -= nSize; nBorder /= 2; // #i21774# Footnotes and centering the grid does not work together: const bool bAdjust = 0 == ((SwPageFrm*)GetUpper())->GetFmt()->GetDoc()-> GetFtnIdxs().Count(); (Prt().*fnRect->fnSetPosY)( bAdjust ? nBorder : 0 ); (Prt().*fnRect->fnSetHeight)( nSize ); } } if( bNoGrid ) { Prt().Pos().X() = Prt().Pos().Y() = 0; Prt().Height( Frm().Height() ); Prt().Width( Frm().Width() ); } bValidSize = bValidPrtArea = sal_True; } void SwBodyFrm::Paint( const SwRect& rRect, const SwPrintData* ) const { #if OSL_DEBUG_LAYOUT > 1 // Paint a red border around the SwBodyFrm in debug mode ViewShell *pSh = GetShell(); OutputDevice* pOut = pSh->GetOut(); pOut->Push(); pOut->SetLineColor(Color(255, 0, 0)); pOut->SetFillColor(COL_TRANSPARENT); SwRect aRect = Frm(); pOut->DrawRect(aRect.SVRect()); pOut->Pop(); #endif SwLayoutFrm::Paint(rRect); } /************************************************************************* |* |* SwPageFrm::SwPageFrm(), ~SwPageFrm() |* |*************************************************************************/ SwPageFrm::SwPageFrm( SwFrmFmt *pFmt, SwFrm* pSib, SwPageDesc *pPgDsc ) : SwFtnBossFrm( pFmt, pSib ), pSortedObjs( 0 ), pDesc( pPgDsc ), nPhyPageNum( 0 ), // OD 2004-05-17 #i28701# mbLayoutInProgress( false ) { SetDerivedVert( sal_False ); SetDerivedR2L( sal_False ); if( pDesc ) { bHasGrid = sal_True; GETGRID( this ) if( !pGrid ) bHasGrid = sal_False; } else bHasGrid = sal_False; SetMaxFtnHeight( pPgDsc->GetFtnInfo().GetHeight() ? pPgDsc->GetFtnInfo().GetHeight() : LONG_MAX ), nType = FRMC_PAGE; bInvalidLayout = bInvalidCntnt = bInvalidSpelling = bInvalidSmartTags = bInvalidAutoCmplWrds = bInvalidWordCount = sal_True; bInvalidFlyLayout = bInvalidFlyCntnt = bInvalidFlyInCnt = bFtnPage = bEndNotePage = sal_False; ViewShell *pSh = getRootFrm()->GetCurrShell(); const bool bBrowseMode = pSh && pSh->GetViewOptions()->getBrowseMode(); if ( bBrowseMode ) { Frm().Height( 0 ); long nWidth = pSh->VisArea().Width(); if ( !nWidth ) nWidth = 5000L; //aendert sich sowieso Frm().Width ( nWidth ); } else Frm().SSize( pFmt->GetFrmSize().GetSize() ); //Body-Bereich erzeugen und einsetzen, aber nur wenn ich nicht gerade //eine Leerseite bin. SwDoc *pDoc = pFmt->GetDoc(); if ( sal_False == (bEmptyPage = pFmt == pDoc->GetEmptyPageFmt()) ) { bEmptyPage = sal_False; Calc(); //Damit die PrtArea stimmt. SwBodyFrm *pBodyFrm = new SwBodyFrm( pDoc->GetDfltFrmFmt(), this ); pBodyFrm->ChgSize( Prt().SSize() ); pBodyFrm->Paste( this ); pBodyFrm->Calc(); //Damit die Spalten korrekt //eingesetzt werden koennen. pBodyFrm->InvalidatePos(); if ( bBrowseMode ) _InvalidateSize(); //Alles nur gelogen //Header/Footer einsetzen, nur rufen wenn aktiv. if ( pFmt->GetHeader().IsActive() ) PrepareHeader(); if ( pFmt->GetFooter().IsActive() ) PrepareFooter(); const SwFmtCol &rCol = pFmt->GetCol(); if ( rCol.GetNumCols() > 1 ) { const SwFmtCol aOld; //ChgColumns() verlaesst sich darauf, dass ein //Old-Wert hereingereicht wird. pBodyFrm->ChgColumns( aOld, rCol ); } } } SwPageFrm::~SwPageFrm() { //FlyContainer entleeren, delete der Flys uebernimmt der Anchor //(Basisklasse SwFrm) if ( pSortedObjs ) { //Objekte koennen (warum auch immer) auch an Seiten verankert sein, //die vor Ihren Ankern stehen. Dann wuerde auf bereits freigegebenen //Speicher zugegriffen. for ( sal_uInt16 i = 0; i < pSortedObjs->Count(); ++i ) { SwAnchoredObject* pAnchoredObj = (*pSortedObjs)[i]; pAnchoredObj->SetPageFrm( 0L ); } delete pSortedObjs; pSortedObjs = 0; //Auf 0 setzen, sonst rauchts beim Abmdelden von Flys! } //Damit der Zugriff auf zerstoerte Seiten verhindert werden kann. if ( !IsEmptyPage() ) //#59184# sollte fuer Leerseiten unnoetig sein. { SwDoc *pDoc = GetFmt()->GetDoc(); if( pDoc && !pDoc->IsInDtor() ) { ViewShell *pSh = getRootFrm()->GetCurrShell(); if ( pSh ) { SwViewImp *pImp = pSh->Imp(); pImp->SetFirstVisPageInvalid(); if ( pImp->IsAction() ) pImp->GetLayAction().SetAgain(); // OD 12.02.2003 #i9719#, #105645# - retouche area of page // including border and shadow area. const bool bRightSidebar = (SidebarPosition() == sw::sidebarwindows::SIDEBAR_RIGHT); SwRect aRetoucheRect; SwPageFrm::GetBorderAndShadowBoundRect( Frm(), pSh, aRetoucheRect, bRightSidebar ); pSh->AddPaintRect( aRetoucheRect ); } } } } void SwPageFrm::CheckGrid( sal_Bool bInvalidate ) { sal_Bool bOld = bHasGrid; bHasGrid = sal_True; GETGRID( this ) bHasGrid = 0 != pGrid; if( bInvalidate || bOld != bHasGrid ) { SwLayoutFrm* pBody = FindBodyCont(); if( pBody ) { pBody->InvalidatePrt(); SwCntntFrm* pFrm = pBody->ContainsCntnt(); while( pBody->IsAnLower( pFrm ) ) { ((SwTxtFrm*)pFrm)->Prepare( PREP_CLEAR ); pFrm = pFrm->GetNextCntntFrm(); } } SetCompletePaint(); } } void SwPageFrm::CheckDirection( sal_Bool bVert ) { sal_uInt16 nDir = ((SvxFrameDirectionItem&)GetFmt()->GetFmtAttr( RES_FRAMEDIR )).GetValue(); if( bVert ) { if( FRMDIR_HORI_LEFT_TOP == nDir || FRMDIR_HORI_RIGHT_TOP == nDir ) { //Badaa: 2008-04-18 * Support for Classical Mongolian Script (SCMS) joint with Jiayanmin bVertLR = 0; bVertical = 0; } else { const ViewShell *pSh = getRootFrm()->GetCurrShell(); if( pSh && pSh->GetViewOptions()->getBrowseMode() ) { //Badaa: 2008-04-18 * Support for Classical Mongolian Script (SCMS) joint with Jiayanmin bVertLR = 0; bVertical = 0; } else { bVertical = 1; //Badaa: 2008-04-18 * Support for Classical Mongolian Script (SCMS) joint with Jiayanmin if(FRMDIR_VERT_TOP_RIGHT == nDir) bVertLR = 0; else if(FRMDIR_VERT_TOP_LEFT==nDir) bVertLR = 1; } } bReverse = 0; bInvalidVert = 0; } else { if( FRMDIR_HORI_RIGHT_TOP == nDir ) bRightToLeft = 1; else bRightToLeft = 0; bInvalidR2L = 0; } } /************************************************************************* |* |* SwPageFrm::PreparePage() |* |* Beschreibung Erzeugt die Spezifischen Flys zur Seite und formatiert |* generischen Cntnt |* |*************************************************************************/ void MA_FASTCALL lcl_FormatLay( SwLayoutFrm *pLay ) { //Alle LayoutFrms - nicht aber Tables, Flys o.ae. - formatieren. SwFrm *pTmp = pLay->Lower(); //Erst die untergeordneten while ( pTmp ) { if ( pTmp->GetType() & 0x00FF ) ::lcl_FormatLay( (SwLayoutFrm*)pTmp ); pTmp = pTmp->GetNext(); } pLay->Calc(); } void MA_FASTCALL lcl_MakeObjs( const SwSpzFrmFmts &rTbl, SwPageFrm *pPage ) { //Anlegen bzw. registrieren von Flys und Drawobjekten. //Die Formate stehen in der SpzTbl (vom Dokument). //Flys werden angelegt, DrawObjekte werden bei der Seite angemeldet. for ( sal_uInt16 i = 0; i < rTbl.Count(); ++i ) { SdrObject *pSdrObj; SwFrmFmt *pFmt = rTbl[i]; const SwFmtAnchor &rAnch = pFmt->GetAnchor(); if ( rAnch.GetPageNum() == pPage->GetPhyPageNum() ) { if( rAnch.GetCntntAnchor() ) { if (FLY_AT_PAGE == rAnch.GetAnchorId()) { SwFmtAnchor aAnch( rAnch ); aAnch.SetAnchor( 0 ); pFmt->SetFmtAttr( aAnch ); } else continue; } //Wird ein Rahmen oder ein SdrObject beschrieben? sal_Bool bSdrObj = RES_DRAWFRMFMT == pFmt->Which(); pSdrObj = 0; if ( bSdrObj && 0 == (pSdrObj = pFmt->FindSdrObject()) ) { OSL_FAIL( "DrawObject not found." ); pFmt->GetDoc()->DelFrmFmt( pFmt ); --i; continue; } //Das Objekt kann noch an einer anderen Seite verankert sein. //Z.B. beim Einfuegen einer neuen Seite aufgrund eines //Pagedescriptor-Wechsels. Das Objekt muss dann umgehaengt //werden. //Fuer bestimmte Faelle ist das Objekt bereits an der richtigen //Seite verankert. Das wird hier automatisch erledigt und braucht //- wenngleich performater machbar - nicht extra codiert werden. SwPageFrm *pPg = pPage->IsEmptyPage() ? (SwPageFrm*)pPage->GetNext() : pPage; if ( bSdrObj ) { // OD 23.06.2003 #108784# - consider 'virtual' drawing objects SwDrawContact *pContact = static_cast(::GetUserCall(pSdrObj)); if ( pSdrObj->ISA(SwDrawVirtObj) ) { SwDrawVirtObj* pDrawVirtObj = static_cast(pSdrObj); if ( pContact ) { pDrawVirtObj->RemoveFromWriterLayout(); pDrawVirtObj->RemoveFromDrawingPage(); pPg->AppendDrawObj( *(pContact->GetAnchoredObj( pDrawVirtObj )) ); } } else { if ( pContact->GetAnchorFrm() ) pContact->DisconnectFromLayout( false ); pPg->AppendDrawObj( *(pContact->GetAnchoredObj( pSdrObj )) ); } } else { SwIterator aIter( *pFmt ); SwFlyFrm *pFly = aIter.First(); if ( pFly) { if( pFly->GetAnchorFrm() ) pFly->AnchorFrm()->RemoveFly( pFly ); } else pFly = new SwFlyLayFrm( (SwFlyFrmFmt*)pFmt, pPg, pPg ); pPg->AppendFly( pFly ); ::RegistFlys( pPg, pFly ); } } } } void SwPageFrm::PreparePage( sal_Bool bFtn ) { SetFtnPage( bFtn ); // --> OD 2008-01-30 #i82258# // Due to made change on OOo 2.0 code line, method <::lcl_FormatLay(..)> has // the side effect, that the content of page header and footer are formatted. // For this formatting it is needed that the anchored objects are registered // at the instance. // Thus, first calling <::RegistFlys(..)>, then call <::lcl_FormatLay(..)> ::RegistFlys( this, this ); if ( Lower() ) { ::lcl_FormatLay( this ); } // <-- //Flys und DrawObjekte die noch am Dokument bereitstehen. //Fussnotenseiten tragen keine Seitengebundenen Flys! //Es kann Flys und Objekte geben, die auf Leerseiten (Seitennummernmaessig) //stehen wollen, diese werden jedoch von den Leerseiten ignoriert; //sie werden von den Folgeseiten aufgenommen. if ( !bFtn && !IsEmptyPage() ) { SwDoc *pDoc = GetFmt()->GetDoc(); if ( GetPrev() && ((SwPageFrm*)GetPrev())->IsEmptyPage() ) lcl_MakeObjs( *pDoc->GetSpzFrmFmts(), (SwPageFrm*)GetPrev() ); lcl_MakeObjs( *pDoc->GetSpzFrmFmts(), this ); //Kopf-/Fusszeilen) formatieren. SwLayoutFrm *pLow = (SwLayoutFrm*)Lower(); while ( pLow ) { if ( pLow->GetType() & (FRMTYPE_HEADER|FRMTYPE_FOOTER) ) { SwCntntFrm *pCntnt = pLow->ContainsCntnt(); while ( pCntnt && pLow->IsAnLower( pCntnt ) ) { pCntnt->OptCalc(); //Nicht die Vorgaenger pCntnt = pCntnt->GetNextCntntFrm(); } } pLow = (SwLayoutFrm*)pLow->GetNext(); } } } /************************************************************************* |* |* SwPageFrm::Modify() |* |*************************************************************************/ void SwPageFrm::Modify( const SfxPoolItem* pOld, const SfxPoolItem * pNew ) { ViewShell *pSh = getRootFrm()->GetCurrShell(); if ( pSh ) pSh->SetFirstVisPageInvalid(); sal_uInt8 nInvFlags = 0; if( pNew && RES_ATTRSET_CHG == pNew->Which() ) { SfxItemIter aNIter( *((SwAttrSetChg*)pNew)->GetChgSet() ); SfxItemIter aOIter( *((SwAttrSetChg*)pOld)->GetChgSet() ); SwAttrSetChg aOldSet( *(SwAttrSetChg*)pOld ); SwAttrSetChg aNewSet( *(SwAttrSetChg*)pNew ); while( sal_True ) { _UpdateAttr( (SfxPoolItem*)aOIter.GetCurItem(), (SfxPoolItem*)aNIter.GetCurItem(), nInvFlags, &aOldSet, &aNewSet ); if( aNIter.IsAtEnd() ) break; aNIter.NextItem(); aOIter.NextItem(); } if ( aOldSet.Count() || aNewSet.Count() ) SwLayoutFrm::Modify( &aOldSet, &aNewSet ); } else _UpdateAttr( pOld, pNew, nInvFlags ); if ( nInvFlags != 0 ) { InvalidatePage( this ); if ( nInvFlags & 0x01 ) _InvalidatePrt(); if ( nInvFlags & 0x02 ) SetCompletePaint(); if ( nInvFlags & 0x04 && GetNext() ) GetNext()->InvalidatePos(); if ( nInvFlags & 0x08 ) PrepareHeader(); if ( nInvFlags & 0x10 ) PrepareFooter(); if ( nInvFlags & 0x20 ) CheckGrid( nInvFlags & 0x40 ); } } void SwPageFrm::_UpdateAttr( const SfxPoolItem *pOld, const SfxPoolItem *pNew, sal_uInt8 &rInvFlags, SwAttrSetChg *pOldSet, SwAttrSetChg *pNewSet ) { sal_Bool bClear = sal_True; const sal_uInt16 nWhich = pOld ? pOld->Which() : pNew ? pNew->Which() : 0; switch( nWhich ) { case RES_FMT_CHG: { //Wenn sich das FrmFmt aendert kann hier einiges passieren. //Abgesehen von den Grossenverhaeltnissen sind noch andere //Dinge betroffen. //1. Spaltigkeit. OSL_ENSURE( pOld && pNew, "FMT_CHG Missing Format." ); const SwFmt* pOldFmt = ((SwFmtChg*)pOld)->pChangedFmt; const SwFmt* pNewFmt = ((SwFmtChg*)pNew)->pChangedFmt; OSL_ENSURE( pOldFmt && pNewFmt, "FMT_CHG Missing Format." ); const SwFmtCol &rOldCol = pOldFmt->GetCol(); const SwFmtCol &rNewCol = pNewFmt->GetCol(); if( rOldCol != rNewCol ) { SwLayoutFrm *pB = FindBodyCont(); OSL_ENSURE( pB, "Seite ohne Body." ); pB->ChgColumns( rOldCol, rNewCol ); rInvFlags |= 0x20; } //2. Kopf- und Fusszeilen. const SwFmtHeader &rOldH = pOldFmt->GetHeader(); const SwFmtHeader &rNewH = pNewFmt->GetHeader(); if( rOldH != rNewH ) rInvFlags |= 0x08; const SwFmtFooter &rOldF = pOldFmt->GetFooter(); const SwFmtFooter &rNewF = pNewFmt->GetFooter(); if( rOldF != rNewF ) rInvFlags |= 0x10; CheckDirChange(); } /* kein break hier */ case RES_FRM_SIZE: { const SwRect aOldPageFrmRect( Frm() ); ViewShell *pSh = getRootFrm()->GetCurrShell(); if( pSh && pSh->GetViewOptions()->getBrowseMode() ) { bValidSize = sal_False; // OD 28.10.2002 #97265# - Don't call // Calculation of the page is not necessary, because its size is // is invalidated here and further invalidation is done in the // calling method and probably by calling // at the end. // It can also causes inconsistences, because the lowers are // adjusted, but not calculated, and a of // a next page is called. This is performed on the switch to the // online layout. //MakeAll(); } else { const SwFmtFrmSize &rSz = nWhich == RES_FMT_CHG ? ((SwFmtChg*)pNew)->pChangedFmt->GetFrmSize() : (const SwFmtFrmSize&)*pNew; Frm().Height( Max( rSz.GetHeight(), long(MINLAY) ) ); Frm().Width ( Max( rSz.GetWidth(), long(MINLAY) ) ); // PAGES01 if ( GetUpper() ) static_cast(GetUpper())->CheckViewLayout( 0, 0 ); } //Window aufraeumen. if( pSh && pSh->GetWin() && aOldPageFrmRect.HasArea() ) { // OD 12.02.2003 #i9719#, #105645# - consider border and shadow of // page frame for determine 'old' rectangle - it's used for invalidating. const bool bRightSidebar = (SidebarPosition() == sw::sidebarwindows::SIDEBAR_RIGHT); SwRect aOldRectWithBorderAndShadow; SwPageFrm::GetBorderAndShadowBoundRect( aOldPageFrmRect, pSh, aOldRectWithBorderAndShadow, bRightSidebar ); pSh->InvalidateWindows( aOldRectWithBorderAndShadow ); } rInvFlags |= 0x03; if ( aOldPageFrmRect.Height() != Frm().Height() ) rInvFlags |= 0x04; } break; case RES_COL: { SwLayoutFrm *pB = FindBodyCont(); OSL_ENSURE( pB, "Seite ohne Body." ); pB->ChgColumns( *(const SwFmtCol*)pOld, *(const SwFmtCol*)pNew ); rInvFlags |= 0x22; } break; case RES_HEADER: rInvFlags |= 0x08; break; case RES_FOOTER: rInvFlags |= 0x10; break; case RES_TEXTGRID: rInvFlags |= 0x60; break; case RES_PAGEDESC_FTNINFO: //Die derzeit einzig sichere Methode: ((SwRootFrm*)GetUpper())->SetSuperfluous(); SetMaxFtnHeight( pDesc->GetFtnInfo().GetHeight() ); if ( !GetMaxFtnHeight() ) SetMaxFtnHeight( LONG_MAX ); SetColMaxFtnHeight(); //Hier wird die Seite ggf. zerstoert! ((SwRootFrm*)GetUpper())->RemoveFtns( 0, sal_False, sal_True ); break; case RES_FRAMEDIR : CheckDirChange(); break; default: bClear = sal_False; } if ( bClear ) { if ( pOldSet || pNewSet ) { if ( pOldSet ) pOldSet->ClearItem( nWhich ); if ( pNewSet ) pNewSet->ClearItem( nWhich ); } else SwLayoutFrm::Modify( pOld, pNew ); } } /************************************************************************* |* |* SwPageFrm::GetInfo() |* |* Beschreibung erfragt Informationen |* *************************************************************************/ // erfrage vom Modify Informationen sal_Bool SwPageFrm::GetInfo( SfxPoolItem & rInfo ) const { if( RES_AUTOFMT_DOCNODE == rInfo.Which() ) { // es gibt einen PageFrm also wird er benutzt return sal_False; } return sal_True; // weiter suchen } /************************************************************************* |* |* SwPageFrm::SetPageDesc() |* |*************************************************************************/ void SwPageFrm::SetPageDesc( SwPageDesc *pNew, SwFrmFmt *pFmt ) { pDesc = pNew; if ( pFmt ) SetFrmFmt( pFmt ); } /************************************************************************* |* |* SwPageFrm::FindPageDesc() |* |* Beschreibung Der richtige PageDesc wird bestimmt: |* 0. Vom Dokument bei Fussnotenseiten und Endnotenseiten |* 1. vom ersten BodyCntnt unterhalb der Seite. |* 2. vom PageDesc der vorstehenden Seite. |* 3. bei Leerseiten vom PageDesc der vorigen Seite. |* 3.1 vom PageDesc der folgenden Seite wenn es keinen Vorgaenger gibt. |* 4. es ist der Default-PageDesc sonst. |* 5. Im BrowseMode ist der Pagedesc immer der vom ersten Absatz im |* Dokument oder Standard (der 0-te) wenn der erste Absatz keinen |* wuenscht. |* (6. Im HTML-Mode ist der Pagedesc immer die HTML-Seitenvorlage.) |* |*************************************************************************/ SwPageDesc *SwPageFrm::FindPageDesc() { //0. if ( IsFtnPage() ) { SwDoc *pDoc = GetFmt()->GetDoc(); if ( IsEndNotePage() ) return pDoc->GetEndNoteInfo().GetPageDesc( *pDoc ); else return pDoc->GetFtnInfo().GetPageDesc( *pDoc ); } //6. //if ( GetFmt()->GetDoc()->IsHTMLMode() ) // return GetFmt()->GetDoc()->GetPageDescFromPool( RES_POOLPAGE_HTML ); SwPageDesc *pRet = 0; //5. const ViewShell *pSh = getRootFrm()->GetCurrShell(); if( pSh && pSh->GetViewOptions()->getBrowseMode() ) { SwCntntFrm *pFrm = GetUpper()->ContainsCntnt(); while ( !pFrm->IsInDocBody() ) pFrm = pFrm->GetNextCntntFrm(); SwFrm *pFlow = pFrm; if ( pFlow->IsInTab() ) pFlow = pFlow->FindTabFrm(); pRet = (SwPageDesc*)pFlow->GetAttrSet()->GetPageDesc().GetPageDesc(); if ( !pRet ) pRet = &GetFmt()->GetDoc()->_GetPageDesc( 0 ); return pRet; } SwFrm *pFlow = FindFirstBodyCntnt(); if ( pFlow && pFlow->IsInTab() ) pFlow = pFlow->FindTabFrm(); //1. if ( pFlow ) { SwFlowFrm *pTmp = SwFlowFrm::CastFlowFrm( pFlow ); if ( !pTmp->IsFollow() ) pRet = (SwPageDesc*)pFlow->GetAttrSet()->GetPageDesc().GetPageDesc(); } //3. und 3.1 if ( !pRet && IsEmptyPage() ) // FME 2008-03-03 #i81544# lijian/fme: an empty page should have // the same page description as its prev, just like after construction // of the empty page. pRet = GetPrev() ? ((SwPageFrm*)GetPrev())->GetPageDesc() : GetNext() ? ((SwPageFrm*)GetNext())->GetPageDesc() : 0; //2. if ( !pRet ) pRet = GetPrev() ? ((SwPageFrm*)GetPrev())->GetPageDesc()->GetFollow() : 0; //4. if ( !pRet ) pRet = (SwPageDesc*)&(const_cast(GetFmt()->GetDoc()) ->GetPageDesc( 0 )); OSL_ENSURE( pRet, "Kein Descriptor gefunden." ); return pRet; } //Wenn der RootFrm seine Groesse aendert muss benachrichtigt werden. void AdjustSizeChgNotify( SwRootFrm *pRoot ) { const sal_Bool bOld = pRoot->IsSuperfluous(); pRoot->bCheckSuperfluous = sal_False; ViewShell *pSh = pRoot->GetCurrShell(); if ( pSh ) { do { if( pRoot == pSh->GetLayout() ) { pSh->SizeChgNotify(); pSh->Imp()->NotifySizeChg( pRoot->Frm().SSize() ); } pSh = (ViewShell*)pSh->GetNext(); } while ( pSh != pRoot->GetCurrShell() ); } pRoot->bCheckSuperfluous = bOld; } inline void SetLastPage( SwPageFrm *pPage ) { ((SwRootFrm*)pPage->GetUpper())->pLastPage = pPage; } /************************************************************************* |* |* SwPageFrm::Cut() |* |*************************************************************************/ void SwPageFrm::Cut() { // PAGES01 //AdjustRootSize( CHG_CUTPAGE, 0 ); ViewShell *pSh = getRootFrm()->GetCurrShell(); if ( !IsEmptyPage() ) { if ( GetNext() ) GetNext()->InvalidatePos(); //Flys deren Anker auf anderen Seiten stehen umhaengen. //DrawObjecte spielen hier keine Rolle. if ( GetSortedObjs() ) { for ( int i = 0; GetSortedObjs() && (sal_uInt16)i < GetSortedObjs()->Count(); ++i ) { // --> OD 2004-06-29 #i28701# SwAnchoredObject* pAnchoredObj = (*GetSortedObjs())[i]; if ( pAnchoredObj->ISA(SwFlyAtCntFrm) ) { SwFlyFrm* pFly = static_cast(pAnchoredObj); SwPageFrm *pAnchPage = pFly->GetAnchorFrm() ? pFly->AnchorFrm()->FindPageFrm() : 0; if ( pAnchPage && (pAnchPage != this) ) { MoveFly( pFly, pAnchPage ); --i; pFly->InvalidateSize(); pFly->_InvalidatePos(); } } // <-- } } //Window aufraeumen if ( pSh && pSh->GetWin() ) pSh->InvalidateWindows( Frm() ); } // die Seitennummer der Root runterzaehlen. ((SwRootFrm*)GetUpper())->DecrPhyPageNums(); SwPageFrm *pPg = (SwPageFrm*)GetNext(); if ( pPg ) { while ( pPg ) { pPg->DecrPhyPageNum(); //inline --nPhyPageNum pPg = (SwPageFrm*)pPg->GetNext(); } } else ::SetLastPage( (SwPageFrm*)GetPrev() ); SwFrm* pRootFrm = GetUpper(); // Alle Verbindungen kappen. Remove(); // PAGES01 if ( pRootFrm ) static_cast(pRootFrm)->CheckViewLayout( 0, 0 ); } /************************************************************************* |* |* SwPageFrm::Paste() |* |*************************************************************************/ void SwPageFrm::Paste( SwFrm* pParent, SwFrm* pSibling ) { OSL_ENSURE( pParent->IsRootFrm(), "Parent ist keine Root." ); OSL_ENSURE( pParent, "Kein Parent fuer Paste." ); OSL_ENSURE( pParent != this, "Bin selbst der Parent." ); OSL_ENSURE( pSibling != this, "Bin mein eigener Nachbar." ); OSL_ENSURE( !GetPrev() && !GetNext() && !GetUpper(), "Bin noch irgendwo angemeldet." ); //In den Baum einhaengen. InsertBefore( (SwLayoutFrm*)pParent, pSibling ); // die Seitennummer am Root hochzaehlen. ((SwRootFrm*)GetUpper())->IncrPhyPageNums(); if( GetPrev() ) SetPhyPageNum( ((SwPageFrm*)GetPrev())->GetPhyPageNum() + 1 ); else SetPhyPageNum( 1 ); SwPageFrm *pPg = (SwPageFrm*)GetNext(); if ( pPg ) { while ( pPg ) { pPg->IncrPhyPageNum(); //inline ++nPhyPageNum pPg->_InvalidatePos(); pPg->InvalidateLayout(); pPg = (SwPageFrm*)pPg->GetNext(); } } else ::SetLastPage( this ); if( Frm().Width() != pParent->Prt().Width() ) _InvalidateSize(); InvalidatePos(); ViewShell *pSh = getRootFrm()->GetCurrShell(); if ( pSh ) pSh->SetFirstVisPageInvalid(); // PAGES01 getRootFrm()->CheckViewLayout( 0, 0 ); } /************************************************************************* |* |* SwPageFrm::PrepareRegisterChg() |* |*************************************************************************/ void lcl_PrepFlyInCntRegister( SwCntntFrm *pFrm ) { pFrm->Prepare( PREP_REGISTER ); if( pFrm->GetDrawObjs() ) { for( sal_uInt16 i = 0; i < pFrm->GetDrawObjs()->Count(); ++i ) { // --> OD 2004-06-29 #i28701# SwAnchoredObject* pAnchoredObj = (*pFrm->GetDrawObjs())[i]; if ( pAnchoredObj->ISA(SwFlyInCntFrm) ) { SwFlyFrm* pFly = static_cast(pAnchoredObj); SwCntntFrm *pCnt = pFly->ContainsCntnt(); while ( pCnt ) { lcl_PrepFlyInCntRegister( pCnt ); pCnt = pCnt->GetNextCntntFrm(); } } // <-- } } } void SwPageFrm::PrepareRegisterChg() { SwCntntFrm *pFrm = FindFirstBodyCntnt(); while( pFrm ) { lcl_PrepFlyInCntRegister( pFrm ); pFrm = pFrm->GetNextCntntFrm(); if( !IsAnLower( pFrm ) ) break; } if( GetSortedObjs() ) { for( sal_uInt16 i = 0; i < GetSortedObjs()->Count(); ++i ) { // --> OD 2004-06-29 #i28701# SwAnchoredObject* pAnchoredObj = (*GetSortedObjs())[i]; if ( pAnchoredObj->ISA(SwFlyFrm) ) { SwFlyFrm *pFly = static_cast(pAnchoredObj); pFrm = pFly->ContainsCntnt(); while ( pFrm ) { ::lcl_PrepFlyInCntRegister( pFrm ); pFrm = pFrm->GetNextCntntFrm(); } } } } } /************************************************************************* |* |* SwFrm::CheckPageDescs() |* |* Beschreibung Prueft alle Seiten ab der uebergebenen, daraufhin, |* ob sie das richtige FrmFmt verwenden. Wenn 'falsche' Seiten |* aufgespuehrt werden, so wird versucht die Situation moeglichst |* einfache zu bereinigen. |* |*************************************************************************/ void SwFrm::CheckPageDescs( SwPageFrm *pStart, sal_Bool bNotifyFields ) { OSL_ENSURE( pStart, "Keine Startpage." ); ViewShell *pSh = pStart->getRootFrm()->GetCurrShell(); SwViewImp *pImp = pSh ? pSh->Imp() : 0; if ( pImp && pImp->IsAction() && !pImp->GetLayAction().IsCheckPages() ) { pImp->GetLayAction().SetCheckPageNum( pStart->GetPhyPageNum() ); return; } //Fuer das Aktualisieren der Seitennummern-Felder gibt nDocPos //die Seitenposition an, _ab_ der invalidiert werden soll. SwTwips nDocPos = LONG_MAX; SwRootFrm *pRoot = (SwRootFrm*)pStart->GetUpper(); SwDoc* pDoc = pStart->GetFmt()->GetDoc(); const sal_Bool bFtns = 0 != pDoc->GetFtnIdxs().Count(); SwPageFrm *pPage = pStart; if( pPage->GetPrev() && ((SwPageFrm*)pPage->GetPrev())->IsEmptyPage() ) pPage = (SwPageFrm*)pPage->GetPrev(); while ( pPage ) { //gewuenschten PageDesc und FrmFmt festellen. SwPageDesc *pDesc = pPage->FindPageDesc(); sal_Bool bCheckEmpty = pPage->IsEmptyPage(); sal_Bool bActOdd = pPage->OnRightPage(); sal_Bool bOdd = pPage->WannaRightPage(); SwFrmFmt *pFmtWish = bOdd ? pDesc->GetRightFmt() : pDesc->GetLeftFmt(); if ( bActOdd != bOdd || pDesc != pPage->GetPageDesc() || //falscher Desc ( pFmtWish != pPage->GetFmt() && //falsches Format und ( !pPage->IsEmptyPage() || pFmtWish ) //nicht Leerseite ) ) { //Wenn wir schon ein Seite veraendern muessen kann das eine //Weile dauern, deshalb hier den WaitCrsr pruefen. if( pImp ) pImp->CheckWaitCrsr(); //Ab hier muessen die Felder invalidiert werden! if ( nDocPos == LONG_MAX ) nDocPos = pPage->GetPrev() ? pPage->GetPrev()->Frm().Top() : pPage->Frm().Top(); //Faelle: //1. Wir haben eine EmptyPage und wollen eine "Normalseite". // ->EmptyPage wegwerfen und weiter mit der naechsten. //2. Wir haben eine EmptyPage und wollen eine EmptyPage mit // anderem Descriptor. // ->Descriptor austauschen. //3. Wir haben eine "Normalseite" und wollen eine EmptyPage. // ->Emptypage einfuegen, nicht aber wenn die Vorseite // bereits eine EmptyPage ist -> 6. //4. Wir haben eine "Normalseite" und wollen eine "Normalseite" // mit anderem Descriptor // ->Descriptor und Format austauschen //5. Wir haben eine "Normalseite" und wollen eine "Normalseite" // mit anderem Format // ->Format austauschen. //6. Wir haben kein Wunschformat erhalten, also nehmen wir das // 'andere' Format (rechts/links) des PageDesc. if ( pPage->IsEmptyPage() && ( pFmtWish || //1. ( !bOdd && !pPage->GetPrev() ) ) ) { SwPageFrm *pTmp = (SwPageFrm*)pPage->GetNext(); pPage->Cut(); delete pPage; if ( pStart == pPage ) pStart = pTmp; pPage = pTmp; continue; } else if ( pPage->IsEmptyPage() && !pFmtWish && //2. pDesc != pPage->GetPageDesc() ) { pPage->SetPageDesc( pDesc, 0 ); } else if ( !pPage->IsEmptyPage() && //3. bActOdd != bOdd && ( ( !pPage->GetPrev() && !bOdd ) || ( pPage->GetPrev() && !((SwPageFrm*)pPage->GetPrev())->IsEmptyPage() ) ) ) { if ( pPage->GetPrev() ) pDesc = ((SwPageFrm*)pPage->GetPrev())->GetPageDesc(); SwPageFrm *pTmp = new SwPageFrm( pDoc->GetEmptyPageFmt(),pRoot,pDesc); pTmp->Paste( pRoot, pPage ); pTmp->PreparePage( sal_False ); pPage = pTmp; } else if ( pPage->GetPageDesc() != pDesc ) //4. { SwPageDesc *pOld = pPage->GetPageDesc(); pPage->SetPageDesc( pDesc, pFmtWish ); if ( bFtns ) { //Wenn sich bestimmte Werte der FtnInfo veraendert haben //muss etwas passieren. Wir versuchen den Schaden zu //begrenzen. //Wenn die Seiten keinen FtnCont hat, ist zwar theoretisches //ein Problem denkbar, aber das ignorieren wir mit aller Kraft. //Bei Aenderungen hoffen wir mal, dass eine Invalidierung //ausreicht, denn alles andere wuerde viel Kraft kosten. SwFtnContFrm *pCont = pPage->FindFtnCont(); if ( pCont && !(pOld->GetFtnInfo() == pDesc->GetFtnInfo()) ) pCont->_InvalidateAll(); } } else if ( pFmtWish && pPage->GetFmt() != pFmtWish ) //5. { pPage->SetFrmFmt( pFmtWish ); } else if ( !pFmtWish ) //6. { //Format mit verdrehter Logic besorgen. pFmtWish = bOdd ? pDesc->GetLeftFmt() : pDesc->GetRightFmt(); if ( pPage->GetFmt() != pFmtWish ) pPage->SetFrmFmt( pFmtWish ); } #if OSL_DEBUG_LEVEL > 1 else { OSL_FAIL( "CheckPageDescs, missing solution" ); } #endif } if ( bCheckEmpty ) { //Es kann noch sein, dass die Leerseite schlicht ueberflussig ist. //Obiger Algorithmus kann dies leider nicht feststellen. //Eigentlich muesste die Leerseite einfach praeventiv entfernt //werden; sie wuerde ja ggf. wieder eingefuegt. //Die EmptyPage ist genau dann ueberfluessig, wenn die Folgeseite //auch ohne sie auskommt. Dazu muessen wir uns die Verhaeltnisse //genauer ansehen. Wir bestimmen den PageDesc und die virtuelle //Seitennummer manuell. SwPageFrm *pPg = (SwPageFrm*)pPage->GetNext(); if( !pPg || pPage->OnRightPage() == pPg->WannaRightPage() ) { //Die Folgeseite hat kein Problem ein FrmFmt zu finden oder keinen //Nachfolger, also ist die Leerseite ueberfluessig. SwPageFrm *pTmp = (SwPageFrm*)pPage->GetNext(); pPage->Cut(); delete pPage; if ( pStart == pPage ) pStart = pTmp; pPage = pTmp; continue; } } pPage = (SwPageFrm*)pPage->GetNext(); } pRoot->SetAssertFlyPages(); pRoot->AssertPageFlys( pStart ); if ( bNotifyFields && (!pImp || !pImp->IsUpdateExpFlds()) ) { SwDocPosUpdate aMsgHnt( nDocPos ); pDoc->UpdatePageFlds( &aMsgHnt ); } #if OSL_DEBUG_LEVEL > 1 //Ein paar Pruefungen muessen schon erlaubt sein. //1. Keine zwei EmptyPages hintereinander. //2. Alle PageDescs richtig? sal_Bool bEmpty = sal_False; SwPageFrm *pPg = pStart; while ( pPg ) { if ( pPg->IsEmptyPage() ) { if ( bEmpty ) { OSL_FAIL( "Doppelte Leerseiten." ); break; //Einmal reicht. } bEmpty = sal_True; } else bEmpty = sal_False; //MA 21. Jun. 95: Kann zu testzwecken 'rein, ist aber bei zyklen durchaus //moeglich: Ein paar Seiten, auf der ersten 'erste Seite' anwenden, //rechte als folge der ersten, linke als folge der rechten, rechte als //folge der linken. // OSL_ENSURE( pPg->GetPageDesc() == pPg->FindPageDesc(), // "Seite mit falschem Descriptor." ); pPg = (SwPageFrm*)pPg->GetNext(); } #endif } /************************************************************************* |* |* SwFrm::InsertPage() |* |*************************************************************************/ SwPageFrm *SwFrm::InsertPage( SwPageFrm *pPrevPage, sal_Bool bFtn ) { SwRootFrm *pRoot = (SwRootFrm*)pPrevPage->GetUpper(); SwPageFrm *pSibling = (SwPageFrm*)pRoot->GetLower(); SwPageDesc *pDesc = pSibling->GetPageDesc(); pSibling = (SwPageFrm*)pPrevPage->GetNext(); //Rechte (ungerade) oder linke (gerade) Seite einfuegen? sal_Bool bNextOdd = !pPrevPage->OnRightPage(); sal_Bool bWishedOdd = bNextOdd; //Welcher PageDesc gilt? //Bei CntntFrm der aus dem Format wenn einer angegeben ist, //der Follow vom bereits in der PrevPage gueltigen sonst. pDesc = 0; if ( IsFlowFrm() && !SwFlowFrm::CastFlowFrm( this )->IsFollow() ) { SwFmtPageDesc &rDesc = (SwFmtPageDesc&)GetAttrSet()->GetPageDesc(); pDesc = rDesc.GetPageDesc(); if ( rDesc.GetNumOffset() ) { bWishedOdd = rDesc.GetNumOffset() % 2 ? sal_True : sal_False; //Die Gelegenheit nutzen wir um das Flag an der Root zu pflegen. pRoot->SetVirtPageNum( sal_True ); } } if ( !pDesc ) pDesc = pPrevPage->GetPageDesc()->GetFollow(); OSL_ENSURE( pDesc, "Missing PageDesc" ); if( !(bWishedOdd ? pDesc->GetRightFmt() : pDesc->GetLeftFmt()) ) bWishedOdd = !bWishedOdd; SwDoc *pDoc = pPrevPage->GetFmt()->GetDoc(); SwFrmFmt *pFmt; sal_Bool bCheckPages = sal_False; //Wenn ich kein FrmFmt fuer die Seite gefunden habe, muss ich eben eine //Leerseite einfuegen. if( bWishedOdd != bNextOdd ) { pFmt = pDoc->GetEmptyPageFmt(); SwPageDesc *pTmpDesc = pPrevPage->GetPageDesc(); SwPageFrm *pPage = new SwPageFrm( pFmt, pRoot, pTmpDesc ); pPage->Paste( pRoot, pSibling ); pPage->PreparePage( bFtn ); //Wenn der Sibling keinen Bodytext enthaelt kann ich ihn vernichten //Es sei denn, es ist eine Fussnotenseite if ( pSibling && !pSibling->IsFtnPage() && !pSibling->FindFirstBodyCntnt() ) { SwPageFrm *pDel = pSibling; pSibling = (SwPageFrm*)pSibling->GetNext(); if ( pDoc->GetFtnIdxs().Count() ) pRoot->RemoveFtns( pDel, sal_True ); pDel->Cut(); delete pDel; } else bCheckPages = sal_True; } pFmt = bWishedOdd ? pDesc->GetRightFmt() : pDesc->GetLeftFmt(); OSL_ENSURE( pFmt, "Descriptor without format." ); SwPageFrm *pPage = new SwPageFrm( pFmt, pRoot, pDesc ); pPage->Paste( pRoot, pSibling ); pPage->PreparePage( bFtn ); //Wenn der Sibling keinen Bodytext enthaelt kann ich ihn vernichten //Es sei denn es ist eine Fussnotenseite. if ( pSibling && !pSibling->IsFtnPage() && !pSibling->FindFirstBodyCntnt() ) { SwPageFrm *pDel = pSibling; pSibling = (SwPageFrm*)pSibling->GetNext(); if ( pDoc->GetFtnIdxs().Count() ) pRoot->RemoveFtns( pDel, sal_True ); pDel->Cut(); delete pDel; } else bCheckPages = sal_True; if ( pSibling ) { if ( bCheckPages ) { CheckPageDescs( pSibling, sal_False ); ViewShell *pSh = getRootFrm()->GetCurrShell(); SwViewImp *pImp = pSh ? pSh->Imp() : 0; if ( pImp && pImp->IsAction() && !pImp->GetLayAction().IsCheckPages() ) { const sal_uInt16 nNum = pImp->GetLayAction().GetCheckPageNum(); if ( nNum == pPrevPage->GetPhyPageNum() + 1 ) pImp->GetLayAction().SetCheckPageNumDirect( pSibling->GetPhyPageNum() ); return pPage; } } else pRoot->AssertPageFlys( pSibling ); } //Fuer das Aktualisieren der Seitennummern-Felder gibt nDocPos //die Seitenposition an, _ab_ der invalidiert werden soll. ViewShell *pSh = getRootFrm()->GetCurrShell(); if ( !pSh || !pSh->Imp()->IsUpdateExpFlds() ) { SwDocPosUpdate aMsgHnt( pPrevPage->Frm().Top() ); pDoc->UpdatePageFlds( &aMsgHnt ); } return pPage; } sw::sidebarwindows::SidebarPosition SwPageFrm::SidebarPosition() const { ViewShell *pSh = getRootFrm()->GetCurrShell(); if( !pSh || pSh->GetViewOptions()->getBrowseMode() ) { return sw::sidebarwindows::SIDEBAR_RIGHT; } else { const bool bLTR = getRootFrm()->IsLeftToRightViewLayout(); const bool bBookMode = pSh->GetViewOptions()->IsViewLayoutBookMode(); const bool bRightSidebar = bLTR ? (!bBookMode || OnRightPage()) : (bBookMode && !OnRightPage()); return bRightSidebar ? sw::sidebarwindows::SIDEBAR_RIGHT : sw::sidebarwindows::SIDEBAR_LEFT; } } /************************************************************************* |* |* SwRootFrm::GrowFrm() |* |*************************************************************************/ SwTwips SwRootFrm::GrowFrm( SwTwips nDist, sal_Bool bTst, sal_Bool ) { if ( !bTst ) Frm().SSize().Height() += nDist; return nDist; } /************************************************************************* |* |* SwRootFrm::ShrinkFrm() |* |*************************************************************************/ SwTwips SwRootFrm::ShrinkFrm( SwTwips nDist, sal_Bool bTst, sal_Bool ) { OSL_ENSURE( nDist >= 0, "nDist < 0." ); OSL_ENSURE( nDist <= Frm().Height(), "nDist > als aktuelle Groesse." ); if ( !bTst ) Frm().SSize().Height() -= nDist; return nDist; } /************************************************************************* |* |* SwRootFrm::RemoveSuperfluous() |* |* Beschreibung: Entfernung von ueberfluessigen Seiten. |* Arbeitet nur wenn das Flag bCheckSuperfluous gesetzt ist. |* Definition: Eine Seite ist genau dann leer, wenn der |* Body-Textbereich keinen CntntFrm enthaelt, aber nicht, wenn noch |* mindestens ein Fly an der Seite klebt. |* Die Seite ist auch dann nicht leer, wenn sie noch eine |* Fussnote enthaelt. |* Es muss zweimal angesetzt werden um leeren Seiten aufzuspueren: |* - einmal fuer die Endnotenseiten. |* - und einmal fuer die Seiten des Bodytextes. |* |*************************************************************************/ void SwRootFrm::RemoveSuperfluous() { if ( !IsSuperfluous() ) return; bCheckSuperfluous = sal_False; SwPageFrm *pPage = GetLastPage(); long nDocPos = LONG_MAX; //Jetzt wird fuer die jeweils letzte Seite geprueft ob sie leer ist //bei der ersten nicht leeren Seite wird die Schleife beendet. do { bool bExistEssentialObjs = ( 0 != pPage->GetSortedObjs() ); if ( bExistEssentialObjs ) { //Nur weil die Seite Flys hat sind wir noch lange nicht fertig, //denn wenn alle Flys an generischem Inhalt haengen, so ist sie //trotzdem ueberfluessig (Ueberpruefung auf DocBody sollte reichen). // OD 19.06.2003 #108784# - consider that drawing objects in // header/footer are supported now. bool bOnlySuperfluosObjs = true; SwSortedObjs &rObjs = *pPage->GetSortedObjs(); for ( sal_uInt16 i = 0; bOnlySuperfluosObjs && i < rObjs.Count(); ++i ) { // --> OD 2004-06-29 #i28701# SwAnchoredObject* pAnchoredObj = rObjs[i]; // OD 2004-01-19 #110582# - do not consider hidden objects if ( pPage->GetFmt()->GetDoc()->IsVisibleLayerId( pAnchoredObj->GetDrawObj()->GetLayer() ) && !pAnchoredObj->GetAnchorFrm()->FindFooterOrHeader() ) { bOnlySuperfluosObjs = false; } // <-- } bExistEssentialObjs = !bOnlySuperfluosObjs; } // OD 19.06.2003 #108784# - optimization: check first, if essential objects // exists. const SwLayoutFrm* pBody = 0; if ( bExistEssentialObjs || pPage->FindFtnCont() || ( 0 != ( pBody = pPage->FindBodyCont() ) && ( pBody->ContainsCntnt() || // --> FME 2005-05-18 #i47580# // Do not delete page if there's an empty tabframe // left. I think it might be correct to use ContainsAny() // instead of ContainsCntnt() to cover the empty-table-case, // but I'm not fully sure, since ContainsAny() also returns // SectionFrames. Therefore I prefer to do it the safe way: ( pBody->Lower() && pBody->Lower()->IsTabFrm() ) ) ) ) // <-- { if ( pPage->IsFtnPage() ) { while ( pPage->IsFtnPage() ) { pPage = (SwPageFrm*)pPage->GetPrev(); OSL_ENSURE( pPage, "Nur noch Endnotenseiten uebrig." ); } continue; } else pPage = 0; } if ( pPage ) { SwPageFrm *pEmpty = pPage; pPage = (SwPageFrm*)pPage->GetPrev(); if ( GetFmt()->GetDoc()->GetFtnIdxs().Count() ) RemoveFtns( pEmpty, sal_True ); pEmpty->Cut(); delete pEmpty; nDocPos = pPage ? pPage->Frm().Top() : 0; } } while ( pPage ); ViewShell *pSh = getRootFrm()->GetCurrShell(); if ( nDocPos != LONG_MAX && (!pSh || !pSh->Imp()->IsUpdateExpFlds()) ) { SwDocPosUpdate aMsgHnt( nDocPos ); GetFmt()->GetDoc()->UpdatePageFlds( &aMsgHnt ); } } /************************************************************************* |* |* SwRootFrm::AssertFlyPages() |* |* Beschreibung Stellt sicher, dass genuegend Seiten vorhanden |* sind, damit alle Seitengebundenen Rahmen und DrawObject |* untergebracht sind. |* |*************************************************************************/ void SwRootFrm::AssertFlyPages() { if ( !IsAssertFlyPages() ) return; bAssertFlyPages = sal_False; SwDoc *pDoc = GetFmt()->GetDoc(); const SwSpzFrmFmts *pTbl = pDoc->GetSpzFrmFmts(); //Auf welche Seite will der 'letzte' Fly? sal_uInt16 nMaxPg = 0; sal_uInt16 i; for ( i = 0; i < pTbl->Count(); ++i ) { const SwFmtAnchor &rAnch = (*pTbl)[i]->GetAnchor(); if ( !rAnch.GetCntntAnchor() && nMaxPg < rAnch.GetPageNum() ) nMaxPg = rAnch.GetPageNum(); } //Wieviele Seiten haben wir derzeit? SwPageFrm *pPage = (SwPageFrm*)Lower(); while ( pPage && pPage->GetNext() && !((SwPageFrm*)pPage->GetNext())->IsFtnPage() ) { pPage = (SwPageFrm*)pPage->GetNext(); } if ( nMaxPg > pPage->GetPhyPageNum() ) { //Die Seiten werden ausgehend von der letzten Seite konsequent //nach den Regeln der PageDescs weitergefuehrt. sal_Bool bOdd = pPage->GetPhyPageNum() % 2 ? sal_True : sal_False; SwPageDesc *pDesc = pPage->GetPageDesc(); SwFrm *pSibling = pPage->GetNext(); for ( i = pPage->GetPhyPageNum(); i < nMaxPg; ++i ) { if ( !(bOdd ? pDesc->GetRightFmt() : pDesc->GetLeftFmt()) ) { //Leerseite einfuegen, die Flys werden aber erst von //der naechsten Seite aufgenommen! pPage = new SwPageFrm( pDoc->GetEmptyPageFmt(), this, pDesc ); pPage->Paste( this, pSibling ); pPage->PreparePage( sal_False ); bOdd = bOdd ? sal_False : sal_True; ++i; } pPage = new SwPageFrm( (bOdd ? pDesc->GetRightFmt() : pDesc->GetLeftFmt()), this, pDesc ); pPage->Paste( this, pSibling ); pPage->PreparePage( sal_False ); bOdd = bOdd ? sal_False : sal_True; pDesc = pDesc->GetFollow(); } //Jetzt koennen die Endnotenseiten natuerlich wieder krumm sein; //in diesem Fall werden sie vernichtet. if ( pDoc->GetFtnIdxs().Count() ) { pPage = (SwPageFrm*)Lower(); while ( pPage && !pPage->IsFtnPage() ) pPage = (SwPageFrm*)pPage->GetNext(); if ( pPage ) { SwPageDesc *pTmpDesc = pPage->FindPageDesc(); bOdd = pPage->OnRightPage(); if ( pPage->GetFmt() != (bOdd ? pTmpDesc->GetRightFmt() : pTmpDesc->GetLeftFmt()) ) RemoveFtns( pPage, sal_False, sal_True ); } } } } /************************************************************************* |* |* SwRootFrm::AssertPageFlys() |* |* Beschreibung Stellt sicher, dass ab der uebergebenen Seite |* auf allen Seiten die Seitengebunden Objecte auf der richtigen |* Seite (Seitennummer stehen). |* |*************************************************************************/ void SwRootFrm::AssertPageFlys( SwPageFrm *pPage ) { while ( pPage ) { if ( pPage->GetSortedObjs() ) { pPage->GetSortedObjs(); for ( int i = 0; pPage->GetSortedObjs() && sal_uInt16(i) < pPage->GetSortedObjs()->Count(); ++i) { // --> OD 2004-06-29 #i28701# SwFrmFmt& rFmt = (*pPage->GetSortedObjs())[i]->GetFrmFmt(); const SwFmtAnchor &rAnch = rFmt.GetAnchor(); const sal_uInt16 nPg = rAnch.GetPageNum(); if ((rAnch.GetAnchorId() == FLY_AT_PAGE) && nPg != pPage->GetPhyPageNum() ) { //Das er auf der falschen Seite steht muss noch nichts //heissen, wenn er eigentlich auf der Vorseite //stehen will und diese eine EmptyPage ist. if( nPg && !(pPage->GetPhyPageNum()-1 == nPg && ((SwPageFrm*)pPage->GetPrev())->IsEmptyPage()) ) { //Umhaengen kann er sich selbst, indem wir ihm //einfach ein Modify mit seinem AnkerAttr schicken. #if OSL_DEBUG_LEVEL > 1 const sal_uInt32 nCnt = pPage->GetSortedObjs()->Count(); rFmt.NotifyClients( 0, (SwFmtAnchor*)&rAnch ); OSL_ENSURE( !pPage->GetSortedObjs() || nCnt != pPage->GetSortedObjs()->Count(), "Object couldn't be reattached!" ); #else rFmt.NotifyClients( 0, (SwFmtAnchor*)&rAnch ); #endif --i; } } } } pPage = (SwPageFrm*)pPage->GetNext(); } } /************************************************************************* |* |* SwRootFrm::ChgSize() |* |*************************************************************************/ Size SwRootFrm::ChgSize( const Size& aNewSize ) { Frm().SSize() = aNewSize; _InvalidatePrt(); bFixSize = sal_False; return Frm().SSize(); } /************************************************************************* |* |* SwRootFrm::MakeAll() |* |*************************************************************************/ void SwRootFrm::MakeAll() { if ( !bValidPos ) { bValidPos = sal_True; aFrm.Pos().X() = aFrm.Pos().Y() = DOCUMENTBORDER; } if ( !bValidPrtArea ) { bValidPrtArea = sal_True; aPrt.Pos().X() = aPrt.Pos().Y() = 0; aPrt.SSize( aFrm.SSize() ); } if ( !bValidSize ) //SSize wird von den Seiten (Cut/Paste) eingestellt. bValidSize = sal_True; } /************************************************************************* |* |* SwRootFrm::ImplInvalidateBrowseWidth() |* |*************************************************************************/ void SwRootFrm::ImplInvalidateBrowseWidth() { bBrowseWidthValid = sal_False; SwFrm *pPg = Lower(); while ( pPg ) { pPg->InvalidateSize(); pPg = pPg->GetNext(); } } /************************************************************************* |* |* SwRootFrm::ImplCalcBrowseWidth() |* |*************************************************************************/ void SwRootFrm::ImplCalcBrowseWidth() { OSL_ENSURE( GetCurrShell() && GetCurrShell()->GetViewOptions()->getBrowseMode(), "CalcBrowseWidth and not in BrowseView" ); //Die (minimale) Breite wird von Rahmen, Tabellen und Zeichenobjekten //bestimmt. Die Breite wird nicht anhand ihrer aktuellen Groessen bestimmt, //sondern anhand der Attribute. Es interessiert also nicht wie breit sie //sind, sondern wie breit sie sein wollen. //Rahmen und Zeichenobjekte innerhalb ander Objekte (Rahmen, Tabellen) //Zaehlen nicht. //Seitenraender und Spalten werden hier nicht beruecksichtigt. SwFrm *pFrm = ContainsCntnt(); while ( pFrm && !pFrm->IsInDocBody() ) pFrm = ((SwCntntFrm*)pFrm)->GetNextCntntFrm(); if ( !pFrm ) return; bBrowseWidthValid = sal_True; ViewShell *pSh = getRootFrm()->GetCurrShell(); nBrowseWidth = pSh ? MINLAY + 2 * pSh->GetOut()-> PixelToLogic( pSh->GetBrowseBorder() ).Width() : 5000; do { if ( pFrm->IsInTab() ) pFrm = pFrm->FindTabFrm(); if ( pFrm->IsTabFrm() && !((SwLayoutFrm*)pFrm)->GetFmt()->GetFrmSize().GetWidthPercent() ) { SwBorderAttrAccess aAccess( SwFrm::GetCache(), pFrm ); const SwBorderAttrs &rAttrs = *aAccess.Get(); const SwFmtHoriOrient &rHori = rAttrs.GetAttrSet().GetHoriOrient(); long nWidth = rAttrs.GetSize().Width(); if ( nWidth < USHRT_MAX-2000 && //-2000, weil bei Randeinstellung per //Zuppeln das USHRT_MAX verlorengeht! text::HoriOrientation::FULL != rHori.GetHoriOrient() ) { const SwHTMLTableLayout *pLayoutInfo = ((const SwTabFrm *)pFrm)->GetTable() ->GetHTMLTableLayout(); if ( pLayoutInfo ) nWidth = Min( nWidth, pLayoutInfo->GetBrowseWidthMin() ); switch ( rHori.GetHoriOrient() ) { case text::HoriOrientation::NONE: // OD 23.01.2003 #106895# - add 1st param to nWidth += rAttrs.CalcLeft( pFrm ) + rAttrs.CalcRight( pFrm ); break; case text::HoriOrientation::LEFT_AND_WIDTH: nWidth += rAttrs.CalcLeft( pFrm ); break; default: break; } nBrowseWidth = Max( nBrowseWidth, nWidth ); } } else if ( pFrm->GetDrawObjs() ) { for ( sal_uInt16 i = 0; i < pFrm->GetDrawObjs()->Count(); ++i ) { // --> OD 2004-06-29 #i28701# SwAnchoredObject* pAnchoredObj = (*pFrm->GetDrawObjs())[i]; const SwFrmFmt& rFmt = pAnchoredObj->GetFrmFmt(); const sal_Bool bFly = pAnchoredObj->ISA(SwFlyFrm); if ((bFly && (WEIT_WECH == pAnchoredObj->GetObjRect().Width())) || rFmt.GetFrmSize().GetWidthPercent()) { continue; } long nWidth = 0; switch ( rFmt.GetAnchor().GetAnchorId() ) { case FLY_AS_CHAR: nWidth = bFly ? rFmt.GetFrmSize().GetWidth() : pAnchoredObj->GetObjRect().Width(); break; case FLY_AT_PARA: { // --> FME 2004-09-13 #i33170# // Reactivated old code because // nWidth = pAnchoredObj->GetObjRect().Right() // gives wrong results for objects that are still // at position WEIT_WECH. if ( bFly ) { nWidth = rFmt.GetFrmSize().GetWidth(); const SwFmtHoriOrient &rHori = rFmt.GetHoriOrient(); switch ( rHori.GetHoriOrient() ) { case text::HoriOrientation::NONE: nWidth += rHori.GetPos(); break; case text::HoriOrientation::INSIDE: case text::HoriOrientation::LEFT: if ( text::RelOrientation::PRINT_AREA == rHori.GetRelationOrient() ) nWidth += pFrm->Prt().Left(); break; default: break; } } else //Fuer Zeichenobjekte ist die Auswahl sehr klein, //weil sie keine Attribute haben, also durch ihre //aktuelle Groesse bestimmt werden. nWidth = pAnchoredObj->GetObjRect().Right() - pAnchoredObj->GetDrawObj()->GetAnchorPos().X(); // <-- } break; default: /* do nothing */; } nBrowseWidth = Max( nBrowseWidth, nWidth ); } } pFrm = pFrm->FindNextCnt(); } while ( pFrm ); } /************************************************************************* |* |* SwRootFrm::StartAllAction() |* |*************************************************************************/ void SwRootFrm::StartAllAction() { ViewShell *pSh = GetCurrShell(); if ( pSh ) do { if ( pSh->ISA( SwCrsrShell ) ) ((SwCrsrShell*)pSh)->StartAction(); else pSh->StartAction(); pSh = (ViewShell*)pSh->GetNext(); } while ( pSh != GetCurrShell() ); } void SwRootFrm::EndAllAction( sal_Bool bVirDev ) { ViewShell *pSh = GetCurrShell(); if ( pSh ) do { const sal_Bool bOldEndActionByVirDev = pSh->IsEndActionByVirDev(); pSh->SetEndActionByVirDev( bVirDev ); if ( pSh->ISA( SwCrsrShell ) ) { ((SwCrsrShell*)pSh)->EndAction(); ((SwCrsrShell*)pSh)->CallChgLnk(); if ( pSh->ISA( SwFEShell ) ) ((SwFEShell*)pSh)->SetChainMarker(); } else pSh->EndAction(); pSh->SetEndActionByVirDev( bOldEndActionByVirDev ); pSh = (ViewShell*)pSh->GetNext(); } while ( pSh != GetCurrShell() ); } void SwRootFrm::UnoRemoveAllActions() { ViewShell *pSh = GetCurrShell(); if ( pSh ) do { // --> OD 2008-05-16 #i84729# // No end action, if instance is currently in its end action. // Recursives calls to <::EndAction()> are not allowed. if ( !pSh->IsInEndAction() ) { DBG_ASSERT(!pSh->GetRestoreActions(), "Restore action count is already set!"); sal_Bool bCrsr = pSh->ISA( SwCrsrShell ); sal_Bool bFE = pSh->ISA( SwFEShell ); sal_uInt16 nRestore = 0; while( pSh->ActionCount() ) { if( bCrsr ) { ((SwCrsrShell*)pSh)->EndAction(); ((SwCrsrShell*)pSh)->CallChgLnk(); if ( bFE ) ((SwFEShell*)pSh)->SetChainMarker(); } else pSh->EndAction(); nRestore++; } pSh->SetRestoreActions(nRestore); } // <-- pSh->LockView(sal_True); pSh = (ViewShell*)pSh->GetNext(); } while ( pSh != GetCurrShell() ); } void SwRootFrm::UnoRestoreAllActions() { ViewShell *pSh = GetCurrShell(); if ( pSh ) do { sal_uInt16 nActions = pSh->GetRestoreActions(); while( nActions-- ) { if ( pSh->ISA( SwCrsrShell ) ) ((SwCrsrShell*)pSh)->StartAction(); else pSh->StartAction(); } pSh->SetRestoreActions(0); pSh->LockView(sal_False); pSh = (ViewShell*)pSh->GetNext(); } while ( pSh != GetCurrShell() ); } // PAGES01: Helper functions for SwRootFrm::CheckViewLayout void lcl_MoveAllLowers( SwFrm* pFrm, const Point& rOffset ); void lcl_MoveAllLowerObjs( SwFrm* pFrm, const Point& rOffset ) { SwSortedObjs* pSortedObj = 0; const bool bPage = pFrm->IsPageFrm(); if ( bPage ) pSortedObj = static_cast(pFrm)->GetSortedObjs(); else pSortedObj = pFrm->GetDrawObjs(); for ( sal_uInt16 i = 0; pSortedObj && i < pSortedObj->Count(); ++i) { SwAnchoredObject* pAnchoredObj = (*pSortedObj)[i]; const SwFrmFmt& rObjFmt = pAnchoredObj->GetFrmFmt(); const SwFmtAnchor& rAnchor = rObjFmt.GetAnchor(); // all except from the as character anchored objects are moved // when processing the page frame: const bool bAsChar = (rAnchor.GetAnchorId() == FLY_AS_CHAR); if ( !bPage && !bAsChar ) continue; SwObjPositioningInProgress aPosInProgress( *pAnchoredObj ); if ( pAnchoredObj->ISA(SwFlyFrm) ) { SwFlyFrm* pFlyFrm( static_cast(pAnchoredObj) ); lcl_MoveAllLowers( pFlyFrm, rOffset ); pFlyFrm->NotifyDrawObj(); // --> let the active embedded object be moved if ( pFlyFrm->Lower() ) { if ( pFlyFrm->Lower()->IsNoTxtFrm() ) { SwCntntFrm* pCntntFrm = static_cast(pFlyFrm->Lower()); SwRootFrm* pRoot = pFlyFrm->Lower()->getRootFrm(); ViewShell *pSh = pRoot ? pRoot->GetCurrShell() : 0; if ( pSh ) { SwOLENode* pNode = pCntntFrm->GetNode()->GetOLENode(); if ( pNode ) { svt::EmbeddedObjectRef& xObj = pNode->GetOLEObj().GetObject(); if ( xObj.is() ) { ViewShell* pTmp = pSh; do { SwFEShell* pFEShell = dynamic_cast< SwFEShell* >( pTmp ); if ( pFEShell ) pFEShell->MoveObjectIfActive( xObj, rOffset ); pTmp = static_cast( pTmp->GetNext() ); } while( pTmp != pSh ); } } } } } // <-- } else if ( pAnchoredObj->ISA(SwAnchoredDrawObject) ) { SwAnchoredDrawObject* pAnchoredDrawObj( static_cast(pAnchoredObj) ); // don't touch objects that are not yet positioned: const bool bNotYetPositioned = pAnchoredDrawObj->NotYetPositioned(); if ( bNotYetPositioned ) continue; const Point aCurrAnchorPos = pAnchoredDrawObj->GetDrawObj()->GetAnchorPos(); const Point aNewAnchorPos( ( aCurrAnchorPos + rOffset ) ); pAnchoredDrawObj->DrawObj()->SetAnchorPos( aNewAnchorPos ); pAnchoredDrawObj->SetLastObjRect( pAnchoredDrawObj->GetObjRect().SVRect() ); } // --> OD 2009-08-20 #i92511# // cache for object rectangle inclusive spaces has to be invalidated. pAnchoredObj->InvalidateObjRectWithSpaces(); // <-- } } void lcl_MoveAllLowers( SwFrm* pFrm, const Point& rOffset ) { const SwRect aFrm( pFrm->Frm() ); // first move the current frame pFrm->Frm().Pos() += rOffset; // Don't forget accessibility: if( pFrm->IsAccessibleFrm() ) { SwRootFrm *pRootFrm = pFrm->getRootFrm(); if( pRootFrm && pRootFrm->IsAnyShellAccessible() && pRootFrm->GetCurrShell() ) { pRootFrm->GetCurrShell()->Imp()->MoveAccessibleFrm( pFrm, aFrm ); } } // the move any objects lcl_MoveAllLowerObjs( pFrm, rOffset ); // finally, for layout frames we have to call this function recursively: if ( pFrm->ISA(SwLayoutFrm) ) { SwFrm* pLowerFrm = pFrm->GetLower(); while ( pLowerFrm ) { lcl_MoveAllLowers( pLowerFrm, rOffset ); pLowerFrm = pLowerFrm->GetNext(); } } } // PAGES01: Calculate how the pages have to be positioned void SwRootFrm::CheckViewLayout( const SwViewOption* pViewOpt, const SwRect* pVisArea ) { // --> OD 2008-07-07 #i91432# // No calculation of page positions, if only an empty page is present. // This situation occurs when instance is in construction // and the document contains only left pages. if ( Lower()->GetNext() == 0 && static_cast(Lower())->IsEmptyPage() ) { return; } // <-- if ( !pVisArea ) { // no early return for bNewPage if ( mnViewWidth < 0 ) mnViewWidth = 0; } else { OSL_ENSURE( pViewOpt, "CheckViewLayout required ViewOptions" ); const sal_uInt16 nColumns = pViewOpt->GetViewLayoutColumns(); const bool bBookMode = pViewOpt->IsViewLayoutBookMode(); if ( nColumns == mnColumns && bBookMode == mbBookMode && pVisArea->Width() == mnViewWidth && !mbSidebarChanged ) return; mnColumns = nColumns; mbBookMode = bBookMode; mnViewWidth = pVisArea->Width(); mbSidebarChanged = false; } if( GetFmt()->getIDocumentSettingAccess()->get(IDocumentSettingAccess::BROWSE_MODE ) ) { mnColumns = 1; mbBookMode = false; } Calc(); const sal_Bool bOldCallbackActionEnabled = IsCallbackActionEnabled(); SetCallbackActionEnabled( sal_False ); maPageRects.clear(); const long nBorder = Frm().Pos().X(); const long nVisWidth = mnViewWidth - 2 * nBorder; const long nGapBetweenPages = GAPBETWEENPAGES; // check how many pages fit into the first page layout row: SwPageFrm* pPageFrm = static_cast(Lower()); // will contain the number of pages per row. 0 means that // the page does not fit. long nWidthRemain = nVisWidth; // after one row has been processed, these variables contain // the width of the row and the maxium of the page heights long nCurrentRowHeight = 0; long nCurrentRowWidth = 0; // these variables are used to finally set the size of the // root frame long nSumRowHeight = 0; SwTwips nMinPageLeft = TWIPS_MAX; SwTwips nMaxPageRight = 0; SwPageFrm* pStartOfRow = pPageFrm; sal_uInt16 nNumberOfPagesInRow = mbBookMode ? 1 : 0; // in book view, start with right page bool bFirstRow = true; bool bPageChanged = false; const bool bRTL = !IsLeftToRightViewLayout(); const SwTwips nSidebarWidth = SwPageFrm::GetSidebarBorderWidth( GetCurrShell() ); while ( pPageFrm ) { // we consider the current page to be "start of row" if // 1. it is the first page in the current row or // 2. it is the second page in the row and the first page is an empty page in non-book view: const bool bStartOfRow = pPageFrm == pStartOfRow || ( pStartOfRow->IsEmptyPage() && pPageFrm == pStartOfRow->GetNext() && !mbBookMode ); const bool bEmptyPage = pPageFrm->IsEmptyPage() && !mbBookMode; // no half doc border space for first page in each row and long nPageWidth = 0; long nPageHeight = 0; if ( mbBookMode ) { const SwFrm& rFormatPage = pPageFrm->GetFormatPage(); nPageWidth = rFormatPage.Frm().Width() + nSidebarWidth + ((bStartOfRow || 1 == (pPageFrm->GetPhyPageNum()%2)) ? 0 : nGapBetweenPages); nPageHeight = rFormatPage.Frm().Height() + nGapBetweenPages; } else { SwRect aPageFrm; if ( !pPageFrm->IsEmptyPage() ) { nPageWidth = pPageFrm->Frm().Width() + nSidebarWidth + (bStartOfRow ? 0 : nGapBetweenPages); nPageHeight = pPageFrm->Frm().Height() + nGapBetweenPages; } } if ( !bEmptyPage ) ++nNumberOfPagesInRow; // finish current row if // 1. in dynamic mode the current page does not fit anymore or // 2. the current page exceeds the maximum number of columns bool bRowFinished = (0 == mnColumns && nWidthRemain < nPageWidth ) || (0 != mnColumns && mnColumns < nNumberOfPagesInRow); // make sure that at least one page goes to the current row: if ( !bRowFinished || bStartOfRow ) { // current page is allowed to be in current row nWidthRemain = nWidthRemain - nPageWidth; nCurrentRowWidth = nCurrentRowWidth + nPageWidth; nCurrentRowHeight = Max( nCurrentRowHeight, nPageHeight ); pPageFrm = static_cast(pPageFrm->GetNext()); if ( !pPageFrm ) bRowFinished = true; } if ( bRowFinished ) { // pPageFrm now points to the first page in the new row or null // pStartOfRow points to the first page in the current row // special centering for last row. pretend to fill the last row with virtual copies of the last page before centering: if ( !pPageFrm && nWidthRemain > 0 ) { // find last page in current row: const SwPageFrm* pLastPageInCurrentRow = pStartOfRow; while( pLastPageInCurrentRow->GetNext() ) pLastPageInCurrentRow = static_cast(pLastPageInCurrentRow->GetNext()); if ( pLastPageInCurrentRow->IsEmptyPage() ) pLastPageInCurrentRow = static_cast(pLastPageInCurrentRow->GetPrev()); // check how many times the last page would still fit into the remaining space: sal_uInt16 nNumberOfVirtualPages = 0; const sal_uInt16 nMaxNumberOfVirtualPages = mnColumns > 0 ? mnColumns - nNumberOfPagesInRow : USHRT_MAX; SwTwips nRemain = nWidthRemain; SwTwips nVirtualPagesWidth = 0; SwTwips nLastPageWidth = pLastPageInCurrentRow->Frm().Width() + nSidebarWidth; while ( ( mnColumns > 0 || nRemain > 0 ) && nNumberOfVirtualPages < nMaxNumberOfVirtualPages ) { SwTwips nLastPageWidthWithGap = nLastPageWidth; if ( !mbBookMode || ( 0 == (nNumberOfVirtualPages + nNumberOfPagesInRow) %2) ) nLastPageWidthWithGap += nGapBetweenPages; if ( mnColumns > 0 || nLastPageWidthWithGap < nRemain ) { ++nNumberOfVirtualPages; nVirtualPagesWidth += nLastPageWidthWithGap; } nRemain = nRemain - nLastPageWidthWithGap; } nCurrentRowWidth = nCurrentRowWidth + nVirtualPagesWidth; } // first page in book mode is always special: if ( bFirstRow && mbBookMode ) { // #i88036# nCurrentRowWidth += pStartOfRow->GetFormatPage().Frm().Width() + nSidebarWidth; } // center page if possible const long nSizeDiff = nVisWidth > nCurrentRowWidth ? ( nVisWidth - nCurrentRowWidth ) / 2 : 0; // adjust positions of pages in current row long nX = nSizeDiff; const long nRowStart = nBorder + nSizeDiff; const long nRowEnd = nRowStart + nCurrentRowWidth; if ( bFirstRow && mbBookMode ) { // #i88036# nX += pStartOfRow->GetFormatPage().Frm().Width() + nSidebarWidth; } SwPageFrm* pEndOfRow = pPageFrm; SwPageFrm* pPageToAdjust = pStartOfRow; do { const SwPageFrm* pFormatPage = pPageToAdjust; if ( mbBookMode ) pFormatPage = &pPageToAdjust->GetFormatPage(); const SwTwips nCurrentPageWidth = pFormatPage->Frm().Width() + (pFormatPage->IsEmptyPage() ? 0 : nSidebarWidth); const Point aOldPagePos = pPageToAdjust->Frm().Pos(); const bool bLeftSidebar = pPageToAdjust->SidebarPosition() == sw::sidebarwindows::SIDEBAR_LEFT; const SwTwips nLeftPageAddOffset = bLeftSidebar ? nSidebarWidth : 0; Point aNewPagePos( nBorder + nX, nBorder + nSumRowHeight ); Point aNewPagePosWithLeftOffset( nBorder + nX + nLeftPageAddOffset, nBorder + nSumRowHeight ); // RTL view layout: Calculate mirrored page position if ( bRTL ) { const long nXOffsetInRow = aNewPagePos.X() - nRowStart; aNewPagePos.X() = nRowEnd - nXOffsetInRow - nCurrentPageWidth; aNewPagePosWithLeftOffset = aNewPagePos; aNewPagePosWithLeftOffset.X() += nLeftPageAddOffset; } if ( aNewPagePosWithLeftOffset != aOldPagePos ) { lcl_MoveAllLowers( pPageToAdjust, aNewPagePosWithLeftOffset - aOldPagePos ); pPageToAdjust->SetCompletePaint(); bPageChanged = true; } // calculate area covered by the current page and store to // maPageRects. This is used e.g., for cursor setting const bool bFirstColumn = pPageToAdjust == pStartOfRow; const bool bLastColumn = pPageToAdjust->GetNext() == pEndOfRow; const bool bLastRow = !pEndOfRow; nMinPageLeft = Min( nMinPageLeft, aNewPagePos.X() ); nMaxPageRight = Max( nMaxPageRight, aNewPagePos.X() + nCurrentPageWidth); // border of nGapBetweenPages around the current page: SwRect aPageRectWithBorders( aNewPagePos.X() - nGapBetweenPages, aNewPagePos.Y(), pPageToAdjust->Frm().SSize().Width() + nGapBetweenPages + nSidebarWidth, nCurrentRowHeight ); static const long nOuterClickDiff = 1000000; // adjust borders for these special cases: if ( (bFirstColumn && !bRTL) || (bLastColumn && bRTL) ) aPageRectWithBorders.SubLeft( nOuterClickDiff ); if ( (bLastColumn && !bRTL) || (bFirstColumn && bRTL) ) aPageRectWithBorders.AddRight( nOuterClickDiff ); if ( bFirstRow ) aPageRectWithBorders.SubTop( nOuterClickDiff ); if ( bLastRow ) aPageRectWithBorders.AddBottom( nOuterClickDiff ); maPageRects.push_back( aPageRectWithBorders ); nX = nX + nCurrentPageWidth; pPageToAdjust = static_cast(pPageToAdjust->GetNext()); // distance to next page if ( pPageToAdjust && pPageToAdjust != pEndOfRow ) { // in book view, we add the x gap before left (even) pages: if ( mbBookMode ) { if ( 0 == (pPageToAdjust->GetPhyPageNum()%2) ) nX = nX + nGapBetweenPages; } else { // in non-book view, dont add x gap before // 1. the last empty page in a row // 2. after an empty page const bool bDontAddGap = ( pPageToAdjust->IsEmptyPage() && pPageToAdjust->GetNext() == pEndOfRow ) || ( static_cast(pPageToAdjust->GetPrev())->IsEmptyPage() ); if ( !bDontAddGap ) nX = nX + nGapBetweenPages; } } } while ( pPageToAdjust != pEndOfRow ); // adjust values for root frame size nSumRowHeight = nSumRowHeight + nCurrentRowHeight; // start new row: nCurrentRowHeight = 0; nCurrentRowWidth = 0; pStartOfRow = pEndOfRow; nWidthRemain = nVisWidth; nNumberOfPagesInRow = 0; bFirstRow = false; } // end row finished } // end while // set size of root frame: const Size aOldSize( Frm().SSize() ); const Size aNewSize( nMaxPageRight - nBorder, nSumRowHeight - nGapBetweenPages ); if ( bPageChanged || aNewSize != aOldSize ) { ChgSize( aNewSize ); ::AdjustSizeChgNotify( this ); Calc(); ViewShell* pSh = GetCurrShell(); if ( pSh && pSh->GetDoc()->GetDocShell() ) { pSh->SetFirstVisPageInvalid(); if (bOldCallbackActionEnabled) { pSh->InvalidateWindows( SwRect( 0, 0, LONG_MAX, LONG_MAX ) ); pSh->GetDoc()->GetDocShell()->Broadcast(SfxSimpleHint(SFX_HINT_DOCCHANGED)); } } } maPagesArea.Pos( Frm().Pos() ); maPagesArea.SSize( aNewSize ); if ( TWIPS_MAX != nMinPageLeft ) maPagesArea._Left( nMinPageLeft ); SetCallbackActionEnabled( bOldCallbackActionEnabled ); } bool SwRootFrm::IsLeftToRightViewLayout() const { // Layout direction determined by layout direction of the first page. // --> OD 2008-04-08 #i88036# // Only ask a non-empty page frame for its layout direction // const SwPageFrm* pPage = dynamic_cast(Lower()); // return !pPage->IsRightToLeft() && !pPage->IsVertical(); const SwPageFrm& rPage = dynamic_cast(Lower())->GetFormatPage(); return !rPage.IsRightToLeft() && !rPage.IsVertical(); // <-- } const SwPageFrm& SwPageFrm::GetFormatPage() const { const SwPageFrm* pRet = this; if ( IsEmptyPage() ) { pRet = static_cast( OnRightPage() ? GetNext() : GetPrev() ); // --> OD 2008-04-08 #i88035# // Typically a right empty page frame has a next non-empty page frame and // a left empty page frame has a previous non-empty page frame. // But under certain cirsumstances this assumption is not true - // e.g. during insertion of a left page at the end of the document right // after a left page in an intermediate state a right empty page does not // have a next page frame. if ( pRet == 0 ) { if ( OnRightPage() ) { pRet = static_cast( GetPrev() ); } else { pRet = static_cast( GetNext() ); } } OSL_ENSURE( pRet, " - inconsistent layout: empty page without previous and next page frame --> crash." ); // <-- } return *pRet; } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */