blob: 2124fc15bf9f860ca97827163d1052fe91234a40 [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 "ndtxt.hxx" // GetNode()
#include "pam.hxx" // SwPosition
#include "frmtool.hxx"
#include "viewopt.hxx"
#include "paratr.hxx"
#include "rootfrm.hxx"
#include "pagefrm.hxx"
#include "colfrm.hxx"
#include "txttypes.hxx"
#include <sfx2/printer.hxx>
#include <editeng/lrspitem.hxx>
#include <editeng/tstpitem.hxx>
#include <editeng/ulspitem.hxx>
#include <editeng/lspcitem.hxx>
#include <pormulti.hxx> // SwMultiPortion
#include <doc.hxx>
#include <sortedobjs.hxx>
#include <unicode/ubidi.h>
#include "txtcfg.hxx"
#include "txtfrm.hxx" // SwTxtFrm
#include "inftxt.hxx" // SwTxtSizeInfo
#include "itrtxt.hxx" // SwTxtCursor
#include "crstate.hxx" // SwTxtCursor
#include "viewsh.hxx" // InvalidateWindows
#include "swfntcch.hxx" // SwFontAccess
#include "flyfrm.hxx"
#if OSL_DEBUG_LEVEL > 1
#include "txtpaint.hxx"
#endif
#define MIN_OFFSET_STEP 10
using namespace ::com::sun::star;
/*
* 1170-SurvivalKit: Wie gelangt man hinter das letzte Zeichen der Zeile.
* - RightMargin verzichtet auf den Positionsausgleich mit -1
* - GetCharRect liefert bei MV_RIGHTMARGIN ein GetEndCharRect
* - GetEndCharRect setzt bRightMargin auf sal_True
* - SwTxtCursor::bRightMargin wird per CharCrsrToLine auf sal_False gesetzt
*/
/*************************************************************************
* GetAdjFrmAtPos()
*************************************************************************/
SwTxtFrm *GetAdjFrmAtPos( SwTxtFrm *pFrm, const SwPosition &rPos,
const sal_Bool bRightMargin, const sal_Bool bNoScroll = sal_True )
{
// 8810: vgl. 1170, RightMargin in der letzten Masterzeile...
const xub_StrLen nOffset = rPos.nContent.GetIndex();
SwTxtFrm *pFrmAtPos = pFrm;
if( !bNoScroll || pFrm->GetFollow() )
{
pFrmAtPos = pFrm->GetFrmAtPos( rPos );
if( nOffset < pFrmAtPos->GetOfst() &&
!pFrmAtPos->IsFollow() )
{
xub_StrLen nNew = nOffset;
if( nNew < MIN_OFFSET_STEP )
nNew = 0;
else
nNew -= MIN_OFFSET_STEP;
lcl_ChangeOffset( pFrmAtPos, nNew );
}
}
while( pFrm != pFrmAtPos )
{
pFrm = pFrmAtPos;
pFrm->GetFormatted();
pFrmAtPos = (SwTxtFrm*)pFrm->GetFrmAtPos( rPos );
}
if( nOffset && bRightMargin )
{
while( pFrmAtPos && pFrmAtPos->GetOfst() == nOffset &&
pFrmAtPos->IsFollow() )
{
pFrmAtPos->GetFormatted();
pFrmAtPos = pFrmAtPos->FindMaster();
}
ASSERT( pFrmAtPos, "+GetCharRect: no frame with my rightmargin" );
}
return pFrmAtPos ? pFrmAtPos : pFrm;
}
sal_Bool lcl_ChangeOffset( SwTxtFrm* pFrm, xub_StrLen nNew )
{
// In Bereichen und ausserhalb von Flies wird nicht mehr gescrollt.
ASSERT( !pFrm->IsFollow(), "Illegal Scrolling by Follow!" );
if( pFrm->GetOfst() != nNew && !pFrm->IsInSct() )
{
SwFlyFrm *pFly = pFrm->FindFlyFrm();
// Vorsicht, wenn z.B. bei einem spaltigen Rahmen die Groesse noch invalide ist,
// duerfen wir nicht mal eben herumscrollen
if ( ( pFly && pFly->IsValid() &&
!pFly->GetNextLink() && !pFly->GetPrevLink() ) ||
( !pFly && pFrm->IsInTab() ) )
{
ViewShell* pVsh = pFrm->getRootFrm()->GetCurrShell();
if( pVsh )
{
if( pVsh->GetNext() != pVsh ||
( pFrm->GetDrawObjs() && pFrm->GetDrawObjs()->Count() ) )
{
if( !pFrm->GetOfst() )
return sal_False;
nNew = 0;
}
pFrm->SetOfst( nNew );
pFrm->SetPara( 0 );
pFrm->GetFormatted();
if( pFrm->Frm().HasArea() )
pFrm->getRootFrm()->GetCurrShell()->InvalidateWindows( pFrm->Frm() );
return sal_True;
}
}
}
return sal_False;
}
/*************************************************************************
* GetFrmAtOfst(), GetFrmAtPos()
*************************************************************************/
// OD 07.10.2003 #110978#
SwTxtFrm& SwTxtFrm::GetFrmAtOfst( const xub_StrLen nWhere )
{
SwTxtFrm* pRet = this;
while( pRet->HasFollow() && nWhere >= pRet->GetFollow()->GetOfst() )
pRet = pRet->GetFollow();
return *pRet;
}
SwTxtFrm *SwTxtFrm::GetFrmAtPos( const SwPosition &rPos )
{
SwTxtFrm *pFoll = (SwTxtFrm*)this;
while( pFoll->GetFollow() )
{
if( rPos.nContent.GetIndex() > pFoll->GetFollow()->GetOfst() )
pFoll = pFoll->GetFollow();
else
{
if( rPos.nContent.GetIndex() == pFoll->GetFollow()->GetOfst()
&& !SwTxtCursor::IsRightMargin() )
pFoll = pFoll->GetFollow();
else
break;
}
}
return pFoll;
}
/*************************************************************************
* SwTxtFrm::GetCharRect()
*************************************************************************/
/*
* GetCharRect() findet die Characterzelle des Characters, dass
* durch aPos beschrieben wird. GetCrsrOfst() findet den
* umgekehrten Weg: Von einer Dokumentkoordinate zu einem Pam.
* Beide sind virtuell in der Framebasisklasse und werden deshalb
* immer angezogen.
*/
sal_Bool SwTxtFrm::GetCharRect( SwRect& rOrig, const SwPosition &rPos,
SwCrsrMoveState *pCMS ) const
{
ASSERT( ! IsVertical() || ! IsSwapped(),"SwTxtFrm::GetCharRect with swapped frame" );
if( IsLocked() || IsHiddenNow() )
return sal_False;
//Erstmal den richtigen Frm finden, dabei muss beachtet werden, dass:
//- die gecachten Informationen verworfen sein koennen (GetPara() == 0)
//- das ein Follow gemeint sein kann
//- das die Kette der Follows dynamisch waechst; der in den wir
// schliesslich gelangen muss aber Formatiert sein.
// opt: reading ahead erspart uns ein GetAdjFrmAtPos
const sal_Bool bRightMargin = pCMS && ( MV_RIGHTMARGIN == pCMS->eState );
const sal_Bool bNoScroll = pCMS && pCMS->bNoScroll;
SwTxtFrm *pFrm = GetAdjFrmAtPos( (SwTxtFrm*)this, rPos, bRightMargin,
bNoScroll );
pFrm->GetFormatted();
const SwFrm* pTmpFrm = (SwFrm*)pFrm->GetUpper();
SWRECTFN ( pFrm )
const SwTwips nUpperMaxY = (pTmpFrm->*fnRect->fnGetPrtBottom)();
const SwTwips nFrmMaxY = (pFrm->*fnRect->fnGetPrtBottom)();
// nMaxY is an absolute value
//Badaa: 2008-04-18 * Support for Classical Mongolian Script (SCMS) joint with Jiayanmin
SwTwips nMaxY = bVert ?
( bVertL2R ? Min( nFrmMaxY, nUpperMaxY ) : Max( nFrmMaxY, nUpperMaxY ) ) :
Min( nFrmMaxY, nUpperMaxY );
sal_Bool bRet = sal_False;
if ( pFrm->IsEmpty() || ! (pFrm->Prt().*fnRect->fnGetHeight)() )
{
Point aPnt1 = pFrm->Frm().Pos() + pFrm->Prt().Pos();
SwTxtNode* pTxtNd = ((SwTxtFrm*)this)->GetTxtNode();
short nFirstOffset;
pTxtNd->GetFirstLineOfsWithNum( nFirstOffset );
Point aPnt2;
if ( bVert )
{
if( nFirstOffset > 0 )
aPnt1.Y() += nFirstOffset;
//Badaa: 2008-04-18 * Support for Classical Mongolian Script (SCMS) joint with Jiayanmin
if ( aPnt1.X() < nMaxY && !bVertL2R )
aPnt1.X() = nMaxY;
aPnt2.X() = aPnt1.X() + pFrm->Prt().Width();
aPnt2.Y() = aPnt1.Y();
if( aPnt2.X() < nMaxY )
aPnt2.X() = nMaxY;
}
else
{
if( nFirstOffset > 0 )
aPnt1.X() += nFirstOffset;
if( aPnt1.Y() > nMaxY )
aPnt1.Y() = nMaxY;
aPnt2.X() = aPnt1.X();
aPnt2.Y() = aPnt1.Y() + pFrm->Prt().Height();
if( aPnt2.Y() > nMaxY )
aPnt2.Y() = nMaxY;
}
rOrig = SwRect( aPnt1, aPnt2 );
if ( pCMS )
{
pCMS->aRealHeight.X() = 0;
pCMS->aRealHeight.Y() = bVert ? -rOrig.Width() : rOrig.Height();
}
if ( pFrm->IsRightToLeft() )
pFrm->SwitchLTRtoRTL( rOrig );
bRet = sal_True;
}
else
{
if( !pFrm->HasPara() )
return sal_False;
SwFrmSwapper aSwapper( pFrm, sal_True );
if ( bVert )
nMaxY = pFrm->SwitchVerticalToHorizontal( nMaxY );
sal_Bool bGoOn = sal_True;
xub_StrLen nOffset = rPos.nContent.GetIndex();
xub_StrLen nNextOfst;
do
{
{
SwTxtSizeInfo aInf( pFrm );
SwTxtCursor aLine( pFrm, &aInf );
nNextOfst = aLine.GetEnd();
// Siehe Kommentar in AdjustFrm
// 1170: das letzte Zeichen der Zeile mitnehmen?
bRet = bRightMargin ? aLine.GetEndCharRect( &rOrig, nOffset, pCMS, nMaxY )
: aLine.GetCharRect( &rOrig, nOffset, pCMS, nMaxY );
}
if ( pFrm->IsRightToLeft() )
pFrm->SwitchLTRtoRTL( rOrig );
if ( bVert )
pFrm->SwitchHorizontalToVertical( rOrig );
if( pFrm->IsUndersized() && pCMS && !pFrm->GetNext() &&
(rOrig.*fnRect->fnGetBottom)() == nUpperMaxY &&
pFrm->GetOfst() < nOffset &&
!pFrm->IsFollow() && !bNoScroll &&
pFrm->GetTxtNode()->GetTxt().Len() != nNextOfst )
bGoOn = lcl_ChangeOffset( pFrm, nNextOfst );
else
bGoOn = sal_False;
} while ( bGoOn );
if ( pCMS )
{
if ( pFrm->IsRightToLeft() )
{
if( pCMS->b2Lines && pCMS->p2Lines)
{
pFrm->SwitchLTRtoRTL( pCMS->p2Lines->aLine );
pFrm->SwitchLTRtoRTL( pCMS->p2Lines->aPortion );
}
}
if ( bVert )
{
if ( pCMS->bRealHeight )
{
pCMS->aRealHeight.Y() = -pCMS->aRealHeight.Y();
if ( pCMS->aRealHeight.Y() < 0 )
{
// writing direction is from top to bottom
pCMS->aRealHeight.X() = ( rOrig.Width() -
pCMS->aRealHeight.X() +
pCMS->aRealHeight.Y() );
}
}
if( pCMS->b2Lines && pCMS->p2Lines)
{
pFrm->SwitchHorizontalToVertical( pCMS->p2Lines->aLine );
pFrm->SwitchHorizontalToVertical( pCMS->p2Lines->aPortion );
}
}
}
}
if( bRet )
{
SwPageFrm *pPage = pFrm->FindPageFrm();
ASSERT( pPage, "Text esaped from page?" );
const SwTwips nOrigTop = (rOrig.*fnRect->fnGetTop)();
const SwTwips nPageTop = (pPage->Frm().*fnRect->fnGetTop)();
const SwTwips nPageBott = (pPage->Frm().*fnRect->fnGetBottom)();
// Following situation: if the frame is in an invalid sectionframe,
// it's possible that the frame is outside the page. If we restrict
// the cursor position to the page area, we enforce the formatting
// of the page, of the section frame and the frame himself.
if( (*fnRect->fnYDiff)( nPageTop, nOrigTop ) > 0 )
(rOrig.*fnRect->fnSetTop)( nPageTop );
if ( (*fnRect->fnYDiff)( nOrigTop, nPageBott ) > 0 )
(rOrig.*fnRect->fnSetTop)( nPageBott );
}
return bRet;
}
/*************************************************************************
* SwTxtFrm::GetAutoPos()
*************************************************************************/
/*
* GetAutoPos() findet die Characterzelle des Characters, dass
* durch aPos beschrieben wird und wird von autopositionierten Rahmen genutzt.
*/
sal_Bool SwTxtFrm::GetAutoPos( SwRect& rOrig, const SwPosition &rPos ) const
{
if( IsHiddenNow() )
return sal_False;
xub_StrLen nOffset = rPos.nContent.GetIndex();
SwTxtFrm* pFrm = &(const_cast<SwTxtFrm*>(this)->GetFrmAtOfst( nOffset ));
pFrm->GetFormatted();
const SwFrm* pTmpFrm = (SwFrm*)pFrm->GetUpper();
SWRECTFN( pTmpFrm )
SwTwips nUpperMaxY = (pTmpFrm->*fnRect->fnGetPrtBottom)();
// nMaxY is in absolute value
//Badaa: 2008-04-18 * Support for Classical Mongolian Script (SCMS) joint with Jiayanmin
SwTwips nMaxY = bVert ?
( bVertL2R ? Min( (pFrm->*fnRect->fnGetPrtBottom)(), nUpperMaxY ) : Max( (pFrm->*fnRect->fnGetPrtBottom)(), nUpperMaxY ) ) :
Min( (pFrm->*fnRect->fnGetPrtBottom)(), nUpperMaxY );
if ( pFrm->IsEmpty() || ! (pFrm->Prt().*fnRect->fnGetHeight)() )
{
Point aPnt1 = pFrm->Frm().Pos() + pFrm->Prt().Pos();
Point aPnt2;
if ( bVert )
{
if ( aPnt1.X() < nMaxY && !bVertL2R )
aPnt1.X() = nMaxY;
aPnt2.X() = aPnt1.X() + pFrm->Prt().Width();
aPnt2.Y() = aPnt1.Y();
if( aPnt2.X() < nMaxY )
aPnt2.X() = nMaxY;
}
else
{
if( aPnt1.Y() > nMaxY )
aPnt1.Y() = nMaxY;
aPnt2.X() = aPnt1.X();
aPnt2.Y() = aPnt1.Y() + pFrm->Prt().Height();
if( aPnt2.Y() > nMaxY )
aPnt2.Y() = nMaxY;
}
rOrig = SwRect( aPnt1, aPnt2 );
return sal_True;
}
else
{
if( !pFrm->HasPara() )
return sal_False;
SwFrmSwapper aSwapper( pFrm, sal_True );
if ( bVert )
nMaxY = pFrm->SwitchVerticalToHorizontal( nMaxY );
SwTxtSizeInfo aInf( pFrm );
SwTxtCursor aLine( pFrm, &aInf );
SwCrsrMoveState aTmpState( MV_SETONLYTEXT );
aTmpState.bRealHeight = sal_True;
if( aLine.GetCharRect( &rOrig, nOffset, &aTmpState, nMaxY ) )
{
if( aTmpState.aRealHeight.X() >= 0 )
{
rOrig.Pos().Y() += aTmpState.aRealHeight.X();
rOrig.Height( aTmpState.aRealHeight.Y() );
}
if ( pFrm->IsRightToLeft() )
pFrm->SwitchLTRtoRTL( rOrig );
if ( bVert )
pFrm->SwitchHorizontalToVertical( rOrig );
return sal_True;
}
return sal_False;
}
}
/** determine top of line for given position in the text frame
OD 11.11.2003 #i22341#
OD 2004-03-18 #114789# - corrections:
- Top of first paragraph line is the top of the printing area of the text frame
- If a proportional line spacing is applied use top of anchor character as
top of the line.
@author OD
*/
bool SwTxtFrm::GetTopOfLine( SwTwips& _onTopOfLine,
const SwPosition& _rPos ) const
{
bool bRet = true;
// get position offset
xub_StrLen nOffset = _rPos.nContent.GetIndex();
if ( GetTxt().Len() < nOffset )
{
bRet = false;
}
else
{
SWRECTFN( this )
if ( IsEmpty() || !(Prt().*fnRect->fnGetHeight)() )
{
// OD 2004-03-18 #i11860# - consider upper space amount considered
// for previous frame and the page grid.
_onTopOfLine = (this->*fnRect->fnGetPrtTop)();
}
else
{
// determine formatted text frame that contains the requested position
SwTxtFrm* pFrm = &(const_cast<SwTxtFrm*>(this)->GetFrmAtOfst( nOffset ));
pFrm->GetFormatted();
SWREFRESHFN( pFrm )
// OD 2004-03-18 #114789# - If proportional line spacing is applied
// to the text frame, the top of the anchor character is also the
// top of the line.
// Otherwise the line layout determines the top of the line
const SvxLineSpacingItem& rSpace = GetAttrSet()->GetLineSpacing();
if ( rSpace.GetInterLineSpaceRule() == SVX_INTER_LINE_SPACE_PROP )
{
SwRect aCharRect;
if ( GetAutoPos( aCharRect, _rPos ) )
{
_onTopOfLine = (aCharRect.*fnRect->fnGetTop)();
}
else
{
bRet = false;
}
}
else
{
// assure that text frame is in a horizontal layout
SwFrmSwapper aSwapper( pFrm, sal_True );
// determine text line that contains the requested position
SwTxtSizeInfo aInf( pFrm );
SwTxtCursor aLine( pFrm, &aInf );
aLine.CharCrsrToLine( nOffset );
// determine top of line
_onTopOfLine = aLine.Y();
if ( bVert )
{
_onTopOfLine = pFrm->SwitchHorizontalToVertical( _onTopOfLine );
}
}
}
}
return bRet;
}
/*************************************************************************
* SwTxtFrm::_GetCrsrOfst()
*************************************************************************/
// Minimaler Abstand von nichtleeren Zeilen etwas weniger als 2 cm
#define FILL_MIN_DIST 1100
struct SwFillData
{
SwRect aFrm;
const SwCrsrMoveState *pCMS;
SwPosition* pPos;
const Point& rPoint;
SwTwips nLineWidth;
sal_Bool bFirstLine : 1;
sal_Bool bInner : 1;
sal_Bool bColumn : 1;
sal_Bool bEmpty : 1;
SwFillData( const SwCrsrMoveState *pC, SwPosition* pP, const SwRect& rR,
const Point& rPt ) : aFrm( rR ), pCMS( pC ), pPos( pP ), rPoint( rPt ),
nLineWidth( 0 ), bFirstLine( sal_True ), bInner( sal_False ), bColumn( sal_False ),
bEmpty( sal_True ){}
SwFillMode Mode() const { return pCMS->pFill->eMode; }
long X() const { return rPoint.X(); }
long Y() const { return rPoint.Y(); }
long Left() const { return aFrm.Left(); }
long Right() const { return aFrm.Right(); }
long Bottom() const { return aFrm.Bottom(); }
SwRect& Frm() { return aFrm; }
SwFillCrsrPos &Fill() const { return *pCMS->pFill; }
void SetTab( MSHORT nNew ) { pCMS->pFill->nTabCnt = nNew; }
void SetSpace( MSHORT nNew ) { pCMS->pFill->nSpaceCnt = nNew; }
void SetOrient( const sal_Int16 eNew ){ pCMS->pFill->eOrient = eNew; }
};
sal_Bool SwTxtFrm::_GetCrsrOfst(SwPosition* pPos, const Point& rPoint,
const sal_Bool bChgFrm, SwCrsrMoveState* pCMS ) const
{
// 8804: _GetCrsrOfst wird vom GetCrsrOfst und GetKeyCrsrOfst gerufen.
// In keinem Fall nur ein return sal_False.
if( IsLocked() || IsHiddenNow() )
return sal_False;
((SwTxtFrm*)this)->GetFormatted();
Point aOldPoint( rPoint );
if ( IsVertical() )
{
SwitchVerticalToHorizontal( (Point&)rPoint );
((SwTxtFrm*)this)->SwapWidthAndHeight();
}
if ( IsRightToLeft() )
SwitchRTLtoLTR( (Point&)rPoint );
SwFillData *pFillData = ( pCMS && pCMS->pFill ) ?
new SwFillData( pCMS, pPos, Frm(), rPoint ) : NULL;
if ( IsEmpty() )
{
SwTxtNode* pTxtNd = ((SwTxtFrm*)this)->GetTxtNode();
pPos->nNode = *pTxtNd;
pPos->nContent.Assign( pTxtNd, 0 );
if( pCMS && pCMS->bFieldInfo )
{
SwTwips nDiff = rPoint.X() - Frm().Left() - Prt().Left();
if( nDiff > 50 || nDiff < 0 )
((SwCrsrMoveState*)pCMS)->bPosCorr = sal_True;
}
}
else
{
SwTxtSizeInfo aInf( (SwTxtFrm*)this );
SwTxtCursor aLine( ((SwTxtFrm*)this), &aInf );
// Siehe Kommentar in AdjustFrm()
SwTwips nMaxY = Frm().Top() + Prt().Top() + Prt().Height();
aLine.TwipsToLine( rPoint.Y() );
while( aLine.Y() + aLine.GetLineHeight() > nMaxY )
{
DBG_LOOP;
if( !aLine.Prev() )
break;
}
if( aLine.GetDropLines() >= aLine.GetLineNr() && 1 != aLine.GetLineNr()
&& rPoint.X() < aLine.FirstLeft() + aLine.GetDropLeft() )
while( aLine.GetLineNr() > 1 )
aLine.Prev();
xub_StrLen nOffset = aLine.GetCrsrOfst( pPos, rPoint, bChgFrm, pCMS );
if( pCMS && pCMS->eState == MV_NONE && aLine.GetEnd() == nOffset )
((SwCrsrMoveState*)pCMS)->eState = MV_RIGHTMARGIN;
// 6776: pPos ist ein reiner IN-Parameter, der nicht ausgewertet werden darf.
// Das pIter->GetCrsrOfst returnt aus einer Verschachtelung mit STRING_LEN.
// Wenn SwTxtIter::GetCrsrOfst von sich aus weitere GetCrsrOfst
// ruft, so aendert sich nNode der Position. In solchen Faellen
// darf pPos nicht berechnet werden.
if( STRING_LEN != nOffset )
{
SwTxtNode* pTxtNd = ((SwTxtFrm*)this)->GetTxtNode();
pPos->nNode = *pTxtNd;
pPos->nContent.Assign( pTxtNd, nOffset );
if( pFillData )
{
if( pTxtNd->GetTxt().Len() > nOffset ||
rPoint.Y() < Frm().Top() )
pFillData->bInner = sal_True;
pFillData->bFirstLine = aLine.GetLineNr() < 2;
if( pTxtNd->GetTxt().Len() )
{
pFillData->bEmpty = sal_False;
pFillData->nLineWidth = aLine.GetCurr()->Width();
}
}
}
}
sal_Bool bChgFillData = sal_False;
if( pFillData && FindPageFrm()->Frm().IsInside( aOldPoint ) )
{
FillCrsrPos( *pFillData );
bChgFillData = sal_True;
}
if ( IsVertical() )
{
if ( bChgFillData )
SwitchHorizontalToVertical( pFillData->Fill().aCrsr.Pos() );
((SwTxtFrm*)this)->SwapWidthAndHeight();
}
if ( IsRightToLeft() && bChgFillData )
{
SwitchLTRtoRTL( pFillData->Fill().aCrsr.Pos() );
const sal_Int16 eOrient = pFillData->pCMS->pFill->eOrient;
if ( text::HoriOrientation::LEFT == eOrient )
pFillData->SetOrient( text::HoriOrientation::RIGHT );
else if ( text::HoriOrientation::RIGHT == eOrient )
pFillData->SetOrient( text::HoriOrientation::LEFT );
}
(Point&)rPoint = aOldPoint;
delete pFillData;
return sal_True;
}
/*************************************************************************
* virtual SwTxtFrm::GetCrsrOfst()
*************************************************************************/
sal_Bool SwTxtFrm::GetCrsrOfst(SwPosition* pPos, Point& rPoint,
SwCrsrMoveState* pCMS ) const
{
MSHORT nChgFrm = 2;
if( pCMS )
{
if( MV_UPDOWN == pCMS->eState )
nChgFrm = 0;
else if( MV_SETONLYTEXT == pCMS->eState ||
MV_TBLSEL == pCMS->eState )
nChgFrm = 1;
}
return _GetCrsrOfst( pPos, rPoint, nChgFrm != 0, pCMS );
}
/*************************************************************************
* SwTxtFrm::LeftMargin()
*************************************************************************/
/*
* Layout-orientierte Cursorbewegungen
*/
/*
* an den Zeilenanfang
*/
sal_Bool SwTxtFrm::LeftMargin(SwPaM *pPam) const
{
if( ((const SwNode*)pPam->GetNode()) != GetNode() )
pPam->GetPoint()->nNode = *((SwTxtFrm*)this)->GetTxtNode();
SwTxtFrm *pFrm = GetAdjFrmAtPos( (SwTxtFrm*)this, *pPam->GetPoint(),
SwTxtCursor::IsRightMargin() );
pFrm->GetFormatted();
xub_StrLen nIndx;
if ( pFrm->IsEmpty() )
nIndx = 0;
else
{
SwTxtSizeInfo aInf( pFrm );
SwTxtCursor aLine( pFrm, &aInf );
aLine.CharCrsrToLine(pPam->GetPoint()->nContent.GetIndex());
nIndx = aLine.GetStart();
if( pFrm->GetOfst() && !pFrm->IsFollow() && !aLine.GetPrev() )
{
lcl_ChangeOffset( pFrm, 0 );
nIndx = 0;
}
}
pPam->GetPoint()->nContent = SwIndex( pFrm->GetTxtNode(), nIndx );
SwTxtCursor::SetRightMargin( sal_False );
return sal_True;
}
/*************************************************************************
* SwTxtFrm::RightMargin()
*************************************************************************/
/*
* An das Zeilenende:Das ist die Position vor dem letzten
* Character in der Zeile. Ausnahme: In der letzten Zeile soll
* der Cursor auch hinter dem letzten Character stehen koennen,
* um Text anhaengen zu koennen.
*
*/
sal_Bool SwTxtFrm::RightMargin(SwPaM *pPam, sal_Bool bAPI) const
{
if( ((const SwNode*)pPam->GetNode()) != GetNode() )
pPam->GetPoint()->nNode = *((SwTxtFrm*)this)->GetTxtNode();
SwTxtFrm *pFrm = GetAdjFrmAtPos( (SwTxtFrm*)this, *pPam->GetPoint(),
SwTxtCursor::IsRightMargin() );
pFrm->GetFormatted();
xub_StrLen nRightMargin;
if ( IsEmpty() )
nRightMargin = 0;
else
{
SwTxtSizeInfo aInf( pFrm );
SwTxtCursor aLine( pFrm, &aInf );
aLine.CharCrsrToLine(pPam->GetPoint()->nContent.GetIndex());
nRightMargin = aLine.GetStart() + aLine.GetCurr()->GetLen();
// Harte Zeilenumbrueche lassen wir hinter uns.
if( aLine.GetCurr()->GetLen() &&
CH_BREAK == aInf.GetTxt().GetChar( nRightMargin - 1 ) )
--nRightMargin;
else if( !bAPI && (aLine.GetNext() || pFrm->GetFollow()) )
{
while( nRightMargin > aLine.GetStart() &&
' ' == aInf.GetTxt().GetChar( nRightMargin - 1 ) )
--nRightMargin;
}
}
pPam->GetPoint()->nContent = SwIndex( pFrm->GetTxtNode(), nRightMargin );
SwTxtCursor::SetRightMargin( !bAPI );
return sal_True;
}
/*************************************************************************
* SwTxtFrm::_UnitUp()
*************************************************************************/
//Die beiden folgenden Methoden versuchen zunaechst den Crsr in die
//nachste/folgende Zeile zu setzen. Gibt es im Frame keine vorhergehende/
//folgende Zeile, so wird der Aufruf an die Basisklasse weitergeleitet.
//Die Horizontale Ausrichtung des Crsr wird hinterher von der CrsrShell
//vorgenommen.
class SwSetToRightMargin
{
sal_Bool bRight;
public:
inline SwSetToRightMargin() : bRight( sal_False ) { }
inline ~SwSetToRightMargin() { SwTxtCursor::SetRightMargin( bRight ); }
inline void SetRight( const sal_Bool bNew ) { bRight = bNew; }
};
sal_Bool SwTxtFrm::_UnitUp( SwPaM *pPam, const SwTwips nOffset,
sal_Bool bSetInReadOnly ) const
{
// 8626: Im Notfall den RightMargin setzen.
SwSetToRightMargin aSet;
if( IsInTab() &&
pPam->GetNode( sal_True )->StartOfSectionNode() !=
pPam->GetNode( sal_False )->StartOfSectionNode() )
{
//Wenn der PaM in unterschiedlichen Boxen sitzt, so handelt es sich um
//eine Tabellenselektion; diese wird von der Basisklasse abgearbeitet.
return SwCntntFrm::UnitUp( pPam, nOffset, bSetInReadOnly );
}
((SwTxtFrm*)this)->GetFormatted();
const xub_StrLen nPos = pPam->GetPoint()->nContent.GetIndex();
SwRect aCharBox;
if( !IsEmpty() && !IsHiddenNow() )
{
xub_StrLen nFormat = STRING_LEN;
do
{
if( nFormat != STRING_LEN && !IsFollow() )
lcl_ChangeOffset( ((SwTxtFrm*)this), nFormat );
SwTxtSizeInfo aInf( (SwTxtFrm*)this );
SwTxtCursor aLine( ((SwTxtFrm*)this), &aInf );
// 8116: Flys ohne Umlauf und IsDummy(); hier wegoptimiert
if( nPos )
aLine.CharCrsrToLine( nPos );
else
aLine.Top();
const SwLineLayout *pPrevLine = aLine.GetPrevLine();
const xub_StrLen nStart = aLine.GetStart();
aLine.GetCharRect( &aCharBox, nPos );
sal_Bool bSecondOfDouble = ( aInf.IsMulti() && ! aInf.IsFirstMulti() );
sal_Bool bPrevLine = ( pPrevLine && pPrevLine != aLine.GetCurr() );
if( !pPrevLine && !bSecondOfDouble && GetOfst() && !IsFollow() )
{
nFormat = GetOfst();
xub_StrLen nDiff = aLine.GetLength();
if( !nDiff )
nDiff = MIN_OFFSET_STEP;
if( nFormat > nDiff )
nFormat = nFormat - nDiff;
else
nFormat = 0;
continue;
}
// we select the target line for the cursor, in case we are in a
// double line portion, prev line = curr line
if( bPrevLine && !bSecondOfDouble )
{
aLine.PrevLine();
while ( aLine.GetStart() == nStart &&
0 != ( pPrevLine = aLine.GetPrevLine() ) &&
pPrevLine != aLine.GetCurr() )
aLine.PrevLine();
}
if ( bPrevLine || bSecondOfDouble )
{
aCharBox.SSize().Width() /= 2;
aCharBox.Pos().X() = aCharBox.Pos().X() - 150;
// siehe Kommentar in SwTxtFrm::GetCrsrOfst()
#ifdef DBG_UTIL
const sal_uLong nOldNode = pPam->GetPoint()->nNode.GetIndex();
#endif
// Der Node soll nicht gewechselt werden
xub_StrLen nTmpOfst = aLine.GetCrsrOfst( pPam->GetPoint(),
aCharBox.Pos(), sal_False );
ASSERT( nOldNode == pPam->GetPoint()->nNode.GetIndex(),
"SwTxtFrm::UnitUp: illegal node change" )
// 7684: Wir stellen sicher, dass wir uns nach oben bewegen.
if( nTmpOfst >= nStart && nStart && !bSecondOfDouble )
{
nTmpOfst = nStart;
aSet.SetRight( sal_True );
}
pPam->GetPoint()->nContent =
SwIndex( ((SwTxtFrm*)this)->GetTxtNode(), nTmpOfst );
return sal_True;
}
if ( IsFollow() )
{
aLine.GetCharRect( &aCharBox, nPos );
aCharBox.SSize().Width() /= 2;
}
break;
} while ( sal_True );
}
/* Wenn this ein Follow ist und ein Prev miszlang, so
* muessen wir in die letzte Zeile des Master ... und der sind wir.
* Oder wir sind ein Follow mit Follow, dann muessen wir uns den
* Master extra besorgen...
*/
if ( IsFollow() )
{
const SwTxtFrm *pTmpPrev = FindMaster();
xub_StrLen nOffs = GetOfst();
if( pTmpPrev )
{
ViewShell *pSh = getRootFrm()->GetCurrShell();
sal_Bool bProtectedAllowed = pSh && pSh->GetViewOptions()->IsCursorInProtectedArea();
const SwTxtFrm *pPrevPrev = pTmpPrev;
// Hier werden geschuetzte Frames und Frame ohne Inhalt ausgelassen
while( pPrevPrev && ( pPrevPrev->GetOfst() == nOffs ||
( !bProtectedAllowed && pPrevPrev->IsProtected() ) ) )
{
pTmpPrev = pPrevPrev;
nOffs = pTmpPrev->GetOfst();
if ( pPrevPrev->IsFollow() )
pPrevPrev = pTmpPrev->FindMaster();
else
pPrevPrev = NULL;
}
if ( !pPrevPrev )
return pTmpPrev->SwCntntFrm::UnitUp( pPam, nOffset, bSetInReadOnly );
aCharBox.Pos().Y() = pPrevPrev->Frm().Bottom() - 1;
return pPrevPrev->GetKeyCrsrOfst( pPam->GetPoint(), aCharBox.Pos() );
}
}
return SwCntntFrm::UnitUp( pPam, nOffset, bSetInReadOnly );
}
//
// Used for Bidi. nPos is the logical position in the string, bLeft indicates
// if left arrow or right arrow was pressed. The return values are:
// nPos: the new visual position
// bLeft: whether the break iterator has to add or subtract from the
// current position
void lcl_VisualMoveRecursion( const SwLineLayout& rCurrLine, xub_StrLen nIdx,
xub_StrLen& nPos, sal_Bool& bRight,
sal_uInt8& nCrsrLevel, sal_uInt8 nDefaultDir )
{
const SwLinePortion* pPor = rCurrLine.GetFirstPortion();
const SwLinePortion* pLast = 0;
// what's the current portion
while ( pPor && nIdx + pPor->GetLen() <= nPos )
{
nIdx = nIdx + pPor->GetLen();
pLast = pPor;
pPor = pPor->GetPortion();
}
if ( bRight )
{
sal_Bool bRecurse = pPor && pPor->IsMultiPortion() &&
((SwMultiPortion*)pPor)->IsBidi();
// 1. special case: at beginning of bidi portion
if ( bRecurse && nIdx == nPos )
{
nPos = nPos + pPor->GetLen();
// leave bidi portion
if ( nCrsrLevel != nDefaultDir )
{
bRecurse = sal_False;
}
else
// special case:
// buffer: abcXYZ123 in LTR paragraph
// view: abc123ZYX
// cursor is between c and X in the buffer and cursor level = 0
nCrsrLevel++;
}
// 2. special case: at beginning of portion after bidi portion
else if ( pLast && pLast->IsMultiPortion() &&
((SwMultiPortion*)pLast)->IsBidi() && nIdx == nPos )
{
// enter bidi portion
if ( nCrsrLevel != nDefaultDir )
{
bRecurse = sal_True;
nIdx = nIdx - pLast->GetLen();
pPor = pLast;
}
}
// Recursion
if ( bRecurse )
{
const SwLineLayout& rLine = ((SwMultiPortion*)pPor)->GetRoot();
xub_StrLen nTmpPos = nPos - nIdx;
sal_Bool bTmpForward = ! bRight;
sal_uInt8 nTmpCrsrLevel = nCrsrLevel;
lcl_VisualMoveRecursion( rLine, 0, nTmpPos, bTmpForward,
nTmpCrsrLevel, nDefaultDir + 1 );
nPos = nTmpPos + nIdx;
bRight = bTmpForward;
nCrsrLevel = nTmpCrsrLevel;
}
// go forward
else
{
bRight = sal_True;
nCrsrLevel = nDefaultDir;
}
}
else
{
sal_Bool bRecurse = pPor && pPor->IsMultiPortion() && ((SwMultiPortion*)pPor)->IsBidi();
// 1. special case: at beginning of bidi portion
if ( bRecurse && nIdx == nPos )
{
// leave bidi portion
if ( nCrsrLevel == nDefaultDir )
{
bRecurse = sal_False;
}
}
// 2. special case: at beginning of portion after bidi portion
else if ( pLast && pLast->IsMultiPortion() &&
((SwMultiPortion*)pLast)->IsBidi() && nIdx == nPos )
{
nPos = nPos - pLast->GetLen();
// enter bidi portion
if ( nCrsrLevel % 2 == nDefaultDir % 2 )
{
bRecurse = sal_True;
nIdx = nIdx - pLast->GetLen();
pPor = pLast;
// special case:
// buffer: abcXYZ123 in LTR paragraph
// view: abc123ZYX
// cursor is behind 3 in the buffer and cursor level = 2
if ( nDefaultDir + 2 == nCrsrLevel )
nPos = nPos + pLast->GetLen();
}
}
// go forward
if ( bRecurse )
{
const SwLineLayout& rLine = ((SwMultiPortion*)pPor)->GetRoot();
xub_StrLen nTmpPos = nPos - nIdx;
sal_Bool bTmpForward = ! bRight;
sal_uInt8 nTmpCrsrLevel = nCrsrLevel;
lcl_VisualMoveRecursion( rLine, 0, nTmpPos, bTmpForward,
nTmpCrsrLevel, nDefaultDir + 1 );
// special case:
// buffer: abcXYZ123 in LTR paragraph
// view: abc123ZYX
// cursor is between Z and 1 in the buffer and cursor level = 2
if ( nTmpPos == pPor->GetLen() && nTmpCrsrLevel == nDefaultDir + 1 )
{
nTmpPos = nTmpPos - pPor->GetLen();
nTmpCrsrLevel = nDefaultDir;
bTmpForward = ! bTmpForward;
}
nPos = nTmpPos + nIdx;
bRight = bTmpForward;
nCrsrLevel = nTmpCrsrLevel;
}
// go backward
else
{
bRight = sal_False;
nCrsrLevel = nDefaultDir;
}
}
}
void SwTxtFrm::PrepareVisualMove( xub_StrLen& nPos, sal_uInt8& nCrsrLevel,
sal_Bool& bForward, sal_Bool bInsertCrsr )
{
if( IsEmpty() || IsHiddenNow() )
return;
((SwTxtFrm*)this)->GetFormatted();
SwTxtSizeInfo aInf( (SwTxtFrm*)this );
SwTxtCursor aLine( ((SwTxtFrm*)this), &aInf );
if( nPos )
aLine.CharCrsrToLine( nPos );
else
aLine.Top();
const SwLineLayout* pLine = aLine.GetCurr();
const xub_StrLen nStt = aLine.GetStart();
const xub_StrLen nLen = pLine->GetLen();
// We have to distinguish between an insert and overwrite cursor:
// The insert cursor position depends on the cursor level:
// buffer: abcXYZdef in LTR paragraph
// display: abcZYXdef
// If cursor is between c and X in the buffer and cursor level is 0,
// the cursor blinks between c and Z and -> sets the cursor between Z and Y.
// If the cursor level is 1, the cursor blinks between X and d and
// -> sets the cursor between d and e.
// The overwrite cursor simply travels to the next visual character.
if ( bInsertCrsr )
{
lcl_VisualMoveRecursion( *pLine, nStt, nPos, bForward,
nCrsrLevel, IsRightToLeft() ? 1 : 0 );
return;
}
const sal_uInt8 nDefaultDir = static_cast<sal_uInt8>(IsRightToLeft() ? UBIDI_RTL : UBIDI_LTR);
const sal_Bool bVisualRight = ( nDefaultDir == UBIDI_LTR && bForward ) ||
( nDefaultDir == UBIDI_RTL && ! bForward );
//
// Bidi functions from icu 2.0
//
const sal_Unicode* pLineString = GetTxtNode()->GetTxt().GetBuffer();
pLine += nStt;
UErrorCode nError = U_ZERO_ERROR;
UBiDi* pBidi = ubidi_openSized( nLen, 0, &nError );
ubidi_setPara( pBidi, reinterpret_cast<const UChar *>(pLineString), nLen, nDefaultDir, NULL, &nError ); // UChar != sal_Unicode in MinGW
xub_StrLen nTmpPos;
sal_Bool bOutOfBounds = sal_False;
if ( nPos < nStt + nLen )
{
nTmpPos = (xub_StrLen)ubidi_getVisualIndex( pBidi, nPos, &nError );
// visual indices are always LTR aligned
if ( bVisualRight )
{
if ( nTmpPos + 1 < nStt + nLen )
++nTmpPos;
else
{
nPos = nDefaultDir == UBIDI_RTL ? 0 : nStt + nLen;
bOutOfBounds = sal_True;
}
}
else
{
if ( nTmpPos )
--nTmpPos;
else
{
nPos = nDefaultDir == UBIDI_RTL ? nStt + nLen : 0;
bOutOfBounds = sal_True;
}
}
}
else
{
nTmpPos = nDefaultDir == UBIDI_LTR ? nPos - 1 : 0;
}
if ( ! bOutOfBounds )
{
nPos = (xub_StrLen)ubidi_getLogicalIndex( pBidi, nTmpPos, &nError );
if ( bForward )
{
if ( nPos )
--nPos;
else
{
++nPos;
bForward = ! bForward;
}
}
else
++nPos;
}
ubidi_close( pBidi );
}
/*************************************************************************
* SwTxtFrm::_UnitDown()
*************************************************************************/
sal_Bool SwTxtFrm::_UnitDown(SwPaM *pPam, const SwTwips nOffset,
sal_Bool bSetInReadOnly ) const
{
if ( IsInTab() &&
pPam->GetNode( sal_True )->StartOfSectionNode() !=
pPam->GetNode( sal_False )->StartOfSectionNode() )
{
//Wenn der PaM in unterschiedlichen Boxen sitzt, so handelt es sich um
//eine Tabellenselektion; diese wird von der Basisklasse abgearbeitet.
return SwCntntFrm::UnitDown( pPam, nOffset, bSetInReadOnly );
}
((SwTxtFrm*)this)->GetFormatted();
const xub_StrLen nPos = pPam->GetPoint()->nContent.GetIndex();
SwRect aCharBox;
const SwCntntFrm *pTmpFollow = 0;
if ( IsVertical() )
((SwTxtFrm*)this)->SwapWidthAndHeight();
if ( !IsEmpty() && !IsHiddenNow() )
{
xub_StrLen nFormat = STRING_LEN;
do
{
if( nFormat != STRING_LEN && !IsFollow() &&
!lcl_ChangeOffset( ((SwTxtFrm*)this), nFormat ) )
break;
SwTxtSizeInfo aInf( (SwTxtFrm*)this );
SwTxtCursor aLine( ((SwTxtFrm*)this), &aInf );
nFormat = aLine.GetEnd();
aLine.CharCrsrToLine( nPos );
const SwLineLayout* pNextLine = aLine.GetNextLine();
const xub_StrLen nStart = aLine.GetStart();
aLine.GetCharRect( &aCharBox, nPos );
sal_Bool bFirstOfDouble = ( aInf.IsMulti() && aInf.IsFirstMulti() );
if( pNextLine || bFirstOfDouble )
{
aCharBox.SSize().Width() /= 2;
#ifdef DBG_UTIL
// siehe Kommentar in SwTxtFrm::GetCrsrOfst()
const sal_uLong nOldNode = pPam->GetPoint()->nNode.GetIndex();
#endif
if ( pNextLine && ! bFirstOfDouble )
aLine.NextLine();
xub_StrLen nTmpOfst = aLine.GetCrsrOfst( pPam->GetPoint(),
aCharBox.Pos(), sal_False );
ASSERT( nOldNode == pPam->GetPoint()->nNode.GetIndex(),
"SwTxtFrm::UnitDown: illegal node change" )
// 7684: Wir stellen sicher, dass wir uns nach unten bewegen.
if( nTmpOfst <= nStart && ! bFirstOfDouble )
nTmpOfst = nStart + 1;
pPam->GetPoint()->nContent =
SwIndex( ((SwTxtFrm*)this)->GetTxtNode(), nTmpOfst );
if ( IsVertical() )
((SwTxtFrm*)this)->SwapWidthAndHeight();
return sal_True;
}
if( 0 != ( pTmpFollow = GetFollow() ) )
{ // geschuetzte Follows auslassen
const SwCntntFrm* pTmp = pTmpFollow;
ViewShell *pSh = getRootFrm()->GetCurrShell();
if( !pSh || !pSh->GetViewOptions()->IsCursorInProtectedArea() )
{
while( pTmpFollow && pTmpFollow->IsProtected() )
{
pTmp = pTmpFollow;
pTmpFollow = pTmpFollow->GetFollow();
}
}
if( !pTmpFollow ) // nur noch geschuetzte
{
if ( IsVertical() )
((SwTxtFrm*)this)->SwapWidthAndHeight();
return pTmp->SwCntntFrm::UnitDown( pPam, nOffset, bSetInReadOnly );
}
aLine.GetCharRect( &aCharBox, nPos );
aCharBox.SSize().Width() /= 2;
}
else if( !IsFollow() )
{
xub_StrLen nTmpLen = aInf.GetTxt().Len();
if( aLine.GetEnd() < nTmpLen )
{
if( nFormat <= GetOfst() )
{
nFormat = Min( xub_StrLen( GetOfst() + MIN_OFFSET_STEP ),
nTmpLen );
if( nFormat <= GetOfst() )
break;
}
continue;
}
}
break;
} while( sal_True );
}
else
pTmpFollow = GetFollow();
if ( IsVertical() )
((SwTxtFrm*)this)->SwapWidthAndHeight();
// Bei Follows schlagen wir eine Abkuerzung
if( pTmpFollow )
{
aCharBox.Pos().Y() = pTmpFollow->Frm().Top() + 1;
return ((SwTxtFrm*)pTmpFollow)->GetKeyCrsrOfst( pPam->GetPoint(),
aCharBox.Pos() );
}
return SwCntntFrm::UnitDown( pPam, nOffset, bSetInReadOnly );
}
/*************************************************************************
* virtual SwTxtFrm::UnitUp()
*************************************************************************/
sal_Bool SwTxtFrm::UnitUp(SwPaM *pPam, const SwTwips nOffset,
sal_Bool bSetInReadOnly ) const
{
/* Im CrsrSh::Up() wird CntntNode::GetFrm() gerufen.
* Dies liefert _immer_ den Master zurueck.
* Um das Cursortravelling nicht zu belasten, korrigieren wir
* hier im SwTxtFrm.
* Wir ermittelt UnitUp fuer pFrm, pFrm ist entweder ein Master (=this)
* oder ein Follow (!=this)
*/
const SwTxtFrm *pFrm = GetAdjFrmAtPos( (SwTxtFrm*)this, *(pPam->GetPoint()),
SwTxtCursor::IsRightMargin() );
const sal_Bool bRet = pFrm->_UnitUp( pPam, nOffset, bSetInReadOnly );
// 8626: kein SwTxtCursor::SetRightMargin( sal_False );
// statt dessen steht ein SwSetToRightMargin im _UnitUp
return bRet;
}
/*************************************************************************
* virtual SwTxtFrm::UnitDown()
*************************************************************************/
sal_Bool SwTxtFrm::UnitDown(SwPaM *pPam, const SwTwips nOffset,
sal_Bool bSetInReadOnly ) const
{
const SwTxtFrm *pFrm = GetAdjFrmAtPos((SwTxtFrm*)this, *(pPam->GetPoint()),
SwTxtCursor::IsRightMargin() );
const sal_Bool bRet = pFrm->_UnitDown( pPam, nOffset, bSetInReadOnly );
SwTxtCursor::SetRightMargin( sal_False );
return bRet;
}
void SwTxtFrm::FillCrsrPos( SwFillData& rFill ) const
{
if( !rFill.bColumn && GetUpper()->IsColBodyFrm() ) // ColumnFrms jetzt mit BodyFrm
{
const SwColumnFrm* pTmp =
(SwColumnFrm*)GetUpper()->GetUpper()->GetUpper()->Lower(); // die 1. Spalte
// der erste SwFrm im BodyFrm der ersten Spalte
const SwFrm* pFrm = ((SwLayoutFrm*)pTmp->Lower())->Lower();
MSHORT nNextCol = 0;
// In welcher Spalte landen wir?
while( rFill.X() > pTmp->Frm().Right() && pTmp->GetNext() )
{
pTmp = (SwColumnFrm*)pTmp->GetNext();
if( ((SwLayoutFrm*)pTmp->Lower())->Lower() ) // ColumnFrms jetzt mit BodyFrm
{
pFrm = ((SwLayoutFrm*)pTmp->Lower())->Lower();
nNextCol = 0;
}
else
++nNextCol; // leere Spalten erfordern Spaltenumbrueche
}
if( pTmp != GetUpper()->GetUpper() ) // Sind wir in einer anderen Spalte gelandet?
{
if( !pFrm )
return;
if( nNextCol )
{
while( pFrm->GetNext() )
pFrm = pFrm->GetNext();
}
else
{
while( pFrm->GetNext() && pFrm->Frm().Bottom() < rFill.Y() )
pFrm = pFrm->GetNext();
}
// Kein Fuellen, wenn als letzter Frame in der anvisierten
// Spalte kein Absatz, sondern z.B. eine Tabelle steht
if( pFrm->IsTxtFrm() )
{
rFill.Fill().nColumnCnt = nNextCol;
rFill.bColumn = sal_True;
if( rFill.pPos )
{
SwTxtNode* pTxtNd = ((SwTxtFrm*)pFrm)->GetTxtNode();
rFill.pPos->nNode = *pTxtNd;
rFill.pPos->nContent.Assign( pTxtNd, pTxtNd->GetTxt().Len() );
}
if( nNextCol )
{
rFill.aFrm = pTmp->Prt();
rFill.aFrm += pTmp->Frm().Pos();
}
else
rFill.aFrm = pFrm->Frm();
((SwTxtFrm*)pFrm)->FillCrsrPos( rFill );
}
return;
}
}
sal_Bool bFill = sal_True;
SwFont *pFnt;
SwTxtFmtColl* pColl = GetTxtNode()->GetTxtColl();
MSHORT nFirst = GetTxtNode()->GetSwAttrSet().GetULSpace().GetLower();
SwTwips nDiff = rFill.Y() - Frm().Bottom();
if( nDiff < nFirst )
nDiff = -1;
else
pColl = &pColl->GetNextTxtFmtColl();
SwAttrSet aSet( ((SwDoc*)GetTxtNode()->GetDoc())->GetAttrPool(), aTxtFmtCollSetRange );
const SwAttrSet* pSet = &pColl->GetAttrSet();
ViewShell *pSh = getRootFrm()->GetCurrShell();
if( GetTxtNode()->HasSwAttrSet() )
{
aSet.Put( *GetTxtNode()->GetpSwAttrSet() );
aSet.SetParent( pSet );
pSet = &aSet;
pFnt = new SwFont( pSet, GetNode()->getIDocumentSettingAccess() );
}
else
{
SwFontAccess aFontAccess( pColl, pSh );
pFnt = new SwFont( *aFontAccess.Get()->GetFont() );
pFnt->ChkMagic( pSh, pFnt->GetActual() );
}
OutputDevice* pOut = pSh->GetOut();
if( !pSh->GetViewOptions()->getBrowseMode() || pSh->GetViewOptions()->IsPrtFormat() )
pOut = GetTxtNode()->getIDocumentDeviceAccess()->getReferenceDevice( true );
pFnt->SetFntChg( sal_True );
pFnt->ChgPhysFnt( pSh, *pOut );
SwTwips nLineHeight = pFnt->GetHeight( pSh, *pOut );
if( nLineHeight )
{
const SvxULSpaceItem &rUL = pSet->GetULSpace();
SwTwips nDist = Max( rUL.GetLower(), rUL.GetUpper() );
if( rFill.Fill().nColumnCnt )
{
rFill.aFrm.Height( nLineHeight );
nDiff = rFill.Y() - rFill.Bottom();
nFirst = 0;
}
else if( nDist < nFirst )
nFirst = nFirst - (sal_uInt16)nDist;
else
nFirst = 0;
nDist = Max( nDist, long( GetLineSpace() ) );
nDist += nLineHeight;
nDiff -= nFirst;
if( nDiff > 0 )
{
nDiff /= nDist;
rFill.Fill().nParaCnt = static_cast<sal_uInt16>(nDiff + 1);
rFill.nLineWidth = 0;
rFill.bInner = sal_False;
rFill.bEmpty = sal_True;
rFill.SetOrient( text::HoriOrientation::LEFT );
}
else
nDiff = -1;
if( rFill.bInner )
bFill = sal_False;
else
{
const SvxTabStopItem &rRuler = pSet->GetTabStops();
const SvxLRSpaceItem &rLRSpace = pSet->GetLRSpace();
SwRect &rRect = rFill.Fill().aCrsr;
rRect.Top( rFill.Bottom() + (nDiff+1) * nDist - nLineHeight );
if( nFirst && nDiff > -1 )
rRect.Top( rRect.Top() + nFirst );
rRect.Height( nLineHeight );
SwTwips nLeft = rFill.Left() + rLRSpace.GetLeft() +
GetTxtNode()->GetLeftMarginWithNum( sal_False );
SwTwips nRight = rFill.Right() - rLRSpace.GetRight();
SwTwips nCenter = ( nLeft + nRight ) / 2;
rRect.Left( nLeft );
if( FILL_MARGIN == rFill.Mode() )
{
if( rFill.bEmpty )
{
rFill.SetOrient( text::HoriOrientation::LEFT );
if( rFill.X() < nCenter )
{
if( rFill.X() > ( nLeft + 2 * nCenter ) / 3 )
{
rFill.SetOrient( text::HoriOrientation::CENTER );
rRect.Left( nCenter );
}
}
else if( rFill.X() > ( nRight + 2 * nCenter ) / 3 )
{
rFill.SetOrient( text::HoriOrientation::RIGHT );
rRect.Left( nRight );
}
else
{
rFill.SetOrient( text::HoriOrientation::CENTER );
rRect.Left( nCenter );
}
}
else
bFill = sal_False;
}
else
{
SwTwips nSpace = 0;
if( FILL_TAB != rFill.Mode() )
{
static sal_Char __READONLY_DATA sDoubleSpace[] = " ";
const XubString aTmp( sDoubleSpace, RTL_TEXTENCODING_MS_1252 );
SwDrawTextInfo aDrawInf( pSh, *pOut, 0, aTmp, 0, 2 );
nSpace = pFnt->_GetTxtSize( aDrawInf ).Width()/2;
}
if( rFill.X() >= nRight )
{
if( FILL_INDENT != rFill.Mode() && ( rFill.bEmpty ||
rFill.X() > rFill.nLineWidth + FILL_MIN_DIST ) )
{
rFill.SetOrient( text::HoriOrientation::RIGHT );
rRect.Left( nRight );
}
else
bFill = sal_False;
}
else if( FILL_INDENT == rFill.Mode() )
{
SwTwips nIndent = rFill.X();
if( !rFill.bEmpty || nIndent > nRight )
bFill = sal_False;
else
{
nIndent -= rFill.Left();
if( nIndent >= 0 && nSpace )
{
nIndent /= nSpace;
nIndent *= nSpace;
rFill.SetTab( MSHORT( nIndent ) );
rRect.Left( nIndent + rFill.Left() );
}
else
bFill = sal_False;
}
}
else if( rFill.X() > nLeft )
{
SwTwips nTxtLeft = rFill.Left() + rLRSpace.GetTxtLeft() +
GetTxtNode()->GetLeftMarginWithNum( sal_True );
rFill.nLineWidth += rFill.bFirstLine ? nLeft : nTxtLeft;
SwTwips nLeftTab = nLeft;
SwTwips nRightTab = nLeft;
MSHORT nSpaceCnt = 0;
MSHORT nTabCnt = 0;
MSHORT nIdx = 0;
do
{
nLeftTab = nRightTab;
if( nIdx < rRuler.Count() )
{
const SvxTabStop &rTabStop = rRuler.operator[](nIdx);
nRightTab = nTxtLeft + rTabStop.GetTabPos();
if( nLeftTab < nTxtLeft && nRightTab > nTxtLeft )
nRightTab = nTxtLeft;
else
++nIdx;
if( nRightTab > rFill.nLineWidth )
++nTabCnt;
}
else
{
const SvxTabStopItem& rTab =
(const SvxTabStopItem &)pSet->
GetPool()->GetDefaultItem( RES_PARATR_TABSTOP );
MSHORT nDefTabDist = (MSHORT)rTab.GetStart()->GetTabPos();
nRightTab = nLeftTab - nTxtLeft;
nRightTab /= nDefTabDist;
nRightTab = nRightTab * nDefTabDist + nTxtLeft;
while ( nRightTab <= nLeftTab )
nRightTab += nDefTabDist;
if( nRightTab > rFill.nLineWidth )
++nTabCnt;
while ( nRightTab < rFill.X() )
{
nRightTab += nDefTabDist;
if( nRightTab > rFill.nLineWidth )
++nTabCnt;
}
if( nLeftTab < nRightTab - nDefTabDist )
nLeftTab = nRightTab - nDefTabDist;
}
if( nRightTab > nRight )
nRightTab = nRight;
}
while( rFill.X() > nRightTab );
--nTabCnt;
if( FILL_TAB != rFill.Mode() )
{
if( nSpace > 0 )
{
if( !nTabCnt )
nLeftTab = rFill.nLineWidth;
while( nLeftTab < rFill.X() )
{
nLeftTab += nSpace;
++nSpaceCnt;
}
if( nSpaceCnt )
{
nLeftTab -= nSpace;
--nSpaceCnt;
}
if( rFill.X() - nLeftTab > nRightTab - rFill.X() )
{
nSpaceCnt = 0;
++nTabCnt;
rRect.Left( nRightTab );
}
else
{
if( rFill.X() - nLeftTab > nSpace/2 )
{
++nSpaceCnt;
rRect.Left( nLeftTab + nSpace );
}
else
rRect.Left( nLeftTab );
}
}
else if( rFill.X() - nLeftTab < nRightTab - rFill.X() )
rRect.Left( nLeftTab );
else
{
if( nRightTab >= nRight )
{
rFill.SetOrient( text::HoriOrientation::RIGHT );
rRect.Left( nRight );
nTabCnt = 0;
nSpaceCnt = 0;
}
else
{
rRect.Left( nRightTab );
++nTabCnt;
}
}
}
else
{
if( rFill.X() - nLeftTab < nRightTab - rFill.X() )
rRect.Left( nLeftTab );
else
{
if( nRightTab >= nRight )
{
rFill.SetOrient( text::HoriOrientation::RIGHT );
rRect.Left( nRight );
nTabCnt = 0;
nSpaceCnt = 0;
}
else
{
rRect.Left( nRightTab );
++nTabCnt;
}
}
}
rFill.SetTab( nTabCnt );
rFill.SetSpace( nSpaceCnt );
if( bFill )
{
if( Abs( rFill.X() - nCenter ) <=
Abs( rFill.X() - rRect.Left() ) )
{
rFill.SetOrient( text::HoriOrientation::CENTER );
rFill.SetTab( 0 );
rFill.SetSpace( 0 );
rRect.Left( nCenter );
}
if( !rFill.bEmpty )
rFill.nLineWidth += FILL_MIN_DIST;
if( rRect.Left() < rFill.nLineWidth )
bFill = sal_False;
}
}
}
// Gehen wir ueber die Unterkante der Seite/Spalte etc. hinaus?
const SwFrm* pUp = GetUpper();
if( pUp->IsInSct() )
{
if( pUp->IsSctFrm() )
pUp = pUp->GetUpper();
else if( pUp->IsColBodyFrm() &&
pUp->GetUpper()->GetUpper()->IsSctFrm() )
pUp = pUp->GetUpper()->GetUpper()->GetUpper();
}
SWRECTFN( this )
SwTwips nLimit = (pUp->*fnRect->fnGetPrtBottom)();
SwTwips nRectBottom = rRect.Bottom();
if ( bVert )
nRectBottom = SwitchHorizontalToVertical( nRectBottom );
if( (*fnRect->fnYDiff)( nLimit, nRectBottom ) < 0 )
bFill = sal_False;
else
rRect.Width( 1 );
}
}
else
bFill = sal_False;
((SwCrsrMoveState*)rFill.pCMS)->bFillRet = bFill;
delete pFnt;
}