blob: b5ef649715539b4b3f495494891364b6897214e1 [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 "errhdl.hxx"
#include "ndtxt.hxx"
#include "frmfmt.hxx"
#include "paratr.hxx"
#include "flyfrm.hxx"
#include "pam.hxx"
#include "swselectionlist.hxx"
#include <sortedobjs.hxx>
#include <editeng/protitem.hxx>
#include <editeng/adjitem.hxx>
#include <editeng/lspcitem.hxx>
#include <editeng/lrspitem.hxx>
#include <frmatr.hxx>
#include <pagedesc.hxx> // SwPageDesc
#include <tgrditem.hxx>
#include <IDocumentSettingAccess.hxx>
#include <pagefrm.hxx>
#include "txtcfg.hxx"
#include "itrtxt.hxx"
#include "txtfrm.hxx"
#include "flyfrms.hxx"
#include "porglue.hxx" // SwFlyCnt
#include "porfld.hxx" // SwFldPortion::IsFollow()
#include "porfly.hxx" // GetFlyCrsrOfst()
#include "pordrop.hxx"
#include "crstate.hxx" // SwCrsrMoveState
#include <pormulti.hxx> // SwMultiPortion
// --> OD 2010-05-05 #i111284#
#include <numrule.hxx>
// <--
// Nicht reentrant !!!
// wird in GetCharRect gesetzt und im UnitUp/Down ausgewertet.
sal_Bool SwTxtCursor::bRightMargin = sal_False;
/*************************************************************************
* lcl_GetCharRectInsideField
*
* After calculating the position of a character during GetCharRect
* this function allows to find the coordinates of a position (defined
* in pCMS->pSpecialPos) inside a special portion (e.g., a field)
*************************************************************************/
void lcl_GetCharRectInsideField( SwTxtSizeInfo& rInf, SwRect& rOrig,
const SwCrsrMoveState& rCMS,
const SwLinePortion& rPor )
{
ASSERT( rCMS.pSpecialPos, "Information about special pos missing" )
if ( rPor.InFldGrp() && ((SwFldPortion&)rPor).GetExp().Len() )
{
const sal_uInt16 nCharOfst = rCMS.pSpecialPos->nCharOfst;
sal_uInt16 nFldIdx = 0;
sal_uInt16 nFldLen = 0;
const XubString* pString = 0;
const SwLinePortion* pPor = &rPor;
do
{
if ( pPor->InFldGrp() )
{
pString = &((SwFldPortion*)pPor)->GetExp();
nFldLen = pString->Len();
}
else
{
pString = 0;
nFldLen = 0;
}
if ( ! pPor->GetPortion() || nFldIdx + nFldLen > nCharOfst )
break;
nFldIdx = nFldIdx + nFldLen;
rOrig.Pos().X() += pPor->Width();
pPor = pPor->GetPortion();
} while ( sal_True );
ASSERT( nCharOfst >= nFldIdx, "Request of position inside field failed" )
sal_uInt16 nLen = nCharOfst - nFldIdx + 1;
if ( pString )
{
// get script for field portion
rInf.GetFont()->SetActual( SwScriptInfo::WhichFont( 0, pString, 0 ) );
xub_StrLen nOldLen = pPor->GetLen();
((SwLinePortion*)pPor)->SetLen( nLen - 1 );
const SwTwips nX1 = pPor->GetLen() ?
pPor->GetTxtSize( rInf ).Width() :
0;
SwTwips nX2 = 0;
if ( rCMS.bRealWidth )
{
((SwLinePortion*)pPor)->SetLen( nLen );
nX2 = pPor->GetTxtSize( rInf ).Width();
}
((SwLinePortion*)pPor)->SetLen( nOldLen );
rOrig.Pos().X() += nX1;
rOrig.Width( ( nX2 > nX1 ) ?
( nX2 - nX1 ) :
1 );
}
}
else
{
// special cases: no common fields, e.g., graphic number portion,
// FlyInCntPortions, Notes
rOrig.Width( rCMS.bRealWidth && rPor.Width() ? rPor.Width() : 1 );
}
}
// --> OD 2010-05-05 #i111284#
namespace {
bool AreListLevelIndentsApplicableAndLabelAlignmentActive( const SwTxtNode& rTxtNode )
{
bool bRet( false );
if ( rTxtNode.AreListLevelIndentsApplicable() )
{
const SwNumFmt& rNumFmt =
rTxtNode.GetNumRule()->Get( static_cast<sal_uInt16>(rTxtNode.GetActualListLevel()) );
if ( rNumFmt.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_ALIGNMENT )
{
bRet = true;
}
}
return bRet;
}
} // end of anonymous namespace
// <--
/*************************************************************************
* SwTxtMargin::CtorInitTxtMargin()
*************************************************************************/
void SwTxtMargin::CtorInitTxtMargin( SwTxtFrm *pNewFrm, SwTxtSizeInfo *pNewInf )
{
CtorInitTxtIter( pNewFrm, pNewInf );
pInf = pNewInf;
GetInfo().SetFont( GetFnt() );
const SwTxtNode *pNode = pFrm->GetTxtNode();
const SvxLRSpaceItem &rSpace = pFrm->GetTxtNode()->GetSwAttrSet().GetLRSpace();
// --> OD 2009-09-08 #i95907#, #b6879723#
// --> OD 2010-05-05 #i111284#
const bool bListLevelIndentsApplicableAndLabelAlignmentActive(
AreListLevelIndentsApplicableAndLabelAlignmentActive( *(pFrm->GetTxtNode()) ) );
// <--
//
// Carefully adjust the text formatting ranges.
//
// This whole area desperately needs some rework. There are
// quite a couple of values that need to be considered:
// 1. paragraph indent
// 2. paragraph first line indent
// 3. numbering indent
// 4. numbering spacing to text
// 5. paragraph border
// Note: These values have already been used during calculation
// of the printing area of the paragraph.
const int nLMWithNum = pNode->GetLeftMarginWithNum( sal_True );
if ( pFrm->IsRightToLeft() )
{
// --> OD 2008-01-23 #newlistlevelattrs#
// this calculation is identical this the calculation for L2R layout - see below
nLeft = pFrm->Frm().Left() +
pFrm->Prt().Left() +
nLMWithNum -
pNode->GetLeftMarginWithNum( sal_False ) -
// --> OD 2009-09-08 #i95907#, #b6879723#
// --> OD 2010-05-05 #i111284#
// rSpace.GetLeft() +
// rSpace.GetTxtLeft();
( bListLevelIndentsApplicableAndLabelAlignmentActive
? 0
: ( rSpace.GetLeft() - rSpace.GetTxtLeft() ) );
// <--
}
else
{
// --> OD 2009-09-08 #i95907#, #b6879723#
// --> OD 2010-05-05 #i111284#
// if ( !pNode->getIDocumentSettingAccess()->get(IDocumentSettingAccess::IGNORE_FIRST_LINE_INDENT_IN_NUMBERING) )
if ( bListLevelIndentsApplicableAndLabelAlignmentActive ||
!pNode->getIDocumentSettingAccess()->get(IDocumentSettingAccess::IGNORE_FIRST_LINE_INDENT_IN_NUMBERING) )
// <--
{
// this calculation is identical this the calculation for R2L layout - see above
nLeft = pFrm->Frm().Left() +
pFrm->Prt().Left() +
nLMWithNum -
pNode->GetLeftMarginWithNum( sal_False ) -
// --> OD 2009-09-08 #i95907#, #b6879723#
// --> OD 2010-05-05 #i111284#
// rSpace.GetLeft() +
// rSpace.GetTxtLeft();
( bListLevelIndentsApplicableAndLabelAlignmentActive
? 0
: ( rSpace.GetLeft() - rSpace.GetTxtLeft() ) );
// <--
}
else
{
nLeft = pFrm->Frm().Left() +
Max( long( rSpace.GetTxtLeft() + nLMWithNum ),
pFrm->Prt().Left() );
}
}
nRight = pFrm->Frm().Left() + pFrm->Prt().Left() + pFrm->Prt().Width();
if( nLeft >= nRight &&
// --> FME 2005-08-10 #i53066# Omit adjustment of nLeft for numbered
// paras inside cells inside new documents:
( pNode->getIDocumentSettingAccess()->get(IDocumentSettingAccess::IGNORE_FIRST_LINE_INDENT_IN_NUMBERING) ||
!pFrm->IsInTab() ||
!nLMWithNum ) )
// <--
{
nLeft = pFrm->Prt().Left() + pFrm->Frm().Left();
if( nLeft >= nRight ) // z.B. bei grossen Absatzeinzuegen in schmalen Tabellenspalten
nRight = nLeft + 1; // einen goennen wir uns immer
}
if( pFrm->IsFollow() && pFrm->GetOfst() )
nFirst = nLeft;
else
{
short nFLOfst = 0;
long nFirstLineOfs = 0;
if( !pNode->GetFirstLineOfsWithNum( nFLOfst ) &&
rSpace.IsAutoFirst() )
{
nFirstLineOfs = GetFnt()->GetSize( GetFnt()->GetActual() ).Height();
const SvxLineSpacingItem *pSpace = aLineInf.GetLineSpacing();
if( pSpace )
{
switch( pSpace->GetLineSpaceRule() )
{
case SVX_LINE_SPACE_AUTO:
break;
case SVX_LINE_SPACE_MIN:
{
if( nFirstLineOfs < KSHORT( pSpace->GetLineHeight() ) )
nFirstLineOfs = pSpace->GetLineHeight();
break;
}
case SVX_LINE_SPACE_FIX:
nFirstLineOfs = pSpace->GetLineHeight();
break;
default: ASSERT( sal_False, ": unknown LineSpaceRule" );
}
switch( pSpace->GetInterLineSpaceRule() )
{
case SVX_INTER_LINE_SPACE_OFF:
break;
case SVX_INTER_LINE_SPACE_PROP:
{
long nTmp = pSpace->GetPropLineSpace();
// 50% ist das Minimum, bei 0% schalten wir auf
// den Defaultwert 100% um ...
if( nTmp < 50 )
nTmp = nTmp ? 50 : 100;
nTmp *= nFirstLineOfs;
nTmp /= 100;
if( !nTmp )
++nTmp;
nFirstLineOfs = (KSHORT)nTmp;
break;
}
case SVX_INTER_LINE_SPACE_FIX:
{
nFirstLineOfs += pSpace->GetInterLineSpace();
break;
}
default: ASSERT( sal_False, ": unknown InterLineSpaceRule" );
}
}
}
else
nFirstLineOfs = nFLOfst;
// --> OD 2009-09-08 #i95907#, #b6879723#
// --> OD 2010-05-05 #i111284#
// if ( pFrm->IsRightToLeft() ||
// !pNode->getIDocumentSettingAccess()->get(IDocumentSettingAccess::IGNORE_FIRST_LINE_INDENT_IN_NUMBERING) )
if ( pFrm->IsRightToLeft() ||
bListLevelIndentsApplicableAndLabelAlignmentActive ||
!pNode->getIDocumentSettingAccess()->get(IDocumentSettingAccess::IGNORE_FIRST_LINE_INDENT_IN_NUMBERING) )
// <--
{
nFirst = nLeft + nFirstLineOfs;
}
else
{
nFirst = pFrm->Frm().Left() +
Max( rSpace.GetTxtLeft() + nLMWithNum+ nFirstLineOfs,
pFrm->Prt().Left() );
}
// --> OD 2008-01-31 #newlistlevelattrs#
// Note: <SwTxtFrm::GetAdditionalFirstLineOffset()> returns a negative
// value for the new list label postion and space mode LABEL_ALIGNMENT
// and label alignment CENTER and RIGHT in L2R layout respectively
// label alignment LEFT and CENTER in R2L layout
nFirst += pFrm->GetAdditionalFirstLineOffset();
// <--
if( nFirst >= nRight )
nFirst = nRight - 1;
}
const SvxAdjustItem& rAdjust = pFrm->GetTxtNode()->GetSwAttrSet().GetAdjust();
nAdjust = static_cast<sal_uInt16>(rAdjust.GetAdjust());
// left is left and right is right
if ( pFrm->IsRightToLeft() )
{
if ( SVX_ADJUST_LEFT == nAdjust )
nAdjust = SVX_ADJUST_RIGHT;
else if ( SVX_ADJUST_RIGHT == nAdjust )
nAdjust = SVX_ADJUST_LEFT;
}
bOneBlock = rAdjust.GetOneWord() == SVX_ADJUST_BLOCK;
bLastBlock = rAdjust.GetLastBlock() == SVX_ADJUST_BLOCK;
bLastCenter = rAdjust.GetLastBlock() == SVX_ADJUST_CENTER;
// --> OD 2008-07-01 #i91133#
mnTabLeft = pNode->GetLeftMarginForTabCalculation();
// <--
#if OSL_DEBUG_LEVEL > 1
static sal_Bool bOne = sal_False;
static sal_Bool bLast = sal_False;
static sal_Bool bCenter = sal_False;
bOneBlock |= bOne;
bLastBlock |= bLast;
bLastCenter |= bCenter;
#endif
DropInit();
}
/*************************************************************************
* SwTxtMargin::DropInit()
*************************************************************************/
void SwTxtMargin::DropInit()
{
nDropLeft = nDropLines = nDropHeight = nDropDescent = 0;
const SwParaPortion *pPara = GetInfo().GetParaPortion();
if( pPara )
{
const SwDropPortion *pPorDrop = pPara->FindDropPortion();
if ( pPorDrop )
{
nDropLeft = pPorDrop->GetDropLeft();
nDropLines = pPorDrop->GetLines();
nDropHeight = pPorDrop->GetDropHeight();
nDropDescent = pPorDrop->GetDropDescent();
}
}
}
/*************************************************************************
* SwTxtMargin::GetLineStart()
*************************************************************************/
// Unter Beruecksichtigung des Erstzeileneinzuges und der angebenen Breite.
SwTwips SwTxtMargin::GetLineStart() const
{
SwTwips nRet = GetLeftMargin();
if( GetAdjust() != SVX_ADJUST_LEFT &&
!pCurr->GetFirstPortion()->IsMarginPortion() )
{
// Wenn die erste Portion ein Margin ist, dann wird das
// Adjustment durch die Portions ausgedrueckt.
if( GetAdjust() == SVX_ADJUST_RIGHT )
nRet = Right() - CurrWidth();
else if( GetAdjust() == SVX_ADJUST_CENTER )
nRet += (GetLineWidth() - CurrWidth()) / 2;
}
return nRet;
}
/*************************************************************************
* SwTxtCursor::CtorInitTxtCursor()
*************************************************************************/
void SwTxtCursor::CtorInitTxtCursor( SwTxtFrm *pNewFrm, SwTxtSizeInfo *pNewInf )
{
CtorInitTxtMargin( pNewFrm, pNewInf );
// 6096: Vorsicht, die Iteratoren sind abgeleitet!
// GetInfo().SetOut( GetInfo().GetWin() );
}
/*************************************************************************
* SwTxtCursor::GetEndCharRect()
*************************************************************************/
// 1170: Antikbug: Shift-Ende vergisst das letzte Zeichen ...
sal_Bool SwTxtCursor::GetEndCharRect( SwRect* pOrig, const xub_StrLen nOfst,
SwCrsrMoveState* pCMS, const long nMax )
{
// 1170: Mehrdeutigkeit von Dokumentpositionen
bRightMargin = sal_True;
CharCrsrToLine(nOfst);
// Etwas verdreht: nOfst bezeichnet die Position hinter dem letzten
// Zeichen der letzten Zeile == Position vor dem ersten Zeichen der
// Zeile in der wir gerade stehen:
if( nOfst != GetStart() || !pCurr->GetLen() )
{
// 8810: Masterzeile RightMargin, danach LeftMargin
const sal_Bool bRet = GetCharRect( pOrig, nOfst, pCMS, nMax );
bRightMargin = nOfst >= GetEnd() && nOfst < GetInfo().GetTxt().Len();
return bRet;
}
if( !GetPrev() || !GetPrev()->GetLen() || !PrevLine() )
return GetCharRect( pOrig, nOfst, pCMS, nMax );
// Adjustierung ggf. nachholen
GetAdjusted();
KSHORT nX = 0;
KSHORT nLast = 0;
SwLinePortion *pPor = pCurr->GetFirstPortion();
KSHORT nTmpHeight, nTmpAscent;
CalcAscentAndHeight( nTmpAscent, nTmpHeight );
KSHORT nPorHeight = nTmpHeight;
KSHORT nPorAscent = nTmpAscent;
// Die letzte Text/EndPortion der Zeile suchen
while( pPor )
{
nX = nX + pPor->Width();
if( pPor->InTxtGrp() || ( pPor->GetLen() && !pPor->IsFlyPortion()
&& !pPor->IsHolePortion() ) || pPor->IsBreakPortion() )
{
nLast = nX;
nPorHeight = pPor->Height();
nPorAscent = pPor->GetAscent();
}
pPor = pPor->GetPortion();
}
const Size aCharSize( 1, nTmpHeight );
pOrig->Pos( GetTopLeft() );
pOrig->SSize( aCharSize );
pOrig->Pos().X() += nLast;
const SwTwips nTmpRight = Right() - 1;
if( pOrig->Left() > nTmpRight )
pOrig->Pos().X() = nTmpRight;
if ( pCMS && pCMS->bRealHeight )
{
if ( nTmpAscent > nPorAscent )
pCMS->aRealHeight.X() = nTmpAscent - nPorAscent;
else
pCMS->aRealHeight.X() = 0;
ASSERT( nPorHeight, "GetCharRect: Missing Portion-Height" );
pCMS->aRealHeight.Y() = nPorHeight;
}
return sal_True;
}
/*************************************************************************
* void SwTxtCursor::_GetCharRect(..)
* internal function, called by SwTxtCursor::GetCharRect() to calculate
* the relative character position in the current line.
* pOrig referes to x and y coordinates, width and height of the cursor
* pCMS is used for restricting the cursor, if there are different font
* heights in one line ( first value = offset to y of pOrig, second
* value = real height of (shortened) cursor
*************************************************************************/
void SwTxtCursor::_GetCharRect( SwRect* pOrig, const xub_StrLen nOfst,
SwCrsrMoveState* pCMS )
{
const XubString &rText = GetInfo().GetTxt();
SwTxtSizeInfo aInf( GetInfo(), rText, nStart );
if( GetPropFont() )
aInf.GetFont()->SetProportion( GetPropFont() );
KSHORT nTmpAscent, nTmpHeight; // Zeilenhoehe
CalcAscentAndHeight( nTmpAscent, nTmpHeight );
const Size aCharSize( 1, nTmpHeight );
const Point aCharPos;
pOrig->Pos( aCharPos );
pOrig->SSize( aCharSize );
// If we are looking for a position inside a field which covers
// more than one line we may not skip any "empty portions" at the
// beginning of a line
const sal_Bool bInsideFirstField = pCMS && pCMS->pSpecialPos &&
( pCMS->pSpecialPos->nLineOfst ||
SP_EXTEND_RANGE_BEFORE ==
pCMS->pSpecialPos->nExtendRange );
sal_Bool bWidth = pCMS && pCMS->bRealWidth;
if( !pCurr->GetLen() && !pCurr->Width() )
{
if ( pCMS && pCMS->bRealHeight )
{
pCMS->aRealHeight.X() = 0;
pCMS->aRealHeight.Y() = nTmpHeight;
}
}
else
{
KSHORT nPorHeight = nTmpHeight;
KSHORT nPorAscent = nTmpAscent;
SwTwips nX = 0;
SwTwips nTmpFirst = 0;
SwLinePortion *pPor = pCurr->GetFirstPortion();
SwBidiPortion* pLastBidiPor = 0;
SwTwips nLastBidiPorWidth = 0;
SvUShorts* pKanaComp = pCurr->GetpKanaComp();
MSHORT nSpaceIdx = 0;
MSHORT nKanaIdx = 0;
long nSpaceAdd = pCurr->IsSpaceAdd() ? pCurr->GetLLSpaceAdd( 0 ) : 0;
sal_Bool bNoTxt = sal_True;
// Zuerst werden alle Portions ohne Len am Zeilenanfang uebersprungen.
// Ausnahme bilden die fiesen Spezialportions aus WhichFirstPortion:
// Num, ErgoSum, FtnNum, FeldReste
// 8477: aber auch die einzige Textportion einer leeren Zeile mit
// Right/Center-Adjustment! Also nicht nur pPor->GetExpandPortion() ...
while( pPor && !pPor->GetLen() && ! bInsideFirstField )
{
nX += pPor->Width();
if ( pPor->InSpaceGrp() && nSpaceAdd )
nX += pPor->CalcSpacing( nSpaceAdd, aInf );
if( bNoTxt )
nTmpFirst = nX;
// 8670: EndPortions zaehlen hier einmal als TxtPortions.
// --> OD 2008-01-28 #newlistlevelattrs#
// if( pPor->InTxtGrp() || pPor->IsBreakPortion() )
if( pPor->InTxtGrp() || pPor->IsBreakPortion() || pPor->InTabGrp() )
// <--
{
bNoTxt = sal_False;
nTmpFirst = nX;
}
if( pPor->IsMultiPortion() && ((SwMultiPortion*)pPor)->HasTabulator() )
{
if ( pCurr->IsSpaceAdd() )
{
if ( ++nSpaceIdx < pCurr->GetLLSpaceAddCount() )
nSpaceAdd = pCurr->GetLLSpaceAdd( nSpaceIdx );
else
nSpaceAdd = 0;
}
if( pKanaComp && ( nKanaIdx + 1 ) < pKanaComp->Count() )
++nKanaIdx;
}
if( pPor->InFixMargGrp() )
{
if( pPor->IsMarginPortion() )
bNoTxt = sal_False;
else
{
// fix margin portion => next SpaceAdd, KanaComp value
if ( pCurr->IsSpaceAdd() )
{
if ( ++nSpaceIdx < pCurr->GetLLSpaceAddCount() )
nSpaceAdd = pCurr->GetLLSpaceAdd( nSpaceIdx );
else
nSpaceAdd = 0;
}
if( pKanaComp && ( nKanaIdx + 1 ) < pKanaComp->Count() )
++nKanaIdx;
}
}
pPor = pPor->GetPortion();
}
if( !pPor )
{
// Es sind nur Spezialportions unterwegs.
nX = nTmpFirst;
}
else
{
if( !pPor->IsMarginPortion() && !pPor->IsPostItsPortion() &&
(!pPor->InFldGrp() || pPor->GetAscent() ) )
{
nPorHeight = pPor->Height();
nPorAscent = pPor->GetAscent();
}
while( pPor && !pPor->IsBreakPortion() && ( aInf.GetIdx() < nOfst ||
( bWidth && ( pPor->IsKernPortion() || pPor->IsMultiPortion() ) ) ) )
{
if( !pPor->IsMarginPortion() && !pPor->IsPostItsPortion() &&
(!pPor->InFldGrp() || pPor->GetAscent() ) )
{
nPorHeight = pPor->Height();
nPorAscent = pPor->GetAscent();
}
// If we are behind the portion, we add the portion width to
// nX. Special case: nOfst = aInf.GetIdx() + pPor->GetLen().
// For common portions (including BidiPortions) we want to add
// the portion width to nX. For MultiPortions, nExtra = 0,
// therefore we go to the 'else' branch and start a recursion.
const sal_uInt8 nExtra = pPor->IsMultiPortion() &&
! ((SwMultiPortion*)pPor)->IsBidi() &&
! bWidth ? 0 : 1;
if ( aInf.GetIdx() + pPor->GetLen() < nOfst + nExtra )
{
if ( pPor->InSpaceGrp() && nSpaceAdd )
nX += pPor->PrtWidth() +
pPor->CalcSpacing( nSpaceAdd, aInf );
else
{
if( pPor->InFixMargGrp() && ! pPor->IsMarginPortion() )
{
// update to current SpaceAdd, KanaComp values
if ( pCurr->IsSpaceAdd() )
{
if ( ++nSpaceIdx < pCurr->GetLLSpaceAddCount() )
nSpaceAdd = pCurr->GetLLSpaceAdd( nSpaceIdx );
else
nSpaceAdd = 0;
}
if ( pKanaComp &&
( nKanaIdx + 1 ) < pKanaComp->Count()
)
++nKanaIdx;
}
if ( !pPor->IsFlyPortion() || ( pPor->GetPortion() &&
!pPor->GetPortion()->IsMarginPortion() ) )
nX += pPor->PrtWidth();
}
if( pPor->IsMultiPortion() )
{
if ( ((SwMultiPortion*)pPor)->HasTabulator() )
{
if ( pCurr->IsSpaceAdd() )
{
if ( ++nSpaceIdx < pCurr->GetLLSpaceAddCount() )
nSpaceAdd = pCurr->GetLLSpaceAdd( nSpaceIdx );
else
nSpaceAdd = 0;
}
if( pKanaComp && ( nKanaIdx + 1 ) < pKanaComp->Count() )
++nKanaIdx;
}
// if we are right behind a BidiPortion, we have to
// hold a pointer to the BidiPortion in order to
// find the correct cursor position, depending on the
// cursor level
if ( ((SwMultiPortion*)pPor)->IsBidi() &&
aInf.GetIdx() + pPor->GetLen() == nOfst )
{
pLastBidiPor = (SwBidiPortion*)pPor;
nLastBidiPorWidth = pLastBidiPor->Width() +
pLastBidiPor->CalcSpacing( nSpaceAdd, aInf );;
}
}
aInf.SetIdx( aInf.GetIdx() + pPor->GetLen() );
pPor = pPor->GetPortion();
}
else
{
if( pPor->IsMultiPortion() )
{
nTmpAscent = AdjustBaseLine( *pCurr, pPor );
GetInfo().SetMulti( sal_True );
pOrig->Pos().Y() += nTmpAscent - nPorAscent;
if( pCMS && pCMS->b2Lines )
{
sal_Bool bRecursion = sal_True;
if ( ! pCMS->p2Lines )
{
pCMS->p2Lines = new Sw2LinesPos;
pCMS->p2Lines->aLine = SwRect(aCharPos, aCharSize);
bRecursion = sal_False;
}
if( ((SwMultiPortion*)pPor)->HasRotation() )
{
if( ((SwMultiPortion*)pPor)->IsRevers() )
pCMS->p2Lines->nMultiType = MT_ROT_270;
else
pCMS->p2Lines->nMultiType = MT_ROT_90;
}
else if( ((SwMultiPortion*)pPor)->IsDouble() )
pCMS->p2Lines->nMultiType = MT_TWOLINE;
else if( ((SwMultiPortion*)pPor)->IsBidi() )
pCMS->p2Lines->nMultiType = MT_BIDI;
else
pCMS->p2Lines->nMultiType = MT_RUBY;
SwTwips nTmpWidth = pPor->Width();
if( nSpaceAdd )
nTmpWidth += pPor->CalcSpacing(nSpaceAdd, aInf);
SwRect aRect( Point(aCharPos.X() + nX, pOrig->Top() ),
Size( nTmpWidth, pPor->Height() ) );
if ( ! bRecursion )
pCMS->p2Lines->aPortion = aRect;
else
pCMS->p2Lines->aPortion2 = aRect;
}
// In a multi-portion we use GetCharRect()-function
// recursively and must add the x-position
// of the multi-portion.
xub_StrLen nOldStart = nStart;
SwTwips nOldY = nY;
sal_uInt8 nOldProp = GetPropFont();
nStart = aInf.GetIdx();
SwLineLayout* pOldCurr = pCurr;
pCurr = &((SwMultiPortion*)pPor)->GetRoot();
if( ((SwMultiPortion*)pPor)->IsDouble() )
SetPropFont( 50 );
GETGRID( GetTxtFrm()->FindPageFrm() )
const sal_Bool bHasGrid = pGrid && GetInfo().SnapToGrid();
const sal_uInt16 nRubyHeight = bHasGrid ?
pGrid->GetRubyHeight() : 0;
if( nStart + pCurr->GetLen() <= nOfst && GetNext() &&
( ! ((SwMultiPortion*)pPor)->IsRuby() ||
((SwMultiPortion*)pPor)->OnTop() ) )
{
sal_uInt16 nOffset;
// in grid mode we may only add the height of the
// ruby line if ruby line is on top
if ( bHasGrid &&
((SwMultiPortion*)pPor)->IsRuby() &&
((SwMultiPortion*)pPor)->OnTop() )
nOffset = nRubyHeight;
else
nOffset = GetLineHeight();
pOrig->Pos().Y() += nOffset;
Next();
}
sal_Bool bSpaceChg = ((SwMultiPortion*)pPor)->
ChgSpaceAdd( pCurr, nSpaceAdd );
Point aOldPos = pOrig->Pos();
// Ok, for ruby portions in grid mode we have to
// temporarily set the inner line height to the
// outer line height because that value is needed
// for the adjustment inside the recursion
const sal_uInt16 nOldRubyHeight = pCurr->Height();
const sal_uInt16 nOldRubyRealHeight = pCurr->GetRealHeight();
const sal_Bool bChgHeight =
((SwMultiPortion*)pPor)->IsRuby() && bHasGrid;
if ( bChgHeight )
{
pCurr->Height( pOldCurr->Height() - nRubyHeight );
pCurr->SetRealHeight( pOldCurr->GetRealHeight() -
nRubyHeight );
}
SwLayoutModeModifier aLayoutModeModifier( *GetInfo().GetOut() );
if ( ((SwMultiPortion*)pPor)->IsBidi() )
{
aLayoutModeModifier.Modify(
((SwBidiPortion*)pPor)->GetLevel() % 2 );
}
_GetCharRect( pOrig, nOfst, pCMS );
if ( bChgHeight )
{
pCurr->Height( nOldRubyHeight );
pCurr->SetRealHeight( nOldRubyRealHeight );
}
// if we are still in the first row of
// our 2 line multiportion, we use the FirstMulti flag
// to indicate this
if ( ((SwMultiPortion*)pPor)->IsDouble() )
{
// the recursion may have damaged our font size
SetPropFont( nOldProp );
if ( !nOldProp )
nOldProp = 100;
GetInfo().GetFont()->SetProportion( 100 );
if ( pCurr == &((SwMultiPortion*)pPor)->GetRoot() )
{
GetInfo().SetFirstMulti( sal_True );
// we want to treat a double line portion like a
// single line portion, if there is no text in
// the second line
if ( !pCurr->GetNext() ||
!pCurr->GetNext()->GetLen() )
GetInfo().SetMulti( sal_False );
}
}
// ruby portions are treated like single line portions
else if( ((SwMultiPortion*)pPor)->IsRuby() ||
((SwMultiPortion*)pPor)->IsBidi() )
GetInfo().SetMulti( sal_False );
// calculate cursor values
if( ((SwMultiPortion*)pPor)->HasRotation() )
{
GetInfo().SetMulti( sal_False );
long nTmp = pOrig->Width();
pOrig->Width( pOrig->Height() );
pOrig->Height( nTmp );
nTmp = pOrig->Left() - aOldPos.X();
// if we travel into our rotated portion from
// a line below, we have to take care, that the
// y coord in pOrig is less than line height:
if ( nTmp )
nTmp--;
pOrig->Pos().X() = nX + aOldPos.X();
if( ((SwMultiPortion*)pPor)->IsRevers() )
pOrig->Pos().Y() = aOldPos.Y() + nTmp;
else
pOrig->Pos().Y() = aOldPos.Y()
+ pPor->Height() - nTmp - pOrig->Height();
if ( pCMS && pCMS->bRealHeight )
{
pCMS->aRealHeight.Y() = -pCMS->aRealHeight.Y();
// result for rotated multi portion is not
// correct for reverse (270 degree) portions
if( ((SwMultiPortion*)pPor)->IsRevers() )
{
if ( SvxParaVertAlignItem::AUTOMATIC ==
GetLineInfo().GetVertAlign() )
// if vertical alignment is set to auto,
// we switch from base line alignment
// to centered alignment
pCMS->aRealHeight.X() =
( pOrig->Width() +
pCMS->aRealHeight.Y() ) / 2;
else
pCMS->aRealHeight.X() =
( pOrig->Width() -
pCMS->aRealHeight.X() +
pCMS->aRealHeight.Y() );
}
}
}
else
{
pOrig->Pos().Y() += aOldPos.Y();
if ( ((SwMultiPortion*)pPor)->IsBidi() )
{
const SwTwips nPorWidth = pPor->Width() +
pPor->CalcSpacing( nSpaceAdd, aInf );
const SwTwips nInsideOfst = pOrig->Pos().X();
pOrig->Pos().X() = nX + nPorWidth -
nInsideOfst - pOrig->Width();
}
else
pOrig->Pos().X() += nX;
if( ((SwMultiPortion*)pPor)->HasBrackets() )
pOrig->Pos().X() +=
((SwDoubleLinePortion*)pPor)->PreWidth();
}
if( bSpaceChg )
SwDoubleLinePortion::ResetSpaceAdd( pCurr );
pCurr = pOldCurr;
nStart = nOldStart;
nY = nOldY;
bPrev = sal_False;
return;
}
if ( pPor->PrtWidth() )
{
xub_StrLen nOldLen = pPor->GetLen();
pPor->SetLen( nOfst - aInf.GetIdx() );
aInf.SetLen( pPor->GetLen() );
if( nX || !pPor->InNumberGrp() )
{
SeekAndChg( aInf );
const sal_Bool bOldOnWin = aInf.OnWin();
aInf.SetOnWin( sal_False ); // keine BULLETs!
SwTwips nTmp = nX;
aInf.SetKanaComp( pKanaComp );
aInf.SetKanaIdx( nKanaIdx );
nX += pPor->GetTxtSize( aInf ).Width();
aInf.SetOnWin( bOldOnWin );
if ( pPor->InSpaceGrp() && nSpaceAdd )
nX += pPor->CalcSpacing( nSpaceAdd, aInf );
if( bWidth )
{
pPor->SetLen( pPor->GetLen() + 1 );
aInf.SetLen( pPor->GetLen() );
aInf.SetOnWin( sal_False ); // keine BULLETs!
nTmp += pPor->GetTxtSize( aInf ).Width();
aInf.SetOnWin( bOldOnWin );
if ( pPor->InSpaceGrp() && nSpaceAdd )
nTmp += pPor->CalcSpacing(nSpaceAdd, aInf);
pOrig->Width( nTmp - nX );
}
}
pPor->SetLen( nOldLen );
}
bWidth = sal_False;
break;
}
}
}
if( pPor )
{
ASSERT( !pPor->InNumberGrp() || bInsideFirstField, "Number surprise" );
sal_Bool bEmptyFld = sal_False;
if( pPor->InFldGrp() && pPor->GetLen() )
{
SwFldPortion *pTmp = (SwFldPortion*)pPor;
while( pTmp->HasFollow() && !pTmp->GetExp().Len() )
{
KSHORT nAddX = pTmp->Width();
SwLinePortion *pNext = pTmp->GetPortion();
while( pNext && !pNext->InFldGrp() )
{
ASSERT( !pNext->GetLen(), "Where's my field follow?" );
nAddX = nAddX + pNext->Width();
pNext = pNext->GetPortion();
}
if( !pNext )
break;
pTmp = (SwFldPortion*)pNext;
nPorHeight = pTmp->Height();
nPorAscent = pTmp->GetAscent();
nX += nAddX;
bEmptyFld = sal_True;
}
}
// 8513: Felder im Blocksatz, ueberspringen
while( pPor && !pPor->GetLen() && ! bInsideFirstField &&
( pPor->IsFlyPortion() || pPor->IsKernPortion() ||
pPor->IsBlankPortion() || pPor->InTabGrp() ||
( !bEmptyFld && pPor->InFldGrp() ) ) )
{
if ( pPor->InSpaceGrp() && nSpaceAdd )
nX += pPor->PrtWidth() +
pPor->CalcSpacing( nSpaceAdd, aInf );
else
{
if( pPor->InFixMargGrp() && ! pPor->IsMarginPortion() )
{
if ( pCurr->IsSpaceAdd() )
{
if ( ++nSpaceIdx < pCurr->GetLLSpaceAddCount() )
nSpaceAdd = pCurr->GetLLSpaceAdd( nSpaceIdx );
else
nSpaceAdd = 0;
}
if( pKanaComp && ( nKanaIdx + 1 ) < pKanaComp->Count() )
++nKanaIdx;
}
if ( !pPor->IsFlyPortion() || ( pPor->GetPortion() &&
!pPor->GetPortion()->IsMarginPortion() ) )
nX += pPor->PrtWidth();
}
if( pPor->IsMultiPortion() &&
((SwMultiPortion*)pPor)->HasTabulator() )
{
if ( pCurr->IsSpaceAdd() )
{
if ( ++nSpaceIdx < pCurr->GetLLSpaceAddCount() )
nSpaceAdd = pCurr->GetLLSpaceAdd( nSpaceIdx );
else
nSpaceAdd = 0;
}
if( pKanaComp && ( nKanaIdx + 1 ) < pKanaComp->Count() )
++nKanaIdx;
}
if( !pPor->IsFlyPortion() )
{
nPorHeight = pPor->Height();
nPorAscent = pPor->GetAscent();
}
pPor = pPor->GetPortion();
}
if( aInf.GetIdx() == nOfst && pPor && pPor->InHyphGrp() &&
pPor->GetPortion() && pPor->GetPortion()->InFixGrp() )
{
// Alle Sonderportions muessen uebersprungen werden
// Beispiel: zu-[FLY]sammen, 'u' == 19, 's' == 20; Right()
// Ohne den Ausgleich landen wir vor '-' mit dem
// Ausgleich vor 's'.
while( pPor && !pPor->GetLen() )
{
DBG_LOOP;
nX += pPor->Width();
if( !pPor->IsMarginPortion() )
{
nPorHeight = pPor->Height();
nPorAscent = pPor->GetAscent();
}
pPor = pPor->GetPortion();
}
}
if( pPor && pCMS )
{
if( pCMS->bFieldInfo && pPor->InFldGrp() && pPor->Width() )
pOrig->Width( pPor->Width() );
if( pPor->IsDropPortion() )
{
nPorAscent = ((SwDropPortion*)pPor)->GetDropHeight();
// The drop height is only calculated, if we have more than
// one line. Otherwise it is 0.
if ( ! nPorAscent)
nPorAscent = pPor->Height();
nPorHeight = nPorAscent;
pOrig->Height( nPorHeight +
((SwDropPortion*)pPor)->GetDropDescent() );
if( nTmpHeight < pOrig->Height() )
{
nTmpAscent = nPorAscent;
nTmpHeight = sal_uInt16( pOrig->Height() );
}
}
if( bWidth && pPor->PrtWidth() && pPor->GetLen() &&
aInf.GetIdx() == nOfst )
{
if( !pPor->IsFlyPortion() && pPor->Height() &&
pPor->GetAscent() )
{
nPorHeight = pPor->Height();
nPorAscent = pPor->GetAscent();
}
SwTwips nTmp;
if( 2 > pPor->GetLen() )
{
nTmp = pPor->Width();
if ( pPor->InSpaceGrp() && nSpaceAdd )
nTmp += pPor->CalcSpacing( nSpaceAdd, aInf );
}
else
{
const sal_Bool bOldOnWin = aInf.OnWin();
xub_StrLen nOldLen = pPor->GetLen();
pPor->SetLen( 1 );
aInf.SetLen( pPor->GetLen() );
SeekAndChg( aInf );
aInf.SetOnWin( sal_False ); // keine BULLETs!
aInf.SetKanaComp( pKanaComp );
aInf.SetKanaIdx( nKanaIdx );
nTmp = pPor->GetTxtSize( aInf ).Width();
aInf.SetOnWin( bOldOnWin );
if ( pPor->InSpaceGrp() && nSpaceAdd )
nTmp += pPor->CalcSpacing( nSpaceAdd, aInf );
pPor->SetLen( nOldLen );
}
pOrig->Width( nTmp );
}
// travel inside field portion?
if ( pCMS->pSpecialPos )
{
// apply attributes to font
Seek( nOfst );
lcl_GetCharRectInsideField( aInf, *pOrig, *pCMS, *pPor );
}
}
}
// special case: We are at the beginning of a BidiPortion or
// directly behind a BidiPortion
if ( pCMS &&
( pLastBidiPor ||
( pPor &&
pPor->IsMultiPortion() &&
((SwMultiPortion*)pPor)->IsBidi() ) ) )
{
// we determine if the cursor has to blink before or behind
// the bidi portion
if ( pLastBidiPor )
{
const sal_uInt8 nPortionLevel = pLastBidiPor->GetLevel();
if ( pCMS->nCursorBidiLevel >= nPortionLevel )
{
// we came from inside the bidi portion, we want to blink
// behind the portion
pOrig->Pos().X() -= nLastBidiPorWidth;
// Again, there is a special case: logically behind
// the portion can actually mean that the cursor is inside
// the portion. This can happen is the last portion
// inside the bidi portion is a nested bidi portion
SwLineLayout& rLineLayout =
((SwMultiPortion*)pLastBidiPor)->GetRoot();
const SwLinePortion *pLast = rLineLayout.FindLastPortion();
if ( pLast->IsMultiPortion() )
{
ASSERT( ((SwMultiPortion*)pLast)->IsBidi(),
"Non-BidiPortion inside BidiPortion" )
pOrig->Pos().X() += pLast->Width() +
pLast->CalcSpacing( nSpaceAdd, aInf );
}
}
}
else
{
const sal_uInt8 nPortionLevel = ((SwBidiPortion*)pPor)->GetLevel();
if ( pCMS->nCursorBidiLevel >= nPortionLevel )
{
// we came from inside the bidi portion, we want to blink
// behind the portion
pOrig->Pos().X() += pPor->Width() +
pPor->CalcSpacing( nSpaceAdd, aInf );
}
}
}
pOrig->Pos().X() += nX;
if ( pCMS && pCMS->bRealHeight )
{
nTmpAscent = AdjustBaseLine( *pCurr, 0, nPorHeight, nPorAscent );
if ( nTmpAscent > nPorAscent )
pCMS->aRealHeight.X() = nTmpAscent - nPorAscent;
else
pCMS->aRealHeight.X() = 0;
ASSERT( nPorHeight, "GetCharRect: Missing Portion-Height" );
if ( nTmpHeight > nPorHeight )
pCMS->aRealHeight.Y() = nPorHeight;
else
pCMS->aRealHeight.Y() = nTmpHeight;
}
}
}
/*************************************************************************
* SwTxtCursor::GetCharRect()
*************************************************************************/
sal_Bool SwTxtCursor::GetCharRect( SwRect* pOrig, const xub_StrLen nOfst,
SwCrsrMoveState* pCMS, const long nMax )
{
CharCrsrToLine(nOfst);
// Indicates that a position inside a special portion (field, number portion)
// is requested.
const sal_Bool bSpecialPos = pCMS && pCMS->pSpecialPos;
xub_StrLen nFindOfst = nOfst;
if ( bSpecialPos )
{
const sal_uInt8 nExtendRange = pCMS->pSpecialPos->nExtendRange;
ASSERT( ! pCMS->pSpecialPos->nLineOfst || SP_EXTEND_RANGE_BEFORE != nExtendRange,
"LineOffset AND Number Portion?" )
// portions which are behind the string
if ( SP_EXTEND_RANGE_BEHIND == nExtendRange )
++nFindOfst;
// skip lines for fields which cover more than one line
for ( sal_uInt16 i = 0; i < pCMS->pSpecialPos->nLineOfst; i++ )
Next();
}
// Adjustierung ggf. nachholen
GetAdjusted();
const Point aCharPos( GetTopLeft() );
sal_Bool bRet = sal_True;
_GetCharRect( pOrig, nFindOfst, pCMS );
const SwTwips nTmpRight = Right() - 12;
pOrig->Pos().X() += aCharPos.X();
pOrig->Pos().Y() += aCharPos.Y();
if( pCMS && pCMS->b2Lines && pCMS->p2Lines )
{
pCMS->p2Lines->aLine.Pos().X() += aCharPos.X();
pCMS->p2Lines->aLine.Pos().Y() += aCharPos.Y();
pCMS->p2Lines->aPortion.Pos().X() += aCharPos.X();
pCMS->p2Lines->aPortion.Pos().Y() += aCharPos.Y();
}
if( pOrig->Left() > nTmpRight )
pOrig->Pos().X() = nTmpRight;
if( nMax )
{
if( pOrig->Top() + pOrig->Height() > nMax )
{
if( pOrig->Top() > nMax )
pOrig->Top( nMax );
pOrig->Height( nMax - pOrig->Top() );
}
if ( pCMS && pCMS->bRealHeight && pCMS->aRealHeight.Y() >= 0 )
{
long nTmp = pCMS->aRealHeight.X() + pOrig->Top();
if( nTmp >= nMax )
{
pCMS->aRealHeight.X() = nMax - pOrig->Top();
pCMS->aRealHeight.Y() = 0;
}
else if( nTmp + pCMS->aRealHeight.Y() > nMax )
pCMS->aRealHeight.Y() = nMax - nTmp;
}
}
long nOut = pOrig->Right() - GetTxtFrm()->Frm().Right();
if( nOut > 0 )
{
if( GetTxtFrm()->Frm().Width() < GetTxtFrm()->Prt().Left()
+ GetTxtFrm()->Prt().Width() )
nOut += GetTxtFrm()->Frm().Width() - GetTxtFrm()->Prt().Left()
- GetTxtFrm()->Prt().Width();
if( nOut > 0 )
pOrig->Pos().X() -= nOut + 10;
}
return bRet;
}
/*************************************************************************
* SwTxtCursor::GetCrsrOfst()
*
* Return: Offset im String
*************************************************************************/
xub_StrLen SwTxtCursor::GetCrsrOfst( SwPosition *pPos, const Point &rPoint,
const MSHORT nChgNode, SwCrsrMoveState* pCMS ) const
{
// Adjustierung ggf. nachholen
GetAdjusted();
const XubString &rText = GetInfo().GetTxt();
xub_StrLen nOffset = 0;
// x ist der horizontale Offset innerhalb der Zeile.
SwTwips x = rPoint.X();
CONST SwTwips nLeftMargin = GetLineStart();
SwTwips nRightMargin = GetLineEnd();
if( nRightMargin == nLeftMargin )
nRightMargin += 30;
const sal_Bool bLeftOver = x < nLeftMargin;
if( bLeftOver )
x = nLeftMargin;
const sal_Bool bRightOver = x > nRightMargin;
if( bRightOver )
x = nRightMargin;
sal_Bool bRightAllowed = pCMS && ( pCMS->eState == MV_NONE );
// Bis hierher in Dokumentkoordinaten.
x -= nLeftMargin;
KSHORT nX = KSHORT( x );
// Wenn es in der Zeile Attributwechsel gibt, den Abschnitt
// suchen, in dem nX liegt.
SwLinePortion *pPor = pCurr->GetFirstPortion();
xub_StrLen nCurrStart = nStart;
sal_Bool bHolePortion = sal_False;
sal_Bool bLastHyph = sal_False;
SvUShorts *pKanaComp = pCurr->GetpKanaComp();
xub_StrLen nOldIdx = GetInfo().GetIdx();
MSHORT nSpaceIdx = 0;
MSHORT nKanaIdx = 0;
long nSpaceAdd = pCurr->IsSpaceAdd() ? pCurr->GetLLSpaceAdd( 0 ) : 0;
short nKanaComp = pKanaComp ? (*pKanaComp)[0] : 0;
// nWidth ist die Breite der Zeile, oder die Breite des
// Abschnitts mit dem Fontwechsel, in dem nX liegt.
KSHORT nWidth = pPor->Width();
if ( pCurr->IsSpaceAdd() || pKanaComp )
{
if ( pPor->InSpaceGrp() && nSpaceAdd )
{
((SwTxtSizeInfo&)GetInfo()).SetIdx( nCurrStart );
nWidth = nWidth + sal_uInt16( pPor->CalcSpacing( nSpaceAdd, GetInfo() ) );
}
if( ( pPor->InFixMargGrp() && ! pPor->IsMarginPortion() ) ||
( pPor->IsMultiPortion() && ((SwMultiPortion*)pPor)->HasTabulator() )
)
{
if ( pCurr->IsSpaceAdd() )
{
if ( ++nSpaceIdx < pCurr->GetLLSpaceAddCount() )
nSpaceAdd = pCurr->GetLLSpaceAdd( nSpaceIdx );
else
nSpaceAdd = 0;
}
if( pKanaComp )
{
if ( nKanaIdx + 1 < pKanaComp->Count() )
nKanaComp = (*pKanaComp)[++nKanaIdx];
else
nKanaComp = 0;
}
}
}
KSHORT nWidth30;
if ( pPor->IsPostItsPortion() )
nWidth30 = 30 + pPor->GetViewWidth( GetInfo() ) / 2;
else
nWidth30 = ! nWidth && pPor->GetLen() && pPor->InToxRefOrFldGrp() ?
30 :
nWidth;
while( pPor->GetPortion() && nWidth30 < nX && !pPor->IsBreakPortion() )
{
nX = nX - nWidth;
nCurrStart = nCurrStart + pPor->GetLen();
bHolePortion = pPor->IsHolePortion();
pPor = pPor->GetPortion();
nWidth = pPor->Width();
if ( pCurr->IsSpaceAdd() || pKanaComp )
{
if ( pPor->InSpaceGrp() && nSpaceAdd )
{
((SwTxtSizeInfo&)GetInfo()).SetIdx( nCurrStart );
nWidth = nWidth + sal_uInt16( pPor->CalcSpacing( nSpaceAdd, GetInfo() ) );
}
if( ( pPor->InFixMargGrp() && ! pPor->IsMarginPortion() ) ||
( pPor->IsMultiPortion() && ((SwMultiPortion*)pPor)->HasTabulator() )
)
{
if ( pCurr->IsSpaceAdd() )
{
if ( ++nSpaceIdx < pCurr->GetLLSpaceAddCount() )
nSpaceAdd = pCurr->GetLLSpaceAdd( nSpaceIdx );
else
nSpaceAdd = 0;
}
if ( pKanaComp )
{
if( nKanaIdx + 1 < pKanaComp->Count() )
nKanaComp = (*pKanaComp)[++nKanaIdx];
else
nKanaComp = 0;
}
}
}
if ( pPor->IsPostItsPortion() )
nWidth30 = 30 + pPor->GetViewWidth( GetInfo() ) / 2;
else
nWidth30 = ! nWidth && pPor->GetLen() && pPor->InToxRefOrFldGrp() ?
30 :
nWidth;
if( !pPor->IsFlyPortion() && !pPor->IsMarginPortion() )
bLastHyph = pPor->InHyphGrp();
}
const sal_Bool bLastPortion = (0 == pPor->GetPortion());
if( nX==nWidth )
{
SwLinePortion *pNextPor = pPor->GetPortion();
while( pNextPor && pNextPor->InFldGrp() && !pNextPor->Width() )
{
nCurrStart = nCurrStart + pPor->GetLen();
pPor = pNextPor;
if( !pPor->IsFlyPortion() && !pPor->IsMarginPortion() )
bLastHyph = pPor->InHyphGrp();
pNextPor = pPor->GetPortion();
}
}
((SwTxtSizeInfo&)GetInfo()).SetIdx( nOldIdx );
xub_StrLen nLength = pPor->GetLen();
sal_Bool bFieldInfo = pCMS && pCMS->bFieldInfo;
if( bFieldInfo && ( nWidth30 < nX || bRightOver || bLeftOver ||
( pPor->InNumberGrp() && !pPor->IsFtnNumPortion() ) ||
( pPor->IsMarginPortion() && nWidth > nX + 30 ) ) )
((SwCrsrMoveState*)pCMS)->bPosCorr = sal_True;
// #i27615#
if (pCMS)
{
if( pCMS->bInFrontOfLabel)
{
if (! (2 * nX < nWidth && pPor->InNumberGrp() &&
!pPor->IsFtnNumPortion()))
pCMS->bInFrontOfLabel = sal_False;
}
}
// 7684: Wir sind genau auf der HyphPortion angelangt und muessen dafuer
// sorgen, dass wir in dem String landen.
// 7993: Wenn die Laenge 0 ist muessen wir raus...
if( !nLength )
{
if( pCMS )
{
if( pPor->IsFlyPortion() && bFieldInfo )
((SwCrsrMoveState*)pCMS)->bPosCorr = sal_True;
if (!bRightOver && nX)
{
if( pPor->IsFtnNumPortion())
((SwCrsrMoveState*)pCMS)->bFtnNoInfo = sal_True;
else if (pPor->InNumberGrp() ) // #i23726#
{
((SwCrsrMoveState*)pCMS)->nInNumPostionOffset = nX;
((SwCrsrMoveState*)pCMS)->bInNumPortion = sal_True;
}
}
}
if( !nCurrStart )
return 0;
// 7849, 7816: auf pPor->GetHyphPortion kann nicht verzichtet werden!
if( bHolePortion || ( !bRightAllowed && bLastHyph ) ||
( pPor->IsMarginPortion() && !pPor->GetPortion() &&
// 46598: In der letzten Zeile eines zentrierten Absatzes wollen
// wir auch mal hinter dem letzten Zeichen landen.
nCurrStart < rText.Len() ) )
--nCurrStart;
else if( pPor->InFldGrp() && ((SwFldPortion*)pPor)->IsFollow()
&& nWidth > nX )
{
if( bFieldInfo )
--nCurrStart;
else
{
KSHORT nHeight = pPor->Height();
if ( !nHeight || nHeight > nWidth )
nHeight = nWidth;
if( nChgNode && nWidth - nHeight/2 > nX )
--nCurrStart;
}
}
return nCurrStart;
}
if ( 1 == nLength )
{
if ( nWidth )
{
// Sonst kommen wir nicht mehr in zeichengeb. Rahmen hinein...
if( !( nChgNode && pPos && pPor->IsFlyCntPortion() ) )
{
if ( pPor->InFldGrp() ||
( pPor->IsMultiPortion() &&
((SwMultiPortion*)pPor)->IsBidi() ) )
{
KSHORT nHeight = 0;
if( !bFieldInfo )
{
nHeight = pPor->Height();
if ( !nHeight || nHeight > nWidth )
nHeight = nWidth;
}
if( nWidth - nHeight/2 <= nX &&
( ! pPor->InFldGrp() ||
!((SwFldPortion*)pPor)->HasFollow() ) )
++nCurrStart;
}
else if ( ( !pPor->IsFlyPortion() || ( pPor->GetPortion() &&
!pPor->GetPortion()->IsMarginPortion() &&
!pPor->GetPortion()->IsHolePortion() ) )
&& ( nWidth/2 < nX ) &&
( !bFieldInfo ||
( pPor->GetPortion() &&
pPor->GetPortion()->IsPostItsPortion() ) )
&& ( bRightAllowed || !bLastHyph ))
++nCurrStart;
// if we want to get the position inside the field, we should not return
if ( !pCMS || !pCMS->pSpecialPos )
return nCurrStart;
}
}
else
{
if ( pPor->IsPostItsPortion() || pPor->IsBreakPortion() ||
pPor->InToxRefGrp() )
return nCurrStart;
if ( pPor->InFldGrp() )
{
if( bRightOver && !((SwFldPortion*)pPor)->HasFollow() )
++nCurrStart;
return nCurrStart;
}
}
}
if( bLastPortion && (pCurr->GetNext() || pFrm->GetFollow() ) )
--nLength;
if( nWidth > nX ||
( nWidth == nX && pPor->IsMultiPortion() && ((SwMultiPortion*)pPor)->IsDouble() ) )
{
if( pPor->IsMultiPortion() )
{
// In a multi-portion we use GetCrsrOfst()-function recursively
SwTwips nTmpY = rPoint.Y() - pCurr->GetAscent() + pPor->GetAscent();
// if we are in the first line of a double line portion, we have
// to add a value to nTmpY for not staying in this line
// we also want to skip the first line, if we are inside ruby
if ( ( ((SwTxtSizeInfo*)pInf)->IsMulti() &&
((SwTxtSizeInfo*)pInf)->IsFirstMulti() ) ||
( ((SwMultiPortion*)pPor)->IsRuby() &&
((SwMultiPortion*)pPor)->OnTop() ) )
nTmpY += ((SwMultiPortion*)pPor)->Height();
// Important for cursor traveling in ruby portions:
// We have to set nTmpY to 0 in order to stay in the first row
// if the phonetic line is the second row
if ( ((SwMultiPortion*)pPor)->IsRuby() &&
! ((SwMultiPortion*)pPor)->OnTop() )
nTmpY = 0;
SwTxtCursorSave aSave( (SwTxtCursor*)this, (SwMultiPortion*)pPor,
nTmpY, nX, nCurrStart, nSpaceAdd );
SwLayoutModeModifier aLayoutModeModifier( *GetInfo().GetOut() );
if ( ((SwMultiPortion*)pPor)->IsBidi() )
{
const sal_uInt8 nBidiLevel = ((SwBidiPortion*)pPor)->GetLevel();
aLayoutModeModifier.Modify( nBidiLevel % 2 );
}
if( ((SwMultiPortion*)pPor)->HasRotation() )
{
nTmpY -= nY;
if( !((SwMultiPortion*)pPor)->IsRevers() )
nTmpY = pPor->Height() - nTmpY;
if( nTmpY < 0 )
nTmpY = 0;
nX = (KSHORT)nTmpY;
}
if( ((SwMultiPortion*)pPor)->HasBrackets() )
{
sal_uInt16 nPreWidth = ((SwDoubleLinePortion*)pPor)->PreWidth();
if ( nX > nPreWidth )
nX = nX - nPreWidth;
else
nX = 0;
}
return GetCrsrOfst( pPos, Point( GetLineStart() + nX, rPoint.Y() ),
nChgNode, pCMS );
}
if( pPor->InTxtGrp() )
{
sal_uInt8 nOldProp;
if( GetPropFont() )
{
((SwFont*)GetFnt())->SetProportion( GetPropFont() );
nOldProp = GetFnt()->GetPropr();
}
else
nOldProp = 0;
{
SwTxtSizeInfo aSizeInf( GetInfo(), rText, nCurrStart );
((SwTxtCursor*)this)->SeekAndChg( aSizeInf );
SwTxtSlot aDiffTxt( &aSizeInf, ((SwTxtPortion*)pPor), false, false );
SwFontSave aSave( aSizeInf, pPor->IsDropPortion() ?
((SwDropPortion*)pPor)->GetFnt() : NULL );
SwParaPortion* pPara = (SwParaPortion*)GetInfo().GetParaPortion();
ASSERT( pPara, "No paragraph!" );
SwDrawTextInfo aDrawInf( aSizeInf.GetVsh(),
*aSizeInf.GetOut(),
&pPara->GetScriptInfo(),
aSizeInf.GetTxt(),
aSizeInf.GetIdx(),
pPor->GetLen() );
aDrawInf.SetOfst( nX );
if ( nSpaceAdd )
{
xub_StrLen nCharCnt;
// --> FME 2005-04-04 #i41860# Thai justified alignemt needs some
// additional information:
aDrawInf.SetNumberOfBlanks( pPor->InTxtGrp() ?
static_cast<const SwTxtPortion*>(pPor)->GetSpaceCnt( aSizeInf, nCharCnt ) :
0 );
// <--
}
if ( pPor->InFldGrp() && pCMS && pCMS->pSpecialPos )
aDrawInf.SetLen( STRING_LEN ); // SMARTTAGS
aDrawInf.SetSpace( nSpaceAdd );
aDrawInf.SetFont( aSizeInf.GetFont() );
aDrawInf.SetFrm( pFrm );
aDrawInf.SetSnapToGrid( aSizeInf.SnapToGrid() );
aDrawInf.SetPosMatchesBounds( pCMS && pCMS->bPosMatchesBounds );
if ( SW_CJK == aSizeInf.GetFont()->GetActual() &&
pPara->GetScriptInfo().CountCompChg() &&
! pPor->InFldGrp() )
aDrawInf.SetKanaComp( nKanaComp );
nLength = aSizeInf.GetFont()->_GetCrsrOfst( aDrawInf );
// get position inside field portion?
if ( pPor->InFldGrp() && pCMS && pCMS->pSpecialPos )
{
pCMS->pSpecialPos->nCharOfst = nLength;
nLength = 0; // SMARTTAGS
}
// set cursor bidi level
if ( pCMS )
((SwCrsrMoveState*)pCMS)->nCursorBidiLevel =
aDrawInf.GetCursorBidiLevel();
if( bFieldInfo && nLength == pPor->GetLen() &&
( ! pPor->GetPortion() ||
! pPor->GetPortion()->IsPostItsPortion() ) )
--nLength;
}
if( nOldProp )
((SwFont*)GetFnt())->SetProportion( nOldProp );
}
else
{
if( nChgNode && pPos && pPor->IsFlyCntPortion()
&& !( (SwFlyCntPortion*)pPor )->IsDraw() )
{
// JP 24.11.94: liegt die Pos nicht im Fly, dann
// darf nicht mit STRING_LEN returnt werden!
// (BugId: 9692 + Aenderung in feshview)
SwFlyInCntFrm *pTmp = ( (SwFlyCntPortion*)pPor )->GetFlyFrm();
sal_Bool bChgNode = 1 < nChgNode;
if( !bChgNode )
{
SwFrm* pLower = pTmp->GetLower();
if( pLower && (pLower->IsTxtFrm() || pLower->IsLayoutFrm()) )
bChgNode = sal_True;
}
Point aTmpPoint( rPoint );
if ( pFrm->IsRightToLeft() )
pFrm->SwitchLTRtoRTL( aTmpPoint );
if ( pFrm->IsVertical() )
pFrm->SwitchHorizontalToVertical( aTmpPoint );
if( bChgNode && pTmp->Frm().IsInside( aTmpPoint ) &&
!( pTmp->IsProtected() ) )
{
nLength = ((SwFlyCntPortion*)pPor)->
GetFlyCrsrOfst( nX, aTmpPoint, pPos, pCMS );
// Sobald der Frame gewechselt wird, muessen wir aufpassen, dass
// unser Font wieder im OutputDevice steht.
// vgl. Paint und new SwFlyCntPortion !
((SwTxtSizeInfo*)pInf)->SelectFont();
// 6776: Das pIter->GetCrsrOfst returnt
// aus einer Verschachtelung mit STRING_LEN.
return STRING_LEN;
}
}
else
nLength = pPor->GetCrsrOfst( nX );
}
}
nOffset = nCurrStart + nLength;
// 7684: Wir sind vor der HyphPortion angelangt und muessen dafuer
// sorgen, dass wir in dem String landen.
// Bei Zeilenenden vor FlyFrms muessen ebenso behandelt werden.
if( nOffset && pPor->GetLen() == nLength && pPor->GetPortion() &&
!pPor->GetPortion()->GetLen() && pPor->GetPortion()->InHyphGrp() )
--nOffset;
return nOffset;
}
/** Looks for text portions which are inside the given rectangle
For a rectangular text selection every text portions which is inside the given
rectangle has to be put into the SwSelectionList as SwPaM
From these SwPaM the SwCursors will be created.
@param rSelList
The container for the overlapped text portions
@param rRect
A rectangle in document coordinates, text inside this rectangle has to be
selected.
@return [ true, false ]
true if any overlapping text portion has been found and put into list
false if no portion overlaps, the list has been unchanged
*/
bool SwTxtFrm::FillSelection( SwSelectionList& rSelList, const SwRect& rRect ) const
{
bool bRet = false;
// PaintArea() instead Frm() for negative indents
SwRect aTmpFrm( PaintArea() );
if( !rRect.IsOver( aTmpFrm ) )
return false;
if( rSelList.checkContext( this ) )
{
SwRect aRect( aTmpFrm );
aRect.Intersection( rRect );
// rNode without const to create SwPaMs
SwCntntNode &rNode = const_cast<SwCntntNode&>( *GetNode() );
SwNodeIndex aIdx( rNode );
SwPosition aPosL( aIdx, SwIndex( &rNode, 0 ) );
if( IsEmpty() )
{
SwPaM *pPam = new SwPaM( aPosL, aPosL );
rSelList.insertPaM( pPam );
}
else if( aRect.HasArea() )
{
xub_StrLen nOld = STRING_LEN;
SwPosition aPosR( aPosL );
Point aPoint;
SwTxtInfo aInf( const_cast<SwTxtFrm*>(this) );
SwTxtIter aLine( const_cast<SwTxtFrm*>(this), &aInf );
// We have to care for top-to-bottom layout, where right becomes top etc.
SWRECTFN( this )
SwTwips nTop = (aRect.*fnRect->fnGetTop)();
SwTwips nBottom = (aRect.*fnRect->fnGetBottom)();
SwTwips nLeft = (aRect.*fnRect->fnGetLeft)();
SwTwips nRight = (aRect.*fnRect->fnGetRight)();
SwTwips nY = aLine.Y(); // Top position of the first line
SwTwips nLastY = nY;
while( nY < nTop && aLine.Next() ) // line above rectangle
{
nLastY = nY;
nY = aLine.Y();
}
bool bLastLine = false;
if( nY < nTop && !aLine.GetNext() )
{
bLastLine = true;
nY += aLine.GetLineHeight();
}
do // check the lines for overlapping
{
if( nLastY < nTop ) // if the last line was above rectangle
nLastY = nTop;
if( nY > nBottom ) // if the current line leaves the rectangle
nY = nBottom;
if( nY >= nLastY ) // gotcha: overlapping
{
nLastY += nY;
nLastY /= 2;
if( bVert )
{
aPoint.X() = nLastY;
aPoint.Y() = nLeft;
}
else
{
aPoint.X() = nLeft;
aPoint.Y() = nLastY;
}
// Looking for the position of the left border of the rectangle
// in this text line
SwCrsrMoveState aState( MV_UPDOWN );
if( GetCrsrOfst( &aPosL, aPoint, &aState ) )
{
if( bVert )
{
aPoint.X() = nLastY;
aPoint.Y() = nRight;
}
else
{
aPoint.X() = nRight;
aPoint.Y() = nLastY;
}
// If we get a right position and if the left position
// is not the same like the left position of the line before
// which cound happen e.g. for field portions or fly frames
// a SwPaM will be inserted with these positions
if( GetCrsrOfst( &aPosR, aPoint, &aState ) &&
nOld != aPosL.nContent.GetIndex() )
{
SwPaM *pPam = new SwPaM( aPosL, aPosR );
rSelList.insertPaM( pPam );
nOld = aPosL.nContent.GetIndex();
}
}
}
if( aLine.Next() )
{
nLastY = nY;
nY = aLine.Y();
}
else if( !bLastLine )
{
bLastLine = true;
nLastY = nY;
nY += aLine.GetLineHeight();
}
else
break;
}while( nLastY < nBottom );
}
}
if( GetDrawObjs() )
{
const SwSortedObjs &rObjs = *GetDrawObjs();
for ( sal_uInt16 i = 0; i < rObjs.Count(); ++i )
{
const SwAnchoredObject* pAnchoredObj = rObjs[i];
if( !pAnchoredObj->ISA(SwFlyFrm) )
continue;
const SwFlyFrm* pFly = static_cast<const SwFlyFrm*>(pAnchoredObj);
if( pFly->IsFlyInCntFrm() && pFly->FillSelection( rSelList, rRect ) )
bRet = true;
}
}
return bRet;
}