/* -*- 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 "viewsh.hxx" #include "pagefrm.hxx" #include "rootfrm.hxx" #include "viewimp.hxx" // SwViewImp #include "pam.hxx" // SwPosition #include "swregion.hxx" // SwRegionRects #include "dcontact.hxx" // SwContact #include "dflyobj.hxx" // SdrObject #include "flyfrm.hxx" // SwFlyFrm #include "frmtool.hxx" // ::DrawGraphic #include "porfld.hxx" // SwGrfNumPortion #include "txtfrm.hxx" // SwTxtFrm #include "itrform2.hxx" // SwTxtFormatter #include "porfly.hxx" // NewFlyCntPortion #include "porfld.hxx" // SwGrfNumPortion #include "txtfly.hxx" // SwTxtFly #include "txtpaint.hxx" // SwSaveClip #include "txtatr.hxx" // SwTxtFlyCnt #include "notxtfrm.hxx" #include "flyfrms.hxx" #include "fmtcnct.hxx" // SwFmtChain #include // SwMultiPortion #include #include #include #include // #i28701# #include #include #include #include #include #include #include // SwPageDesc #include #include #include #include #include #include #include #include "doc.hxx" #if OSL_DEBUG_LEVEL > 1 #include "viewopt.hxx" // SwViewOptions, nur zum Testen (Test2) #include "doc.hxx" #endif using namespace ::com::sun::star; /***************************************************************************** * Beschreibung: * Die Klasse SwTxtFly soll die Universalschnittstelle zwischen der * Formatierung/Textausgabe und den u.U. ueberlappenden freifliegenden * Frames sein. * Waehrend der Formatierung erkundigt sich der Formatierer beim SwTxtFly, * ob ein bestimmter Bereich durch die Attribute eines ueberlappenden * Frames vorliegt. Solche Bereiche werden in Form von Dummy-Portions * abgebildet. * Die gesamte Textausgabe und Retusche wird ebenfalls an ein SwTxtFly * weitergeleitet. Dieser entscheidet, ob Textteile geclippt werden muessen * und zerteilt z.B. die Bereiche bei einem DrawRect. * Zu beachten ist, dass alle freifliegenden Frames in einem nach TopLeft * sortiertem PtrArray an der Seite zu finden sind. Intern wird immer nur * in dokumentglobalen Werten gerechnet. Die IN- und OUT-Parameter sind * jedoch in den meisten Faellen an die Beduerfnisse des LineIters * zugeschnitten, d.h. sie werden in frame- oder windowlokalen Koordinaten * konvertiert. * Wenn mehrere Frames mit Umlaufattributen in einer Zeile liegen, * ergeben sich unterschiedliche Auswirkungen fuer den Textfluss: * * L/R P L R K * P -P-P- -P-L -P R- -P K * L -L P- -L L -L R- -L K * R R-P- R-L R R- R K * K K P- K L K R- K K * * (P=parallel, L=links, R=rechts, K=kein Umlauf) * * Das Verhalten so beschreiben: * Jeder Rahmen kann Text verdraengen, wobei der Einfluss allerdings nur * bis zum naechsten Rahmen reicht. *****************************************************************************/ void SwTxtFormatter::CalcUnclipped( SwTwips& rTop, SwTwips& rBottom ) { OSL_ENSURE( ! pFrm->IsVertical() || pFrm->IsSwapped(), "SwTxtFormatter::CalcUnclipped with unswapped frame" ); long nFlyAsc, nFlyDesc; // OD 08.01.2004 #i11859# - use new method //lcl_MaxAscDescent( pCurr, rTop, rBottom, nFlyAsc, nFlyDesc ); pCurr->MaxAscentDescent( rTop, rBottom, nFlyAsc, nFlyDesc ); rTop = Y() + GetCurr()->GetAscent(); rBottom = rTop + nFlyDesc; rTop -= nFlyAsc; } /************************************************************************* * SwTxtFormatter::UpdatePos() aktualisiert die Referenzpunkte der zeichengeb. * Objekte, z. B. nach Adjustierung ( rechtsbuendig, Blocksatz etc. ) * ( hauptsaechlich Korrrektur der X-Position ) *************************************************************************/ void SwTxtFormatter::UpdatePos( SwLineLayout *pCurrent, Point aStart, xub_StrLen nStartIdx, sal_Bool bAllWays ) const { OSL_ENSURE( ! pFrm->IsVertical() || pFrm->IsSwapped(), "SwTxtFormatter::UpdatePos with unswapped frame" ); if( GetInfo().IsTest() ) return; SwLinePortion *pFirst = pCurrent->GetFirstPortion(); SwLinePortion *pPos = pFirst; SwTxtPaintInfo aTmpInf( GetInfo() ); aTmpInf.SetpSpaceAdd( pCurrent->GetpLLSpaceAdd() ); aTmpInf.ResetSpaceIdx(); aTmpInf.SetKanaComp( pCurrent->GetpKanaComp() ); aTmpInf.ResetKanaIdx(); // Die Groesse des Frames aTmpInf.SetIdx( nStartIdx ); aTmpInf.SetPos( aStart ); long nTmpAscent, nTmpDescent, nFlyAsc, nFlyDesc; // OD 08.01.2004 #i11859# - use new method //lcl_MaxAscDescent( pPos, nTmpAscent, nTmpDescent, nFlyAsc, nFlyDesc ); pCurrent->MaxAscentDescent( nTmpAscent, nTmpDescent, nFlyAsc, nFlyDesc ); KSHORT nTmpHeight = pCurrent->GetRealHeight(); KSHORT nAscent = pCurrent->GetAscent() + nTmpHeight - pCurrent->Height(); objectpositioning::AsCharFlags nFlags = AS_CHAR_ULSPACE; if( GetMulti() ) { aTmpInf.SetDirection( GetMulti()->GetDirection() ); if( GetMulti()->HasRotation() ) { nFlags |= AS_CHAR_ROTATE; if( GetMulti()->IsRevers() ) { nFlags |= AS_CHAR_REVERSE; aTmpInf.X( aTmpInf.X() - nAscent ); } else aTmpInf.X( aTmpInf.X() + nAscent ); } else { if ( GetMulti()->IsBidi() ) nFlags |= AS_CHAR_BIDI; aTmpInf.Y( aTmpInf.Y() + nAscent ); } } else aTmpInf.Y( aTmpInf.Y() + nAscent ); while( pPos ) { // bislang ist mir nur ein Fall bekannt, wo die Positionsaenderung // (verursacht durch das Adjustment) fuer eine Portion wichtig // sein koennte: Bei FlyCntPortions muss ein SetRefPoint erfolgen. if( ( pPos->IsFlyCntPortion() || pPos->IsGrfNumPortion() ) && ( bAllWays || !IsQuick() ) ) { // OD 08.01.2004 #i11859# - use new method //lcl_MaxAscDescent( pFirst, nTmpAscent, nTmpDescent, // nFlyAsc, nFlyDesc, pPos ); pCurrent->MaxAscentDescent( nTmpAscent, nTmpDescent, nFlyAsc, nFlyDesc, pPos ); if( pPos->IsGrfNumPortion() ) { if( !nFlyAsc && !nFlyDesc ) { nTmpAscent = nAscent; nFlyAsc = nAscent; nTmpDescent = nTmpHeight - nAscent; nFlyDesc = nTmpDescent; } ((SwGrfNumPortion*)pPos)->SetBase( nTmpAscent, nTmpDescent, nFlyAsc, nFlyDesc ); } else { Point aBase( aTmpInf.GetPos() ); if ( GetInfo().GetTxtFrm()->IsVertical() ) GetInfo().GetTxtFrm()->SwitchHorizontalToVertical( aBase ); ((SwFlyCntPortion*)pPos)->SetBase( *aTmpInf.GetTxtFrm(), aBase, nTmpAscent, nTmpDescent, nFlyAsc, nFlyDesc, nFlags ); } } if( pPos->IsMultiPortion() && ((SwMultiPortion*)pPos)->HasFlyInCntnt() ) { OSL_ENSURE( !GetMulti(), "Too much multi" ); ((SwTxtFormatter*)this)->pMulti = (SwMultiPortion*)pPos; SwLineLayout *pLay = &GetMulti()->GetRoot(); Point aSt( aTmpInf.X(), aStart.Y() ); if ( GetMulti()->HasBrackets() ) { OSL_ENSURE( GetMulti()->IsDouble(), "Brackets only for doubles"); aSt.X() += ((SwDoubleLinePortion*)GetMulti())->PreWidth(); } else if( GetMulti()->HasRotation() ) { aSt.Y() += pCurrent->GetAscent() - GetMulti()->GetAscent(); if( GetMulti()->IsRevers() ) aSt.X() += GetMulti()->Width(); else aSt.Y() += GetMulti()->Height(); } else if ( GetMulti()->IsBidi() ) // jump to end of the bidi portion aSt.X() += pLay->Width(); xub_StrLen nStIdx = aTmpInf.GetIdx(); do { UpdatePos( pLay, aSt, nStIdx, bAllWays ); nStIdx = nStIdx + pLay->GetLen(); aSt.Y() += pLay->Height(); pLay = pLay->GetNext(); } while ( pLay ); ((SwTxtFormatter*)this)->pMulti = NULL; } pPos->Move( aTmpInf ); pPos = pPos->GetPortion(); } } /************************************************************************* * SwTxtFormatter::AlignFlyInCntBase() * richtet die zeichengeb. Objekte in Y-Richtung ggf. neu aus. *************************************************************************/ void SwTxtFormatter::AlignFlyInCntBase( long nBaseLine ) const { OSL_ENSURE( ! pFrm->IsVertical() || pFrm->IsSwapped(), "SwTxtFormatter::AlignFlyInCntBase with unswapped frame" ); if( GetInfo().IsTest() ) return; SwLinePortion *pFirst = pCurr->GetFirstPortion(); SwLinePortion *pPos = pFirst; objectpositioning::AsCharFlags nFlags = AS_CHAR_NOFLAG; if( GetMulti() && GetMulti()->HasRotation() ) { nFlags |= AS_CHAR_ROTATE; if( GetMulti()->IsRevers() ) nFlags |= AS_CHAR_REVERSE; } long nTmpAscent, nTmpDescent, nFlyAsc, nFlyDesc; while( pPos ) { if( pPos->IsFlyCntPortion() || pPos->IsGrfNumPortion() ) { // OD 08.01.2004 #i11859# - use new method //lcl_MaxAscDescent( pFirst, nTmpAscent, nTmpDescent, // nFlyAsc, nFlyDesc, pPos ); pCurr->MaxAscentDescent( nTmpAscent, nTmpDescent, nFlyAsc, nFlyDesc, pPos ); if( pPos->IsGrfNumPortion() ) ((SwGrfNumPortion*)pPos)->SetBase( nTmpAscent, nTmpDescent, nFlyAsc, nFlyDesc ); else { Point aBase; if ( GetInfo().GetTxtFrm()->IsVertical() ) { nBaseLine = GetInfo().GetTxtFrm()->SwitchHorizontalToVertical( nBaseLine ); aBase = Point( nBaseLine, ((SwFlyCntPortion*)pPos)->GetRefPoint().Y() ); } else aBase = Point( ((SwFlyCntPortion*)pPos)->GetRefPoint().X(), nBaseLine ); ((SwFlyCntPortion*)pPos)->SetBase( *GetInfo().GetTxtFrm(), aBase, nTmpAscent, nTmpDescent, nFlyAsc, nFlyDesc, nFlags ); } } pPos = pPos->GetPortion(); } } /************************************************************************* * SwTxtFly::ChkFlyUnderflow() * This is called after the real height of the line has been calculated * Therefore it is possible, that more flys from below intersect with the * line, or that flys from above do not intersect with the line anymore * We check this and return true if so, meaning that the line has to be * formatted again *************************************************************************/ sal_Bool SwTxtFormatter::ChkFlyUnderflow( SwTxtFormatInfo &rInf ) const { OSL_ENSURE( rInf.GetTxtFly()->IsOn(), "SwTxtFormatter::ChkFlyUnderflow: why?" ); if( GetCurr() ) { // Erst pruefen wir, ob ueberhaupt ein Fly mit der Zeile ueberlappt. // = GetLineHeight() const long nHeight = GetCurr()->GetRealHeight(); SwRect aLine( GetLeftMargin(), Y(), rInf.RealWidth(), nHeight ); SwRect aLineVert( aLine ); if ( pFrm->IsVertical() ) pFrm->SwitchHorizontalToVertical( aLineVert ); SwRect aInter( rInf.GetTxtFly()->GetFrm( aLineVert ) ); if ( pFrm->IsVertical() ) pFrm->SwitchVerticalToHorizontal( aInter ); if( !aInter.HasArea() ) return sal_False; // Nun ueberpruefen wir jede Portion, die sich haette senken koennen, // ob sie mit dem Fly ueberlappt. const SwLinePortion *pPos = GetCurr()->GetFirstPortion(); aLine.Pos().Y() = Y() + GetCurr()->GetRealHeight() - GetCurr()->Height(); aLine.Height( GetCurr()->Height() ); while( pPos ) { aLine.Width( pPos->Width() ); aLineVert = aLine; if ( pFrm->IsVertical() ) pFrm->SwitchHorizontalToVertical( aLineVert ); aInter = rInf.GetTxtFly()->GetFrm( aLineVert ); if ( pFrm->IsVertical() ) pFrm->SwitchVerticalToHorizontal( aInter ); // new flys from below? if( !pPos->IsFlyPortion() ) { if( aInter.IsOver( aLine ) ) { aInter._Intersection( aLine ); if( aInter.HasArea() ) { // to be evaluated during reformat of this line: // RealHeight including spacing rInf.SetLineHeight( KSHORT(nHeight) ); // Height without extra spacing rInf.SetLineNettoHeight( KSHORT( pCurr->Height() ) ); return sal_True; } } } else { // the fly portion is not anylonger intersected by a fly if ( ! aInter.IsOver( aLine ) ) { rInf.SetLineHeight( KSHORT(nHeight) ); rInf.SetLineNettoHeight( KSHORT( pCurr->Height() ) ); return sal_True; } else { aInter._Intersection( aLine ); // no area means a fly has become invalid because of // lowering the line => reformat the line // we also have to reformat the line, if the fly size // differs from the intersection intervals size if( ! aInter.HasArea() || ((SwFlyPortion*)pPos)->GetFixWidth() != aInter.Width() ) { rInf.SetLineHeight( KSHORT(nHeight) ); rInf.SetLineNettoHeight( KSHORT( pCurr->Height() ) ); return sal_True; } } } aLine.Left( aLine.Left() + pPos->Width() ); pPos = pPos->GetPortion(); } } return sal_False; } /************************************************************************* * SwTxtFormatter::CalcFlyWidth() * ermittelt das naechste Objekt, das in die restliche Zeile ragt und * konstruiert die zugehoerige FlyPortion. * Dazu wird SwTxtFly.GetFrm(..) benutzt. *************************************************************************/ // Durch Flys kann sich der rechte Rand verkuerzen. void SwTxtFormatter::CalcFlyWidth( SwTxtFormatInfo &rInf ) { if( GetMulti() || rInf.GetFly() ) return; SwTxtFly *pTxtFly = rInf.GetTxtFly(); if( !pTxtFly->IsOn() || rInf.IsIgnoreFly() ) return; const SwLinePortion *pLast = rInf.GetLast(); long nAscent; long nTop = Y(); long nHeight; if( rInf.GetLineHeight() ) { // real line height has already been calculated, we only have to // search for intersections in the lower part of the strip nAscent = pCurr->GetAscent(); nHeight = rInf.GetLineNettoHeight(); nTop += rInf.GetLineHeight() - nHeight; } else { nAscent = pLast->GetAscent(); nHeight = pLast->Height(); // we make a first guess for the lines real height if ( ! pCurr->GetRealHeight() ) CalcRealHeight(); if ( pCurr->GetRealHeight() > nHeight ) nTop += pCurr->GetRealHeight() - nHeight; else // important for fixed space between lines nHeight = pCurr->GetRealHeight(); } const long nLeftMar = GetLeftMargin(); const long nLeftMin = (rInf.X() || GetDropLeft()) ? nLeftMar : GetLeftMin(); SwRect aLine( rInf.X() + nLeftMin, nTop, rInf.RealWidth() - rInf.X() + nLeftMar - nLeftMin , nHeight ); SwRect aLineVert( aLine ); if ( pFrm->IsRightToLeft() ) pFrm->SwitchLTRtoRTL( aLineVert ); if ( pFrm->IsVertical() ) pFrm->SwitchHorizontalToVertical( aLineVert ); SwRect aInter( pTxtFly->GetFrm( aLineVert ) ); if ( pFrm->IsRightToLeft() ) pFrm->SwitchRTLtoLTR( aInter ); if ( pFrm->IsVertical() ) pFrm->SwitchVerticalToHorizontal( aInter ); if( aInter.IsOver( aLine ) ) { aLine.Left( rInf.X() + nLeftMar ); sal_Bool bForced = sal_False; if( aInter.Left() <= nLeftMin ) { SwTwips nFrmLeft = GetTxtFrm()->Frm().Left(); if( GetTxtFrm()->Prt().Left() < 0 ) nFrmLeft += GetTxtFrm()->Prt().Left(); if( aInter.Left() < nFrmLeft ) aInter.Left( nFrmLeft ); long nAddMar = 0; if ( pFrm->IsRightToLeft() ) { nAddMar = pFrm->Frm().Right() - Right(); if ( nAddMar < 0 ) nAddMar = 0; } else nAddMar = nLeftMar - nFrmLeft; aInter.Width( aInter.Width() + nAddMar ); // Bei negativem Erstzeileneinzug setzen wir das Flag, // um anzuzeigen, dass der Einzug/Rand verschoben wurde // Dies muss beim DefaultTab an der Nullposition beruecksichtigt // werden. if( IsFirstTxtLine() && HasNegFirst() ) bForced = sal_True; } aInter.Intersection( aLine ); if( !aInter.HasArea() ) return; const sal_Bool bFullLine = aLine.Left() == aInter.Left() && aLine.Right() == aInter.Right(); // Obwohl kein Text mehr da ist, muss eine weitere Zeile // formatiert werden, weil auch leere Zeilen einem Fly // ohne Umlauf ausweichen muessen. if( bFullLine && rInf.GetIdx() == rInf.GetTxt().Len() ) { rInf.SetNewLine( sal_True ); // 8221: Dummies erkennt man an Ascent == Height pCurr->SetDummy(sal_True); } // aInter wird framelokal aInter.Pos().X() -= nLeftMar; SwFlyPortion *pFly = new SwFlyPortion( aInter ); if( bForced ) { pCurr->SetForcedLeftMargin( sal_True ); rInf.ForcedLeftMargin( (sal_uInt16)aInter.Width() ); } if( bFullLine ) { // 8110: wir muessen um Einheiten von Zeilenhoehen anwachsen, // um nebeneinanderliegende Flys mit unterschiedlichen // Umlaufattributen angemessen zu umfliessen. // Die letzte ausweichende Zeile, sollte in der Hoehe angepasst // sein, damit nicht der Eindruck von "Rahmenabstaenden" aufkommt. // 8221: Wichtig ist, dass Ascent == Height ist, weil die FlyPortionWerte // im CalcLine in pCurr uebertragen werden und IsDummy() darauf // angewiesen ist. // Es gibt meines Wissens nur zwei Stellen, in denen DummyLines // entstehen koennen: hier und in MakeFlyDummies. // Ausgewertet wird IsDummy() in IsFirstTxtLine() und // beim Zeilenwandern und im Zusammenhang mit DropCaps. pFly->Height( KSHORT(aInter.Height()) ); // In nNextTop steckt jetzt die Unterkante des Rahmens, dem wir // ausweichen oder die Oberkante des naechsten Rahmens, den wir // beachten muessen. Wir koennen also jetzt getrost bis zu diesem // Wert anwachsen, so sparen wir einige Leerzeilen. long nNextTop = pTxtFly->GetNextTop(); if ( pFrm->IsVertical() ) nNextTop = pFrm->SwitchVerticalToHorizontal( nNextTop ); if( nNextTop > aInter.Bottom() ) { SwTwips nH = nNextTop - aInter.Top(); if( nH < KSHRT_MAX ) pFly->Height( KSHORT( nH ) ); } if( nAscent < pFly->Height() ) pFly->SetAscent( KSHORT(nAscent) ); else pFly->SetAscent( pFly->Height() ); } else { if( rInf.GetIdx() == rInf.GetTxt().Len() ) { // Nicht nHeight nehmen, sonst haben wir einen Riesendescent pFly->Height( pLast->Height() ); pFly->SetAscent( pLast->GetAscent() ); } else { pFly->Height( KSHORT(aInter.Height()) ); if( nAscent < pFly->Height() ) pFly->SetAscent( KSHORT(nAscent) ); else pFly->SetAscent( pFly->Height() ); } } rInf.SetFly( pFly ); if( pFly->Fix() < rInf.Width() ) rInf.Width( pFly->Fix() ); GETGRID( pFrm->FindPageFrm() ) if ( pGrid ) { const SwPageFrm* pPageFrm = pFrm->FindPageFrm(); const SwLayoutFrm* pBody = pPageFrm->FindBodyCont(); SWRECTFN( pPageFrm ) const long nGridOrigin = pBody ? (pBody->*fnRect->fnGetPrtLeft)() : (pPageFrm->*fnRect->fnGetPrtLeft)(); const SwDoc *pDoc = rInf.GetTxtFrm()->GetNode()->GetDoc(); const sal_uInt16 nGridWidth = GETGRIDWIDTH( pGrid, pDoc); //for textgrid refactor SwTwips nStartX = GetLeftMargin(); if ( bVert ) { Point aPoint( nStartX, 0 ); pFrm->SwitchHorizontalToVertical( aPoint ); nStartX = aPoint.Y(); } const SwTwips nOfst = nStartX - nGridOrigin; const SwTwips nTmpWidth = rInf.Width() + nOfst; const sal_uLong i = nTmpWidth / nGridWidth + 1; const long nNewWidth = ( i - 1 ) * nGridWidth - nOfst; if ( nNewWidth > 0 ) rInf.Width( (sal_uInt16)nNewWidth ); else rInf.Width( 0 ); } } } /***************************************************************************** * SwTxtFormatter::NewFlyCntPortion * legt eine neue Portion fuer ein zeichengebundenes Objekt an. *****************************************************************************/ SwFlyCntPortion *SwTxtFormatter::NewFlyCntPortion( SwTxtFormatInfo &rInf, SwTxtAttr *pHint ) const { SwFlyCntPortion *pRet = 0; const SwFrm *pFrame = (SwFrm*)pFrm; SwFlyInCntFrm *pFly; SwFrmFmt* pFrmFmt = ((SwTxtFlyCnt*)pHint)->GetFlyCnt().GetFrmFmt(); if( RES_FLYFRMFMT == pFrmFmt->Which() ) pFly = ((SwTxtFlyCnt*)pHint)->GetFlyFrm(pFrame); else pFly = NULL; // aBase bezeichnet die dokumentglobale Position, // ab der die neue Extraportion plaziert wird. // aBase.X() = Offset in der Zeile, // hinter der aktuellen Portion // aBase.Y() = LineIter.Y() + Ascent der aktuellen Portion long nTmpAscent, nTmpDescent, nFlyAsc, nFlyDesc; // OD 08.01.2004 #i11859# - use new method //SwLinePortion *pPos = pCurr->GetFirstPortion(); //lcl_MaxAscDescent( pPos, nTmpAscent, nTmpDescent, nFlyAsc, nFlyDesc ); pCurr->MaxAscentDescent( nTmpAscent, nTmpDescent, nFlyAsc, nFlyDesc ); // Wenn der Ascent des Rahmens groesser als der Ascent der akt. Portion // ist, wird dieser bei der Base-Berechnung verwendet, sonst wuerde // der Rahmen zunaechst zu weit nach oben gesetzt, um dann doch wieder // nach unten zu rutschen und dabei ein Repaint in einem Bereich ausloesen, // indem er niemals wirklich war. KSHORT nAscent = 0; const bool bTxtFrmVertical = GetInfo().GetTxtFrm()->IsVertical(); const bool bUseFlyAscent = pFly && pFly->GetValidPosFlag() && 0 != ( bTxtFrmVertical ? pFly->GetRefPoint().X() : pFly->GetRefPoint().Y() ); if ( bUseFlyAscent ) nAscent = static_cast( Abs( int( bTxtFrmVertical ? pFly->GetRelPos().X() : pFly->GetRelPos().Y() ) ) ); // check if be prefer to use the ascent of the last portion: if ( IsQuick() || !bUseFlyAscent || nAscent < rInf.GetLast()->GetAscent() ) { nAscent = rInf.GetLast()->GetAscent(); } else if( nAscent > nFlyAsc ) nFlyAsc = nAscent; Point aBase( GetLeftMargin() + rInf.X(), Y() + nAscent ); objectpositioning::AsCharFlags nMode = IsQuick() ? AS_CHAR_QUICK : 0; if( GetMulti() && GetMulti()->HasRotation() ) { nMode |= AS_CHAR_ROTATE; if( GetMulti()->IsRevers() ) nMode |= AS_CHAR_REVERSE; } Point aTmpBase( aBase ); if ( GetInfo().GetTxtFrm()->IsVertical() ) GetInfo().GetTxtFrm()->SwitchHorizontalToVertical( aTmpBase ); if( pFly ) { pRet = new SwFlyCntPortion( *GetInfo().GetTxtFrm(), pFly, aTmpBase, nTmpAscent, nTmpDescent, nFlyAsc, nFlyDesc, nMode ); // Wir muessen sicherstellen, dass unser Font wieder im OutputDevice // steht. Es koennte sein, dass der FlyInCnt frisch eingefuegt wurde, // dann hat GetFlyFrm dazu gefuehrt, dass er neu angelegt wird. // Dessen Frames werden sofort formatiert, die verstellen den Font // und schon haben wir den Salat (3322). rInf.SelectFont(); if( pRet->GetAscent() > nAscent ) { aBase.Y() = Y() + pRet->GetAscent(); nMode |= AS_CHAR_ULSPACE; if( !rInf.IsTest() ) aTmpBase = aBase; if ( GetInfo().GetTxtFrm()->IsVertical() ) GetInfo().GetTxtFrm()->SwitchHorizontalToVertical( aTmpBase ); pRet->SetBase( *rInf.GetTxtFrm(), aTmpBase, nTmpAscent, nTmpDescent, nFlyAsc, nFlyDesc, nMode ); } } else { pRet = new SwFlyCntPortion( *rInf.GetTxtFrm(), (SwDrawContact*)pFrmFmt->FindContactObj(), aTmpBase, nTmpAscent, nTmpDescent, nFlyAsc, nFlyDesc, nMode ); } return pRet; } /************************************************************************* * SwTxtFly::SwTxtFly() *************************************************************************/ SwTxtFly::SwTxtFly( const SwTxtFly& rTxtFly ) { pPage = rTxtFly.pPage; mpCurrAnchoredObj = rTxtFly.mpCurrAnchoredObj; pCurrFrm = rTxtFly.pCurrFrm; pMaster = rTxtFly.pMaster; if( rTxtFly.mpAnchoredObjList ) { mpAnchoredObjList = new SwAnchoredObjList( *(rTxtFly.mpAnchoredObjList) ); } else { mpAnchoredObjList = NULL; } bOn = rTxtFly.bOn; bLeftSide = rTxtFly.bLeftSide; bTopRule = rTxtFly.bTopRule; nMinBottom = rTxtFly.nMinBottom; nNextTop = rTxtFly.nNextTop; nIndex = rTxtFly.nIndex; mbIgnoreCurrentFrame = rTxtFly.mbIgnoreCurrentFrame; mbIgnoreContour = rTxtFly.mbIgnoreContour; mbIgnoreObjsInHeaderFooter = rTxtFly.mbIgnoreObjsInHeaderFooter; } void SwTxtFly::CtorInitTxtFly( const SwTxtFrm *pFrm ) { mbIgnoreCurrentFrame = sal_False; mbIgnoreContour = sal_False; // #118809# mbIgnoreObjsInHeaderFooter = sal_False; pPage = pFrm->FindPageFrm(); const SwFlyFrm* pTmp = pFrm->FindFlyFrm(); // #i68520# mpCurrAnchoredObj = pTmp; pCurrFrm = pFrm; pMaster = pCurrFrm->IsFollow() ? NULL : pCurrFrm; // #i68520# mpAnchoredObjList = NULL; // Wenn wir nicht von einem Frame ueberlappt werden, oder wenn // es gar keine FlyCollection gibt, dann schaltet wir uns fuer immer ab. // Aber es koennte sein, dass waehrend der Formatierung eine Zeile // hinzukommt, die in einen Frame hineinragt. Deswegen keine Optimierung // per bOn = pSortedFlys && IsAnyFrm(); bOn = pPage->GetSortedObjs() != 0; bTopRule = sal_True; bLeftSide = sal_False; nMinBottom = 0; nIndex = ULONG_MAX; } /************************************************************************* * SwTxtFly::_GetFrm() * * IN: dokumentglobal (rRect) * OUT: framelokal (return-Wert) * Diese Methode wird waehrend der Formatierung vom LineIter gerufen. * 1. um die naechste FlyPortion vorzubereiten * 2. um nach Aenderung der Zeilenhoehe neue Ueberlappungen festzustellen *************************************************************************/ SwRect SwTxtFly::_GetFrm( const SwRect &rRect, sal_Bool bTop ) const { SwRect aRet; if( ForEach( rRect, &aRet, sal_True ) ) { SWRECTFN( pCurrFrm ) if( bTop ) (aRet.*fnRect->fnSetTop)( (rRect.*fnRect->fnGetTop)() ); // 8110: Bottom nicht immer anpassen. const SwTwips nRetBottom = (aRet.*fnRect->fnGetBottom)(); const SwTwips nRectBottom = (rRect.*fnRect->fnGetBottom)(); if ( (*fnRect->fnYDiff)( nRetBottom, nRectBottom ) > 0 || (aRet.*fnRect->fnGetHeight)() < 0 ) (aRet.*fnRect->fnSetBottom)( nRectBottom ); } return aRet; } /************************************************************************* * SwTxtFly::IsAnyFrm() * * IN: dokumentglobal * fuer die Printarea des aktuellen Frame * * dient zum Abschalten des SwTxtFly, wenn keine Objekte ueberlappen (Relax) * *************************************************************************/ sal_Bool SwTxtFly::IsAnyFrm() const { SWAP_IF_SWAPPED( pCurrFrm ) OSL_ENSURE( bOn, "IsAnyFrm: Why?" ); SwRect aRect( pCurrFrm->Frm().Pos() + pCurrFrm->Prt().Pos(), pCurrFrm->Prt().SSize() ); const sal_Bool bRet = ForEach( aRect, NULL, sal_False ); UNDO_SWAP( pCurrFrm ) return bRet; } /************************************************************************* * SwTxtFly::IsAnyObj() * * IN: dokumentglobal * OUT: sal_True Wenn ein Rahmen oder DrawObj beruecksichtigt werden muss * Nur wenn IsAnyObj sal_False liefert, koennen Optimierungen benutzt werden * wie Paint/FormatEmpty fuer leere Absaetze * und auch das virtuelle Outputdevice. *************************************************************************/ sal_Bool SwTxtFly::IsAnyObj( const SwRect &rRect ) const { OSL_ENSURE( bOn, "SwTxtFly::IsAnyObj: Who's knocking?" ); SwRect aRect( rRect ); if ( aRect.IsEmpty() ) aRect = SwRect( pCurrFrm->Frm().Pos() + pCurrFrm->Prt().Pos(), pCurrFrm->Prt().SSize() ); const SwSortedObjs *pSorted = pPage->GetSortedObjs(); if( pSorted ) // Eigentlich ist durch bOn sichergestellt, dass es an der // Seite Objekte gibt, aber wer weiss, wer inzwischen etwas geloescht hat. { for ( MSHORT i = 0; i < pSorted->Count(); ++i ) { const SwAnchoredObject* pObj = (*pSorted)[i]; const SwRect aBound( pObj->GetObjRectWithSpaces() ); // Optimierung if( pObj->GetObjRect().Left() > aRect.Right() ) continue; // #i68520# if( mpCurrAnchoredObj != pObj && aBound.IsOver( aRect ) ) return sal_True; } } return sal_False; } const SwCntntFrm* SwTxtFly::_GetMaster() { pMaster = pCurrFrm; while( pMaster && pMaster->IsFollow() ) pMaster = (SwCntntFrm*)pMaster->FindMaster(); return pMaster; } /************************************************************************* * SwTxtFly::DrawTextOpaque() * * IN: dokumentglobal * DrawTextOpaque() wird von DrawText() gerufen. * Die Clipregions werden so gesetzt, dass nur die Teile ausgegeben werden, * die nicht in den Bereichen von FlyFrms liegen, die undurchsichtig und * ueber dem aktuellen Frame liegen. * Die On-Optimierung uebernimmt DrawText()! *************************************************************************/ sal_Bool SwTxtFly::DrawTextOpaque( SwDrawTextInfo &rInf ) { SwSaveClip aClipSave( rInf.GetpOut() ); SwRect aRect( rInf.GetPos(), rInf.GetSize() ); if( rInf.GetSpace() ) { xub_StrLen nTmpLen = STRING_LEN == rInf.GetLen() ? rInf.GetText().Len() : rInf.GetLen(); if( rInf.GetSpace() > 0 ) { xub_StrLen nSpaceCnt = 0; const xub_StrLen nEndPos = rInf.GetIdx() + nTmpLen; for( xub_StrLen nPos = rInf.GetIdx(); nPos < nEndPos; ++nPos ) { if( CH_BLANK == rInf.GetText().GetChar( nPos ) ) ++nSpaceCnt; } if( nSpaceCnt ) aRect.Width( aRect.Width() + nSpaceCnt * rInf.GetSpace() ); } else aRect.Width( aRect.Width() - nTmpLen * rInf.GetSpace() ); } if( aClipSave.IsOn() && rInf.GetOut().IsClipRegion() ) { SwRect aClipRect( rInf.GetOut().GetClipRegion().GetBoundRect() ); aRect.Intersection( aClipRect ); } SwRegionRects aRegion( aRect ); sal_Bool bOpaque = sal_False; // #i68520# const sal_uInt32 nCurrOrd = mpCurrAnchoredObj ? mpCurrAnchoredObj->GetDrawObj()->GetOrdNum() : SAL_MAX_UINT32; OSL_ENSURE( !bTopRule, "DrawTextOpaque: Wrong TopRule" ); // #i68520# SwAnchoredObjList::size_type nCount( bOn ? GetAnchoredObjList()->size() : 0 ); if ( bOn && nCount > 0 ) { MSHORT nHellId = pPage->getRootFrm()->GetCurrShell()->getIDocumentDrawModelAccess()->GetHellId(); for( MSHORT i = 0; i < nCount; ++i ) { // #i68520# const SwAnchoredObject* pTmpAnchoredObj = (*mpAnchoredObjList)[i]; if( dynamic_cast(pTmpAnchoredObj) && mpCurrAnchoredObj != pTmpAnchoredObj ) { // #i68520# const SwFlyFrm* pFly = dynamic_cast(pTmpAnchoredObj); if( aRegion.GetOrigin().IsOver( pFly->Frm() ) ) { const SwFrmFmt *pFmt = pFly->GetFmt(); const SwFmtSurround &rSur = pFmt->GetSurround(); const SwFmtAnchor& rAnchor = pFmt->GetAnchor(); //Nur undurchsichtige und weiter oben liegende. /// OD 08.10.2002 #103898# - add condition /// IsBackgroundTransparent() || pFly->IsShadowTransparent())> if( !( pFly->IsBackgroundTransparent() || pFly->IsShadowTransparent() ) && SURROUND_THROUGHT == rSur.GetSurround() && ( !rSur.IsAnchorOnly() || // #i68520# GetMaster() == pFly->GetAnchorFrm() || ((FLY_AT_PARA != rAnchor.GetAnchorId()) && (FLY_AT_CHAR != rAnchor.GetAnchorId()) ) ) && // #i68520# pTmpAnchoredObj->GetDrawObj()->GetLayer() != nHellId && nCurrOrd < pTmpAnchoredObj->GetDrawObj()->GetOrdNum() ) { //Ausser der Inhalt ist Transparent const SwNoTxtFrm *pNoTxt = pFly->Lower() && pFly->Lower()->IsNoTxtFrm() ? (SwNoTxtFrm*)pFly->Lower() : 0; if ( !pNoTxt || (!pNoTxt->IsTransparent() && !rSur.IsContour()) ) { bOpaque = sal_True; aRegion -= pFly->Frm(); } } } } } } Point aPos( rInf.GetPos().X(), rInf.GetPos().Y() + rInf.GetAscent() ); const Point &rOld = rInf.GetPos(); rInf.SetPos( aPos ); if( !bOpaque ) { if( rInf.GetKern() ) rInf.GetFont()->_DrawStretchText( rInf ); else rInf.GetFont()->_DrawText( rInf ); rInf.SetPos( rOld ); return sal_False; } else if( aRegion.Count() ) { // Was fuer ein Aufwand ... SwSaveClip aClipVout( rInf.GetpOut() ); for( MSHORT i = 0; i < aRegion.Count(); ++i ) { SwRect &rRect = aRegion[i]; if( rRect != aRegion.GetOrigin() ) aClipVout.ChgClip( rRect ); if( rInf.GetKern() ) rInf.GetFont()->_DrawStretchText( rInf ); else rInf.GetFont()->_DrawText( rInf ); } } rInf.SetPos( rOld ); return sal_True; } /************************************************************************* * SwTxtFly::DrawFlyRect() * * IN: windowlokal * Zwei Feinheiten gilt es zu beachten: * 1) DrawRect() oberhalb des ClipRects sind erlaubt ! * 2) FlyToRect() liefert groessere Werte als die Framedaten ! *************************************************************************/ void SwTxtFly::DrawFlyRect( OutputDevice* pOut, const SwRect &rRect, const SwTxtPaintInfo &rInf, sal_Bool bNoGraphic ) { SwRegionRects aRegion( rRect ); OSL_ENSURE( !bTopRule, "DrawFlyRect: Wrong TopRule" ); // #i68520# SwAnchoredObjList::size_type nCount( bOn ? GetAnchoredObjList()->size() : 0 ); if ( bOn && nCount > 0 ) { MSHORT nHellId = pPage->getRootFrm()->GetCurrShell()->getIDocumentDrawModelAccess()->GetHellId(); for( MSHORT i = 0; i < nCount; ++i ) { // #i68520# const SwAnchoredObject* pAnchoredObjTmp = (*mpAnchoredObjList)[i]; if( mpCurrAnchoredObj != pAnchoredObjTmp && dynamic_cast(pAnchoredObjTmp) ) { // #i68520# const SwFmtSurround& rSur = pAnchoredObjTmp->GetFrmFmt().GetSurround(); // OD 24.01.2003 #106593# - correct clipping of fly frame area. // Consider that fly frame background/shadow can be transparent // and fly frame area // #i68520# const SwFlyFrm* pFly = dynamic_cast(pAnchoredObjTmp); // #i47804# - consider transparent graphics // and OLE objects. bool bClipFlyArea = ( ( SURROUND_THROUGHT == rSur.GetSurround() ) // #i68520# ? (pAnchoredObjTmp->GetDrawObj()->GetLayer() != nHellId) : !rSur.IsContour() ) && !pFly->IsBackgroundTransparent() && !pFly->IsShadowTransparent() && ( !pFly->Lower() || !pFly->Lower()->IsNoTxtFrm() || !static_cast(pFly->Lower())->IsTransparent() ); if ( bClipFlyArea ) { // #i68520# SwRect aFly( pAnchoredObjTmp->GetObjRect() ); // OD 24.01.2003 #106593# ::SwAlignRect( aFly, pPage->getRootFrm()->GetCurrShell() ); if( aFly.Width() > 0 && aFly.Height() > 0 ) aRegion -= aFly; } } } } for( MSHORT i = 0; i < aRegion.Count(); ++i ) { if ( bNoGraphic ) pOut->DrawRect( aRegion[i].SVRect() ); else { OSL_ENSURE( ((SvxBrushItem*)-1) != rInf.GetBrushItem(), "DrawRect: Uninitialized BrushItem!" ); ::DrawGraphic( rInf.GetBrushItem(), pOut, rInf.GetBrushRect(), aRegion[i] ); } } } // #i26945# - change first parameter // Now it's the instance of the floating screen object sal_Bool SwTxtFly::GetTop( const SwAnchoredObject* _pAnchoredObj, const sal_Bool bInFtn, const sal_Bool bInFooterOrHeader ) { // #i68520# // is set, if is inside a fly frame if( _pAnchoredObj != mpCurrAnchoredObj ) { // #i26945# const SdrObject* pNew = _pAnchoredObj->GetDrawObj(); // #102344# Ignore connectors which have one or more connections if(pNew && pNew->ISA(SdrEdgeObj)) { if(((SdrEdgeObj*)pNew)->GetConnectedNode(sal_True) || ((SdrEdgeObj*)pNew)->GetConnectedNode(sal_False)) { return sal_False; } } if( ( bInFtn || bInFooterOrHeader ) && bTopRule ) { // #i26945# const SwFrmFmt& rFrmFmt = _pAnchoredObj->GetFrmFmt(); const SwFmtAnchor& rNewA = rFrmFmt.GetAnchor(); if (FLY_AT_PAGE == rNewA.GetAnchorId()) { if ( bInFtn ) return sal_False; if ( bInFooterOrHeader ) { SwFmtVertOrient aVert( rFrmFmt.GetVertOrient() ); sal_Bool bVertPrt = aVert.GetRelationOrient() == text::RelOrientation::PRINT_AREA || aVert.GetRelationOrient() == text::RelOrientation::PAGE_PRINT_AREA; if( bVertPrt ) return sal_False; } } } // #i68520# // bEvade: consider pNew, if we are not inside a fly // consider pNew, if pNew is lower of sal_Bool bEvade = !mpCurrAnchoredObj || Is_Lower_Of( dynamic_cast(mpCurrAnchoredObj), pNew); if ( !bEvade ) { // We are currently inside a fly frame and pNew is not // inside this fly frame. We can do some more checks if // we have to consider pNew. // If bTopRule is not set, we ignore the frame types. // We directly check the z-order if ( !bTopRule ) bEvade = sal_True; else { // innerhalb von verketteten Flys wird nur Lowern ausgewichen // #i68520# const SwFmtChain &rChain = mpCurrAnchoredObj->GetFrmFmt().GetChain(); if ( !rChain.GetPrev() && !rChain.GetNext() ) { // #i26945# const SwFmtAnchor& rNewA = _pAnchoredObj->GetFrmFmt().GetAnchor(); // #i68520# const SwFmtAnchor& rCurrA = mpCurrAnchoredObj->GetFrmFmt().GetAnchor(); // If is anchored as character, its content // does not wrap around pNew if (FLY_AS_CHAR == rCurrA.GetAnchorId()) return sal_False; // If pNew is anchored to page and does not wrap around pNew // If both pNew and are anchored to page, we can do // some more checks if (FLY_AT_PAGE == rNewA.GetAnchorId()) { if (FLY_AT_PAGE == rCurrA.GetAnchorId()) { bEvade = sal_True; } else return sal_False; } else if (FLY_AT_PAGE == rCurrA.GetAnchorId()) return sal_False; // Seitengebundene weichen nur seitengeb. aus else if (FLY_AT_FLY == rNewA.GetAnchorId()) bEvade = sal_True; // Nicht seitengeb. weichen Rahmengeb. aus else if( FLY_AT_FLY == rCurrA.GetAnchorId() ) return sal_False; // Rahmengebundene weichen abs.geb. nicht aus // #i57062# // In order to avoid loop situation, it's decided to adjust // the wrapping behaviour of content of at-paragraph/at-character // anchored objects to one in the page header/footer and // the document body --> content of at-paragraph/at-character // anchored objects doesn't wrap around each other. // else if( bInFooterOrHeader ) // return sal_False; // In header or footer no wrapping // // if both bounded at paragraph // else // Zwei Flies mit (auto-)absatzgebunder Verankerung ... // // ... entscheiden nach der Reihenfolge ihrer Anker im Dok. // bEvade = rNewA.GetCntntAnchor()->nNode.GetIndex() <= // rCurrA.GetCntntAnchor()->nNode.GetIndex(); else return sal_False; } } // aber: es wird niemals einem hierarchisch untergeordnetem // ausgewichen und ausserdem braucht nur bei Ueberlappung // ausgewichen werden. // #i68520# bEvade &= ( mpCurrAnchoredObj->GetDrawObj()->GetOrdNum() < pNew->GetOrdNum() ); if( bEvade ) { // #i68520# SwRect aTmp( _pAnchoredObj->GetObjRectWithSpaces() ); if ( !aTmp.IsOver( mpCurrAnchoredObj->GetObjRectWithSpaces() ) ) bEvade = sal_False; } } if ( bEvade ) { // #i26945# const SwFmtAnchor& rNewA = _pAnchoredObj->GetFrmFmt().GetAnchor(); OSL_ENSURE( FLY_AS_CHAR != rNewA.GetAnchorId(), "Don't call GetTop with a FlyInCntFrm" ); if (FLY_AT_PAGE == rNewA.GetAnchorId()) return sal_True; // Seitengebundenen wird immer ausgewichen. // Wenn absatzgebundene Flys in einem FlyCnt gefangen sind, so // endet deren Einflussbereich an den Grenzen des FlyCnt! // Wenn wir aber gerade den Text des FlyCnt formatieren, dann // muss er natuerlich dem absatzgebundenen Frm ausweichen! // pCurrFrm ist der Anker von pNew? // #i26945# const SwFrm* pTmp = _pAnchoredObj->GetAnchorFrm(); if( pTmp == pCurrFrm ) return sal_True; if( pTmp->IsTxtFrm() && ( pTmp->IsInFly() || pTmp->IsInFtn() ) ) { // #i26945# Point aPos = _pAnchoredObj->GetObjRect().Pos(); pTmp = GetVirtualUpper( pTmp, aPos ); } // #i26945# // #115759# // If is a text frame inside a table, take the upper // of the anchor frame, which contains the anchor position. else if ( pTmp->IsTxtFrm() && pTmp->IsInTab() ) { pTmp = const_cast(_pAnchoredObj) ->GetAnchorFrmContainingAnchPos()->GetUpper(); } // #i28701# - consider all objects in same context, // if wrapping style is considered on object positioning. // Thus, text will wrap around negative positioned objects. // #i3317# - remove condition on checking, // if wrappings style is considered on object postioning. // Thus, text is wrapping around negative positioned objects. // #i35640# - no consideration of negative // positioned objects, if wrapping style isn't considered on // object position and former text wrapping is applied. // This condition is typically for documents imported from the // OpenOffice.org file format. const IDocumentSettingAccess* pIDSA = pCurrFrm->GetTxtNode()->getIDocumentSettingAccess(); if ( ( pIDSA->get(IDocumentSettingAccess::CONSIDER_WRAP_ON_OBJECT_POSITION) || !pIDSA->get(IDocumentSettingAccess::USE_FORMER_TEXT_WRAPPING) ) && ::FindKontext( pTmp, 0 ) == ::FindKontext( pCurrFrm, 0 ) ) { return sal_True; } const SwFrm* pHeader = 0; if ( pCurrFrm->GetNext() != pTmp && ( IsFrmInSameKontext( pTmp, pCurrFrm ) || // #i13832#, #i24135# wrap around objects in page header ( !pIDSA->get(IDocumentSettingAccess::USE_FORMER_TEXT_WRAPPING) && 0 != ( pHeader = pTmp->FindFooterOrHeader() ) && !pHeader->IsFooterFrm() && pCurrFrm->IsInDocBody() ) ) ) { if( pHeader || FLY_AT_FLY == rNewA.GetAnchorId() ) return sal_True; // Compare indices: // Den Index des anderen erhalten wir immer ueber das Ankerattr. sal_uLong nTmpIndex = rNewA.GetCntntAnchor()->nNode.GetIndex(); // Jetzt wird noch ueberprueft, ob der aktuelle Absatz vor dem // Anker des verdraengenden Objekts im Text steht, dann wird // nicht ausgewichen. // Der Index wird moeglichst ueber einen SwFmtAnchor ermittelt, // da sonst recht teuer. if( ULONG_MAX == nIndex ) nIndex = pCurrFrm->GetNode()->GetIndex(); if( nIndex >= nTmpIndex ) return sal_True; } } } return sal_False; } // #i68520# struct AnchoredObjOrder { sal_Bool mbR2L; SwRectFn mfnRect; AnchoredObjOrder( const sal_Bool bR2L, SwRectFn fnRect ) : mbR2L( bR2L ), mfnRect( fnRect ) {} bool operator()( const SwAnchoredObject* pListedAnchoredObj, const SwAnchoredObject* pNewAnchoredObj ) { const SwRect aBoundRectOfListedObj( pListedAnchoredObj->GetObjRectWithSpaces() ); const SwRect aBoundRectOfNewObj( pNewAnchoredObj->GetObjRectWithSpaces() ); if ( ( mbR2L && ( (aBoundRectOfListedObj.*mfnRect->fnGetRight)() == (aBoundRectOfNewObj.*mfnRect->fnGetRight)() ) ) || ( !mbR2L && ( (aBoundRectOfListedObj.*mfnRect->fnGetLeft)() == (aBoundRectOfNewObj.*mfnRect->fnGetLeft)() ) ) ) { SwTwips nTopDiff = (*mfnRect->fnYDiff)( (aBoundRectOfNewObj.*mfnRect->fnGetTop)(), (aBoundRectOfListedObj.*mfnRect->fnGetTop)() ); if ( nTopDiff == 0 && ( ( mbR2L && ( (aBoundRectOfNewObj.*mfnRect->fnGetLeft)() > (aBoundRectOfListedObj.*mfnRect->fnGetLeft)() ) ) || ( !mbR2L && ( (aBoundRectOfNewObj.*mfnRect->fnGetRight)() < (aBoundRectOfListedObj.*mfnRect->fnGetRight)() ) ) ) ) { return true; } else if ( nTopDiff > 0 ) { return true; } } else if ( ( mbR2L && ( (aBoundRectOfListedObj.*mfnRect->fnGetRight)() > (aBoundRectOfNewObj.*mfnRect->fnGetRight)() ) ) || ( !mbR2L && ( (aBoundRectOfListedObj.*mfnRect->fnGetLeft)() < (aBoundRectOfNewObj.*mfnRect->fnGetLeft)() ) ) ) { return true; } return false; } }; // #i68520# SwAnchoredObjList* SwTxtFly::InitAnchoredObjList() { OSL_ENSURE( pCurrFrm, "InitFlyList: No Frame, no FlyList" ); // #i68520# OSL_ENSURE( !mpAnchoredObjList, "InitFlyList: FlyList already initialized" ); SWAP_IF_SWAPPED( pCurrFrm ) const SwSortedObjs *pSorted = pPage->GetSortedObjs(); const sal_uInt32 nCount = pSorted ? pSorted->Count() : 0; const sal_Bool bWrapAllowed = !SwLayouter::FrmNotToWrap( *pCurrFrm->GetTxtNode()->getIDocumentLayoutAccess(), *pCurrFrm ); bOn = sal_False; if( nCount && bWrapAllowed ) { // #i68520# mpAnchoredObjList = new SwAnchoredObjList(); // #i28701# - consider complete frame area for new // text wrapping SwRect aRect; const IDocumentSettingAccess* pIDSA = pCurrFrm->GetTxtNode()->getIDocumentSettingAccess(); if ( pIDSA->get(IDocumentSettingAccess::USE_FORMER_TEXT_WRAPPING) ) { aRect = pCurrFrm->Prt(); aRect += pCurrFrm->Frm().Pos(); } else { aRect = pCurrFrm->Frm(); } // Wir machen uns etwas kleiner als wir sind, // damit Ein-Twip-Ueberlappungen ignoriert werden. (#49532) SWRECTFN( pCurrFrm ) const long nRight = (aRect.*fnRect->fnGetRight)() - 1; const long nLeft = (aRect.*fnRect->fnGetLeft)() + 1; const sal_Bool bR2L = pCurrFrm->IsRightToLeft(); const bool bFooterHeader = ( pCurrFrm->FindFooterOrHeader() != NULL ); const IDocumentDrawModelAccess* pIDDMA = pCurrFrm->GetTxtNode()->getIDocumentDrawModelAccess(); for( sal_uInt32 i = 0; i < nCount; i++ ) { // #i68520# // do not consider hidden objects // check, if object has to be considered for text wrap // #118809# - If requested, do not consider // objects in page header|footer for text frames not in page // header|footer. This is requested for the calculation of // the base offset for objects // #i20505# Do not consider oversized objects SwAnchoredObject* pAnchoredObj = (*pSorted)[ i ]; if ( !pIDDMA->IsVisibleLayerId( pAnchoredObj->GetDrawObj()->GetLayer() ) || !pAnchoredObj->ConsiderForTextWrap() || ( mbIgnoreObjsInHeaderFooter && !bFooterHeader && pAnchoredObj->GetAnchorFrm()->FindFooterOrHeader() ) ) { continue; } const SwRect aBound( pAnchoredObj->GetObjRectWithSpaces() ); if ( nRight < (aBound.*fnRect->fnGetLeft)() || (*fnRect->fnYDiff)( (aRect.*fnRect->fnGetTop)(), (aBound.*fnRect->fnGetBottom)() ) > 0 || nLeft > (aBound.*fnRect->fnGetRight)() || (aBound.*fnRect->fnGetHeight)() > 2 * (pPage->Frm().*fnRect->fnGetHeight)() ) { continue; } // #i26945# - pass to method // instead of only the instance of the // anchored object if ( GetTop( pAnchoredObj, pCurrFrm->IsInFtn(), bFooterHeader ) ) { // OD 11.03.2003 #107862# - adjust insert position: // overlapping objects should be sorted from left to right and // inside left to right sorting from top to bottom. // If objects on the same position are found, they are sorted // on its width. // #i68520# // sal_uInt16 nPos = pFlyList->Count(); // while ( nPos ) // { // SdrObject* pTmpObj = (*pFlyList)[ --nPos ]; // const SwRect aBoundRectOfTmpObj( GetBoundRect( pTmpObj ) ); // if ( ( bR2L && // ( (aBoundRectOfTmpObj.*fnRect->fnGetRight)() == // (aBound.*fnRect->fnGetRight)() ) ) || // ( !bR2L && // ( (aBoundRectOfTmpObj.*fnRect->fnGetLeft)() == // (aBound.*fnRect->fnGetLeft)() ) ) ) // { // SwTwips nTopDiff = // (*fnRect->fnYDiff)( (aBound.*fnRect->fnGetTop)(), // (aBoundRectOfTmpObj.*fnRect->fnGetTop)() ); // if ( nTopDiff == 0 && // ( ( bR2L && // ( (aBound.*fnRect->fnGetLeft)() > // (aBoundRectOfTmpObj.*fnRect->fnGetLeft)() ) ) || // ( !bR2L && // ( (aBound.*fnRect->fnGetRight)() < // (aBoundRectOfTmpObj.*fnRect->fnGetRight)() ) ) ) ) // { // ++nPos; // break; // } // else if ( nTopDiff > 0 ) // { // ++nPos; // break; // } // } // else if ( ( bR2L && // ( (aBoundRectOfTmpObj.*fnRect->fnGetRight)() > // (aBound.*fnRect->fnGetRight)() ) ) || // ( !bR2L && // ( (aBoundRectOfTmpObj.*fnRect->fnGetLeft)() < // (aBound.*fnRect->fnGetLeft)() ) ) ) // { // ++nPos; // break; // } // } // SdrObject* pSdrObj = pAnchoredObj->DrawObj(); // pFlyList->C40_INSERT( SdrObject, pSdrObj, nPos ); { SwAnchoredObjList::iterator aInsPosIter = std::lower_bound( mpAnchoredObjList->begin(), mpAnchoredObjList->end(), pAnchoredObj, AnchoredObjOrder( bR2L, fnRect ) ); mpAnchoredObjList->insert( aInsPosIter, pAnchoredObj ); } const SwFmtSurround &rFlyFmt = pAnchoredObj->GetFrmFmt().GetSurround(); // #i68520# if ( rFlyFmt.IsAnchorOnly() && pAnchoredObj->GetAnchorFrm() == GetMaster() ) { const SwFmtVertOrient &rTmpFmt = pAnchoredObj->GetFrmFmt().GetVertOrient(); if( text::VertOrientation::BOTTOM != rTmpFmt.GetVertOrient() ) nMinBottom = ( bVert && nMinBottom ) ? Min( nMinBottom, aBound.Left() ) : Max( nMinBottom, (aBound.*fnRect->fnGetBottom)() ); } bOn = sal_True; } } if( nMinBottom ) { SwTwips nMax = (pCurrFrm->GetUpper()->*fnRect->fnGetPrtBottom)(); if( (*fnRect->fnYDiff)( nMinBottom, nMax ) > 0 ) nMinBottom = nMax; } } else { // #i68520# mpAnchoredObjList = new SwAnchoredObjList(); } UNDO_SWAP( pCurrFrm ) // #i68520# return mpAnchoredObjList; } SwTwips SwTxtFly::CalcMinBottom() const { SwTwips nRet = 0; const SwCntntFrm *pLclMaster = GetMaster(); OSL_ENSURE(pLclMaster, "SwTxtFly without master"); const SwSortedObjs *pDrawObj = pLclMaster ? pLclMaster->GetDrawObjs() : NULL; const sal_uInt32 nCount = pDrawObj ? pDrawObj->Count() : 0; if( nCount ) { SwTwips nEndOfFrm = pCurrFrm->Frm().Bottom(); for( sal_uInt32 i = 0; i < nCount; i++ ) { SwAnchoredObject* pAnchoredObj = (*pDrawObj)[ i ]; const SwFmtSurround &rFlyFmt = pAnchoredObj->GetFrmFmt().GetSurround(); if( rFlyFmt.IsAnchorOnly() ) { const SwFmtVertOrient &rTmpFmt = pAnchoredObj->GetFrmFmt().GetVertOrient(); if( text::VertOrientation::BOTTOM != rTmpFmt.GetVertOrient() ) { const SwRect aBound( pAnchoredObj->GetObjRectWithSpaces() ); if( aBound.Top() < nEndOfFrm ) nRet = Max( nRet, aBound.Bottom() ); } } } SwTwips nMax = pCurrFrm->GetUpper()->Frm().Top() + pCurrFrm->GetUpper()->Prt().Bottom(); if( nRet > nMax ) nRet = nMax; } return nRet; } /************************************************************************* * Hier erfolgt die Berechnung der Kontur ... * CalcBoundRect(..) und andere *************************************************************************/ /************************************************************************* * class SwContourCache *************************************************************************/ SwContourCache::SwContourCache() : nPntCnt( 0 ), nObjCnt( 0 ) { memset( (SdrObject**)pSdrObj, 0, sizeof(pSdrObj) ); memset( pTextRanger, 0, sizeof(pTextRanger) ); } SwContourCache::~SwContourCache() { for( MSHORT i = 0; i < nObjCnt; delete pTextRanger[ i++ ] ) ; } void SwContourCache::ClrObject( MSHORT nPos ) { OSL_ENSURE( pTextRanger[ nPos ], "ClrObject: Allready cleared. Good Bye!" ); nPntCnt -= pTextRanger[ nPos ]->GetPointCount(); delete pTextRanger[ nPos ]; --nObjCnt; memmove( (SdrObject**)pSdrObj + nPos, pSdrObj + nPos + 1, ( nObjCnt - nPos ) * sizeof( SdrObject* ) ); memmove( pTextRanger + nPos, pTextRanger + nPos + 1, ( nObjCnt - nPos ) * sizeof( TextRanger* ) ); } void ClrContourCache( const SdrObject *pObj ) { if( pContourCache && pObj ) for( MSHORT i = 0; i < pContourCache->GetCount(); ++i ) if( pObj == pContourCache->GetObject( i ) ) { pContourCache->ClrObject( i ); break; } } void ClrContourCache() { if( pContourCache ) { for( MSHORT i = 0; i < pContourCache->GetCount(); delete pContourCache->pTextRanger[ i++ ] ) ; pContourCache->nObjCnt = 0; pContourCache->nPntCnt = 0; } } /************************************************************************* * SwContourCache::CalcBoundRect * berechnet das Rechteck, welches vom Objekt in der angegebenen Zeile * ueberdeckt wird. * Bei _nicht_ konturumflossenen Objekten ist dies einfach die Ueber- * lappung von BoundRect (inkl. Abstand!) und Zeile, * bei Konturumfluss wird das Polypolygon des Objekts abgeklappert *************************************************************************/ // #i68520# const SwRect SwContourCache::CalcBoundRect( const SwAnchoredObject* pAnchoredObj, const SwRect &rLine, const SwTxtFrm* pFrm, const long nXPos, const sal_Bool bRight ) { SwRect aRet; const SwFrmFmt* pFmt = &(pAnchoredObj->GetFrmFmt()); if( pFmt->GetSurround().IsContour() && ( !pAnchoredObj->ISA(SwFlyFrm) || ( static_cast(pAnchoredObj)->Lower() && static_cast(pAnchoredObj)->Lower()->IsNoTxtFrm() ) ) ) { aRet = pAnchoredObj->GetObjRectWithSpaces(); if( aRet.IsOver( rLine ) ) { if( !pContourCache ) pContourCache = new SwContourCache; aRet = pContourCache->ContourRect( pFmt, pAnchoredObj->GetDrawObj(), pFrm, rLine, nXPos, bRight ); } else aRet.Width( 0 ); } else { aRet = pAnchoredObj->GetObjRectWithSpaces(); } return aRet; } const SwRect SwContourCache::ContourRect( const SwFmt* pFmt, const SdrObject* pObj, const SwTxtFrm* pFrm, const SwRect &rLine, const long nXPos, const sal_Bool bRight ) { SwRect aRet; MSHORT nPos = 0; // Suche im Cache ... while( nPos < GetCount() && pObj != pSdrObj[ nPos ] ) ++nPos; if( GetCount() == nPos ) // nicht gefunden { if( nObjCnt == POLY_CNT ) { nPntCnt -= pTextRanger[ --nObjCnt ]->GetPointCount(); delete pTextRanger[ nObjCnt ]; } ::basegfx::B2DPolyPolygon aPolyPolygon; ::basegfx::B2DPolyPolygon* pPolyPolygon = 0L; if ( pObj->ISA(SwVirtFlyDrawObj) ) { // Vorsicht #37347: Das GetContour() fuehrt zum Laden der Grafik, // diese aendert dadurch ggf. ihre Groesse, ruft deshalb ein // ClrObject() auf. PolyPolygon aPoly; if( !((SwVirtFlyDrawObj*)pObj)->GetFlyFrm()->GetContour( aPoly ) ) aPoly = PolyPolygon( ((SwVirtFlyDrawObj*)pObj)-> GetFlyFrm()->Frm().SVRect() ); aPolyPolygon.clear(); aPolyPolygon.append(aPoly.getB2DPolyPolygon()); } else { if( !pObj->ISA( E3dObject ) ) { aPolyPolygon = pObj->TakeXorPoly(); } ::basegfx::B2DPolyPolygon aContourPoly(pObj->TakeContour()); pPolyPolygon = new ::basegfx::B2DPolyPolygon(aContourPoly); } const SvxLRSpaceItem &rLRSpace = pFmt->GetLRSpace(); const SvxULSpaceItem &rULSpace = pFmt->GetULSpace(); memmove( pTextRanger + 1, pTextRanger, nObjCnt * sizeof( TextRanger* ) ); memmove( (SdrObject**)pSdrObj + 1, pSdrObj, nObjCnt++ * sizeof( SdrObject* ) ); pSdrObj[ 0 ] = pObj; // Wg. #37347 darf das Object erst nach dem // GetContour() eingetragen werden. pTextRanger[ 0 ] = new TextRanger( aPolyPolygon, pPolyPolygon, 20, (sal_uInt16)rLRSpace.GetLeft(), (sal_uInt16)rLRSpace.GetRight(), pFmt->GetSurround().IsOutside(), sal_False, pFrm->IsVertical() ); pTextRanger[ 0 ]->SetUpper( rULSpace.GetUpper() ); pTextRanger[ 0 ]->SetLower( rULSpace.GetLower() ); delete pPolyPolygon; // UPPER_LOWER_TEST #if OSL_DEBUG_LEVEL > 1 const ViewShell* pTmpViewShell = pFmt->GetDoc()->GetCurrentViewShell(); if( pTmpViewShell ) { sal_Bool bT2 = pTmpViewShell->GetViewOptions()->IsTest2(); sal_Bool bT6 = pTmpViewShell->GetViewOptions()->IsTest6(); if( bT2 || bT6 ) { if( bT2 ) pTextRanger[ 0 ]->SetFlag7( sal_True ); else pTextRanger[ 0 ]->SetFlag6( sal_True ); } } #endif nPntCnt += pTextRanger[ 0 ]->GetPointCount(); while( nPntCnt > POLY_MAX && nObjCnt > POLY_MIN ) { nPntCnt -= pTextRanger[ --nObjCnt ]->GetPointCount(); delete pTextRanger[ nObjCnt ]; } } else if( nPos ) { const SdrObject* pTmpObj = pSdrObj[ nPos ]; TextRanger* pTmpRanger = pTextRanger[ nPos ]; memmove( (SdrObject**)pSdrObj + 1, pSdrObj, nPos * sizeof( SdrObject* ) ); memmove( pTextRanger + 1, pTextRanger, nPos * sizeof( TextRanger* ) ); pSdrObj[ 0 ] = pTmpObj; pTextRanger[ 0 ] = pTmpRanger; } SWRECTFN( pFrm ) long nTmpTop = (rLine.*fnRect->fnGetTop)(); // fnGetBottom is top + height long nTmpBottom = (rLine.*fnRect->fnGetBottom)(); Range aRange( Min( nTmpTop, nTmpBottom ), Max( nTmpTop, nTmpBottom ) ); LongDqPtr pTmp = pTextRanger[ 0 ]->GetTextRanges( aRange ); MSHORT nCount; if( 0 != ( nCount = pTmp->size() ) ) { MSHORT nIdx = 0; while( nIdx < nCount && (*pTmp)[ nIdx ] < nXPos ) ++nIdx; sal_Bool bOdd = (nIdx % 2) ? sal_True : sal_False; sal_Bool bSet = sal_True; if( bOdd ) --nIdx; // innerhalb eines Intervalls else if( ! bRight && ( nIdx >= nCount || (*pTmp)[ nIdx ] != nXPos ) ) { if( nIdx ) nIdx -= 2; // ein Intervall nach links gehen else bSet = sal_False; // vor dem erstem Intervall } if( bSet && nIdx < nCount ) { (aRet.*fnRect->fnSetTopAndHeight)( (rLine.*fnRect->fnGetTop)(), (rLine.*fnRect->fnGetHeight)() ); (aRet.*fnRect->fnSetLeft)( (*pTmp)[ nIdx ] ); (aRet.*fnRect->fnSetRight)( (*pTmp)[ nIdx + 1 ] + 1 ); } } return aRet; } /************************************************************************* * SwContourCache::ShowContour() * zeichnet die PolyPolygone des Caches zu Debugzwecken. *************************************************************************/ #if OSL_DEBUG_LEVEL > 1 void SwContourCache::ShowContour( OutputDevice* pOut, const SdrObject* pObj, const Color& rClosedColor, const Color& rOpenColor ) { MSHORT nPos = 0; // Suche im Cache ... while( nPos < POLY_CNT && pObj != pSdrObj[ nPos ] ) ++nPos; if( POLY_CNT != nPos ) { const PolyPolygon* pPol = pTextRanger[ nPos ]->GetLinePolygon(); if( !pPol ) pPol = &(pTextRanger[ nPos ]->GetPolyPolygon()); for( MSHORT i = 0; i < pPol->Count(); ++i ) { pOut->SetLineColor( rOpenColor ); const Polygon& rPol = (*pPol)[ i ]; MSHORT nCount = rPol.GetSize(); if( nCount > 1 && rPol[ 0 ] == rPol[ nCount - 1 ] ) pOut->SetLineColor( rClosedColor ); pOut->DrawPolygon( rPol ); } static KSHORT nRadius = 0; if( nRadius ) { KSHORT nHalf = nRadius / 2; Size aSz( nRadius, nRadius ); for( MSHORT i = 0; i < pPol->Count(); ++i ) { const Polygon& rPol = (*pPol)[ i ]; MSHORT nCount = rPol.GetSize(); for( MSHORT k = 0; k < nCount; ++k ) { Point aPt( rPol[ k ] ); aPt.X() -= nHalf; aPt.Y() -= nHalf; Rectangle aTmp( aPt, aSz ); pOut->DrawEllipse( aTmp ); } } } } } #endif /************************************************************************* * SwTxtFly::ShowContour() * zeichnet die PolyPolygone des Caches zu Debugzwecken. *************************************************************************/ #if OSL_DEBUG_LEVEL > 1 void SwTxtFly::ShowContour( OutputDevice* pOut ) { MSHORT nFlyCount; if( bOn && ( 0 != ( nFlyCount = static_cast(GetAnchoredObjList()->size() ) ) ) ) { Color aRedColor( COL_LIGHTRED ); Color aGreenColor( COL_LIGHTGREEN ); Color aSaveColor( pOut->GetLineColor() ); for( MSHORT j = 0; j < nFlyCount; ++j ) { const SwAnchoredObject* pObj = (*mpAnchoredObjList)[ j ]; if( !pObj->GetFrmFmt().GetSurround().IsContour() ) { Rectangle aRect = pObj->GetObjRectWithSpaces().SVRect(); pOut->DrawRect( aRect ); continue; } pContourCache->ShowContour( pOut, pObj->GetDrawObj(), aRedColor, aGreenColor ); } pOut->SetLineColor( aSaveColor ); } } #endif /************************************************************************* * SwTxtFly::ForEach() * * sucht nach dem ersten Objekt, welches mit dem Rechteck ueberlappt * *************************************************************************/ sal_Bool SwTxtFly::ForEach( const SwRect &rRect, SwRect* pRect, sal_Bool bAvoid ) const { SWAP_IF_SWAPPED( pCurrFrm ) sal_Bool bRet = sal_False; // #i68520# SwAnchoredObjList::size_type nCount( bOn ? GetAnchoredObjList()->size() : 0 ); if ( bOn && nCount > 0 ) { for( SwAnchoredObjList::size_type i = 0; i < nCount; ++i ) { // #i68520# const SwAnchoredObject* pAnchoredObj = (*mpAnchoredObjList)[i]; SwRect aRect( pAnchoredObj->GetObjRectWithSpaces() ); // Optimierung SWRECTFN( pCurrFrm ) if( (aRect.*fnRect->fnGetLeft)() > (rRect.*fnRect->fnGetRight)() ) break; // #i68520# if ( mpCurrAnchoredObj != pAnchoredObj && aRect.IsOver( rRect ) ) { // #i68520# const SwFmt* pFmt( &(pAnchoredObj->GetFrmFmt()) ); const SwFmtSurround &rSur = pFmt->GetSurround(); if( bAvoid ) { // Wenn der Text drunter durchlaeuft, bleibt die // Formatierung unbeeinflusst. Im LineIter::DrawText() // muessen "nur" geschickt die ClippingRegions gesetzt werden ... const SwFmtAnchor& rAnchor = pFmt->GetAnchor(); if( ( SURROUND_THROUGHT == rSur.GetSurround() && ( !rSur.IsAnchorOnly() || // #i68520# GetMaster() == pAnchoredObj->GetAnchorFrm() || ((FLY_AT_PARA != rAnchor.GetAnchorId()) && (FLY_AT_CHAR != rAnchor.GetAnchorId())) ) ) || aRect.Top() == WEIT_WECH ) continue; } // #i58642# // Compare instead of with the anchor // frame of the anchored object, because a follow frame have // to ignore the anchored objects of its master frame. // Note: Anchored objects are always registered at the master // frame, exception are as-character anchored objects, // but these aren't handled here. // #i68520# if ( mbIgnoreCurrentFrame && GetMaster() == pAnchoredObj->GetAnchorFrm() ) continue; if( pRect ) { // #i68520# SwRect aFly = AnchoredObjToRect( pAnchoredObj, rRect ); if( aFly.IsEmpty() || !aFly.IsOver( rRect ) ) continue; if( !bRet || ( ( !pCurrFrm->IsRightToLeft() && ( (aFly.*fnRect->fnGetLeft)() < (pRect->*fnRect->fnGetLeft)() ) ) || ( pCurrFrm->IsRightToLeft() && ( (aFly.*fnRect->fnGetRight)() > (pRect->*fnRect->fnGetRight)() ) ) ) ) *pRect = aFly; if( rSur.IsContour() ) { bRet = sal_True; continue; } } bRet = sal_True; break; } } } UNDO_SWAP( pCurrFrm ) return bRet; } /************************************************************************* * SwTxtFly::GetPos() * * liefert die Position im sorted Array zurueck *************************************************************************/ // #i68520# SwAnchoredObjList::size_type SwTxtFly::GetPos( const SwAnchoredObject* pAnchoredObj ) const { SwAnchoredObjList::size_type nCount = GetAnchoredObjList()->size(); SwAnchoredObjList::size_type nRet = 0; while ( nRet < nCount && pAnchoredObj != (*mpAnchoredObjList)[ nRet ] ) ++nRet; return nRet; } /************************************************************************* * SwTxtFly::CalcRightMargin() * * pObj ist das Object, der uns gerade ueberlappt. * pCurrFrm ist der aktuelle Textframe, der ueberlappt wird. * Der rechte Rand ist der rechte Rand oder * er wird durch das naechste Object, welches in die Zeile ragt, bestimmt. *************************************************************************/ // #i68520# void SwTxtFly::CalcRightMargin( SwRect &rFly, SwAnchoredObjList::size_type nFlyPos, const SwRect &rLine ) const { // Normalerweise ist der rechte Rand der rechte Rand der Printarea. OSL_ENSURE( ! pCurrFrm->IsVertical() || ! pCurrFrm->IsSwapped(), "SwTxtFly::CalcRightMargin with swapped frame" ); SWRECTFN( pCurrFrm ) // #118796# - correct determination of right of printing area SwTwips nRight = (pCurrFrm->*fnRect->fnGetPrtRight)(); SwTwips nFlyRight = (rFly.*fnRect->fnGetRight)(); SwRect aLine( rLine ); (aLine.*fnRect->fnSetRight)( nRight ); (aLine.*fnRect->fnSetLeft)( (rFly.*fnRect->fnGetLeft)() ); // Es koennte aber sein, dass in die gleiche Zeile noch ein anderes // Object hineinragt, welches _ueber_ uns liegt. // Wunder der Technik: Flys mit Durchlauf sind fuer die darunterliegenden // unsichtbar, das heisst, dass sie bei der Berechnung der Raender // anderer Flys ebenfalls nicht auffallen. // 3301: pNext->Frm().IsOver( rLine ) ist noetig // #i68520# SwSurround eSurroundForTextWrap; sal_Bool bStop = sal_False; // #i68520# SwAnchoredObjList::size_type nPos = 0; // #i68520# while( nPos < mpAnchoredObjList->size() && !bStop ) { if( nPos == nFlyPos ) { ++nPos; continue; } // #i68520# const SwAnchoredObject* pNext = (*mpAnchoredObjList)[ nPos++ ]; if ( pNext == mpCurrAnchoredObj ) continue; eSurroundForTextWrap = _GetSurroundForTextWrap( pNext ); if( SURROUND_THROUGHT == eSurroundForTextWrap ) continue; const SwRect aTmp( SwContourCache::CalcBoundRect ( pNext, aLine, pCurrFrm, nFlyRight, sal_True ) ); SwTwips nTmpRight = (aTmp.*fnRect->fnGetRight)(); // Optimierung: // In nNextTop wird notiert, an welcher Y-Positon mit Aenderung der // Rahmenverhaeltnisse gerechnet werden muss. Dies dient dazu, dass, // obwohl nur die Rahmen in der aktuellen Zeilenhoehe betrachtet werden, // bei Rahmen ohne Umlauf die Zeilenhoehe so erhoeht wird, dass mit einer // einzigen Zeile die Unterkante das Rahmens oder ggf. die Oberkante des // naechsten Rahmen erreicht wird. // Insbesondere bei HTML-Dokumenten kommen oft (Dummy-)Absaetze in einer // 2-Pt.-Schrift vor, bis diese einem groesseren Rahmen ausgewichen sind, // erforderte es frueher Unmengen von Leerzeilen. const long nTmpTop = (aTmp.*fnRect->fnGetTop)(); if( (*fnRect->fnYDiff)( nTmpTop, (aLine.*fnRect->fnGetTop)() ) > 0 ) { if( (*fnRect->fnYDiff)( nNextTop, nTmpTop ) > 0 ) SetNextTop( nTmpTop ); // Die Oberkante des "naechsten" Rahmens } else if( ! (aTmp.*fnRect->fnGetWidth)() ) // Typisch fuer Objekte mit Konturumlauf { // Bei Objekten mit Konturumlauf, die vor der aktuellen Zeile beginnen // und hinter ihr enden, trotzdem aber nicht mit ihr ueberlappen, // muss die Optimierung ausgeschaltet werden, denn bereits in der // naechsten Zeile kann sich dies aendern. if( ! (aTmp.*fnRect->fnGetHeight)() || (*fnRect->fnYDiff)( (aTmp.*fnRect->fnGetBottom)(), (aLine.*fnRect->fnGetTop)() ) > 0 ) SetNextTop( 0 ); } if( aTmp.IsOver( aLine ) && nTmpRight > nFlyRight ) { nFlyRight = nTmpRight; if( SURROUND_RIGHT == eSurroundForTextWrap || SURROUND_PARALLEL == eSurroundForTextWrap ) { // der FlyFrm wird ueberstimmt. if( nRight > nFlyRight ) nRight = nFlyRight; bStop = sal_True; } } } (rFly.*fnRect->fnSetRight)( nRight ); } /************************************************************************* * SwTxtFly::CalcLeftMargin() * * pFly ist der FlyFrm, der uns gerade ueberlappt. * pCurrFrm ist der aktuelle Textframe, der ueberlappt wird. * Der linke Rand ist der linke Rand der aktuellen PrintArea oder * er wird durch den vorigen FlyFrm, der in die Zeile ragt, bestimmt. *************************************************************************/ // #i68520# void SwTxtFly::CalcLeftMargin( SwRect &rFly, SwAnchoredObjList::size_type nFlyPos, const SwRect &rLine ) const { OSL_ENSURE( ! pCurrFrm->IsVertical() || ! pCurrFrm->IsSwapped(), "SwTxtFly::CalcLeftMargin with swapped frame" ); SWRECTFN( pCurrFrm ) // #118796# - correct determination of left of printing area SwTwips nLeft = (pCurrFrm->*fnRect->fnGetPrtLeft)(); const SwTwips nFlyLeft = (rFly.*fnRect->fnGetLeft)(); if( nLeft > nFlyLeft ) nLeft = rFly.Left(); SwRect aLine( rLine ); (aLine.*fnRect->fnSetLeft)( nLeft ); // Es koennte aber sein, dass in die gleiche Zeile noch ein anderes // Object hineinragt, welches _ueber_ uns liegt. // Wunder der Technik: Flys mit Durchlauf sind fuer die darunterliegenden // unsichtbar, das heisst, dass sie bei der Berechnung der Raender // anderer Flys ebenfalls nicht auffallen. // 3301: pNext->Frm().IsOver( rLine ) ist noetig // #i68520# SwAnchoredObjList::size_type nMyPos = nFlyPos; while( ++nFlyPos < mpAnchoredObjList->size() ) { // #i68520# const SwAnchoredObject* pNext = (*mpAnchoredObjList)[ nFlyPos ]; const SwRect aTmp( pNext->GetObjRectWithSpaces() ); if( (aTmp.*fnRect->fnGetLeft)() >= nFlyLeft ) break; } while( nFlyPos ) { if( --nFlyPos == nMyPos ) continue; // #i68520# const SwAnchoredObject* pNext = (*mpAnchoredObjList)[ nFlyPos ]; if( pNext == mpCurrAnchoredObj ) continue; SwSurround eSurroundForTextWrap = _GetSurroundForTextWrap( pNext ); if( SURROUND_THROUGHT == eSurroundForTextWrap ) continue; const SwRect aTmp( SwContourCache::CalcBoundRect ( pNext, aLine, pCurrFrm, nFlyLeft, sal_False ) ); if( (aTmp.*fnRect->fnGetLeft)() < nFlyLeft && aTmp.IsOver( aLine ) ) { // #118796# - no '+1', because <..fnGetRight> // returns the correct value. SwTwips nTmpRight = (aTmp.*fnRect->fnGetRight)(); if ( nLeft <= nTmpRight ) nLeft = nTmpRight; break; } } (rFly.*fnRect->fnSetLeft)( nLeft ); } /************************************************************************* * SwTxtFly::FlyToRect() * * IN: dokumentglobal (rRect) * OUT: dokumentglobal (return-Wert) * Liefert zu einem SwFlyFrm das von ihm in Anspruch genommene Rechteck * unter Beruecksichtigung der eingestellten Attribute fuer den Abstand * zum Text zurueck. *************************************************************************/ // #i68520# SwRect SwTxtFly::AnchoredObjToRect( const SwAnchoredObject* pAnchoredObj, const SwRect &rLine ) const { SWRECTFN( pCurrFrm ) const long nXPos = pCurrFrm->IsRightToLeft() ? rLine.Right() : (rLine.*fnRect->fnGetLeft)(); SwRect aFly = mbIgnoreContour ? pAnchoredObj->GetObjRectWithSpaces() : SwContourCache::CalcBoundRect( pAnchoredObj, rLine, pCurrFrm, nXPos, ! pCurrFrm->IsRightToLeft() ); if( !aFly.Width() ) return aFly; SetNextTop( (aFly.*fnRect->fnGetBottom)() ); // Damit die Zeile ggf. bis zur Unterkante // des Rahmens waechst. SwAnchoredObjList::size_type nFlyPos = GetPos( pAnchoredObj ); // Bei LEFT und RIGHT vergroessern wir das Rechteck. // Hier gibt es einige Probleme, wenn mehrere Frames zu sehen sind. // Zur Zeit wird nur der einfachste Fall angenommen: // LEFT bedeutet, dass der Text links vom Frame fliessen soll, // d.h. der Frame blaeht sich bis zum rechten Rand der Printarea // oder bis zum naechsten Frame auf. // Bei RIGHT ist es umgekehrt. // Ansonsten wird immer der eingestellte Abstand zwischen Text // und Frame aufaddiert. switch( _GetSurroundForTextWrap( pAnchoredObj ) ) { case SURROUND_LEFT : { CalcRightMargin( aFly, nFlyPos, rLine ); break; } case SURROUND_RIGHT : { CalcLeftMargin( aFly, nFlyPos, rLine ); break; } case SURROUND_NONE : { CalcRightMargin( aFly, nFlyPos, rLine ); CalcLeftMargin( aFly, nFlyPos, rLine ); break; } default: break; } return aFly; } // #i68520# // new method <_GetSurroundForTextWrap(..)> replaces methods // and /************************************************************************* * SwTxtFly::CalcSmart() * * CalcSmart() liefert die Umlaufform zurueck. * * Auf beiden Seiten ist weniger als 2 cm Platz fuer den Text * => kein Umlauf ( SURROUND_NONE ) * Auf genau einer Seite ist mehr als 2 cm Platz * => Umlauf auf dieser Seite ( SURROUND_LEFT / SURROUND_RIGHT ) * Auf beiden Seiten ist mehr als 2 cm Platz, das Objekt ist breiter als 1,5 cm * => Umlauf auf der breiteren Seite ( SURROUND_LEFT / SURROUND_RIGHT ) * Auf beiden Seiten ist mehr als 2 cm Platz, das Objekt ist schmaler als 1,5 cm * => beidseitiger Umlauf ( SURROUND_PARALLEL ) * *************************************************************************/ // Umfluss nur auf Seiten mit mindestens 2 cm Platz fuer den Text #define TEXT_MIN 1134 // Beidseitiger Umfluss bis zu einer Rahmenbreite von maximal 1,5 cm #define FRAME_MAX 850 SwSurround SwTxtFly::_GetSurroundForTextWrap( const SwAnchoredObject* pAnchoredObj ) const { const SwFrmFmt* pFmt = &(pAnchoredObj->GetFrmFmt()); const SwFmtSurround &rFlyFmt = pFmt->GetSurround(); SwSurround eSurroundForTextWrap = rFlyFmt.GetSurround(); if( rFlyFmt.IsAnchorOnly() && pAnchoredObj->GetAnchorFrm() != GetMaster() ) { const SwFmtAnchor& rAnchor = pFmt->GetAnchor(); if ((FLY_AT_PARA == rAnchor.GetAnchorId()) || (FLY_AT_CHAR == rAnchor.GetAnchorId())) { return SURROUND_NONE; } } // Beim Durchlauf und Nowrap wird smart ignoriert. if( SURROUND_THROUGHT == eSurroundForTextWrap || SURROUND_NONE == eSurroundForTextWrap ) return eSurroundForTextWrap; // left is left and right is right if ( pCurrFrm->IsRightToLeft() ) { if ( SURROUND_LEFT == eSurroundForTextWrap ) eSurroundForTextWrap = SURROUND_RIGHT; else if ( SURROUND_RIGHT == eSurroundForTextWrap ) eSurroundForTextWrap = SURROUND_LEFT; } // "idealer Seitenumlauf": if ( SURROUND_IDEAL == eSurroundForTextWrap ) { SWRECTFN( pCurrFrm ) const long nCurrLeft = (pCurrFrm->*fnRect->fnGetPrtLeft)(); const long nCurrRight = (pCurrFrm->*fnRect->fnGetPrtRight)(); const SwRect aRect( pAnchoredObj->GetObjRectWithSpaces() ); long nFlyLeft = (aRect.*fnRect->fnGetLeft)(); long nFlyRight = (aRect.*fnRect->fnGetRight)(); if ( nFlyRight < nCurrLeft || nFlyLeft > nCurrRight ) eSurroundForTextWrap = SURROUND_PARALLEL; else { long nLeft = nFlyLeft - nCurrLeft; long nRight = nCurrRight - nFlyRight; if( nFlyRight - nFlyLeft > FRAME_MAX ) { if( nLeft < nRight ) nLeft = 0; else nRight = 0; } if( nLeft < TEXT_MIN ) nLeft = 0; if( nRight < TEXT_MIN ) nRight = 0; if( nLeft ) eSurroundForTextWrap = nRight ? SURROUND_PARALLEL : SURROUND_LEFT; else eSurroundForTextWrap = nRight ? SURROUND_RIGHT: SURROUND_NONE; } } return eSurroundForTextWrap; } /************************************************************************* * SwTxtFly::IsAnyFrm( SwRect ) * * IN: dokumentglobal * * dient zum Abschalten des SwTxtFly, wenn keine Objekte ueberlappen (Relax) * *************************************************************************/ sal_Bool SwTxtFly::IsAnyFrm( const SwRect &rLine ) const { SWAP_IF_SWAPPED( pCurrFrm ) OSL_ENSURE( bOn, "IsAnyFrm: Why?" ); const sal_Bool bRet = ForEach( rLine, NULL, sal_False ); UNDO_SWAP( pCurrFrm ) return bRet; } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */