/* -*- 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
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/


#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/adjitem.hxx>
#include <editeng/lspcitem.hxx>
#include <editeng/lrspitem.hxx>
#include <frmatr.hxx>
#include <pagedesc.hxx> // SwPageDesc
#include <tgrditem.hxx>
#include <IDocumentSettingAccess.hxx>
#include <pagefrm.hxx>

#include "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>

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

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

        // 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<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 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<const SwTxtPortion*>(pPor)->GetSpaceCnt( aSizeInf, nCharCnt ) :
                                                0 );
                }

                if ( pPor->InFldGrp() && pCMS && pCMS->pSpecialPos )
                    aDrawInf.SetLen( STRING_LEN ); // SMARTTAGS

                aDrawInf.SetSpace( nSpaceAdd );
                aDrawInf.SetFont( aSizeInf.GetFont() );
                aDrawInf.SetFrm( pFrm );
                aDrawInf.SetSnapToGrid( aSizeInf.SnapToGrid() );
                aDrawInf.SetPosMatchesBounds( pCMS && pCMS->bPosMatchesBounds );

                if ( SW_CJK == aSizeInf.GetFont()->GetActual() &&
                     pPara->GetScriptInfo().CountCompChg() &&
                    ! pPor->InFldGrp() )
                    aDrawInf.SetKanaComp( nKanaComp );

                nLength = aSizeInf.GetFont()->_GetCrsrOfst( aDrawInf );

                // get position inside field portion?
                if ( pPor->InFldGrp() && pCMS && pCMS->pSpecialPos )
                {
                    pCMS->pSpecialPos->nCharOfst = nLength;
                    nLength = 0; // SMARTTAGS
                }

                // set cursor bidi level
                if ( pCMS )
                    ((SwCrsrMoveState*)pCMS)->nCursorBidiLevel =
                        aDrawInf.GetCursorBidiLevel();

                if( bFieldInfo && nLength == pPor->GetLen() &&
                    ( ! pPor->GetPortion() ||
                      ! pPor->GetPortion()->IsPostItsPortion() ) )
                    --nLength;
            }
            if( nOldProp )
                ((SwFont*)GetFnt())->SetProportion( nOldProp );
        }
        else
        {
            if( nChgNode && pPos && pPor->IsFlyCntPortion()
                && !( (SwFlyCntPortion*)pPor )->IsDraw() )
            {
                // JP 24.11.94: liegt die Pos nicht im Fly, dann
                //              darf nicht mit STRING_LEN returnt werden!
                //              (BugId: 9692 + Aenderung in feshview)
                SwFlyInCntFrm *pTmp = ( (SwFlyCntPortion*)pPor )->GetFlyFrm();
                sal_Bool bChgNode = 1 < nChgNode;
                if( !bChgNode )
                {
                    SwFrm* pLower = pTmp->GetLower();
                    if( pLower && (pLower->IsTxtFrm() || pLower->IsLayoutFrm()) )
                        bChgNode = sal_True;
                }
                Point aTmpPoint( rPoint );

                if ( pFrm->IsRightToLeft() )
                    pFrm->SwitchLTRtoRTL( aTmpPoint );

                if ( pFrm->IsVertical() )
                    pFrm->SwitchHorizontalToVertical( aTmpPoint );

                if( bChgNode && pTmp->Frm().IsInside( aTmpPoint ) &&
                    !( pTmp->IsProtected() ) )
                {
                    nLength = ((SwFlyCntPortion*)pPor)->
                              GetFlyCrsrOfst( nX, aTmpPoint, pPos, pCMS );
                    // Sobald der Frame gewechselt wird, muessen wir aufpassen, dass
                    // unser Font wieder im OutputDevice steht.
                    // vgl. Paint und new SwFlyCntPortion !
                    ((SwTxtSizeInfo*)pInf)->SelectFont();

                    // 6776: Das pIter->GetCrsrOfst returnt
                    // aus einer Verschachtelung mit STRING_LEN.
                    return STRING_LEN;
                }
            }
            else
                nLength = pPor->GetCrsrOfst( nX );
        }
    }
    nOffset = nCurrStart + nLength;

    // 7684: Wir sind vor der HyphPortion angelangt und muessen dafuer
    // sorgen, dass wir in dem String landen.
    // Bei Zeilenenden vor FlyFrms muessen ebenso behandelt werden.

    if( nOffset && pPor->GetLen() == nLength && pPor->GetPortion() &&
        !pPor->GetPortion()->GetLen() && pPor->GetPortion()->InHyphGrp() )
        --nOffset;

    return nOffset;
}

/** Looks for text portions which are inside the given rectangle

    For a rectangular text selection every text portions which is inside the given
    rectangle has to be put into the SwSelectionList as SwPaM
    From these SwPaM the SwCursors will be created.

    @param rSelList
    The container for the overlapped text portions

    @param rRect
    A rectangle in document coordinates, text inside this rectangle has to be
    selected.

    @return [ true, false ]
    true if any overlapping text portion has been found and put into list
    false if no portion overlaps, the list has been unchanged
*/
bool SwTxtFrm::FillSelection( SwSelectionList& rSelList, const SwRect& rRect ) const
{
    bool bRet = false;
    // PaintArea() instead Frm() for negative indents
    SwRect aTmpFrm( PaintArea() );
    if( !rRect.IsOver( aTmpFrm ) )
        return false;
    if( rSelList.checkContext( this ) )
    {
        SwRect aRect( aTmpFrm );
        aRect.Intersection( rRect );
        // rNode without const to create SwPaMs
        SwCntntNode &rNode = const_cast<SwCntntNode&>( *GetNode() );
        SwNodeIndex aIdx( rNode );
        SwPosition aPosL( aIdx, SwIndex( &rNode, 0 ) );
        if( IsEmpty() )
        {
            SwPaM *pPam = new SwPaM( aPosL, aPosL );
            rSelList.insertPaM( pPam );
        }
        else if( aRect.HasArea() )
        {
            xub_StrLen nOld = STRING_LEN;
            SwPosition aPosR( aPosL );
            Point aPoint;
            SwTxtInfo aInf( const_cast<SwTxtFrm*>(this) );
            SwTxtIter aLine( const_cast<SwTxtFrm*>(this), &aInf );
            // We have to care for top-to-bottom layout, where right becomes top etc.
            SWRECTFN( this )
            SwTwips nTop = (aRect.*fnRect->fnGetTop)();
            SwTwips nBottom = (aRect.*fnRect->fnGetBottom)();
            SwTwips nLeft = (aRect.*fnRect->fnGetLeft)();
            SwTwips nRight = (aRect.*fnRect->fnGetRight)();
            SwTwips nY = aLine.Y(); // Top position of the first line
            SwTwips nLastY = nY;
            while( nY < nTop && aLine.Next() ) // line above rectangle
            {
                nLastY = nY;
                nY = aLine.Y();
            }
            bool bLastLine = false;
            if( nY < nTop && !aLine.GetNext() )
            {
                bLastLine = true;
                nY += aLine.GetLineHeight();
            }
            do // check the lines for overlapping
            {
                if( nLastY < nTop ) // if the last line was above rectangle
                    nLastY = nTop;
                if( nY > nBottom ) // if the current line leaves the rectangle
                    nY = nBottom;
                if( nY >= nLastY ) // gotcha: overlapping
                {
                    nLastY += nY;
                    nLastY /= 2;
                    if( bVert )
                    {
                        aPoint.X() = nLastY;
                        aPoint.Y() = nLeft;
                    }
                    else
                    {
                        aPoint.X() = nLeft;
                        aPoint.Y() = nLastY;
                    }
                    // Looking for the position of the left border of the rectangle
                    // in this text line
                    SwCrsrMoveState aState( MV_UPDOWN );
                    if( GetCrsrOfst( &aPosL, aPoint, &aState ) )
                    {
                        if( bVert )
                        {
                            aPoint.X() = nLastY;
                            aPoint.Y() = nRight;
                        }
                        else
                        {
                            aPoint.X() = nRight;
                            aPoint.Y() = nLastY;
                        }
                        // If we get a right position and if the left position
                        // is not the same like the left position of the line before
                        // which cound happen e.g. for field portions or fly frames
                        // a SwPaM will be inserted with these positions
                        if( GetCrsrOfst( &aPosR, aPoint, &aState ) &&
                            nOld != aPosL.nContent.GetIndex() )
                        {
                            SwPaM *pPam = new SwPaM( aPosL, aPosR );
                            rSelList.insertPaM( pPam );
                            nOld = aPosL.nContent.GetIndex();
                        }
                    }
                }
                if( aLine.Next() )
                {
                    nLastY = nY;
                    nY = aLine.Y();
                }
                else if( !bLastLine )
                {
                    bLastLine = true;
                    nLastY = nY;
                    nY += aLine.GetLineHeight();
                }
                else
                    break;
            }while( nLastY < nBottom );
        }
    }
    if( GetDrawObjs() )
    {
        const SwSortedObjs &rObjs = *GetDrawObjs();
        for ( sal_uInt16 i = 0; i < rObjs.Count(); ++i )
        {
            const SwAnchoredObject* pAnchoredObj = rObjs[i];
            if( !pAnchoredObj->ISA(SwFlyFrm) )
                continue;
            const SwFlyFrm* pFly = static_cast<const SwFlyFrm*>(pAnchoredObj);
            if( pFly->IsFlyInCntFrm() && pFly->FillSelection( rSelList, rRect ) )
                bRet = true;
        }
    }
    return bRet;
}

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