blob: 49e01df88e52023f11a83276387131407b551634 [file] [log] [blame]
/**************************************************************
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*************************************************************/
// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_sw.hxx"
#include <hintids.hxx>
#include <hints.hxx>
#include <svl/ctloptions.hxx>
#include <sfx2/printer.hxx>
#include <sfx2/sfxuno.hxx>
#include <editeng/langitem.hxx>
#include <editeng/lspcitem.hxx>
#include <editeng/lrspitem.hxx>
#include <editeng/ulspitem.hxx>
#include <editeng/brshitem.hxx>
#include <editeng/pgrditem.hxx>
#include <swmodule.hxx>
#include <SwSmartTagMgr.hxx>
#include <doc.hxx> // GetDoc()
#include "rootfrm.hxx"
#include <pagefrm.hxx> // InvalidateSpelling
#include <rootfrm.hxx>
#include <viewsh.hxx> // ViewShell
#include <pam.hxx> // SwPosition
#include <ndtxt.hxx> // SwTxtNode
#include <txtatr.hxx>
#include <paratr.hxx>
#include <viewopt.hxx>
#include <dflyobj.hxx>
#include <flyfrm.hxx>
#include <tabfrm.hxx>
#include <frmtool.hxx>
#include <pagedesc.hxx> // SwPageDesc
#include <tgrditem.hxx>
#include <dbg_lay.hxx>
#include <fmtfld.hxx>
#include <fmtftn.hxx>
#include <txtfld.hxx>
#include <txtftn.hxx>
#include <charatr.hxx>
#include <ftninfo.hxx>
#include <fmtline.hxx>
#include <txtfrm.hxx> // SwTxtFrm
#include <sectfrm.hxx> // SwSectFrm
#include <txtcfg.hxx> // DBG_LOOP
#include <itrform2.hxx> // Iteratoren
#include <widorp.hxx> // SwFrmBreak
#include <txtcache.hxx>
#include <fntcache.hxx> // GetLineSpace benutzt pLastFont
#include <SwGrammarMarkUp.hxx>
#include <lineinfo.hxx>
#include <SwPortionHandler.hxx>
#include <dcontact.hxx>
#include <sortedobjs.hxx>
#include <txtflcnt.hxx> // SwTxtFlyCnt
#include <fmtflcnt.hxx> // SwFmtFlyCnt
#include <fmtcntnt.hxx> // SwFmtCntnt
#include <numrule.hxx>
#include <swtable.hxx>
#include <fldupde.hxx>
#include <IGrammarContact.hxx>
#include <switerator.hxx>
#if OSL_DEBUG_LEVEL > 1
#include <txtpaint.hxx> // DbgRect
extern const sal_Char *GetPrepName( const enum PrepareHint ePrep );
#endif
TYPEINIT1( SwTxtFrm, SwCntntFrm );
// Switches width and height of the text frame
void SwTxtFrm::SwapWidthAndHeight()
{
if ( ! bIsSwapped )
{
const long nPrtOfstX = Prt().Pos().X();
Prt().Pos().X() = Prt().Pos().Y();
//Badaa: 2008-04-18 * Support for Classical Mongolian Script (SCMS) joint with Jiayanmin
if( IsVertLR() )
Prt().Pos().Y() = nPrtOfstX;
else
Prt().Pos().Y() = Frm().Width() - ( nPrtOfstX + Prt().Width() );
}
else
{
const long nPrtOfstY = Prt().Pos().Y();
Prt().Pos().Y() = Prt().Pos().X();
//Badaa: 2008-04-18 * Support for Classical Mongolian Script (SCMS) joint with Jiayanmin
if( IsVertLR() )
Prt().Pos().X() = nPrtOfstY;
else
Prt().Pos().X() = Frm().Height() - ( nPrtOfstY + Prt().Height() );
}
const long nFrmWidth = Frm().Width();
Frm().Width( Frm().Height() );
Frm().Height( nFrmWidth );
const long nPrtWidth = Prt().Width();
Prt().Width( Prt().Height() );
Prt().Height( nPrtWidth );
bIsSwapped = ! bIsSwapped;
}
// Calculates the coordinates of a rectangle when switching from
// horizontal to vertical layout.
void SwTxtFrm::SwitchHorizontalToVertical( SwRect& rRect ) const
{
// calc offset inside frame
//Badaa: 2008-04-18 * Support for Classical Mongolian Script (SCMS) joint with Jiayanmin
long nOfstX, nOfstY;
if ( IsVertLR() )
{
nOfstX = rRect.Left() - Frm().Left();
nOfstY = rRect.Top() - Frm().Top();
}
else
{
nOfstX = rRect.Left() - Frm().Left();
nOfstY = rRect.Top() + rRect.Height() - Frm().Top();
}
const long nWidth = rRect.Width();
const long nHeight = rRect.Height();
//Badaa: 2008-04-18 * Support for Classical Mongolian Script (SCMS) joint with Jiayanmin
if ( IsVertLR() )
rRect.Left(Frm().Left() + nOfstY);
else
{
if ( bIsSwapped )
rRect.Left( Frm().Left() + Frm().Height() - nOfstY );
else
// frame is rotated
rRect.Left( Frm().Left() + Frm().Width() - nOfstY );
}
rRect.Top( Frm().Top() + nOfstX );
rRect.Width( nHeight );
rRect.Height( nWidth );
}
// Calculates the coordinates of a point when switching from
// horizontal to vertical layout.
void SwTxtFrm::SwitchHorizontalToVertical( Point& rPoint ) const
{
// calc offset inside frame
const long nOfstX = rPoint.X() - Frm().Left();
const long nOfstY = rPoint.Y() - Frm().Top();
//Badaa: 2008-04-18 * Support for Classical Mongolian Script (SCMS) joint with Jiayanmin
if ( IsVertLR() )
rPoint.X() = Frm().Left() + nOfstY;
else
{
if ( bIsSwapped )
rPoint.X() = Frm().Left() + Frm().Height() - nOfstY;
else
// calc rotated coords
rPoint.X() = Frm().Left() + Frm().Width() - nOfstY;
}
rPoint.Y() = Frm().Top() + nOfstX;
}
// Calculates the a limit value when switching from
// horizontal to vertical layout.
long SwTxtFrm::SwitchHorizontalToVertical( long nLimit ) const
{
Point aTmp( 0, nLimit );
SwitchHorizontalToVertical( aTmp );
return aTmp.X();
}
// Calculates the coordinates of a rectangle when switching from
// vertical to horizontal layout.
void SwTxtFrm::SwitchVerticalToHorizontal( SwRect& rRect ) const
{
long nOfstX;
// calc offset inside frame
//Badaa: 2008-04-18 * Support for Classical Mongolian Script (SCMS) joint with Jiayanmin
if ( IsVertLR() )
nOfstX = rRect.Left() - Frm().Left();
else
{
if ( bIsSwapped )
nOfstX = Frm().Left() + Frm().Height() - ( rRect.Left() + rRect.Width() );
else
nOfstX = Frm().Left() + Frm().Width() - ( rRect.Left() + rRect.Width() );
}
const long nOfstY = rRect.Top() - Frm().Top();
const long nWidth = rRect.Height();
const long nHeight = rRect.Width();
// calc rotated coords
rRect.Left( Frm().Left() + nOfstY );
rRect.Top( Frm().Top() + nOfstX );
rRect.Width( nWidth );
rRect.Height( nHeight );
}
// Calculates the coordinates of a point when switching from
// vertical to horizontal layout.
void SwTxtFrm::SwitchVerticalToHorizontal( Point& rPoint ) const
{
long nOfstX;
// calc offset inside frame
//Badaa: 2008-04-18 * Support for Classical Mongolian Script (SCMS) joint with Jiayanmin
if ( IsVertLR() )
nOfstX = rPoint.X() - Frm().Left();
else
{
if ( bIsSwapped )
nOfstX = Frm().Left() + Frm().Height() - rPoint.X();
else
nOfstX = Frm().Left() + Frm().Width() - rPoint.X();
}
const long nOfstY = rPoint.Y() - Frm().Top();
// calc rotated coords
rPoint.X() = Frm().Left() + nOfstY;
rPoint.Y() = Frm().Top() + nOfstX;
}
// Calculates the a limit value when switching from
// vertical to horizontal layout.
long SwTxtFrm::SwitchVerticalToHorizontal( long nLimit ) const
{
Point aTmp( nLimit, 0 );
SwitchVerticalToHorizontal( aTmp );
return aTmp.Y();
}
SwFrmSwapper::SwFrmSwapper( const SwTxtFrm* pTxtFrm, sal_Bool bSwapIfNotSwapped )
: pFrm( pTxtFrm ), bUndo( sal_False )
{
if ( pFrm->IsVertical() &&
( ( bSwapIfNotSwapped && ! pFrm->IsSwapped() ) ||
( ! bSwapIfNotSwapped && pFrm->IsSwapped() ) ) )
{
bUndo = sal_True;
((SwTxtFrm*)pFrm)->SwapWidthAndHeight();
}
}
SwFrmSwapper::~SwFrmSwapper()
{
if ( bUndo )
((SwTxtFrm*)pFrm)->SwapWidthAndHeight();
}
void SwTxtFrm::SwitchLTRtoRTL( SwRect& rRect ) const
{
SWAP_IF_NOT_SWAPPED( this )
long nWidth = rRect.Width();
rRect.Left( 2 * ( Frm().Left() + Prt().Left() ) +
Prt().Width() - rRect.Right() - 1 );
rRect.Width( nWidth );
UNDO_SWAP( this )
}
void SwTxtFrm::SwitchLTRtoRTL( Point& rPoint ) const
{
SWAP_IF_NOT_SWAPPED( this )
rPoint.X() = 2 * ( Frm().Left() + Prt().Left() ) + Prt().Width() - rPoint.X() - 1;
UNDO_SWAP( this )
}
SwLayoutModeModifier::SwLayoutModeModifier( const OutputDevice& rOutp ) :
rOut( rOutp ), nOldLayoutMode( rOutp.GetLayoutMode() )
{
}
SwLayoutModeModifier::~SwLayoutModeModifier()
{
((OutputDevice&)rOut).SetLayoutMode( nOldLayoutMode );
}
void SwLayoutModeModifier::Modify( sal_Bool bChgToRTL )
{
((OutputDevice&)rOut).SetLayoutMode( bChgToRTL ?
TEXT_LAYOUT_BIDI_STRONG | TEXT_LAYOUT_BIDI_RTL :
TEXT_LAYOUT_BIDI_STRONG );
}
void SwLayoutModeModifier::SetAuto()
{
const sal_uLong nNewLayoutMode = nOldLayoutMode & ~TEXT_LAYOUT_BIDI_STRONG;
((OutputDevice&)rOut).SetLayoutMode( nNewLayoutMode );
}
SwDigitModeModifier::SwDigitModeModifier( const OutputDevice& rOutp, LanguageType eCurLang ) :
rOut( rOutp ), nOldLanguageType( rOutp.GetDigitLanguage() )
{
LanguageType eLang = eCurLang;
const SvtCTLOptions::TextNumerals nTextNumerals = SW_MOD()->GetCTLOptions().GetCTLTextNumerals();
if ( SvtCTLOptions::NUMERALS_HINDI == nTextNumerals )
eLang = LANGUAGE_ARABIC_SAUDI_ARABIA;
else if ( SvtCTLOptions::NUMERALS_ARABIC == nTextNumerals )
eLang = LANGUAGE_ENGLISH;
else if ( SvtCTLOptions::NUMERALS_SYSTEM == nTextNumerals )
eLang = (LanguageType)::GetAppLanguage();
((OutputDevice&)rOut).SetDigitLanguage( eLang );
}
SwDigitModeModifier::~SwDigitModeModifier()
{
((OutputDevice&)rOut).SetDigitLanguage( nOldLanguageType );
}
/*************************************************************************
* SwTxtFrm::Init()
*************************************************************************/
void SwTxtFrm::Init()
{
ASSERT( !IsLocked(), "+SwTxtFrm::Init: this ist locked." );
if( !IsLocked() )
{
ClearPara();
ResetBlinkPor();
//Die Flags direkt setzen um ResetPreps und damit ein unnuetzes GetPara
//einzusparen.
// Nicht bOrphan, bLocked oder bWait auf sal_False setzen !
// bOrphan = bFlag7 = bFlag8 = sal_False;
}
}
/*************************************************************************
|* SwTxtFrm::CTORen/DTOR
|*************************************************************************/
void SwTxtFrm::InitCtor()
{
nCacheIdx = MSHRT_MAX;
nOfst = 0;
nAllLines = 0;
nThisLines = 0;
mnFlyAnchorOfst = 0;
mnFlyAnchorOfstNoWrap = 0;
mnFtnLine = 0;
// OD 2004-03-17 #i11860#
mnHeightOfLastLine = 0;
// --> OD 2008-01-31 #newlistlevelattrs#
mnAdditionalFirstLineOffset = 0;
// <--
nType = FRMC_TXT;
bLocked = bFormatted = bWidow = bUndersized = bJustWidow =
bEmpty = bInFtnConnect = bFtn = bRepaint = bBlinkPor =
bFieldFollow = bHasAnimation = bIsSwapped = sal_False;
// OD 14.03.2003 #i11760#
mbFollowFormatAllowed = sal_True;
}
/*************************************************************************
* SwTxtFrm::SwTxtFrm()
*************************************************************************/
SwTxtFrm::SwTxtFrm(SwTxtNode * const pNode, SwFrm* pSib )
: SwCntntFrm( pNode, pSib )
{
InitCtor();
}
/*************************************************************************
* SwTxtFrm::~SwTxtFrm()
*************************************************************************/
SwTxtFrm::~SwTxtFrm()
{
// Remove associated SwParaPortion from pTxtCache
ClearPara();
}
const XubString& SwTxtFrm::GetTxt() const
{
return GetTxtNode()->GetTxt();
}
void SwTxtFrm::ResetPreps()
{
if ( GetCacheIdx() != MSHRT_MAX )
{
SwParaPortion *pPara;
if( 0 != (pPara = GetPara()) )
pPara->ResetPreps();
}
}
/*************************************************************************
* SwTxtFrm::IsHiddenNow()
*************************************************************************/
sal_Bool SwTxtFrm::IsHiddenNow() const
{
SwFrmSwapper aSwapper( this, sal_True );
if( !Frm().Width() && IsValid() && GetUpper()->IsValid() )
//bei Stackueberlauf (StackHack) invalid!
{
// ASSERT( false, "SwTxtFrm::IsHiddenNow: thin frame" );
return sal_True;
}
const bool bHiddenCharsHidePara = GetTxtNode()->HasHiddenCharAttribute( true );
const bool bHiddenParaField = GetTxtNode()->HasHiddenParaField();
const ViewShell* pVsh = getRootFrm()->GetCurrShell();
if ( pVsh && ( bHiddenCharsHidePara || bHiddenParaField ) )
{
if (
( bHiddenParaField &&
( !pVsh->GetViewOptions()->IsShowHiddenPara() &&
!pVsh->GetViewOptions()->IsFldName() ) ) ||
( bHiddenCharsHidePara &&
!pVsh->GetViewOptions()->IsShowHiddenChar() ) )
{
return sal_True;
}
}
return sal_False;
}
/*************************************************************************
* SwTxtFrm::HideHidden()
*************************************************************************/
// Entfernt die Anhaengsel des Textfrms wenn dieser hidden ist
void SwTxtFrm::HideHidden()
{
ASSERT( !GetFollow() && IsHiddenNow(),
"HideHidden on visible frame of hidden frame has follow" );
const xub_StrLen nEnd = STRING_LEN;
HideFootnotes( GetOfst(), nEnd );
// OD 2004-01-15 #110582#
HideAndShowObjects();
//Die Formatinfos sind jetzt obsolete
ClearPara();
}
/*************************************************************************
* SwTxtFrm::HideFootnotes()
*************************************************************************/
void SwTxtFrm::HideFootnotes( xub_StrLen nStart, xub_StrLen nEnd )
{
const SwpHints *pHints = GetTxtNode()->GetpSwpHints();
if( pHints )
{
const sal_uInt16 nSize = pHints->Count();
SwPageFrm *pPage = 0;
for ( sal_uInt16 i = 0; i < nSize; ++i )
{
const SwTxtAttr *pHt = (*pHints)[i];
if ( pHt->Which() == RES_TXTATR_FTN )
{
const xub_StrLen nIdx = *pHt->GetStart();
if ( nEnd < nIdx )
break;
if( nStart <= nIdx )
{
if( !pPage )
pPage = FindPageFrm();
pPage->RemoveFtn( this, (SwTxtFtn*)pHt );
}
}
}
}
}
// --> OD 2005-03-30 #120729# - hotfix: WW8 documents contain at its end hidden,
// as-character anchored graphics, which are used for a graphic bullet list.
// As long as these graphic bullet list aren't imported, do not hide a
// at-character anchored object, if
// (a) the document is an imported WW8 document -
// checked by checking certain compatibility options -,
// (b) the paragraph is the last content in the document and
// (c) the anchor character is an as-character anchored graphic.
bool lcl_HideObj( const SwTxtFrm& _rFrm,
const RndStdIds _eAnchorType,
const xub_StrLen _nObjAnchorPos,
SwAnchoredObject* _pAnchoredObj )
{
bool bRet( true );
if (_eAnchorType == FLY_AT_CHAR)
{
const IDocumentSettingAccess* pIDSA = _rFrm.GetTxtNode()->getIDocumentSettingAccess();
if ( !pIDSA->get(IDocumentSettingAccess::USE_FORMER_TEXT_WRAPPING) &&
!pIDSA->get(IDocumentSettingAccess::OLD_LINE_SPACING) &&
!pIDSA->get(IDocumentSettingAccess::USE_FORMER_OBJECT_POS) &&
pIDSA->get(IDocumentSettingAccess::CONSIDER_WRAP_ON_OBJECT_POSITION) &&
_rFrm.IsInDocBody() && !_rFrm.FindNextCnt() )
{
const xub_Unicode cAnchorChar =
_rFrm.GetTxtNode()->GetTxt().GetChar( _nObjAnchorPos );
if ( cAnchorChar == CH_TXTATR_BREAKWORD )
{
const SwTxtAttr* const pHint(
_rFrm.GetTxtNode()->GetTxtAttrForCharAt(_nObjAnchorPos,
RES_TXTATR_FLYCNT) );
if ( pHint )
{
const SwFrmFmt* pFrmFmt =
static_cast<const SwTxtFlyCnt*>(pHint)->GetFlyCnt().GetFrmFmt();
if ( pFrmFmt->Which() == RES_FLYFRMFMT )
{
SwNodeIndex nCntntIndex = *(pFrmFmt->GetCntnt().GetCntntIdx());
nCntntIndex++;
if ( nCntntIndex.GetNode().IsNoTxtNode() )
{
bRet = false;
// set needed data structure values for object positioning
SWRECTFN( (&_rFrm) );
SwRect aLastCharRect( _rFrm.Frm() );
(aLastCharRect.*fnRect->fnSetWidth)( 1 );
_pAnchoredObj->maLastCharRect = aLastCharRect;
_pAnchoredObj->mnLastTopOfLine = (aLastCharRect.*fnRect->fnGetTop)();
}
}
}
}
}
}
return bRet;
}
// <--
/*************************************************************************
* SwTxtFrm::HideAndShowObjects()
*************************************************************************/
/** method to hide/show objects
OD 2004-01-15 #110582#
method hides respectively shows objects, which are anchored at paragraph,
at/as a character of the paragraph, corresponding to the paragraph and
paragraph portion visibility.
- is called from HideHidden() - should hide objects in hidden paragraphs and
- from _Format() - should hide/show objects in partly visible paragraphs
@author OD
*/
void SwTxtFrm::HideAndShowObjects()
{
if ( GetDrawObjs() )
{
if ( IsHiddenNow() )
{
// complete paragraph is hidden. Thus, hide all objects
for ( sal_uInt32 i = 0; i < GetDrawObjs()->Count(); ++i )
{
SdrObject* pObj = (*GetDrawObjs())[i]->DrawObj();
SwContact* pContact = static_cast<SwContact*>(pObj->GetUserCall());
// --> OD 2005-03-30 #120729# - hotfix: do not hide object
// under certain conditions
const RndStdIds eAnchorType( pContact->GetAnchorId() );
const xub_StrLen nObjAnchorPos = pContact->GetCntntAnchorIndex().GetIndex();
if ((eAnchorType != FLY_AT_CHAR) ||
lcl_HideObj( *this, eAnchorType, nObjAnchorPos,
(*GetDrawObjs())[i] ))
{
pContact->MoveObjToInvisibleLayer( pObj );
}
// <--
}
}
else
{
// paragraph is visible, but can contain hidden text portion.
// first we check if objects are allowed to be hidden:
const SwTxtNode& rNode = *GetTxtNode();
const ViewShell* pVsh = getRootFrm()->GetCurrShell();
const bool bShouldBeHidden = !pVsh || !pVsh->GetWin() ||
!pVsh->GetViewOptions()->IsShowHiddenChar();
// Thus, show all objects, which are anchored at paragraph and
// hide/show objects, which are anchored at/as character, according
// to the visibility of the anchor character.
for ( sal_uInt32 i = 0; i < GetDrawObjs()->Count(); ++i )
{
SdrObject* pObj = (*GetDrawObjs())[i]->DrawObj();
SwContact* pContact = static_cast<SwContact*>(pObj->GetUserCall());
// --> OD 2005-03-30 #120729# - determine anchor type only once
const RndStdIds eAnchorType( pContact->GetAnchorId() );
// <--
if (eAnchorType == FLY_AT_PARA)
{
pContact->MoveObjToVisibleLayer( pObj );
}
else if ((eAnchorType == FLY_AT_CHAR) ||
(eAnchorType == FLY_AS_CHAR))
{
xub_StrLen nHiddenStart;
xub_StrLen nHiddenEnd;
xub_StrLen nObjAnchorPos = pContact->GetCntntAnchorIndex().GetIndex();
SwScriptInfo::GetBoundsOfHiddenRange( rNode, nObjAnchorPos, nHiddenStart, nHiddenEnd, 0 );
// --> OD 2005-03-30 #120729# - hotfix: do not hide object
// under certain conditions
if ( nHiddenStart != STRING_LEN && bShouldBeHidden &&
lcl_HideObj( *this, eAnchorType, nObjAnchorPos, (*GetDrawObjs())[i] ) )
// <--
pContact->MoveObjToInvisibleLayer( pObj );
else
pContact->MoveObjToVisibleLayer( pObj );
}
else
{
ASSERT( false,
"<SwTxtFrm::HideAndShowObjects()> - object not anchored at/inside paragraph!?" );
}
}
}
}
if ( IsFollow() )
{
FindMaster()->HideAndShowObjects();
}
}
/*************************************************************************
* SwTxtFrm::FindBrk()
*
* Liefert die erste Trennmoeglichkeit in der aktuellen Zeile zurueck.
* Die Methode wird in SwTxtFrm::Format() benutzt, um festzustellen, ob
* die Vorgaengerzeile mitformatiert werden muss.
* nFound ist <= nEndLine.
*************************************************************************/
xub_StrLen SwTxtFrm::FindBrk( const XubString &rTxt,
const xub_StrLen nStart,
const xub_StrLen nEnd ) const
{
// --> OD 2009-12-28 #i104291# - applying patch to avoid overflow.
unsigned long nFound = nStart;
const xub_StrLen nEndLine = Min( nEnd, rTxt.Len() );
// Wir ueberlesen erst alle Blanks am Anfang der Zeile (vgl. Bug 2235).
while( nFound <= nEndLine &&
' ' == rTxt.GetChar( static_cast<xub_StrLen>(nFound) ) )
{
nFound++;
}
// Eine knifflige Sache mit den TxtAttr-Dummy-Zeichen (hier "$"):
// "Dr.$Meyer" am Anfang der zweiten Zeile. Dahinter ein Blank eingegeben
// und das Wort rutscht nicht in die erste Zeile, obwohl es ginge.
// Aus diesem Grund nehmen wir das Dummy-Zeichen noch mit.
while( nFound <= nEndLine &&
' ' != rTxt.GetChar( static_cast<xub_StrLen>(nFound) ) )
{
nFound++;
}
return nFound <= STRING_LEN
? static_cast<xub_StrLen>(nFound)
: STRING_LEN;
// <--
}
/*************************************************************************
* SwTxtFrm::IsIdxInside()
*************************************************************************/
sal_Bool SwTxtFrm::IsIdxInside( const xub_StrLen nPos, const xub_StrLen nLen ) const
{
if( GetOfst() > nPos + nLen ) // d.h., der Bereich liegt komplett vor uns.
return sal_False;
if( !GetFollow() ) // der Bereich liegt nicht komplett vor uns,
return sal_True; // nach uns kommt niemand mehr.
const xub_StrLen nMax = GetFollow()->GetOfst();
// der Bereich liegt nicht komplett hinter uns bzw.
// unser Text ist geloescht worden.
if( nMax > nPos || nMax > GetTxt().Len() )
return sal_True;
// changes made in the first line of a follow can modify the master
const SwParaPortion* pPara = GetFollow()->GetPara();
return pPara && ( nPos <= nMax + pPara->GetLen() );
}
/*************************************************************************
* SwTxtFrm::InvalidateRange()
*************************************************************************/
inline void SwTxtFrm::InvalidateRange(const SwCharRange &aRange, const long nD)
{
if ( IsIdxInside( aRange.Start(), aRange.Len() ) )
_InvalidateRange( aRange, nD );
}
/*************************************************************************
* SwTxtFrm::_InvalidateRange()
*************************************************************************/
void SwTxtFrm::_InvalidateRange( const SwCharRange &aRange, const long nD)
{
if ( !HasPara() )
{ InvalidateSize();
return;
}
SetWidow( sal_False );
SwParaPortion *pPara = GetPara();
sal_Bool bInv = sal_False;
if( 0 != nD )
{
//Auf nDelta werden die Differenzen zwischen alter und
//neuer Zeilenlaenge aufaddiert, deshalb ist es negativ,
//wenn Zeichen eingefuegt wurden, positiv, wenn Zeichen
//geloescht wurden.
*(pPara->GetDelta()) += nD;
bInv = sal_True;
}
SwCharRange &rReformat = *(pPara->GetReformat());
if(aRange != rReformat) {
if( STRING_LEN == rReformat.Len() )
rReformat = aRange;
else
rReformat += aRange;
bInv = sal_True;
}
if(bInv)
{
// 90365: nD is passed to a follow two times
// if( GetFollow() )
// ((SwTxtFrm*)GetFollow())->InvalidateRange( aRange, nD );
InvalidateSize();
}
}
/*************************************************************************
* SwTxtFrm::CalcLineSpace()
*************************************************************************/
void SwTxtFrm::CalcLineSpace()
{
ASSERT( ! IsVertical() || ! IsSwapped(),
"SwTxtFrm::CalcLineSpace with swapped frame!" )
if( IsLocked() || !HasPara() )
return;
SwParaPortion *pPara;
if( GetDrawObjs() ||
GetTxtNode()->GetSwAttrSet().GetLRSpace().IsAutoFirst() ||
( pPara = GetPara() )->IsFixLineHeight() )
{
Init();
return;
}
Size aNewSize( Prt().SSize() );
SwTxtFormatInfo aInf( this );
SwTxtFormatter aLine( this, &aInf );
if( aLine.GetDropLines() )
{
Init();
return;
}
aLine.Top();
aLine.RecalcRealHeight();
aNewSize.Height() = (aLine.Y() - Frm().Top()) + aLine.GetLineHeight();
SwTwips nDelta = aNewSize.Height() - Prt().Height();
// 4291: Unterlauf bei Flys
if( aInf.GetTxtFly()->IsOn() )
{
SwRect aTmpFrm( Frm() );
if( nDelta < 0 )
aTmpFrm.Height( Prt().Height() );
else
aTmpFrm.Height( aNewSize.Height() );
if( aInf.GetTxtFly()->Relax( aTmpFrm ) )
{
Init();
return;
}
}
if( nDelta )
{
SwTxtFrmBreak aBreak( this );
if( GetFollow() || aBreak.IsBreakNow( aLine ) )
{
// Wenn es einen Follow() gibt, oder wenn wir an dieser
// Stelle aufbrechen muessen, so wird neu formatiert.
Init();
}
else
{
// Alles nimmt seinen gewohnten Gang ...
pPara->SetPrepAdjust();
pPara->SetPrep();
}
}
}
//
// SET_WRONG( nPos, nCnt, bMove )
//
#define SET_WRONG( nPos, nCnt, bMove ) \
{ \
lcl_SetWrong( *this, nPos, nCnt, bMove ); \
}
void lcl_SetWrong( SwTxtFrm& rFrm, xub_StrLen nPos, long nCnt, bool bMove )
{
if ( !rFrm.IsFollow() )
{
SwTxtNode* pTxtNode = rFrm.GetTxtNode();
IGrammarContact* pGrammarContact = getGrammarContact( *pTxtNode );
SwGrammarMarkUp* pWrongGrammar = pGrammarContact ?
pGrammarContact->getGrammarCheck( *pTxtNode, false ) :
pTxtNode->GetGrammarCheck();
bool bGrammarProxy = pWrongGrammar != pTxtNode->GetGrammarCheck();
if( bMove )
{
if( pTxtNode->GetWrong() )
pTxtNode->GetWrong()->Move( nPos, nCnt );
if( pWrongGrammar )
pWrongGrammar->MoveGrammar( nPos, nCnt );
if( bGrammarProxy && pTxtNode->GetGrammarCheck() )
pTxtNode->GetGrammarCheck()->MoveGrammar( nPos, nCnt );
if( pTxtNode->GetSmartTags() )
pTxtNode->GetSmartTags()->Move( nPos, nCnt );
}
else
{
xub_StrLen nLen = (xub_StrLen)nCnt;
if( pTxtNode->GetWrong() )
pTxtNode->GetWrong()->Invalidate( nPos, nLen );
if( pWrongGrammar )
pWrongGrammar->Invalidate( nPos, nLen );
if( pTxtNode->GetSmartTags() )
pTxtNode->GetSmartTags()->Invalidate( nPos, nLen );
}
if ( !pTxtNode->GetWrong() && !pTxtNode->IsWrongDirty() )
{
pTxtNode->SetWrong( new SwWrongList( WRONGLIST_SPELL ) );
pTxtNode->GetWrong()->SetInvalid( nPos, nPos + (sal_uInt16)( nCnt > 0 ? nCnt : 1 ) );
}
if ( !pTxtNode->GetSmartTags() && !pTxtNode->IsSmartTagDirty() )
{
// SMARTTAGS
pTxtNode->SetSmartTags( new SwWrongList( WRONGLIST_SMARTTAG ) );
pTxtNode->GetSmartTags()->SetInvalid( nPos, nPos + (sal_uInt16)( nCnt > 0 ? nCnt : 1 ) );
}
pTxtNode->SetWrongDirty( true );
pTxtNode->SetGrammarCheckDirty( true );
pTxtNode->SetWordCountDirty( true );
pTxtNode->SetAutoCompleteWordDirty( true );
// SMARTTAGS
pTxtNode->SetSmartTagDirty( true );
}
SwRootFrm *pRootFrm = rFrm.getRootFrm();
if (pRootFrm)
{
pRootFrm->SetNeedGrammarCheck( sal_True );
}
SwPageFrm *pPage = rFrm.FindPageFrm();
if( pPage )
{
pPage->InvalidateSpelling();
pPage->InvalidateAutoCompleteWords();
pPage->InvalidateWordCount();
pPage->InvalidateSmartTags();
}
}
//
// SET_SCRIPT_INVAL( nPos )
//
#define SET_SCRIPT_INVAL( nPos )\
lcl_SetScriptInval( *this, nPos );
void lcl_SetScriptInval( SwTxtFrm& rFrm, xub_StrLen nPos )
{
if( rFrm.GetPara() )
rFrm.GetPara()->GetScriptInfo().SetInvalidity( nPos );
}
void lcl_ModifyOfst( SwTxtFrm* pFrm, xub_StrLen nPos, xub_StrLen nLen )
{
while( pFrm && pFrm->GetOfst() <= nPos )
pFrm = pFrm->GetFollow();
while( pFrm )
{
pFrm->ManipOfst( pFrm->GetOfst() + nLen );
pFrm = pFrm->GetFollow();
}
}
/*************************************************************************
* SwTxtFrm::Modify()
*************************************************************************/
void SwTxtFrm::Modify( const SfxPoolItem* pOld, const SfxPoolItem *pNew )
{
const MSHORT nWhich = pOld ? pOld->Which() : pNew ? pNew->Which() : 0;
//Wuensche die FrmAttribute betreffen werden von der Basisklasse
//verarbeitet.
if( IsInRange( aFrmFmtSetRange, nWhich ) || RES_FMT_CHG == nWhich )
{
SwCntntFrm::Modify( pOld, pNew );
if( nWhich == RES_FMT_CHG && getRootFrm()->GetCurrShell() )
{
// Collection hat sich geaendert
Prepare( PREP_CLEAR );
_InvalidatePrt();
SET_WRONG( 0, STRING_LEN, false );
SetDerivedR2L( sal_False );
CheckDirChange();
// OD 09.12.2002 #105576# - Force complete paint due to existing
// indents.
SetCompletePaint();
InvalidateLineNum();
}
return;
}
// Im gelockten Zustand werden keine Bestellungen angenommen.
if( IsLocked() )
return;
// Dies spart Stack, man muss nur aufpassen,
// dass sie Variablen gesetzt werden.
xub_StrLen nPos, nLen;
sal_Bool bSetFldsDirty = sal_False;
sal_Bool bRecalcFtnFlag = sal_False;
switch( nWhich )
{
case RES_LINENUMBER:
{
InvalidateLineNum();
}
break;
case RES_INS_TXT:
{
nPos = ((SwInsTxt*)pNew)->nPos;
nLen = ((SwInsTxt*)pNew)->nLen;
if( IsIdxInside( nPos, nLen ) )
{
if( !nLen )
{
// 6969: Aktualisierung der NumPortions auch bei leeren Zeilen!
if( nPos )
InvalidateSize();
else
Prepare( PREP_CLEAR );
}
else
_InvalidateRange( SwCharRange( nPos, nLen ), nLen );
}
SET_WRONG( nPos, nLen, true )
SET_SCRIPT_INVAL( nPos )
bSetFldsDirty = sal_True;
if( HasFollow() )
lcl_ModifyOfst( this, nPos, nLen );
}
break;
case RES_DEL_CHR:
{
nPos = ((SwDelChr*)pNew)->nPos;
InvalidateRange( SwCharRange( nPos, 1 ), -1 );
SET_WRONG( nPos, -1, true )
SET_SCRIPT_INVAL( nPos )
bSetFldsDirty = bRecalcFtnFlag = sal_True;
if( HasFollow() )
lcl_ModifyOfst( this, nPos, STRING_LEN );
}
break;
case RES_DEL_TXT:
{
nPos = ((SwDelTxt*)pNew)->nStart;
nLen = ((SwDelTxt*)pNew)->nLen;
long m = nLen;
m *= -1;
if( IsIdxInside( nPos, nLen ) )
{
if( !nLen )
InvalidateSize();
else
InvalidateRange( SwCharRange( nPos, 1 ), m );
}
SET_WRONG( nPos, m, true )
SET_SCRIPT_INVAL( nPos )
bSetFldsDirty = bRecalcFtnFlag = sal_True;
if( HasFollow() )
lcl_ModifyOfst( this, nPos, nLen );
}
break;
case RES_UPDATE_ATTR:
{
nPos = ((SwUpdateAttr*)pNew)->nStart;
nLen = ((SwUpdateAttr*)pNew)->nEnd - nPos;
if( IsIdxInside( nPos, nLen ) )
{
// Es muss in jedem Fall neu formatiert werden,
// auch wenn der invalidierte Bereich null ist.
// Beispiel: leere Zeile, 14Pt einstellen !
// if( !nLen ) nLen = 1;
// 6680: FtnNummern muessen formatiert werden.
if( !nLen )
nLen = 1;
_InvalidateRange( SwCharRange( nPos, nLen) );
MSHORT nTmp = ((SwUpdateAttr*)pNew)->nWhichAttr;
if( ! nTmp || RES_TXTATR_CHARFMT == nTmp || RES_TXTATR_AUTOFMT == nTmp ||
RES_FMT_CHG == nTmp || RES_ATTRSET_CHG == nTmp )
{
SET_WRONG( nPos, nPos + nLen, false )
SET_SCRIPT_INVAL( nPos )
}
}
// --> OD 2010-02-16 #i104008#
ViewShell* pViewSh = getRootFrm() ? getRootFrm()->GetCurrShell() : 0;
if ( pViewSh )
{
pViewSh->InvalidateAccessibleParaAttrs( *this );
}
// <--
}
break;
case RES_OBJECTDYING:
break;
case RES_PARATR_LINESPACING:
{
CalcLineSpace();
InvalidateSize();
_InvalidatePrt();
if( IsInSct() && !GetPrev() )
{
SwSectionFrm *pSect = FindSctFrm();
if( pSect->ContainsAny() == this )
pSect->InvalidatePrt();
}
// OD 09.01.2004 #i11859# - correction:
// (1) Also invalidate next frame on next page/column.
// (2) Skip empty sections and hidden paragraphs
// Thus, use method <InvalidateNextPrtArea()>
InvalidateNextPrtArea();
SetCompletePaint();
}
break;
case RES_TXTATR_FIELD:
case RES_TXTATR_ANNOTATION:
{
nPos = *((SwFmtFld*)pNew)->GetTxtFld()->GetStart();
if( IsIdxInside( nPos, 1 ) )
{
if( pNew == pOld )
{
// Nur repainten
// opt: invalidate aufs Window ?
InvalidatePage();
SetCompletePaint();
}
else
_InvalidateRange( SwCharRange( nPos, 1 ) );
}
bSetFldsDirty = sal_True;
// ST2
if ( SwSmartTagMgr::Get().IsSmartTagsEnabled() )
SET_WRONG( nPos, nPos + 1, false )
}
break;
case RES_TXTATR_FTN :
{
nPos = *((SwFmtFtn*)pNew)->GetTxtFtn()->GetStart();
if( IsInFtn() || IsIdxInside( nPos, 1 ) )
Prepare( PREP_FTN, ((SwFmtFtn*)pNew)->GetTxtFtn() );
break;
}
case RES_ATTRSET_CHG:
{
InvalidateLineNum();
SwAttrSet& rNewSet = *((SwAttrSetChg*)pNew)->GetChgSet();
const SfxPoolItem* pItem = 0;
int nClear = 0;
MSHORT nCount = rNewSet.Count();
if( SFX_ITEM_SET == rNewSet.GetItemState( RES_TXTATR_FTN, sal_False, &pItem ))
{
nPos = *((SwFmtFtn*)pItem)->GetTxtFtn()->GetStart();
if( IsIdxInside( nPos, 1 ) )
Prepare( PREP_FTN, pNew );
nClear = 0x01;
--nCount;
}
if( SFX_ITEM_SET == rNewSet.GetItemState( RES_TXTATR_FIELD, sal_False, &pItem ))
{
nPos = *((SwFmtFld*)pItem)->GetTxtFld()->GetStart();
if( IsIdxInside( nPos, 1 ) )
{
const SfxPoolItem& rOldItem =
((SwAttrSetChg*)pOld)->GetChgSet()->Get( RES_TXTATR_FIELD );
if( pItem == &rOldItem )
{
InvalidatePage();
SetCompletePaint();
}
else
_InvalidateRange( SwCharRange( nPos, 1 ) );
}
nClear |= 0x02;
--nCount;
}
sal_Bool bLineSpace = SFX_ITEM_SET == rNewSet.GetItemState(
RES_PARATR_LINESPACING, sal_False ),
bRegister = SFX_ITEM_SET == rNewSet.GetItemState(
RES_PARATR_REGISTER, sal_False );
if ( bLineSpace || bRegister )
{
Prepare( bRegister ? PREP_REGISTER : PREP_ADJUST_FRM );
CalcLineSpace();
InvalidateSize();
_InvalidatePrt();
// OD 09.01.2004 #i11859# - correction:
// (1) Also invalidate next frame on next page/column.
// (2) Skip empty sections and hidden paragraphs
// Thus, use method <InvalidateNextPrtArea()>
InvalidateNextPrtArea();
SetCompletePaint();
nClear |= 0x04;
if ( bLineSpace )
{
--nCount;
if( IsInSct() && !GetPrev() )
{
SwSectionFrm *pSect = FindSctFrm();
if( pSect->ContainsAny() == this )
pSect->InvalidatePrt();
}
}
if ( bRegister )
--nCount;
}
if ( SFX_ITEM_SET == rNewSet.GetItemState( RES_PARATR_SPLIT,
sal_False ))
{
if ( GetPrev() )
CheckKeep();
Prepare( PREP_CLEAR );
InvalidateSize();
nClear |= 0x08;
--nCount;
}
if( SFX_ITEM_SET == rNewSet.GetItemState( RES_BACKGROUND, sal_False)
&& !IsFollow() && GetDrawObjs() )
{
SwSortedObjs *pObjs = GetDrawObjs();
for ( int i = 0; GetDrawObjs() && i < int(pObjs->Count()); ++i )
{
SwAnchoredObject* pAnchoredObj = (*pObjs)[MSHORT(i)];
if ( pAnchoredObj->ISA(SwFlyFrm) )
{
SwFlyFrm *pFly = static_cast<SwFlyFrm*>(pAnchoredObj);
if( !pFly->IsFlyInCntFrm() )
{
const SvxBrushItem &rBack =
pFly->GetAttrSet()->GetBackground();
// OD 20.08.2002 #99657# #GetTransChg#
// following condition determines, if the fly frame
// "inherites" the background color of text frame.
// This is the case, if fly frame background
// color is "no fill"/"auto fill" and if the fly frame
// has no background graphic.
// Thus, check complete fly frame background
// color and *not* only its transparency value
if ( (rBack.GetColor() == COL_TRANSPARENT) &&
//if( rBack.GetColor().GetTransparency() &&
rBack.GetGraphicPos() == GPOS_NONE )
{
pFly->SetCompletePaint();
pFly->InvalidatePage();
}
}
}
}
}
if ( SFX_ITEM_SET ==
rNewSet.GetItemState( RES_TXTATR_CHARFMT, sal_False ) )
{
SET_WRONG( 0, STRING_LEN, false )
SET_SCRIPT_INVAL( 0 )
}
else if ( SFX_ITEM_SET ==
rNewSet.GetItemState( RES_CHRATR_LANGUAGE, sal_False ) ||
SFX_ITEM_SET ==
rNewSet.GetItemState( RES_CHRATR_CJK_LANGUAGE, sal_False ) ||
SFX_ITEM_SET ==
rNewSet.GetItemState( RES_CHRATR_CTL_LANGUAGE, sal_False ) )
SET_WRONG( 0, STRING_LEN, false )
else if ( SFX_ITEM_SET ==
rNewSet.GetItemState( RES_CHRATR_FONT, sal_False ) ||
SFX_ITEM_SET ==
rNewSet.GetItemState( RES_CHRATR_CJK_FONT, sal_False ) ||
SFX_ITEM_SET ==
rNewSet.GetItemState( RES_CHRATR_CTL_FONT, sal_False ) )
SET_SCRIPT_INVAL( 0 )
else if ( SFX_ITEM_SET ==
rNewSet.GetItemState( RES_FRAMEDIR, sal_False ) )
{
SetDerivedR2L( sal_False );
CheckDirChange();
// OD 09.12.2002 #105576# - Force complete paint due to existing
// indents.
SetCompletePaint();
}
if( nCount )
{
if( getRootFrm()->GetCurrShell() )
{
Prepare( PREP_CLEAR );
_InvalidatePrt();
}
if( nClear )
{
SwAttrSetChg aOldSet( *(SwAttrSetChg*)pOld );
SwAttrSetChg aNewSet( *(SwAttrSetChg*)pNew );
if( 0x01 & nClear )
{
aOldSet.ClearItem( RES_TXTATR_FTN );
aNewSet.ClearItem( RES_TXTATR_FTN );
}
if( 0x02 & nClear )
{
aOldSet.ClearItem( RES_TXTATR_FIELD );
aNewSet.ClearItem( RES_TXTATR_FIELD );
}
if ( 0x04 & nClear )
{
if ( bLineSpace )
{
aOldSet.ClearItem( RES_PARATR_LINESPACING );
aNewSet.ClearItem( RES_PARATR_LINESPACING );
}
if ( bRegister )
{
aOldSet.ClearItem( RES_PARATR_REGISTER );
aNewSet.ClearItem( RES_PARATR_REGISTER );
}
}
if ( 0x08 & nClear )
{
aOldSet.ClearItem( RES_PARATR_SPLIT );
aNewSet.ClearItem( RES_PARATR_SPLIT );
}
SwCntntFrm::Modify( &aOldSet, &aNewSet );
}
else
SwCntntFrm::Modify( pOld, pNew );
}
// --> OD 2009-01-06 #i88069#
ViewShell* pViewSh = getRootFrm() ? getRootFrm()->GetCurrShell() : 0;
if ( pViewSh )
{
pViewSh->InvalidateAccessibleParaAttrs( *this );
}
// <--
}
break;
// 6870: SwDocPosUpdate auswerten.
case RES_DOCPOS_UPDATE:
{
if( pOld && pNew )
{
const SwDocPosUpdate *pDocPos = (const SwDocPosUpdate*)pOld;
if( pDocPos->nDocPos <= aFrm.Top() )
{
const SwFmtFld *pFld = (const SwFmtFld *)pNew;
InvalidateRange(
SwCharRange( *pFld->GetTxtFld()->GetStart(), 1 ) );
}
}
break;
}
case RES_PARATR_SPLIT:
if ( GetPrev() )
CheckKeep();
Prepare( PREP_CLEAR );
bSetFldsDirty = sal_True;
break;
case RES_FRAMEDIR :
SetDerivedR2L( sal_False );
CheckDirChange();
break;
default:
{
Prepare( PREP_CLEAR );
_InvalidatePrt();
if ( !nWhich )
{
//Wird z.B. bei HiddenPara mit 0 gerufen.
SwFrm *pNxt;
if ( 0 != (pNxt = FindNext()) )
pNxt->InvalidatePrt();
}
}
} // switch
if( bSetFldsDirty )
GetNode()->getIDocumentFieldsAccess()->SetFieldsDirty( sal_True, GetNode(), 1 );
if ( bRecalcFtnFlag )
CalcFtnFlag();
}
sal_Bool SwTxtFrm::GetInfo( SfxPoolItem &rHnt ) const
{
if ( RES_VIRTPAGENUM_INFO == rHnt.Which() && IsInDocBody() && ! IsFollow() )
{
SwVirtPageNumInfo &rInfo = (SwVirtPageNumInfo&)rHnt;
const SwPageFrm *pPage = FindPageFrm();
if ( pPage )
{
if ( pPage == rInfo.GetOrigPage() && !GetPrev() )
{
//Das sollte er sein (kann allenfalls temporaer anders sein,
// sollte uns das beunruhigen?)
rInfo.SetInfo( pPage, this );
return sal_False;
}
if ( pPage->GetPhyPageNum() < rInfo.GetOrigPage()->GetPhyPageNum() &&
(!rInfo.GetPage() || pPage->GetPhyPageNum() > rInfo.GetPage()->GetPhyPageNum()))
{
//Das koennte er sein.
rInfo.SetInfo( pPage, this );
}
}
}
return sal_True;
}
/*************************************************************************
* SwTxtFrm::PrepWidows()
*************************************************************************/
void SwTxtFrm::PrepWidows( const MSHORT nNeed, sal_Bool bNotify )
{
ASSERT(GetFollow() && nNeed, "+SwTxtFrm::Prepare: lost all friends");
SwParaPortion *pPara = GetPara();
if ( !pPara )
return;
pPara->SetPrepWidows( sal_True );
// These two lines of code have been deleted for #102340#.
// Obviously the widow control does not work if we have a
// pMaster->pFollow->pFollow situation:
// returnen oder nicht ist hier die Frage.
// Ohne IsLocked() ist 5156 gefaehrlich,
// ohne IsFollow() werden die Orphans unterdrueckt: 6968.
// Abfrage auf IsLocked erst hier, weil das Flag gesetzt werden soll.
// if( IsLocked() && IsFollow() )
// return;
MSHORT nHave = nNeed;
// Wir geben ein paar Zeilen ab und schrumpfen im CalcPreps()
SWAP_IF_NOT_SWAPPED( this )
SwTxtSizeInfo aInf( this );
SwTxtMargin aLine( this, &aInf );
aLine.Bottom();
xub_StrLen nTmpLen = aLine.GetCurr()->GetLen();
while( nHave && aLine.PrevLine() )
{
if( nTmpLen )
--nHave;
nTmpLen = aLine.GetCurr()->GetLen();
}
// In dieser Ecke tummelten sich einige Bugs: 7513, 7606.
// Wenn feststeht, dass Zeilen abgegeben werden koennen,
// muss der Master darueber hinaus die Widow-Regel ueberpruefen.
if( !nHave )
{
sal_Bool bSplit;
if( !IsFollow() ) //Nur ein Master entscheidet ueber Orphans
{
const WidowsAndOrphans aWidOrp( this );
bSplit = ( aLine.GetLineNr() >= aWidOrp.GetOrphansLines() &&
aLine.GetLineNr() >= aLine.GetDropLines() );
}
else
bSplit = sal_True;
if( bSplit )
{
GetFollow()->SetOfst( aLine.GetEnd() );
aLine.TruncLines( sal_True );
if( pPara->IsFollowField() )
GetFollow()->SetFieldFollow( sal_True );
}
}
if ( bNotify )
{
_InvalidateSize();
InvalidatePage();
}
UNDO_SWAP( this )
}
/*************************************************************************
* SwTxtFrm::Prepare
*************************************************************************/
sal_Bool lcl_ErgoVadis( SwTxtFrm* pFrm, xub_StrLen &rPos, const PrepareHint ePrep )
{
const SwFtnInfo &rFtnInfo = pFrm->GetNode()->GetDoc()->GetFtnInfo();
if( ePrep == PREP_ERGOSUM )
{
if( !rFtnInfo.aErgoSum.Len() )
return sal_False;;
rPos = pFrm->GetOfst();
}
else
{
if( !rFtnInfo.aQuoVadis.Len() )
return sal_False;
if( pFrm->HasFollow() )
rPos = pFrm->GetFollow()->GetOfst();
else
rPos = pFrm->GetTxt().Len();
if( rPos )
--rPos; // unser letztes Zeichen
}
return sal_True;
}
void SwTxtFrm::Prepare( const PrepareHint ePrep, const void* pVoid,
sal_Bool bNotify )
{
SwFrmSwapper aSwapper( this, sal_False );
#if OSL_DEBUG_LEVEL > 1
const SwTwips nDbgY = Frm().Top();
(void)nDbgY;
#endif
if ( IsEmpty() )
{
switch ( ePrep )
{
case PREP_BOSS_CHGD:
SetInvalidVert( sal_True ); // Test
case PREP_WIDOWS_ORPHANS:
case PREP_WIDOWS:
case PREP_FTN_GONE : return;
case PREP_POS_CHGD :
{
// Auch in (spaltigen) Bereichen ist ein InvalidateSize notwendig,
// damit formatiert wird und ggf. das bUndersized gesetzt wird.
if( IsInFly() || IsInSct() )
{
SwTwips nTmpBottom = GetUpper()->Frm().Top() +
GetUpper()->Prt().Bottom();
if( nTmpBottom < Frm().Bottom() )
break;
}
// Gibt es ueberhaupt Flys auf der Seite ?
SwTxtFly aTxtFly( this );
if( aTxtFly.IsOn() )
{
// Ueberlappt irgendein Fly ?
aTxtFly.Relax();
if ( aTxtFly.IsOn() || IsUndersized() )
break;
}
if( GetTxtNode()->GetSwAttrSet().GetRegister().GetValue())
break;
GETGRID( FindPageFrm() )
if ( pGrid && GetTxtNode()->GetSwAttrSet().GetParaGrid().GetValue() )
break;
// --> OD 2004-07-16 #i28701# - consider anchored objects
if ( GetDrawObjs() )
break;
// <--
return;
}
default:
break;
}
}
if( !HasPara() && PREP_MUST_FIT != ePrep )
{
SetInvalidVert( sal_True ); // Test
ASSERT( !IsLocked(), "SwTxtFrm::Prepare: three of a perfect pair" );
if ( bNotify )
InvalidateSize();
else
_InvalidateSize();
return;
}
//Objekt mit Locking aus dem Cache holen.
SwTxtLineAccess aAccess( this );
SwParaPortion *pPara = aAccess.GetPara();
switch( ePrep )
{
case PREP_MOVEFTN : Frm().Height(0);
Prt().Height(0);
_InvalidatePrt();
_InvalidateSize();
// KEIN break
case PREP_ADJUST_FRM : pPara->SetPrepAdjust( sal_True );
if( IsFtnNumFrm() != pPara->IsFtnNum() ||
IsUndersized() )
{
InvalidateRange( SwCharRange( 0, 1 ), 1);
if( GetOfst() && !IsFollow() )
_SetOfst( 0 );
}
break;
case PREP_MUST_FIT : pPara->SetPrepMustFit( sal_True );
/* no break here */
case PREP_WIDOWS_ORPHANS : pPara->SetPrepAdjust( sal_True );
break;
case PREP_WIDOWS :
// MustFit ist staerker als alles anderes
if( pPara->IsPrepMustFit() )
return;
// Siehe Kommentar in WidowsAndOrphans::FindOrphans und CalcPreps()
PrepWidows( *(const MSHORT *)pVoid, bNotify );
break;
case PREP_FTN :
{
SwTxtFtn *pFtn = (SwTxtFtn *)pVoid;
if( IsInFtn() )
{
// Bin ich der erste TxtFrm einer Fussnote ?
if( !GetPrev() )
// Wir sind also ein TxtFrm der Fussnote, die
// die Fussnotenzahl zur Anzeige bringen muss.
// Oder den ErgoSum-Text...
InvalidateRange( SwCharRange( 0, 1 ), 1);
if( !GetNext() )
{
// Wir sind der letzte Ftn, jetzt muessten die
// QuoVadis-Texte geupdated werden.
const SwFtnInfo &rFtnInfo = GetNode()->GetDoc()->GetFtnInfo();
if( !pPara->UpdateQuoVadis( rFtnInfo.aQuoVadis ) )
{
xub_StrLen nPos = pPara->GetParLen();
if( nPos )
--nPos;
InvalidateRange( SwCharRange( nPos, 1 ), 1);
}
}
}
else
{
// Wir sind also der TxtFrm _mit_ der Fussnote
const xub_StrLen nPos = *pFtn->GetStart();
InvalidateRange( SwCharRange( nPos, 1 ), 1);
}
break;
}
case PREP_BOSS_CHGD :
{
// Test
{
SetInvalidVert( sal_False );
sal_Bool bOld = IsVertical();
SetInvalidVert( sal_True );
if( bOld != IsVertical() )
InvalidateRange( SwCharRange( GetOfst(), STRING_LEN ) );
}
if( HasFollow() )
{
xub_StrLen nNxtOfst = GetFollow()->GetOfst();
if( nNxtOfst )
--nNxtOfst;
InvalidateRange( SwCharRange( nNxtOfst, 1 ), 1);
}
if( IsInFtn() )
{
xub_StrLen nPos;
if( lcl_ErgoVadis( this, nPos, PREP_QUOVADIS ) )
InvalidateRange( SwCharRange( nPos, 1 ), 0 );
if( lcl_ErgoVadis( this, nPos, PREP_ERGOSUM ) )
InvalidateRange( SwCharRange( nPos, 1 ), 0 );
}
// 4739: Wenn wir ein Seitennummernfeld besitzen, muessen wir
// die Stellen invalidieren.
SwpHints *pHints = GetTxtNode()->GetpSwpHints();
if( pHints )
{
const sal_uInt16 nSize = pHints->Count();
const xub_StrLen nEnd = GetFollow() ?
GetFollow()->GetOfst() : STRING_LEN;
for ( sal_uInt16 i = 0; i < nSize; ++i )
{
const SwTxtAttr *pHt = (*pHints)[i];
const xub_StrLen nStart = *pHt->GetStart();
if( nStart >= GetOfst() )
{
if( nStart >= nEnd )
i = nSize; // fuehrt das Ende herbei
else
{
// 4029: wenn wir zurueckfliessen und eine Ftn besitzen, so
// fliesst die Ftn in jedem Fall auch mit. Damit sie nicht im
// Weg steht, schicken wir uns ein ADJUST_FRM.
// pVoid != 0 bedeutet MoveBwd()
const MSHORT nWhich = pHt->Which();
if( RES_TXTATR_FIELD == nWhich ||
(HasFtn() && pVoid && RES_TXTATR_FTN == nWhich))
InvalidateRange( SwCharRange( nStart, 1 ), 1 );
}
}
}
}
// A new boss, a new chance for growing
if( IsUndersized() )
{
_InvalidateSize();
InvalidateRange( SwCharRange( GetOfst(), 1 ), 1);
}
break;
}
case PREP_POS_CHGD :
{
if ( GetValidPrtAreaFlag() )
{
GETGRID( FindPageFrm() )
if ( pGrid && GetTxtNode()->GetSwAttrSet().GetParaGrid().GetValue() )
InvalidatePrt();
}
// Falls wir mit niemandem ueberlappen:
// Ueberlappte irgendein Fly _vor_ der Positionsaenderung ?
sal_Bool bFormat = pPara->HasFly();
if( !bFormat )
{
if( IsInFly() )
{
SwTwips nTmpBottom = GetUpper()->Frm().Top() +
GetUpper()->Prt().Bottom();
if( nTmpBottom < Frm().Bottom() )
bFormat = sal_True;
}
if( !bFormat )
{
if ( GetDrawObjs() )
{
const sal_uInt32 nCnt = GetDrawObjs()->Count();
for ( MSHORT i = 0; i < nCnt; ++i )
{
SwAnchoredObject* pAnchoredObj = (*GetDrawObjs())[i];
// --> OD 2004-07-16 #i28701# - consider all
// to-character anchored objects
if ( pAnchoredObj->GetFrmFmt().GetAnchor().GetAnchorId()
== FLY_AT_CHAR )
{
bFormat = sal_True;
break;
}
}
}
if( !bFormat )
{
// Gibt es ueberhaupt Flys auf der Seite ?
SwTxtFly aTxtFly( this );
if( aTxtFly.IsOn() )
{
// Ueberlappt irgendein Fly ?
aTxtFly.Relax();
bFormat = aTxtFly.IsOn() || IsUndersized();
}
}
}
}
if( bFormat )
{
if( !IsLocked() )
{
if( pPara->GetRepaint()->HasArea() )
SetCompletePaint();
Init();
pPara = 0;
_InvalidateSize();
}
}
else
{
if( GetTxtNode()->GetSwAttrSet().GetRegister().GetValue() )
Prepare( PREP_REGISTER, 0, bNotify );
// Durch Positionsverschiebungen mit Ftns muessen die
// Frames neu adjustiert werden.
else if( HasFtn() )
{
Prepare( PREP_ADJUST_FRM, 0, bNotify );
_InvalidateSize();
}
else
return; // damit kein SetPrep() erfolgt.
}
break;
}
case PREP_REGISTER:
if( GetTxtNode()->GetSwAttrSet().GetRegister().GetValue() )
{
pPara->SetPrepAdjust( sal_True );
CalcLineSpace();
InvalidateSize();
_InvalidatePrt();
SwFrm* pNxt;
if ( 0 != ( pNxt = GetIndNext() ) )
{
pNxt->_InvalidatePrt();
if ( pNxt->IsLayoutFrm() )
pNxt->InvalidatePage();
}
SetCompletePaint();
}
break;
case PREP_FTN_GONE :
{
// Wenn ein Follow uns ruft, weil eine Fussnote geloescht wird, muss unsere
// letzte Zeile formatiert werden, damit ggf. die erste Zeile des Follows
// hochrutschen kann, die extra auf die naechste Seite gerutscht war, um mit
// der Fussnote zusammen zu sein, insbesondere bei spaltigen Bereichen.
ASSERT( GetFollow(), "PREP_FTN_GONE darf nur vom Follow gerufen werden" );
xub_StrLen nPos = GetFollow()->GetOfst();
if( IsFollow() && GetOfst() == nPos ) // falls wir gar keine Textmasse besitzen,
FindMaster()->Prepare( PREP_FTN_GONE ); // rufen wir das Prepare unseres Masters
if( nPos )
--nPos; // das Zeichen vor unserem Follow
InvalidateRange( SwCharRange( nPos, 1 ), 0 );
return;
}
case PREP_ERGOSUM:
case PREP_QUOVADIS:
{
xub_StrLen nPos;
if( lcl_ErgoVadis( this, nPos, ePrep ) )
InvalidateRange( SwCharRange( nPos, 1 ), 0 );
}
break;
case PREP_FLY_ATTR_CHG:
{
if( pVoid )
{
xub_StrLen nWhere = CalcFlyPos( (SwFrmFmt*)pVoid );
ASSERT( STRING_LEN != nWhere, "Prepare: Why me?" );
InvalidateRange( SwCharRange( nWhere, 1 ) );
return;
}
// else ... Laufe in den Default-Switch
}
case PREP_CLEAR:
default:
{
if( IsLocked() )
{
if( PREP_FLY_ARRIVE == ePrep || PREP_FLY_LEAVE == ePrep )
{
xub_StrLen nLen = ( GetFollow() ? GetFollow()->GetOfst() :
STRING_LEN ) - GetOfst();
InvalidateRange( SwCharRange( GetOfst(), nLen ), 0 );
}
}
else
{
if( pPara->GetRepaint()->HasArea() )
SetCompletePaint();
Init();
pPara = 0;
if( GetOfst() && !IsFollow() )
_SetOfst( 0 );
if ( bNotify )
InvalidateSize();
else
_InvalidateSize();
}
return; // damit kein SetPrep() erfolgt.
}
}
if( pPara )
pPara->SetPrep( sal_True );
}
/* -----------------11.02.99 17:56-------------------
* Kleine Hilfsklasse mit folgender Funktion:
* Sie soll eine Probeformatierung vorbereiten.
* Der Frame wird in Groesse und Position angepasst, sein SwParaPortion zur Seite
* gestellt und eine neue erzeugt, dazu wird formatiert mit gesetztem bTestFormat.
* Im Dtor wird der TxtFrm wieder in seinen alten Zustand zurueckversetzt.
*
* --------------------------------------------------*/
class SwTestFormat
{
SwTxtFrm *pFrm;
SwParaPortion *pOldPara;
SwRect aOldFrm, aOldPrt;
public:
SwTestFormat( SwTxtFrm* pTxtFrm, const SwFrm* pPrv, SwTwips nMaxHeight );
~SwTestFormat();
};
SwTestFormat::SwTestFormat( SwTxtFrm* pTxtFrm, const SwFrm* pPre, SwTwips nMaxHeight )
: pFrm( pTxtFrm )
{
aOldFrm = pFrm->Frm();
aOldPrt = pFrm->Prt();
SWRECTFN( pFrm )
SwTwips nLower = (pFrm->*fnRect->fnGetBottomMargin)();
pFrm->Frm() = pFrm->GetUpper()->Prt();
pFrm->Frm() += pFrm->GetUpper()->Frm().Pos();
(pFrm->Frm().*fnRect->fnSetHeight)( nMaxHeight );
if( pFrm->GetPrev() )
(pFrm->Frm().*fnRect->fnSetPosY)(
(pFrm->GetPrev()->Frm().*fnRect->fnGetBottom)() -
( bVert ? nMaxHeight + 1 : 0 ) );
SwBorderAttrAccess aAccess( SwFrm::GetCache(), pFrm );
const SwBorderAttrs &rAttrs = *aAccess.Get();
(pFrm->Prt().*fnRect->fnSetPosX)( rAttrs.CalcLeft( pFrm ) );
if( pPre )
{
SwTwips nUpper = pFrm->CalcUpperSpace( &rAttrs, pPre );
(pFrm->Prt().*fnRect->fnSetPosY)( nUpper );
}
(pFrm->Prt().*fnRect->fnSetHeight)(
Max( 0L , (pFrm->Frm().*fnRect->fnGetHeight)() -
(pFrm->Prt().*fnRect->fnGetTop)() - nLower ) );
(pFrm->Prt().*fnRect->fnSetWidth)(
(pFrm->Frm().*fnRect->fnGetWidth)() -
// OD 23.01.2003 #106895# - add 1st param to <SwBorderAttrs::CalcRight(..)>
( rAttrs.CalcLeft( pFrm ) + rAttrs.CalcRight( pFrm ) ) );
pOldPara = pFrm->HasPara() ? pFrm->GetPara() : NULL;
pFrm->SetPara( new SwParaPortion(), sal_False );
ASSERT( ! pFrm->IsSwapped(), "A frame is swapped before _Format" );
if ( pFrm->IsVertical() )
pFrm->SwapWidthAndHeight();
SwTxtFormatInfo aInf( pFrm, sal_False, sal_True, sal_True );
SwTxtFormatter aLine( pFrm, &aInf );
pFrm->_Format( aLine, aInf );
if ( pFrm->IsVertical() )
pFrm->SwapWidthAndHeight();
ASSERT( ! pFrm->IsSwapped(), "A frame is swapped after _Format" );
}
SwTestFormat::~SwTestFormat()
{
pFrm->Frm() = aOldFrm;
pFrm->Prt() = aOldPrt;
pFrm->SetPara( pOldPara );
}
sal_Bool SwTxtFrm::TestFormat( const SwFrm* pPrv, SwTwips &rMaxHeight, sal_Bool &bSplit )
{
PROTOCOL_ENTER( this, PROT_TESTFORMAT, 0, 0 )
if( IsLocked() && GetUpper()->Prt().Width() <= 0 )
return sal_False;
SwTestFormat aSave( this, pPrv, rMaxHeight );
return SwTxtFrm::WouldFit( rMaxHeight, bSplit, sal_True );
}
/*************************************************************************
* SwTxtFrm::WouldFit()
*************************************************************************/
/* SwTxtFrm::WouldFit()
* sal_True: wenn ich aufspalten kann.
* Es soll und braucht nicht neu formatiert werden.
* Wir gehen davon aus, dass bereits formatiert wurde und dass
* die Formatierungsdaten noch aktuell sind.
* Wir gehen davon aus, dass die Framebreiten des evtl. Masters und
* Follows gleich sind. Deswegen wird kein FindBreak() mit FindOrphans()
* gerufen.
* Die benoetigte Hoehe wird von nMaxHeight abgezogen!
*/
sal_Bool SwTxtFrm::WouldFit( SwTwips &rMaxHeight, sal_Bool &bSplit, sal_Bool bTst )
{
ASSERT( ! IsVertical() || ! IsSwapped(),
"SwTxtFrm::WouldFit with swapped frame" );
SWRECTFN( this );
if( IsLocked() )
return sal_False;
//Kann gut sein, dass mir der IdleCollector mir die gecachten
//Informationen entzogen hat.
if( !IsEmpty() )
GetFormatted();
// OD 2004-05-24 #i27801# - correction: 'short cut' for empty paragraph
// can *not* be applied, if test format is in progress. The test format doesn't
// adjust the frame and the printing area - see method <SwTxtFrm::_Format(..)>,
// which is called in <SwTxtFrm::TestFormat(..)>
if ( IsEmpty() && !bTst )
{
bSplit = sal_False;
SwTwips nHeight = bVert ? Prt().SSize().Width() : Prt().SSize().Height();
if( rMaxHeight < nHeight )
return sal_False;
else
{
rMaxHeight -= nHeight;
return sal_True;
}
}
// In sehr unguenstigen Faellen kann GetPara immer noch 0 sein.
// Dann returnen wir sal_True, um auf der neuen Seite noch einmal
// anformatiert zu werden.
ASSERT( HasPara() || IsHiddenNow(), "WouldFit: GetFormatted() and then !HasPara()" );
if( !HasPara() || ( !(Frm().*fnRect->fnGetHeight)() && IsHiddenNow() ) )
return sal_True;
// Da das Orphan-Flag nur sehr fluechtig existiert, wird als zweite
// Bedingung ueberprueft, ob die Rahmengroesse durch CalcPreps
// auf riesengross gesetzt wird, um ein MoveFwd zu erzwingen.
if( IsWidow() || ( bVert ?
( 0 == Frm().Left() ) :
( LONG_MAX - 20000 < Frm().Bottom() ) ) )
{
SetWidow(sal_False);
if ( GetFollow() )
{
// Wenn wir hier durch eine Widow-Anforderung unseres Follows gelandet
// sind, wird ueberprueft, ob es ueberhaupt einen Follow mit einer
// echten Hoehe gibt, andernfalls (z.B. in neu angelegten SctFrms)
// ignorieren wir das IsWidow() und pruefen doch noch, ob wir
// genung Platz finden.
if( ( ( ! bVert && LONG_MAX - 20000 >= Frm().Bottom() ) ||
( bVert && 0 < Frm().Left() ) ) &&
( GetFollow()->IsVertical() ?
!GetFollow()->Frm().Width() :
!GetFollow()->Frm().Height() ) )
{
SwTxtFrm* pFoll = GetFollow()->GetFollow();
while( pFoll &&
( pFoll->IsVertical() ?
!pFoll->Frm().Width() :
!pFoll->Frm().Height() ) )
pFoll = pFoll->GetFollow();
if( pFoll )
return sal_False;
}
else
return sal_False;
}
}
SWAP_IF_NOT_SWAPPED( this );
SwTxtSizeInfo aInf( this );
SwTxtMargin aLine( this, &aInf );
WidowsAndOrphans aFrmBreak( this, rMaxHeight, bSplit );
sal_Bool bRet = sal_True;
aLine.Bottom();
// Ist Aufspalten ueberhaupt notwendig?
if ( 0 != ( bSplit = !aFrmBreak.IsInside( aLine ) ) )
bRet = !aFrmBreak.IsKeepAlways() && aFrmBreak.WouldFit( aLine, rMaxHeight, bTst );
else
{
//Wir brauchen die Gesamthoehe inklusive der aktuellen Zeile
aLine.Top();
do
{
rMaxHeight -= aLine.GetLineHeight();
} while ( aLine.Next() );
}
UNDO_SWAP( this )
return bRet;
}
/*************************************************************************
* SwTxtFrm::GetParHeight()
*************************************************************************/
KSHORT SwTxtFrm::GetParHeight() const
{
ASSERT( ! IsVertical() || ! IsSwapped(),
"SwTxtFrm::GetParHeight with swapped frame" )
if( !HasPara() )
{ // Fuer nichtleere Absaetze ist dies ein Sonderfall, da koennen wir
// bei UnderSized ruhig nur 1 Twip mehr anfordern.
KSHORT nRet = (KSHORT)Prt().SSize().Height();
if( IsUndersized() )
{
if( IsEmpty() )
nRet = (KSHORT)EmptyHeight();
else
++nRet;
}
return nRet;
}
// FME, OD 08.01.2004 #i11859# - refactoring and improve code
const SwLineLayout* pLineLayout = GetPara();
KSHORT nHeight = pLineLayout->GetRealHeight();
if( GetOfst() && !IsFollow() ) // Ist dieser Absatz gescrollt? Dann ist unsere
nHeight *= 2; // bisherige Hoehe mind. eine Zeilenhoehe zu gering
// OD 2004-03-04 #115793#
while ( pLineLayout && pLineLayout->GetNext() )
{
pLineLayout = pLineLayout->GetNext();
nHeight = nHeight + pLineLayout->GetRealHeight();
}
return nHeight;
}
/*************************************************************************
* SwTxtFrm::GetFormatted()
*************************************************************************/
// returnt this _immer_ im formatierten Zustand!
SwTxtFrm* SwTxtFrm::GetFormatted( bool bForceQuickFormat )
{
SWAP_IF_SWAPPED( this )
//Kann gut sein, dass mir der IdleCollector mir die gecachten
//Informationen entzogen hat. Calc() ruft unser Format.
//Nicht bei leeren Absaetzen!
if( !HasPara() && !(IsValid() && IsEmpty()) )
{
// Calc() muss gerufen werden, weil unsere Frameposition
// nicht stimmen muss.
const sal_Bool bFormat = GetValidSizeFlag();
Calc();
// Es kann durchaus sein, dass Calc() das Format()
// nicht anstiess (weil wir einst vom Idle-Zerstoerer
// aufgefordert wurden unsere Formatinformationen wegzuschmeissen).
// 6995: Optimierung mit FormatQuick()
if( bFormat && !FormatQuick( bForceQuickFormat ) )
Format();
}
UNDO_SWAP( this )
return this;
}
/*************************************************************************
* SwTxtFrm::CalcFitToContent()
*************************************************************************/
SwTwips SwTxtFrm::CalcFitToContent()
{
// --> FME 2004-07-16 #i31490#
// If we are currently locked, we better return with a
// fairly reasonable value:
if ( IsLocked() )
return Prt().Width();
// <--
SwParaPortion* pOldPara = GetPara();
SwParaPortion *pDummy = new SwParaPortion();
SetPara( pDummy, false );
const SwPageFrm* pPage = FindPageFrm();
const Point aOldFrmPos = Frm().Pos();
const SwTwips nOldFrmWidth = Frm().Width();
const SwTwips nOldPrtWidth = Prt().Width();
const SwTwips nPageWidth = GetUpper()->IsVertical() ?
pPage->Prt().Height() :
pPage->Prt().Width();
Frm().Width( nPageWidth );
Prt().Width( nPageWidth );
// --> FME 2004-07-19 #i25422# objects anchored as character in RTL
if ( IsRightToLeft() )
Frm().Pos().X() += nOldFrmWidth - nPageWidth;
// --> FME 2004-07-16 #i31490#
SwTxtFrmLocker aLock( this );
// <--
SwTxtFormatInfo aInf( this, sal_False, sal_True, sal_True );
aInf.SetIgnoreFly( sal_True );
SwTxtFormatter aLine( this, &aInf );
SwHookOut aHook( aInf );
// --> OD 2005-09-06 #i54031# - assure mininum of MINLAY twips.
const SwTwips nMax = Max( (SwTwips)MINLAY,
aLine._CalcFitToContent() + 1 );
// <--
Frm().Width( nOldFrmWidth );
Prt().Width( nOldPrtWidth );
// --> FME 2004-07-19 #i25422# objects anchored as character in RTL
if ( IsRightToLeft() )
Frm().Pos() = aOldFrmPos;
SetPara( pOldPara );
return nMax;
}
/** simulate format for a list item paragraph, whose list level attributes
are in LABEL_ALIGNMENT mode, in order to determine additional first
line offset for the real text formatting due to the value of label
adjustment attribute of the list level.
OD 2008-01-31 #newlistlevelattrs#
@author OD
*/
void SwTxtFrm::CalcAdditionalFirstLineOffset()
{
if ( IsLocked() )
return;
// reset additional first line offset
mnAdditionalFirstLineOffset = 0;
const SwTxtNode* pTxtNode( GetTxtNode() );
if ( pTxtNode && pTxtNode->IsNumbered() && pTxtNode->IsCountedInList() &&
pTxtNode->GetNumRule() )
{
const SwNumFmt& rNumFmt =
pTxtNode->GetNumRule()->Get( static_cast<sal_uInt16>(pTxtNode->GetActualListLevel()) );
if ( rNumFmt.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_ALIGNMENT )
{
// keep current paragraph portion and apply dummy paragraph portion
SwParaPortion* pOldPara = GetPara();
SwParaPortion *pDummy = new SwParaPortion();
SetPara( pDummy, false );
// lock paragraph
SwTxtFrmLocker aLock( this );
// simulate text formatting
SwTxtFormatInfo aInf( this, sal_False, sal_True, sal_True );
aInf.SetIgnoreFly( sal_True );
SwTxtFormatter aLine( this, &aInf );
SwHookOut aHook( aInf );
aLine._CalcFitToContent();
// determine additional first line offset
const SwLinePortion* pFirstPortion = aLine.GetCurr()->GetFirstPortion();
if ( pFirstPortion->InNumberGrp() && !pFirstPortion->IsFtnNumPortion() )
{
SwTwips nNumberPortionWidth( pFirstPortion->Width() );
const SwLinePortion* pPortion = pFirstPortion->GetPortion();
while ( pPortion &&
pPortion->InNumberGrp() && !pPortion->IsFtnNumPortion())
{
nNumberPortionWidth += pPortion->Width();
pPortion = pPortion->GetPortion();
}
if ( ( IsRightToLeft() &&
rNumFmt.GetNumAdjust() == SVX_ADJUST_LEFT ) ||
( !IsRightToLeft() &&
rNumFmt.GetNumAdjust() == SVX_ADJUST_RIGHT ) )
{
mnAdditionalFirstLineOffset = -nNumberPortionWidth;
}
else if ( rNumFmt.GetNumAdjust() == SVX_ADJUST_CENTER )
{
mnAdditionalFirstLineOffset = -(nNumberPortionWidth/2);
}
}
// restore paragraph portion
SetPara( pOldPara );
}
}
}
/** determine height of last line for the calculation of the proportional line
spacing
OD 08.01.2004 #i11859#
OD 2004-03-17 #i11860# - method <GetHeightOfLastLineForPropLineSpacing()>
replace by method <_CalcHeightOfLastLine()>. Height of last line will be
stored in new member <mnHeightOfLastLine> and can be accessed via method
<GetHeightOfLastLine()>
OD 2005-05-20 #i47162# - introduce new optional parameter <_bUseFont>
in order to force the usage of the former algorithm to determine the
height of the last line, which uses the font.
@author OD
*/
void SwTxtFrm::_CalcHeightOfLastLine( const bool _bUseFont )
{
// --> OD 2006-11-13 #i71281#
// invalidate printing area, if height of last line changes
const SwTwips mnOldHeightOfLastLine( mnHeightOfLastLine );
// <--
// determine output device
ViewShell* pVsh = getRootFrm()->GetCurrShell();
ASSERT( pVsh, "<SwTxtFrm::_GetHeightOfLastLineForPropLineSpacing()> - no ViewShell" );
// --> OD 2007-07-02 #i78921# - make code robust, according to provided patch
// There could be no <ViewShell> instance in the case of loading a binary
// StarOffice file format containing an embedded Writer document.
if ( !pVsh )
{
return;
}
OutputDevice* pOut = pVsh->GetOut();
const IDocumentSettingAccess* pIDSA = GetTxtNode()->getIDocumentSettingAccess();
if ( !pVsh->GetViewOptions()->getBrowseMode() ||
pVsh->GetViewOptions()->IsPrtFormat() )
{
pOut = GetTxtNode()->getIDocumentDeviceAccess()->getReferenceDevice( true );
}
ASSERT( pOut, "<SwTxtFrm::_GetHeightOfLastLineForPropLineSpacing()> - no OutputDevice" );
// --> OD 2007-07-02 #i78921# - make code robust, according to provided patch
if ( !pOut )
{
return;
}
// <--
// determine height of last line
if ( _bUseFont || pIDSA->get(IDocumentSettingAccess::OLD_LINE_SPACING ) )
{
// former determination of last line height for proprotional line
// spacing - take height of font set at the paragraph
SwFont aFont( GetAttrSet(), pIDSA );
// Wir muessen dafuer sorgen, dass am OutputDevice der Font
// korrekt restauriert wird, sonst droht ein Last!=Owner.
if ( pLastFont )
{
SwFntObj *pOldFont = pLastFont;
pLastFont = NULL;
aFont.SetFntChg( sal_True );
aFont.ChgPhysFnt( pVsh, *pOut );
mnHeightOfLastLine = aFont.GetHeight( pVsh, *pOut );
pLastFont->Unlock();
pLastFont = pOldFont;
pLastFont->SetDevFont( pVsh, *pOut );
}
else
{
Font aOldFont = pOut->GetFont();
aFont.SetFntChg( sal_True );
aFont.ChgPhysFnt( pVsh, *pOut );
mnHeightOfLastLine = aFont.GetHeight( pVsh, *pOut );
pLastFont->Unlock();
pLastFont = NULL;
pOut->SetFont( aOldFont );
}
}
else
{
// new determination of last line height - take actually height of last line
// --> OD 2008-05-06 #i89000#
// assure same results, if paragraph is undersized
if ( IsUndersized() )
{
mnHeightOfLastLine = 0;
}
else
{
bool bCalcHeightOfLastLine = true;
if ( !HasPara() )
{
if ( IsEmpty() )
{
mnHeightOfLastLine = EmptyHeight();
bCalcHeightOfLastLine = false;
}
}
if ( bCalcHeightOfLastLine )
{
ASSERT( HasPara(),
"<SwTxtFrm::_CalcHeightOfLastLine()> - missing paragraph portions." );
const SwLineLayout* pLineLayout = GetPara();
while ( pLineLayout && pLineLayout->GetNext() )
{
// iteration to last line
pLineLayout = pLineLayout->GetNext();
}
if ( pLineLayout )
{
SwTwips nAscent, nDescent, nDummy1, nDummy2;
// --> OD 2005-05-20 #i47162# - suppress consideration of
// fly content portions and the line portion.
pLineLayout->MaxAscentDescent( nAscent, nDescent,
nDummy1, nDummy2,
0, true );
// <--
// --> OD 2006-11-22 #i71281#
// Suppress wrong invalidation of printing area, if method is
// called recursive.
// Thus, member <mnHeightOfLastLine> is only set directly, if
// no recursive call is needed.
// mnHeightOfLastLine = nAscent + nDescent;
const SwTwips nNewHeightOfLastLine = nAscent + nDescent;
// --> OD 2005-05-20 #i47162# - if last line only contains
// fly content portions, <mnHeightOfLastLine> is zero.
// In this case determine height of last line by the font
if ( nNewHeightOfLastLine == 0 )
{
_CalcHeightOfLastLine( true );
}
else
{
mnHeightOfLastLine = nNewHeightOfLastLine;
}
// <--
// <--
}
}
}
// <--
}
// --> OD 2006-11-13 #i71281#
// invalidate printing area, if height of last line changes
if ( mnHeightOfLastLine != mnOldHeightOfLastLine )
{
InvalidatePrt();
}
// <--
}
/*************************************************************************
* SwTxtFrm::GetLineSpace()
*************************************************************************/
// OD 07.01.2004 #i11859# - change return data type
// add default parameter <_bNoPropLineSpacing> to control, if the
// value of a proportional line spacing is returned or not
// OD 07.01.2004 - trying to describe purpose of method:
// Method returns the value of the inter line spacing for a text frame.
// Such a value exists for proportional line spacings ("1,5 Lines",
// "Double", "Proportional" and for leading line spacing ("Leading").
// By parameter <_bNoPropLineSpace> (default value false) it can be
// controlled, if the value of a proportional line spacing is returned.
long SwTxtFrm::GetLineSpace( const bool _bNoPropLineSpace ) const
{
long nRet = 0;
const SwAttrSet* pSet = GetAttrSet();
const SvxLineSpacingItem &rSpace = pSet->GetLineSpacing();
switch( rSpace.GetInterLineSpaceRule() )
{
case SVX_INTER_LINE_SPACE_PROP:
{
// OD 07.01.2004 #i11859#
if ( _bNoPropLineSpace )
{
break;
}
// OD 2004-03-17 #i11860# - use method <GetHeightOfLastLine()>
nRet = GetHeightOfLastLine();
long nTmp = nRet;
nTmp *= rSpace.GetPropLineSpace();
nTmp /= 100;
nTmp -= nRet;
if ( nTmp > 0 )
nRet = nTmp;
else
nRet = 0;
}
break;
case SVX_INTER_LINE_SPACE_FIX:
{
if ( rSpace.GetInterLineSpace() > 0 )
nRet = rSpace.GetInterLineSpace();
}
break;
default:
break;
}
return nRet;
}
/*************************************************************************
* SwTxtFrm::FirstLineHeight()
*************************************************************************/
KSHORT SwTxtFrm::FirstLineHeight() const
{
if ( !HasPara() )
{
if( IsEmpty() && IsValid() )
return IsVertical() ? (KSHORT)Prt().Width() : (KSHORT)Prt().Height();
return KSHRT_MAX;
}
const SwParaPortion *pPara = GetPara();
if ( !pPara )
return KSHRT_MAX;
return pPara->Height();
}
MSHORT SwTxtFrm::GetLineCount( xub_StrLen nPos )
{
MSHORT nRet = 0;
SwTxtFrm *pFrm = this;
do
{
pFrm->GetFormatted();
if( !pFrm->HasPara() )
break;
SwTxtSizeInfo aInf( pFrm );
SwTxtMargin aLine( pFrm, &aInf );
if( STRING_LEN == nPos )
aLine.Bottom();
else
aLine.CharToLine( nPos );
nRet = nRet + aLine.GetLineNr();
pFrm = pFrm->GetFollow();
} while ( pFrm && pFrm->GetOfst() <= nPos );
return nRet;
}
void SwTxtFrm::ChgThisLines()
{
//not necassary to format here (GerFormatted etc.), because we have to come from there!
sal_uLong nNew = 0;
const SwLineNumberInfo &rInf = GetNode()->getIDocumentLineNumberAccess()->GetLineNumberInfo();
if ( GetTxt().Len() && HasPara() )
{
SwTxtSizeInfo aInf( this );
SwTxtMargin aLine( this, &aInf );
if ( rInf.IsCountBlankLines() )
{
aLine.Bottom();
nNew = (sal_uLong)aLine.GetLineNr();
}
else
{
do
{
if( aLine.GetCurr()->HasCntnt() )
++nNew;
} while ( aLine.NextLine() );
}
}
else if ( rInf.IsCountBlankLines() )
nNew = 1;
if ( nNew != nThisLines )
{
if ( !IsInTab() && GetAttrSet()->GetLineNumber().IsCount() )
{
nAllLines -= nThisLines;
nThisLines = nNew;
nAllLines += nThisLines;
SwFrm *pNxt = GetNextCntntFrm();
while( pNxt && pNxt->IsInTab() )
{
if( 0 != (pNxt = pNxt->FindTabFrm()) )
pNxt = pNxt->FindNextCnt();
}
if( pNxt )
pNxt->InvalidateLineNum();
//Extend repaint to the bottom.
if ( HasPara() )
{
SwRepaint *pRepaint = GetPara()->GetRepaint();
pRepaint->Bottom( Max( pRepaint->Bottom(),
Frm().Top()+Prt().Bottom()));
}
}
else //Paragraphs which are not counted should not manipulate the AllLines.
nThisLines = nNew;
}
//mba: invalidating is not necessary; if mongolian script has a problem, it should be fixed at the ritgh place
//with invalidating we probably get too much flickering
//Badaa: 2008-04-18 * Support for Classical Mongolian Script (SCMS) joint with Jiayanmin
//Ugly. How can we hack if better?
//InvalidatePage();
}
void SwTxtFrm::RecalcAllLines()
{
ValidateLineNum();
const SwAttrSet *pAttrSet = GetAttrSet();
if ( !IsInTab() )
{
const sal_uLong nOld = GetAllLines();
const SwFmtLineNumber &rLineNum = pAttrSet->GetLineNumber();
sal_uLong nNewNum;
const bool bRestart = GetTxtNode()->getIDocumentLineNumberAccess()->GetLineNumberInfo().IsRestartEachPage();
if ( !IsFollow() && rLineNum.GetStartValue() && rLineNum.IsCount() )
nNewNum = rLineNum.GetStartValue() - 1;
//If it is a follow or not has not be considered if it is a restart at each page; the
//restart should also take affekt at follows.
else if ( bRestart && FindPageFrm()->FindFirstBodyCntnt() == this )
{
nNewNum = 0;
}
else
{
SwCntntFrm *pPrv = GetPrevCntntFrm();
while ( pPrv &&
(pPrv->IsInTab() || pPrv->IsInDocBody() != IsInDocBody()) )
pPrv = pPrv->GetPrevCntntFrm();
// --> FME 2007-06-22 #i78254# Restart line numbering at page change:
// First body content may be in table!
if ( bRestart && pPrv && pPrv->FindPageFrm() != FindPageFrm() )
pPrv = 0;
// <--
nNewNum = pPrv ? ((SwTxtFrm*)pPrv)->GetAllLines() : 0;
}
if ( rLineNum.IsCount() )
nNewNum += GetThisLines();
if ( nOld != nNewNum )
{
nAllLines = nNewNum;
SwCntntFrm *pNxt = GetNextCntntFrm();
while ( pNxt &&
(pNxt->IsInTab() || pNxt->IsInDocBody() != IsInDocBody()) )
pNxt = pNxt->GetNextCntntFrm();
if ( pNxt )
{
if ( pNxt->GetUpper() != GetUpper() )
pNxt->InvalidateLineNum();
else
pNxt->_InvalidateLineNum();
}
}
}
}
void SwTxtFrm::VisitPortions( SwPortionHandler& rPH ) const
{
const SwParaPortion* pPara = GetPara();
if( pPara )
{
if ( IsFollow() )
rPH.Skip( GetOfst() );
const SwLineLayout* pLine = pPara;
while ( pLine )
{
const SwLinePortion* pPor = pLine->GetFirstPortion();
while ( pPor )
{
pPor->HandlePortion( rPH );
pPor = pPor->GetPortion();
}
rPH.LineBreak();
pLine = pLine->GetNext();
}
}
rPH.Finish();
}
/*************************************************************************
* SwTxtFrm::GetScriptInfo()
*************************************************************************/
const SwScriptInfo* SwTxtFrm::GetScriptInfo() const
{
const SwParaPortion* pPara = GetPara();
return pPara ? &pPara->GetScriptInfo() : 0;
}
/*************************************************************************
* lcl_CalcFlyBasePos()
* Helper function for SwTxtFrm::CalcBasePosForFly()
*************************************************************************/
SwTwips lcl_CalcFlyBasePos( const SwTxtFrm& rFrm, SwRect aFlyRect,
SwTxtFly& rTxtFly )
{
SWRECTFN( (&rFrm) )
SwTwips nRet = rFrm.IsRightToLeft() ?
(rFrm.Frm().*fnRect->fnGetRight)() :
(rFrm.Frm().*fnRect->fnGetLeft)();
do
{
SwRect aRect = rTxtFly.GetFrm( aFlyRect );
if ( 0 != (aRect.*fnRect->fnGetWidth)() )
{
if ( rFrm.IsRightToLeft() )
{
if ( (aRect.*fnRect->fnGetRight)() -
(aFlyRect.*fnRect->fnGetRight)() >= 0 )
{
(aFlyRect.*fnRect->fnSetRight)(
(aRect.*fnRect->fnGetLeft)() );
nRet = (aRect.*fnRect->fnGetLeft)();
}
else
break;
}
else
{
if ( (aFlyRect.*fnRect->fnGetLeft)() -
(aRect.*fnRect->fnGetLeft)() >= 0 )
{
(aFlyRect.*fnRect->fnSetLeft)(
(aRect.*fnRect->fnGetRight)() + 1 );
nRet = (aRect.*fnRect->fnGetRight)();
}
else
break;
}
}
else
break;
}
while ( (aFlyRect.*fnRect->fnGetWidth)() > 0 );
return nRet;
}
/*************************************************************************
* SwTxtFrm::CalcBasePosForFly()
*************************************************************************/
void SwTxtFrm::CalcBaseOfstForFly()
{
ASSERT( !IsVertical() || !IsSwapped(),
"SwTxtFrm::CalcBasePosForFly with swapped frame!" )
const SwNode* pNode = GetTxtNode();
if ( !pNode->getIDocumentSettingAccess()->get(IDocumentSettingAccess::ADD_FLY_OFFSETS) )
return;
SWRECTFN( this )
SwRect aFlyRect( Frm().Pos() + Prt().Pos(), Prt().SSize() );
// Get first 'real' line and adjust position and height of line rectangle
// OD 08.09.2003 #110978#, #108749#, #110354# - correct behaviour,
// if no 'real' line exists (empty paragraph with and without a dummy portion)
{
SwTwips nTop = (aFlyRect.*fnRect->fnGetTop)();
const SwLineLayout* pLay = GetPara();
SwTwips nLineHeight = 200;
while( pLay && pLay->IsDummy() && pLay->GetNext() )
{
nTop += pLay->Height();
pLay = pLay->GetNext();
}
if ( pLay )
{
nLineHeight = pLay->Height();
}
(aFlyRect.*fnRect->fnSetTopAndHeight)( nTop, nLineHeight );
}
SwTxtFly aTxtFly( this );
aTxtFly.SetIgnoreCurrentFrame( sal_True );
aTxtFly.SetIgnoreContour( sal_True );
// --> OD 2004-12-17 #118809# - ignore objects in page header|footer for
// text frames not in page header|footer
aTxtFly.SetIgnoreObjsInHeaderFooter( sal_True );
// <--
SwTwips nRet1 = lcl_CalcFlyBasePos( *this, aFlyRect, aTxtFly );
aTxtFly.SetIgnoreCurrentFrame( sal_False );
SwTwips nRet2 = lcl_CalcFlyBasePos( *this, aFlyRect, aTxtFly );
// make values relative to frame start position
SwTwips nLeft = IsRightToLeft() ?
(Frm().*fnRect->fnGetRight)() :
(Frm().*fnRect->fnGetLeft)();
mnFlyAnchorOfst = nRet1 - nLeft;
mnFlyAnchorOfstNoWrap = nRet2 - nLeft;
}
/* repaint all text frames of the given text node */
void SwTxtFrm::repaintTextFrames( const SwTxtNode& rNode )
{
SwIterator<SwTxtFrm,SwTxtNode> aIter( rNode );
for( const SwTxtFrm *pFrm = aIter.First(); pFrm; pFrm = aIter.Next() )
{
SwRect aRec( pFrm->PaintArea() );
const SwRootFrm *pRootFrm = pFrm->getRootFrm();
ViewShell *pCurShell = pRootFrm ? pRootFrm->GetCurrShell() : NULL;
if( pCurShell )
pCurShell->InvalidateWindows( aRec );
}
}