Date: prev next · Thread: first prev next last
2013 Archives by date, by thread · List index


All of my past & future contributions to LibreOffice may be licensed under the MPLv2/LGPLv3+ dual license.
 
My 2nd. contribution:
I translated the German comments of
http://cgit.freedesktop.org/libreoffice/core/plain/sw/source/core/text/itrcrsr.cxx
to English,
 
suggested by Michel Meeks,
during LibreOffice Hackfest 2013 in Hamburg, Germany.
Rolf Hemmerling, Germany
/ / / Alone on the data highway...
/ / like on an allee in Hannover-Herrenhausen
/ / / The Hemmerling (R) WEB site
/ / Rolf Hemmerling, Germany
/ / / http://www.hemmerling.com/
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   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 .
 */


#include "hintids.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/adjustitem.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 "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
// #i111284#
#include <numrule.hxx>

// Not reentrant !!!
// is set in GetCharRect and is interpreted in UnitUp/Down.
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)
 *************************************************************************/
static 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().isEmpty() )
    {
        const sal_uInt16 nCharOfst = rCMS.pSpecialPos->nCharOfst;
        sal_Int32 nFldIdx = 0;
        sal_Int32 nFldLen = 0;

        const OUString* pString = 0;
        const SwLinePortion* pPor = &rPor;
        do
        {
            if ( pPor->InFldGrp() )
            {
                pString = &((SwFldPortion*)pPor)->GetExp();
                nFldLen = pString->getLength();
            }
            else
            {
                pString = 0;
                nFldLen = 0;
            }

            if ( ! pPor->GetPortion() || nFldIdx + nFldLen > nCharOfst )
                break;

            nFldIdx = nFldIdx + nFldLen;
            rOrig.Pos().X() += pPor->Width();
            pPor = pPor->GetPortion();

        } while ( true );

        OSL_ENSURE( nCharOfst >= nFldIdx, "Request of position inside field failed" );
        sal_Int32 nLen = nCharOfst - nFldIdx + 1;

        if ( pString )
        {
            // get script for field portion
            rInf.GetFont()->SetActual( SwScriptInfo::WhichFont( 0, pString, 0 ) );

            sal_Int32 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<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();
    // #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() +
                    std::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 )   // e.g. with large paragraph indentations in slim table columns     
       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% is the minimumm, at 0% we switch to 
                        // the default value 100% ...
                        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() +
                     std::max( rSpace.GetTxtLeft() + nLMWithNum+ nFirstLineOfs,
                          pFrm->Prt().Left() );
        }

        // 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;

    // #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()
 *************************************************************************/

// The function is interpreting / observing / evaluating / keeping / respecting the first line 
indention and the specified width.
SwTwips SwTxtMargin::GetLineStart() const
{
    SwTwips nRet = GetLeftMargin();
    if( GetAdjust() != SVX_ADJUST_LEFT &&
        !pCurr->GetFirstPortion()->IsMarginPortion() )
    {
        // If the first portion is a Margin, then the
        // adjustment is expressed by the portions.
        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: Attention, the iterators are derived!
    // GetInfo().SetOut( GetInfo().GetWin() );
}

/*************************************************************************
 *                      SwTxtCursor::GetEndCharRect()
 *************************************************************************/

// 1170: Ancient bug: Shift-End forgets the last character ...

sal_Bool SwTxtCursor::GetEndCharRect( SwRect* pOrig, const xub_StrLen nOfst,
                                  SwCrsrMoveState* pCMS, const long nMax )
{
    // 1170: Ambiguity of document positions
    bRightMargin = sal_True;
    CharCrsrToLine(nOfst);

    // Somehow twisted: nOfst names the position behind the last
    // character of the last line == This is the position in front of the first character
    // of the line, in which we are situated:
    if( nOfst != GetStart() || !pCurr->GetLen() )
    {
        // 8810: Master line RightMargin, after that LeftMargin
        const sal_Bool bRet = GetCharRect( pOrig, nOfst, pCMS, nMax );
        bRightMargin = nOfst >= GetEnd() && nOfst < GetInfo().GetTxt().getLength();
        return bRet;
    }

    if( !GetPrev() || !GetPrev()->GetLen() || !PrevLine() )
        return GetCharRect( pOrig, nOfst, pCMS, nMax );

    // If necessary, as catch up, do the adjustment
    GetAdjusted();

    KSHORT nX = 0;
    KSHORT nLast = 0;
    SwLinePortion *pPor = pCurr->GetFirstPortion();

    KSHORT nTmpHeight, nTmpAscent;
    CalcAscentAndHeight( nTmpAscent, nTmpHeight );
    KSHORT nPorHeight = nTmpHeight;
    KSHORT nPorAscent = nTmpAscent;

    // Search for the last Text/EndPortion of the line
    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 OUString aText = GetInfo().GetTxt();
    SwTxtSizeInfo aInf( GetInfo(), &aText, nStart );
    if( GetPropFont() )
        aInf.GetFont()->SetProportion( GetPropFont() );
    KSHORT nTmpAscent, nTmpHeight;  // Line height
    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<sal_uInt16>* pKanaComp = pCurr->GetpKanaComp();
        MSHORT nSpaceIdx = 0;
        size_t nKanaIdx = 0;
        long nSpaceAdd = pCurr->IsSpaceAdd() ? pCurr->GetLLSpaceAdd( 0 ) : 0;

        sal_Bool bNoTxt = sal_True;

        // First all portions without Len at beginning of line are skipped.
        // Exceptions are the mean special portions from WhichFirstPortion:
        // Num, ErgoSum, FtnNum, FeldReste
        // 8477: but also the only Textportion of an empty line with
        // Right/Center-Adjustment! So not just 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 count once as 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 )
        {
            // There's just Spezialportions.
            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( 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 bool bOldOnWin = aInf.OnWin();
                            aInf.SetOnWin( 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( 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().isEmpty() )
                {
                    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: Fields in justified text, skipped
            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() )
            {
                // All special portions have to be skipped
                // Taking the German word "zusammen" as example: zu-[FLY]sammen, 'u' == 19, 's' == 
20; Right()
                // Without the adjustment we end up in front of '-', with the
                // adjustment in front of the '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 bool bOldOnWin = aInf.OnWin();
                        xub_StrLen nOldLen = pPor->GetLen();
                        pPor->SetLen( 1 );
                        aInf.SetLen( pPor->GetLen() );
                        SeekAndChg( aInf );
                        aInf.SetOnWin( 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();
    }

    // If necessary, as catch up, do the adjustment
    GetAdjusted();

    const Point aCharPos( GetTopLeft() );
    sal_Bool bRet = sal_True;

    _GetCharRect( pOrig, nFindOfst, pCMS );

    // This actually would have to be "-1 LogicToPixel", but that seems too
    // expensive, so it's a value (-12), that should hopefully be OK.
    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();
    }

    const bool bTabOverMargin = 
GetTxtFrm()->GetTxtNode()->getIDocumentSettingAccess()->get(IDocumentSettingAccess::TAB_OVER_MARGIN);
    // Make sure the cursor respects the right margin, unless in compat mode, where the tab size 
has priority over the margin size.
    if( pOrig->Left() > nTmpRight && !bTabOverMargin)
        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
{
    // If necessary, as catch up, do the adjustment
    GetAdjusted();

    const XubString &rText = GetInfo().GetTxt();
    xub_StrLen nOffset = 0;

    // x is the horizontal offset within the line.
    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 );

    // Until here everything in document coordinates.
    x -= nLeftMargin;

    KSHORT nX = KSHORT( x );

    // If there are attribut changes in the line, search for the paragraph,
    // in which nX is situated.
    SwLinePortion *pPor = pCurr->GetFirstPortion();
    xub_StrLen nCurrStart  = nStart;
    sal_Bool bHolePortion = sal_False;
    sal_Bool bLastHyph = sal_False;

    std::deque<sal_uInt16> *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 is the width of the line, or the width of
    // the paragraph with the font change, in which nX is situated.

    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: We are exactly ended up at ther HyphPortion. It is our task to
    // provide, that we end up in the String.
    // 7993: If length = 0, then we must exit...
    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: pPor->GetHyphPortion is mandatory!
        if( bHolePortion || ( !bRightAllowed && bLastHyph ) ||
            ( pPor->IsMarginPortion() && !pPor->GetPortion() &&
            // 46598: Consider the situation: We might end up behind the last character, 
            // in the last line of a centered paragraph 
              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 )
        {
            // Else we may not enter the character-supplying frame...
            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;
            {
                OUString aText = rText;
                SwTxtSizeInfo aSizeInf( GetInfo(), &aText, 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 )
                {
                    sal_Int32 nCharCnt = 0;
                    // #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: if the Position is not in Fly, then
                //              we many not return with STRING_LEN as value!
                //              (BugId: 9692 + Change 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 );
                    // After a change of the frame, our font must be still 
                    // available for/in the OutputDevice.
                    // For comparison: Paint and new SwFlyCntPortion !
                    ((SwTxtSizeInfo*)pInf)->SelectFont();

                    // 6776: The pIter->GetCrsrOfst is returning here
                    // from a nesting with STRING_LEN.
                    return STRING_LEN;
                }
            }
            else
                nLength = pPor->GetCrsrOfst( nX );
        }
    }
    nOffset = nCurrStart + nLength;

    // 7684: We end up in front of the HyphPortion. We must assure
    // that we end up in the string.
    // If we are at end of line in front of FlyFrms, we must proceed the same way.
    
    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;
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Context


Privacy Policy | Impressum (Legal Info) | Copyright information: Unless otherwise specified, all text and images on this website are licensed under the Creative Commons Attribution-Share Alike 3.0 License. This does not include the source code of LibreOffice, which is licensed under the Mozilla Public License (MPLv2). "LibreOffice" and "The Document Foundation" are registered trademarks of their corresponding registered owners or are in actual use as trademarks in one or more countries. Their respective logos and icons are also subject to international copyright laws. Use thereof is explained in our trademark policy.