/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /************************************************************************* * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * Copyright 2000, 2010 Oracle and/or its affiliates. * * OpenOffice.org - a multi-platform office productivity suite * * This file is part of OpenOffice.org. * * OpenOffice.org is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 3 * only, as published by the Free Software Foundation. * * OpenOffice.org is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License version 3 for more details * (a copy is included in the LICENSE file that accompanied this code). * * You should have received a copy of the GNU Lesser General Public License * version 3 along with OpenOffice.org. If not, see * * for a copy of the LGPLv3 License. * ************************************************************************/ // MARKER(update_precomp.py): autogen include statement, do not remove #include "precompiled_sw.hxx" #include "hintids.hxx" #include "ndtxt.hxx" #include "frmfmt.hxx" #include "paratr.hxx" #include "flyfrm.hxx" #include "pam.hxx" #include "swselectionlist.hxx" #include #include #include #include #include #include #include // SwPageDesc #include #include #include #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 // SwMultiPortion // #i111284# #include // 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 ) { OSL_ENSURE( 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 ); OSL_ENSURE( 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 ); } } // #i111284# namespace { bool AreListLevelIndentsApplicableAndLabelAlignmentActive( const SwTxtNode& rTxtNode ) { bool bRet( false ); if ( rTxtNode.AreListLevelIndentsApplicable() ) { const SwNumFmt& rNumFmt = rTxtNode.GetNumRule()->Get( static_cast(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(); // #i95907# // #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() ) { // this calculation is identical this the calculation for L2R layout - see below nLeft = pFrm->Frm().Left() + pFrm->Prt().Left() + nLMWithNum - pNode->GetLeftMarginWithNum( sal_False ) - // #i95907# // #i111284# // rSpace.GetLeft() + // rSpace.GetTxtLeft(); ( bListLevelIndentsApplicableAndLabelAlignmentActive ? 0 : ( rSpace.GetLeft() - rSpace.GetTxtLeft() ) ); } else { // #i95907# // #i111284# 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 ) - // #i95907# // #i111284# ( 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 && // #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: OSL_FAIL( ": 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: OSL_FAIL( ": unknown InterLineSpaceRule" ); } } } else nFirstLineOfs = nFLOfst; // #i95907# // #i111284# 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() ); } // Note: 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(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; // #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; OSL_ENSURE( 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; std::deque* pKanaComp = pCurr->GetpKanaComp(); MSHORT nSpaceIdx = 0; size_t 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. // 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->size() ) ++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->size() ) ++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->size() ) ++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->size() ) ++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 ) { OSL_ENSURE( !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() ) { OSL_ENSURE( !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->size() ) ++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->size() ) ++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() ) { 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() ) { OSL_ENSURE( ((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; OSL_ENSURE( 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; OSL_ENSURE( ! 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; std::deque *pKanaComp = pCurr->GetpKanaComp(); xub_StrLen nOldIdx = GetInfo().GetIdx(); MSHORT nSpaceIdx = 0; size_t 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->size() ) 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->size() ) 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(); OSL_ENSURE( 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; // #i41860# Thai justified alignemt needs some // additional information: aDrawInf.SetNumberOfBlanks( pPor->InTxtGrp() ? static_cast(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( *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(this) ); SwTxtIter aLine( const_cast(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(pAnchoredObj); if( pFly->IsFlyInCntFrm() && pFly->FillSelection( rSelList, rRect ) ) bRet = true; } } return bRet; } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */