Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  itrcrsr.cxx   Sprache: C

 
/* -*- 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 <ndtxt.hxx>
#include <doc.hxx>
#include <paratr.hxx>
#include <flyfrm.hxx>
#include <pam.hxx>
#include <swselectionlist.hxx>
#include <sortedobjs.hxx>
#include <editeng/adjustitem.hxx>
#include <editeng/lspcitem.hxx>
#include <editeng/lrspitem.hxx>
#include <frmatr.hxx>
#include <tgrditem.hxx>
#include <IDocumentSettingAccess.hxx>
#include <pagefrm.hxx>

#include "itrtxt.hxx"
#include <txtfrm.hxx>
#include <flyfrms.hxx>
#include "porfld.hxx"
#include "porfly.hxx"
#include "pordrop.hxx"
#include <crstate.hxx>
#include "pormulti.hxx"
#include <numrule.hxx>
#include <com/sun/star/i18n/ScriptType.hpp>

// Not reentrant !!!
// is set in GetCharRect and is interpreted in UnitUp/Down.
bool SwTextCursor::s_bRightMargin = false;

// 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( SwTextSizeInfo& rInf, SwRect& rOrig,
                                 const SwCursorMoveState& rCMS,
                                 const SwLinePortion& rPor )
{
    assert(rCMS.m_pSpecialPos && "Information about special pos missing");

    if ( rPor.InFieldGrp() && !static_cast<const SwFieldPortion&>(rPor).GetExp().isEmpty() )
    {
        const sal_Int32 nCharOfst = rCMS.m_pSpecialPos->nCharOfst;
        sal_Int32 nFieldIdx = 0;
        sal_Int32 nFieldLen = 0;

        OUString sString;
        const OUString* pString = nullptr;
        const SwLinePortion* pPor = &rPor;
        do
        {
            if ( pPor->InFieldGrp() )
            {
                sString = static_cast<const SwFieldPortion*>(pPor)->GetExp();
                pString = &sString;
                nFieldLen = pString->getLength();
            }
            else
            {
                pString = nullptr;
                nFieldLen = 0;
            }

            if ( ! pPor->GetNextPortion() || nFieldIdx + nFieldLen > nCharOfst )
                break;

            nFieldIdx += nFieldLen;
            rOrig.Pos().AdjustX(pPor->Width() );
            pPor = pPor->GetNextPortion();

        } while ( true );

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

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

            TextFrameIndex const nOldLen = pPor->GetLen();
            const_cast<SwLinePortion*>(pPor)->SetLen(TextFrameIndex(nLen - 1));
            const SwTwips nX1 = pPor->GetLen() ?
                                pPor->GetTextSize( rInf ).Width() :
                                0;

            SwTwips nX2 = 0;
            if ( rCMS.m_bRealWidth )
            {
                const_cast<SwLinePortion*>(pPor)->SetLen(TextFrameIndex(nLen));
                nX2 = pPor->GetTextSize( rInf ).Width();
            }

            const_cast<SwLinePortion*>(pPor)->SetLen( nOldLen );

            rOrig.Pos().AdjustX(nX1 );
            rOrig.Width( ( nX2 > nX1 ) ?
                         ( nX2 - nX1 ) :
                           1 );
        }
    }
    else
    {
        // special cases: no common fields, e.g., graphic number portion,
        // FlyInCntPortions, Notes
        rOrig.Width( rCMS.m_bRealWidth && rPor.Width() ? rPor.Width() : 1 );
    }
}

// #i111284#
namespace {
    bool IsLabelAlignmentActive( const SwTextNode& rTextNode )
    {
        bool bRet( false );

        if ( rTextNode.GetNumRule() )
        {
            int nListLevel = rTextNode.GetActualListLevel();

            if (nListLevel < 0)
                nListLevel = 0;

            if (nListLevel >= MAXLEVEL)
                nListLevel = MAXLEVEL - 1;

            const SwNumFormat& rNumFormat =
                    rTextNode.GetNumRule()->Get( o3tl::narrowing<sal_uInt16>(nListLevel) );
            if ( rNumFormat.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_ALIGNMENT )
            {
                bRet = true;
            }
        }

        return bRet;
    }
// end of anonymous namespace

void SwTextMargin::CtorInitTextMargin( SwTextFrame *pNewFrame, SwTextSizeInfo *pNewInf )
{
    CtorInitTextIter( pNewFrame, pNewInf );

    m_pInf = pNewInf;
    GetInfo().SetFont( GetFnt() );
    const SwTextNode *const pNode = m_pFrame->GetTextNodeForParaProps();

    auto stMetrics = GetFnt()->GetFontUnitMetrics();

    SvxFirstLineIndentItem const& rFirstLine(pNode->GetSwAttrSet().GetFirstLineIndent());
    SvxTextLeftMarginItem const& rTextLeftMargin(pNode->GetSwAttrSet().GetTextLeftMargin());
    SvxRightMarginItem const& rRightMargin(pNode->GetSwAttrSet().GetRightMargin());

    // #i95907#
    // #i111284#
    const SwTextNode *pTextNode = m_pFrame->GetTextNodeForParaProps();
    const bool bLabelAlignmentActive = IsLabelAlignmentActive( *pTextNode );
    const bool bListLevelIndentsApplicable = pTextNode->AreListLevelIndentsApplicable() != ::sw::ListLevelIndents::No;
    const bool bListLevelIndentsApplicableAndLabelAlignmentActive = bListLevelIndentsApplicable && bLabelAlignmentActive;

    // 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( true );
    if ( m_pFrame->IsRightToLeft() )
    {
        // this calculation is identical this the calculation for L2R layout - see below
        mnLeft = m_pFrame->getFrameArea().Left() + m_pFrame->getFramePrintArea().Left() + nLMWithNum
                 - pNode->GetLeftMarginWithNum() -
                 // #i95907#
                 // #i111284#
                 // rSpace.GetLeft() + rSpace.GetTextLeft();
                 (rTextLeftMargin.ResolveLeft(rFirstLine, stMetrics)
                  - rTextLeftMargin.ResolveTextLeft(stMetrics));
    }
    else
    {
        // #i95907#
        // #i111284#
        if ( bListLevelIndentsApplicableAndLabelAlignmentActive ||
             !pNode->getIDocumentSettingAccess()->get(DocumentSettingId::IGNORE_FIRST_LINE_INDENT_IN_NUMBERING) )
        {
            // this calculation is identical this the calculation for R2L layout - see above
            mnLeft = m_pFrame->getFrameArea().Left() + m_pFrame->getFramePrintArea().Left()
                     + nLMWithNum - pNode->GetLeftMarginWithNum() -
                     // #i95907#
                     // #i111284#
                     (rTextLeftMargin.ResolveLeft(rFirstLine, stMetrics)
                      - rTextLeftMargin.ResolveTextLeft(stMetrics));
        }
        else
        {
            mnLeft
                = m_pFrame->getFrameArea().Left()
                  + std::max(tools::Long(rTextLeftMargin.ResolveTextLeft(stMetrics) + nLMWithNum),
                             m_pFrame->getFramePrintArea().Left());
        }
    }

    mnRight = m_pFrame->getFrameArea().Left() + m_pFrame->getFramePrintArea().Left() + m_pFrame->getFramePrintArea().Width();

    // tdf#163913: Apply font-relative adjustment to the margins
    mnLeft += rTextLeftMargin.ResolveLeftVariablePart(rFirstLine, stMetrics);
    mnRight -= rRightMargin.ResolveRightVariablePart(stMetrics);

    if (mnLeft >= mnRight &&
        // #i53066# Omit adjustment of nLeft for numbered
        // paras inside cells inside new documents:
        (pNode->getIDocumentSettingAccess()->get(
             DocumentSettingId::IGNORE_FIRST_LINE_INDENT_IN_NUMBERING)
         || !m_pFrame->IsInTab()
         || (bListLevelIndentsApplicable
             && nLMWithNum == rTextLeftMargin.ResolveTextLeft(stMetrics))
         || (!bLabelAlignmentActive && nLMWithNum == 0)))
    {
        mnLeft = m_pFrame->getFramePrintArea().Left() + m_pFrame->getFrameArea().Left();
        if( mnLeft >= mnRight )   // e.g. with large paragraph indentations in slim table columns
            mnRight = mnLeft + 1; // einen goennen wir uns immer
    }

    if( m_pFrame->IsFollow() && m_pFrame->GetOffset() )
        mnFirst = mnLeft;
    else
    {
        short nFLOfst = 0;
        tools::Long nFirstLineOfs = 0;
        if (!pNode->GetFirstLineOfsWithNum(nFLOfst, stMetrics) && rFirstLine.IsAutoFirst())
        {
            nFirstLineOfs = GetFnt()->GetSize( GetFnt()->GetActual() ).Height();
            LanguageType const aLang = m_pFrame->GetLangOfChar(
                    TextFrameIndex(0), css::i18n::ScriptType::ASIAN);
            if (aLang != LANGUAGE_KOREAN && aLang != LANGUAGE_JAPANESE)
                nFirstLineOfs<<=1;

            // tdf#129448: Auto first-line indent should not be effected by line space.
            // Below is for compatibility with old documents.
            if (!pNode->getIDocumentSettingAccess()->get(DocumentSettingId::AUTO_FIRST_LINE_INDENT_DISREGARD_LINE_SPACE))
            {
                const SvxLineSpacingItem *pSpace = m_aLineInf.GetLineSpacing();
                if( pSpace )
                {
                    switch( pSpace->GetLineSpaceRule() )
                    {
                        case SvxLineSpaceRule::Auto:
                        break;
                        case SvxLineSpaceRule::Min:
                        {
                            if( nFirstLineOfs < pSpace->GetLineHeight() )
                                nFirstLineOfs = pSpace->GetLineHeight();
                            break;
                        }
                        case SvxLineSpaceRule::Fix:
                            nFirstLineOfs = pSpace->GetLineHeight();
                        break;
                        default: OSL_FAIL( ": unknown LineSpaceRule" );
                    }
                    switch( pSpace->GetInterLineSpaceRule() )
                    {
                        case SvxInterLineSpaceRule::Off:
                        break;
                        case SvxInterLineSpaceRule::Prop:
                        {
                            tools::Long nTmp = pSpace->GetPropLineSpace();
                            // 50% is the minimum, at 0% we switch to
                            // the default value 100%...
                            if( nTmp < 50 )
                                nTmp = nTmp ? 50 : 100;

                            nTmp *= nFirstLineOfs;
                            nTmp /= 100;
                            if( !nTmp )
                                ++nTmp;
                            nFirstLineOfs = nTmp;
                            break;
                        }
                        case SvxInterLineSpaceRule::Fix:
                        {
                            nFirstLineOfs += pSpace->GetInterLineSpace();
                            break;
                        }
                        default: OSL_FAIL( ": unknown InterLineSpaceRule" );
                    }
                }
            }
        }
        else
            nFirstLineOfs = nFLOfst;

        // #i95907#
        // #i111284#
        if ( m_pFrame->IsRightToLeft() ||
             bListLevelIndentsApplicableAndLabelAlignmentActive ||
             !pNode->getIDocumentSettingAccess()->get(DocumentSettingId::IGNORE_FIRST_LINE_INDENT_IN_NUMBERING) )
        {
            if ( nFirstLineOfs < 0 && m_pFrame->IsInTab() &&
                 mnLeft == m_pFrame->getFramePrintArea().Left() + m_pFrame->getFrameArea().Left() &&
                 !m_pFrame->IsRightToLeft() &&
                 !bListLevelIndentsApplicableAndLabelAlignmentActive )
            {
                // tdf#130218 always show hanging indent in narrow table cells
                // to avoid hiding the text content of the first line
                mnLeft -= nFirstLineOfs;
            }

            mnFirst = mnLeft + nFirstLineOfs;
        }
        else
        {
            mnFirst = m_pFrame->getFrameArea().Left()
                      + std::max(rTextLeftMargin.ResolveTextLeft(stMetrics) + nLMWithNum
                                     + nFirstLineOfs,
                                 m_pFrame->getFramePrintArea().Left());
        }

        // Note: <SwTextFrame::GetAdditionalFirstLineOffset()> returns a negative
        //       value for the new list label position and space mode LABEL_ALIGNMENT
        //       and label alignment CENTER and RIGHT in L2R layout respectively
        //       label alignment LEFT and CENTER in R2L layout
        mnFirst += m_pFrame->GetAdditionalFirstLineOffset();

        if( mnFirst >= mnRight )
            mnFirst = mnRight - 1;
    }
    const SvxAdjustItem& rAdjust = m_pFrame->GetTextNodeForParaProps()->GetSwAttrSet().GetAdjust();
    mnAdjust = rAdjust.GetAdjust();

    // left is left and right is right
    if ( m_pFrame->IsRightToLeft() )
    {
        if ( SvxAdjust::Left == mnAdjust )
            mnAdjust = SvxAdjust::Right;
        else if ( SvxAdjust::Right == mnAdjust )
            mnAdjust = SvxAdjust::Left;
    }

    m_bOneBlock = rAdjust.GetOneWord() == SvxAdjust::Block;
    m_bLastBlock = rAdjust.GetLastBlock() == SvxAdjust::Block;
    m_bLastCenter = rAdjust.GetLastBlock() == SvxAdjust::Center;

    // #i91133#
    mnTabLeft = pNode->GetLeftMarginForTabCalculation();

    DropInit();
}

void SwTextMargin::DropInit()
{
    mnDropLeft = mnDropLines = mnDropHeight = mnDropDescent = 0;
    const SwParaPortion *pPara = GetInfo().GetParaPortion();
    if( pPara )
    {
        const SwDropPortion *pPorDrop = pPara->FindDropPortion();
        if ( pPorDrop )
        {
            mnDropLeft = pPorDrop->GetDropLeft();
            mnDropLines = pPorDrop->GetLines();
            mnDropHeight = pPorDrop->GetDropHeight();
            mnDropDescent = pPorDrop->GetDropDescent();
        }
    }
}

// The function is interpreting / observing / evaluating / keeping / respecting the first line indention and the specified width.
SwTwips SwTextMargin::GetLineStart() const
{
    SwTwips nRet = GetLeftMargin();
    if( GetAdjust() != SvxAdjust::Left &&
        !m_pCurr->GetFirstPortion()->IsMarginPortion() )
    {
        // If the first portion is a Margin, then the
        // adjustment is expressed by the portions.
        if( GetAdjust() == SvxAdjust::Right )
            nRet = Right() - CurrWidth();
        else if( GetAdjust() == SvxAdjust::Center )
            nRet += (GetLineWidth() - CurrWidth()) / 2;
    }
    return nRet;
}

void SwTextCursor::CtorInitTextCursor( SwTextFrame *pNewFrame, SwTextSizeInfo *pNewInf )
{
    CtorInitTextMargin( pNewFrame, pNewInf );
    // 6096: Attention, the iterators are derived!
    // GetInfo().SetOut( GetInfo().GetWin() );
}

static bool isTrailingDecoration(SwLinePortion* p)
{
    // Optional no-width portion, followed only by no-width portions and/or terminating portions?
    for (; p; p = p->GetNextPortion())
    {
        if (p->IsMarginPortion() || p->IsBreakPortion())
            return true;
        if (p->Width())
            return false;
    }
    return true// no more portions
}

// tdf#120715 tdf#43100: Make width for some HolePortions, so cursor will be able to move into it.
// It should not change the layout, so this should be called after the layout is calculated.
void SwTextCursor::AddExtraBlankWidth()
{
    SwLinePortion* pPos = m_pCurr->GetNextPortion();
    while (pPos)
    {
        SwLinePortion* pNextPos = pPos->GetNextPortion();
        // Do it only if it is the last portion that able to handle the cursor,
        // else the next portion would miscalculate the cursor position
        if (pPos->ExtraBlankWidth() && isTrailingDecoration(pNextPos))
        {
            pPos->Width(pPos->Width() + pPos->ExtraBlankWidth());
            pPos->ExtraBlankWidth(0);
        }
        pPos = pNextPos;
    }
}

// 1170: Ancient bug: Shift-End forgets the last character ...
void SwTextCursor::GetEndCharRect(SwRect* pOrig, const TextFrameIndex nOfst,
                                  SwCursorMoveState* pCMS, const tools::Long nMax )
{
    // 1170: Ambiguity of document positions
    s_bRightMargin = true;
    CharCursorToLine(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() || !m_pCurr->GetLen() )
    {
        // 8810: Master line RightMargin, after that LeftMargin
        GetCharRect( pOrig, nOfst, pCMS, nMax );
        s_bRightMargin = nOfst >= GetEnd() && nOfst < TextFrameIndex(GetInfo().GetText().getLength());
        return;
    }

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

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

    tools::Long nX = 0;
    tools::Long nLast = 0;
    SwLinePortion *pPor = m_pCurr->GetFirstPortion();

    SwTwips nTmpHeight, nTmpAscent;
    CalcAscentAndHeight( nTmpAscent, nTmpHeight );
    sal_uInt16 nPorHeight = nTmpHeight;
    sal_uInt16 nPorAscent = nTmpAscent;

    // Search for the last Text/EndPortion of the line
    while( pPor )
    {
        nX += pPor->Width();
        if( pPor->InTextGrp() || ( pPor->GetLen() && !pPor->IsFlyPortion()
            && !pPor->IsHolePortion() ) || pPor->IsBreakPortion() )
        {
            nLast = nX;
            nPorHeight = pPor->Height();
            nPorAscent = pPor->GetAscent();
        }
        pPor = pPor->GetNextPortion();
    }

    const Size aCharSize( 1, nTmpHeight );
    pOrig->Pos( GetTopLeft() );
    pOrig->SSize( aCharSize );
    pOrig->Pos().AdjustX(nLast );
    const SwTwips nTmpRight = Right() - 1;
    if( pOrig->Left() > nTmpRight )
        pOrig->Pos().setX( nTmpRight );

    if ( pCMS && pCMS->m_bRealHeight )
    {
        if ( nTmpAscent > nPorAscent )
            pCMS->m_aRealHeight.setX( nTmpAscent - nPorAscent );
        else
            pCMS->m_aRealHeight.setX( 0 );
        OSL_ENSURE( nPorHeight, "GetCharRect: Missing Portion-Height" );
        pCMS->m_aRealHeight.setY( nPorHeight );
    }
}

// internal function, called by SwTextCursor::GetCharRect() to calculate
// the relative character position in the current line.
// pOrig refers 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 SwTextCursor::GetCharRect_( SwRect* pOrig, TextFrameIndex const nOfst,
    SwCursorMoveState* pCMS )
{
    const OUString aText = GetInfo().GetText();
    SwTextSizeInfo aInf( GetInfo(), &aText, m_nStart );
    if( GetPropFont() )
        aInf.GetFont()->SetProportion( GetPropFont() );
    SwTwips 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 bool bInsideFirstField = pCMS && pCMS->m_pSpecialPos &&
                                    ( pCMS->m_pSpecialPos->nLineOfst ||
                                      SwSPExtendRange::BEFORE ==
                                      pCMS->m_pSpecialPos->nExtendRange );

    bool bWidth = pCMS && pCMS->m_bRealWidth;
    if( !m_pCurr->GetLen() && !m_pCurr->Width() )
    {
        if ( pCMS && pCMS->m_bRealHeight )
        {
            pCMS->m_aRealHeight.setX( 0 );
            pCMS->m_aRealHeight.setY( nTmpHeight );
        }
    }
    else
    {
        SwTwips nPorHeight = nTmpHeight;
        SwTwips nPorAscent = nTmpAscent;
        SwTwips nX = 0;
        SwTwips nTmpFirst = 0;
        SwLinePortion *pPor = m_pCurr->GetFirstPortion();
        SwBidiPortion* pLastBidiPor = nullptr;
        TextFrameIndex nLastBidiIdx(-1);
        SwTwips nLastBidiPorWidth = 0;
        std::deque<sal_uInt16>* pKanaComp = m_pCurr->GetpKanaComp();
        sal_uInt16 nSpaceIdx = 0;
        size_t nKanaIdx = 0;
        tools::Long nSpaceAdd = m_pCurr->IsSpaceAdd() ? m_pCurr->GetLLSpaceAdd( 0 ) : 0;

        bool bNoText = true;

        // First all portions without Len at beginning of line are skipped.
        // Exceptions are the mean special portions from WhichFirstPortion:
        // Num, ErgoSum, FootnoteNum, FieldRests
        // 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( bNoText )
                nTmpFirst = nX;
            // 8670: EndPortions count once as TextPortions.
            // if( pPor->InTextGrp() || pPor->IsBreakPortion() )
            if( pPor->InTextGrp() || pPor->IsBreakPortion() || pPor->InTabGrp() )
            {
                bNoText = false;
                nTmpFirst = nX;
            }
            if( pPor->IsMultiPortion() && static_cast<SwMultiPortion*>(pPor)->HasTabulator() )
            {
                if ( m_pCurr->IsSpaceAdd() )
                {
                    if ( ++nSpaceIdx < m_pCurr->GetLLSpaceAddCount() )
                        nSpaceAdd = m_pCurr->GetLLSpaceAdd( nSpaceIdx );
                    else
                        nSpaceAdd = 0;
                }

                if( pKanaComp && ( nKanaIdx + 1 ) < pKanaComp->size() )
                    ++nKanaIdx;
            }
            if( pPor->InFixMargGrp() )
            {
                if( pPor->IsMarginPortion() )
                    bNoText = false;
                else
                {
                    // fix margin portion => next SpaceAdd, KanaComp value
                    if ( m_pCurr->IsSpaceAdd() )
                    {
                        if ( ++nSpaceIdx < m_pCurr->GetLLSpaceAddCount() )
                            nSpaceAdd = m_pCurr->GetLLSpaceAdd( nSpaceIdx );
                        else
                            nSpaceAdd = 0;
                    }

                    if( pKanaComp && ( nKanaIdx + 1 ) < pKanaComp->size() )
                        ++nKanaIdx;
                }
            }
            pPor = pPor->GetNextPortion();
        }

        if( !pPor )
        {
            // There's just Spezialportions.
            nX = nTmpFirst;
        }
        else
        {
            if( !pPor->IsMarginPortion() && !pPor->IsPostItsPortion() &&
                (!pPor->InFieldGrp() || 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->InFieldGrp() || 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 TextFrameIndex nExtra( (pPor->IsMultiPortion()
                             && !static_cast<SwMultiPortion*>(pPor)->IsBidi()
                             && !bWidth)
                        ? 0 : 1 );
                if ( aInf.GetIdx() + pPor->GetLen() < nOfst + nExtra )
                {
                    if ( pPor->InSpaceGrp() && nSpaceAdd )
                        // tdf#163042 In the case of shrunk lines with a single portion,
                        // adjust the line width to show the cursor in the correct position
                        nX += ( ( std::abs( m_pCurr->Width() - pPor->PrtWidth() ) <= 1 &&
                                        m_pCurr->ExtraShrunkWidth() > 0 )
                                    ? m_pCurr->ExtraShrunkWidth()
                                    : pPor->PrtWidth() ) +
                              pPor->CalcSpacing( nSpaceAdd, aInf );
                    else
                    {
                        if( pPor->InFixMargGrp() && ! pPor->IsMarginPortion() )
                        {
                            // update to current SpaceAdd, KanaComp values
                            if ( m_pCurr->IsSpaceAdd() )
                            {
                                if ( ++nSpaceIdx < m_pCurr->GetLLSpaceAddCount() )
                                    nSpaceAdd = m_pCurr->GetLLSpaceAdd( nSpaceIdx );
                                else
                                    nSpaceAdd = 0;
                            }

                            if ( pKanaComp &&
                                ( nKanaIdx + 1 ) < pKanaComp->size()
                                )
                                ++nKanaIdx;
                        }
                        if ( !pPor->IsFlyPortion() || ( pPor->GetNextPortion() &&
                                !pPor->GetNextPortion()->IsMarginPortion() ) )
                            nX += pPor->PrtWidth();
                    }
                    if( pPor->IsMultiPortion() )
                    {
                        if ( static_cast<SwMultiPortion*>(pPor)->HasTabulator() )
                        {
                            if ( m_pCurr->IsSpaceAdd() )
                            {
                                if ( ++nSpaceIdx < m_pCurr->GetLLSpaceAddCount() )
                                    nSpaceAdd = m_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 ( static_cast<SwMultiPortion*>(pPor)->IsBidi() &&
                             aInf.GetIdx() + pPor->GetLen() == nOfst )
                        {
                             pLastBidiPor = static_cast<SwBidiPortion*>(pPor);
                             nLastBidiIdx = aInf.GetIdx();
                             nLastBidiPorWidth = pLastBidiPor->Width() +
                                                 pLastBidiPor->CalcSpacing( nSpaceAdd, aInf );
                        }
                    }

                    aInf.SetIdx( aInf.GetIdx() + pPor->GetLen() );
                    pPor = pPor->GetNextPortion();
                }
                else
                {
                    if( pPor->IsMultiPortion() )
                    {
                        GetInfo().SetMulti( true );
                        pOrig->Pos().AdjustY(nTmpAscent - nPorAscent );

                        if( pCMS && pCMS->m_b2Lines )
                        {
                            const bool bRecursion (pCMS->m_p2Lines);
                            if ( !bRecursion )
                            {
                                pCMS->m_p2Lines.reset(new Sw2LinesPos);
                                pCMS->m_p2Lines->aLine = SwRect(aCharPos, aCharSize);
                            }

                            ifstatic_cast<SwMultiPortion*>(pPor)->HasRotation() )
                            {
                                ifstatic_cast<SwMultiPortion*>(pPor)->IsRevers() )
                                    pCMS->m_p2Lines->nMultiType = MultiPortionType::ROT_270;
                                else
                                    pCMS->m_p2Lines->nMultiType = MultiPortionType::ROT_90;
                            }
                            else ifstatic_cast<SwMultiPortion*>(pPor)->IsDouble() )
                                pCMS->m_p2Lines->nMultiType = MultiPortionType::TWOLINE;
                            else ifstatic_cast<SwMultiPortion*>(pPor)->IsBidi() )
                                pCMS->m_p2Lines->nMultiType = MultiPortionType::BIDI;
                            else
                                pCMS->m_p2Lines->nMultiType = MultiPortionType::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->m_p2Lines->aPortion = aRect;
                            else
                                pCMS->m_p2Lines->aPortion2 = aRect;
                        }

                        // In a multi-portion we use GetCharRect()-function
                        // recursively and must add the x-position
                        // of the multi-portion.
                        TextFrameIndex const nOldStart = m_nStart;
                        SwTwips nOldY = m_nY;
                        sal_uInt8 nOldProp = GetPropFont();
                        m_nStart = aInf.GetIdx();
                        SwLineLayout* pOldCurr = m_pCurr;
                        m_pCurr = &static_cast<SwMultiPortion*>(pPor)->GetRoot();
                        ifstatic_cast<SwMultiPortion*>(pPor)->IsDouble() )
                            SetPropFont( 50 );

                        SwTextGridItem const*const pGrid(
                                GetGridItem(GetTextFrame()->FindPageFrame()));
                        const bool bHasGrid = pGrid && GetInfo().SnapToGrid();
                        const sal_uInt16 nRubyHeight = bHasGrid ?
                                                   pGrid->GetRubyHeight() : 0;

                        if( m_nStart + m_pCurr->GetLen() <= nOfst && GetNext() &&
                            ( ! static_cast<SwMultiPortion*>(pPor)->IsRuby() ||
                                static_cast<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 &&
                                static_cast<SwMultiPortion*>(pPor)->IsRuby() &&
                                static_cast<SwMultiPortion*>(pPor)->OnTop() )
                                nOffset = nRubyHeight;
                            else
                                nOffset = GetLineHeight();

                            pOrig->Pos().AdjustY(nOffset );
                            Next();
                        }

                        const bool bSpaceChg = static_cast<SwMultiPortion*>(pPor)->
                                                ChgSpaceAdd( m_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 = m_pCurr->Height();
                        const sal_uInt16 nOldRubyRealHeight = m_pCurr->GetRealHeight();
                        const bool bChgHeight =
                                static_cast<SwMultiPortion*>(pPor)->IsRuby() && bHasGrid;

                        if ( bChgHeight )
                        {
                            m_pCurr->Height( pOldCurr->Height() - nRubyHeight );
                            m_pCurr->SetRealHeight( pOldCurr->GetRealHeight() -
                                                  nRubyHeight );
                        }

                        SwLayoutModeModifier aLayoutModeModifier( *GetInfo().GetOut() );
                        if ( static_cast<SwMultiPortion*>(pPor)->IsBidi() )
                        {
                            aLayoutModeModifier.Modify(
                                static_cast<SwBidiPortion*>(pPor)->GetLevel() % 2 );
                        }

                        GetCharRect_( pOrig, nOfst, pCMS );

                        if ( bChgHeight )
                        {
                            m_pCurr->Height( nOldRubyHeight );
                            m_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 ( static_cast<SwMultiPortion*>(pPor)->IsDouble() )
                        {
                            // the recursion may have damaged our font size
                            SetPropFont( nOldProp );
                            GetInfo().GetFont()->SetProportion( 100 );

                            if ( m_pCurr == &static_cast<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 ( !m_pCurr->GetNext() ||
                                     !m_pCurr->GetNext()->GetLen() )
                                    GetInfo().SetMulti( false );
                            }
                        }
                        // ruby portions are treated like single line portions
                        else ifstatic_cast<SwMultiPortion*>(pPor)->IsRuby() ||
                                 static_cast<SwMultiPortion*>(pPor)->IsBidi() )
                            GetInfo().SetMulti( false );

                        // calculate cursor values
                        ifstatic_cast<SwMultiPortion*>(pPor)->HasRotation() )
                        {
                            GetInfo().SetMulti( false );
                            tools::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().setX( nX + aOldPos.X() );
                            ifstatic_cast<SwMultiPortion*>(pPor)->IsRevers() )
                                pOrig->Pos().setY( aOldPos.Y() + nTmp );
                            else
                                pOrig->Pos().setY( aOldPos.Y()
                                    + pPor->Height() - nTmp - pOrig->Height() );
                            if ( pCMS && pCMS->m_bRealHeight )
                            {
                                pCMS->m_aRealHeight.setY( -pCMS->m_aRealHeight.Y() );
                                // result for rotated multi portion is not
                                // correct for reverse (270 degree) portions
                                ifstatic_cast<SwMultiPortion*>(pPor)->IsRevers() )
                                {
                                    if ( SvxParaVertAlignItem::Align::Automatic ==
                                         GetLineInfo().GetVertAlign() )
                                        // if vertical alignment is set to auto,
                                        // we switch from base line alignment
                                        // to centered alignment
                                        pCMS->m_aRealHeight.setX(
                                            ( pOrig->Width() +
                                              pCMS->m_aRealHeight.Y() ) / 2 );
                                    else
                                        pCMS->m_aRealHeight.setX(
                                            pOrig->Width() -
                                            pCMS->m_aRealHeight.X() +
                                            pCMS->m_aRealHeight.Y() );
                                }
                            }
                        }
                        else
                        {
                            pOrig->Pos().AdjustY(aOldPos.Y() );
                            if ( static_cast<SwMultiPortion*>(pPor)->IsBidi() )
                            {
                                const SwTwips nPorWidth = pPor->Width() +
                                                         pPor->CalcSpacing( nSpaceAdd, aInf );
                                const SwTwips nInsideOfst = pOrig->Pos().X();
                                pOrig->Pos().setX( nX + nPorWidth -
                                                   nInsideOfst - pOrig->Width() );
                            }
                            else
                                pOrig->Pos().AdjustX(nX );

                            ifstatic_cast<SwMultiPortion*>(pPor)->HasBrackets() )
                                pOrig->Pos().AdjustX(
                                    static_cast<SwDoubleLinePortion*>(pPor)->PreWidth() );
                        }

                        if( bSpaceChg )
                            SwDoubleLinePortion::ResetSpaceAdd( m_pCurr );

                        m_pCurr = pOldCurr;
                        m_nStart = nOldStart;
                        m_nY = nOldY;
                        m_bPrev = false;

                        return;
                    }
                    if ( pPor->PrtWidth() )
                    {
                        // tdf#30731: To get the correct nOfst width, we need
                        // to send the whole portion string to GetTextSize()
                        // and ask it to return the width of nOfst by calling
                        // SetMeasureLen(). Cutting the string at nOfst can
                        // give the wrong width if nOfst is in e.g. the middle
                        // of a ligature. See SwFntObj::DrawText().
                        TextFrameIndex const nOldLen = pPor->GetLen();
                        TextFrameIndex nMaxLen = TextFrameIndex(aInf.GetText().getLength()) - aInf.GetIdx();
                        aInf.SetLen( std::min(nMaxLen, pPor->GetLen()) );
                        pPor->SetLen( nOfst - aInf.GetIdx() );
                        aInf.SetMeasureLen(pPor->GetLen());
                        if (aInf.GetLen() < aInf.GetMeasureLen())
                        {
                            pPor->SetLen(aInf.GetMeasureLen());
                            aInf.SetLen(pPor->GetLen());
                        }
                        if( nX || !pPor->InNumberGrp() )
                        {
                            SeekAndChg( aInf );
                            const bool bOldOnWin = aInf.OnWin();
                            aInf.SetOnWin( false ); // no BULLETs!
                            SwTwips nTmp = nX;
                            aInf.SetKanaComp( pKanaComp );
                            aInf.SetKanaIdx( nKanaIdx );
                            nX += pPor->GetTextSize( aInf ).Width();
                            aInf.SetOnWin( bOldOnWin );
                            if ( pPor->InSpaceGrp() && nSpaceAdd )
                                nX += pPor->CalcSpacing( nSpaceAdd, aInf );
                            if( bWidth )
                            {
                                pPor->SetLen(pPor->GetLen() + TextFrameIndex(1));
                                aInf.SetMeasureLen(pPor->GetLen());
                                if (aInf.GetLen() < aInf.GetMeasureLen())
                                {
                                    pPor->SetLen(aInf.GetMeasureLen());
                                    aInf.SetLen(pPor->GetLen());
                                }
                                aInf.SetOnWin( false ); // no BULLETs!
                                nTmp += pPor->GetTextSize( aInf ).Width();
                                aInf.SetOnWin( bOldOnWin );
                                if ( pPor->InSpaceGrp() && nSpaceAdd )
                                    nTmp += pPor->CalcSpacing(nSpaceAdd, aInf);
                                pOrig->Width( nTmp - nX );
                            }
                        }
                        pPor->SetLen( nOldLen );

                        // Shift the cursor with the right border width
                        // Note: nX remains positive because GetTextSize() also include the width of the right border
                        if( aInf.GetIdx() < nOfst && nOfst < aInf.GetIdx() + pPor->GetLen() )
                        {
                            // Find the current drop portion part and use its right border
                            if( pPor->IsDropPortion() && static_cast<SwDropPortion*>(pPor)->GetLines() > 1 )
                            {
                                SwDropPortion* pDrop = static_cast<SwDropPortion*>(pPor);
                                const SwDropPortionPart* pCurrPart = pDrop->GetPart();
                                TextFrameIndex nSumLength(0);
                                while( pCurrPart && (nSumLength += pCurrPart->GetLen()) < nOfst - aInf.GetIdx() )
                                {
                                    pCurrPart = pCurrPart->GetFollow();
                                }
                                if( pCurrPart && nSumLength != nOfst - aInf.GetIdx() &&
                                    pCurrPart->GetFont().GetRightBorder() && !pCurrPart->GetJoinBorderWithNext() )
                                {
                                    nX -= pCurrPart->GetFont().GetRightBorderSpace();
                                }
                            }
                            else if( GetInfo().GetFont()->GetRightBorder() && !pPor->GetJoinBorderWithNext())
                            {
                                nX -= GetInfo().GetFont()->GetRightBorderSpace();
                            }
                        }
                    }
                    bWidth = false;
                    break;
                }
            }
        }

        if( pPor )
        {
            OSL_ENSURE( !pPor->InNumberGrp() || bInsideFirstField, "Number surprise" );
            bool bEmptyField = false;
            if( pPor->InFieldGrp() && pPor->GetLen() )
            {
                SwFieldPortion *pTmp = static_cast<SwFieldPortion*>(pPor);
                while( pTmp->HasFollow() && pTmp->GetExp().isEmpty() )
                {
                    sal_uInt16 nAddX = pTmp->Width();
                    SwLinePortion *pNext = pTmp->GetNextPortion();
                    while( pNext && !pNext->InFieldGrp() )
                    {
                        OSL_ENSURE( !pNext->GetLen(), "Where's my field follow?" );
                        nAddX += pNext->Width();
                        pNext = pNext->GetNextPortion();
                    }
                    if( !pNext )
                        break;
                    pTmp = static_cast<SwFieldPortion*>(pNext);
                    nPorHeight = pTmp->Height();
                    nPorAscent = pTmp->GetAscent();
                    nX += nAddX;
                    bEmptyField = true;
                }
            }
            // 8513: Fields in justified text, skipped
            while( pPor && !pPor->GetLen() && ! bInsideFirstField &&
                   ( pPor->IsFlyPortion() || pPor->IsKernPortion() ||
                     pPor->IsBlankPortion() || pPor->InTabGrp() ||
                     ( !bEmptyField && pPor->InFieldGrp() ) ) )
            {
                if ( pPor->InSpaceGrp() && nSpaceAdd )
                    nX += pPor->PrtWidth() +
                          pPor->CalcSpacing( nSpaceAdd, aInf );
                else
                {
                    if( pPor->InFixMargGrp() && ! pPor->IsMarginPortion() )
                    {
                        if ( m_pCurr->IsSpaceAdd() )
                        {
                            if ( ++nSpaceIdx < m_pCurr->GetLLSpaceAddCount() )
                                nSpaceAdd = m_pCurr->GetLLSpaceAdd( nSpaceIdx );
                            else
                                nSpaceAdd = 0;
                        }

                        if( pKanaComp && ( nKanaIdx + 1 ) < pKanaComp->size() )
                            ++nKanaIdx;
                    }
                    if ( !pPor->IsFlyPortion() || ( pPor->GetNextPortion() &&
                            !pPor->GetNextPortion()->IsMarginPortion() ) )
                        nX += pPor->PrtWidth();
                }
                if( pPor->IsMultiPortion() &&
                    static_cast<SwMultiPortion*>(pPor)->HasTabulator() )
                {
                    if ( m_pCurr->IsSpaceAdd() )
                    {
                        if ( ++nSpaceIdx < m_pCurr->GetLLSpaceAddCount() )
                            nSpaceAdd = m_pCurr->GetLLSpaceAdd( nSpaceIdx );
                        else
                            nSpaceAdd = 0;
                    }

                    if( pKanaComp && ( nKanaIdx + 1 ) < pKanaComp->size() )
                        ++nKanaIdx;
                }
                if( !pPor->IsFlyPortion() )
                {
                    nPorHeight = pPor->Height();
                    nPorAscent = pPor->GetAscent();
                }
                pPor = pPor->GetNextPortion();
            }

            if( aInf.GetIdx() == nOfst && pPor && pPor->InHyphGrp() &&
                pPor->GetNextPortion() && pPor->GetNextPortion()->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->GetNextPortion();
                }
            }
            if( pPor && pCMS )
            {
                if( pCMS->m_bFieldInfo && pPor->InFieldGrp() && pPor->Width() )
                    pOrig->Width( pPor->Width() );
                if( pPor->IsDropPortion() )
                {
                    nPorAscent = static_cast<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 +
                        static_cast<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 (TextFrameIndex(2) > pPor->GetLen())
                    {
                        nTmp = pPor->Width();
                        if ( pPor->InSpaceGrp() && nSpaceAdd )
                            nTmp += pPor->CalcSpacing( nSpaceAdd, aInf );
                    }
                    else
                    {
                        const bool bOldOnWin = aInf.OnWin();
                        TextFrameIndex const nOldLen = pPor->GetLen();
                        aInf.SetLen( pPor->GetLen() );
                        pPor->SetLen( TextFrameIndex(1) );
                        aInf.SetMeasureLen(pPor->GetLen());
                        if (aInf.GetLen() < aInf.GetMeasureLen())
                        {
                            pPor->SetLen(aInf.GetMeasureLen());
                            aInf.SetLen(pPor->GetLen());
                        }
                        SeekAndChg( aInf );
                        aInf.SetOnWin( false ); // no BULLETs!
                        aInf.SetKanaComp( pKanaComp );
                        aInf.SetKanaIdx( nKanaIdx );
                        nTmp = pPor->GetTextSize( 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->m_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() &&
                  static_cast<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->m_nCursorBidiLevel >= nPortionLevel )
                {
                    // we came from inside the bidi portion, we want to blink
                    // behind the portion
                    pOrig->Pos().AdjustX( -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 =
                            static_cast<SwMultiPortion*>(pLastBidiPor)->GetRoot();

                    const SwLinePortion *pLast = rLineLayout.FindLastPortion();
                    if ( pLast->IsMultiPortion() )
                    {
                        OSL_ENSURE( static_cast<const SwMultiPortion*>(pLast)->IsBidi(),
                                 "Non-BidiPortion inside BidiPortion" );
                        TextFrameIndex const nIdx = aInf.GetIdx();
                        // correct the index before using CalcSpacing.
                        aInf.SetIdx(nLastBidiIdx);
                        pOrig->Pos().AdjustX(pLast->Width() +
                                            pLast->CalcSpacing( nSpaceAdd, aInf ) );
                        aInf.SetIdx(nIdx);
                    }
                }
            }
            else
            {
                const sal_uInt8 nPortionLevel = static_cast<SwBidiPortion*>(pPor)->GetLevel();

                if ( pCMS->m_nCursorBidiLevel >= nPortionLevel )
                {
                    // we came from inside the bidi portion, we want to blink
                    // behind the portion
                    pOrig->Pos().AdjustX(pPor->Width() +
                                        pPor->CalcSpacing( nSpaceAdd, aInf ) );
                }
            }
        }

        pOrig->Pos().AdjustX(nX );

        if ( pCMS && pCMS->m_bRealHeight )
        {
            nTmpAscent = AdjustBaseLine( *m_pCurr, nullptr, nPorHeight, nPorAscent );
            if ( nTmpAscent > nPorAscent )
                pCMS->m_aRealHeight.setX( nTmpAscent - nPorAscent );
            else
                pCMS->m_aRealHeight.setX( 0 );
            OSL_ENSURE( nPorHeight, "GetCharRect: Missing Portion-Height" );
            if ( nTmpHeight > nPorHeight )
                pCMS->m_aRealHeight.setY( nPorHeight );
            else
                pCMS->m_aRealHeight.setY( nTmpHeight );
        }
    }
}

void SwTextCursor::GetCharRect( SwRect* pOrig, TextFrameIndex const nOfst,
                               SwCursorMoveState* pCMS, const tools::Long nMax )
{
    CharCursorToLine(nOfst);

    // Indicates that a position inside a special portion (field, number portion)
    // is requested.
    const bool bSpecialPos = pCMS && pCMS->m_pSpecialPos;
    TextFrameIndex nFindOfst = nOfst;

    if ( bSpecialPos )
    {
        const SwSPExtendRange nExtendRange = pCMS->m_pSpecialPos->nExtendRange;

        OSL_ENSURE( ! pCMS->m_pSpecialPos->nLineOfst || SwSPExtendRange::BEFORE != nExtendRange,
                "LineOffset AND Number Portion?" );

        // portions which are behind the string
        if ( SwSPExtendRange::BEHIND == nExtendRange )
            ++nFindOfst;

        // skip lines for fields which cover more than one line
        for ( sal_Int32 i = 0; i < pCMS->m_pSpecialPos->nLineOfst; i++ )
            Next();
    }

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

    const Point aCharPos( GetTopLeft() );

    GetCharRect_( pOrig, nFindOfst, pCMS );

    pOrig->Pos().AdjustX(aCharPos.X() );
    pOrig->Pos().AdjustY(aCharPos.Y() );

    if( pCMS && pCMS->m_b2Lines && pCMS->m_p2Lines )
    {
        pCMS->m_p2Lines->aLine.Pos().AdjustX(aCharPos.X() );
        pCMS->m_p2Lines->aLine.Pos().AdjustY(aCharPos.Y() );
        pCMS->m_p2Lines->aPortion.Pos().AdjustX(aCharPos.X() );
        pCMS->m_p2Lines->aPortion.Pos().AdjustY(aCharPos.Y() );
    }

    if( nMax )
    {
        if( pOrig->Top() + pOrig->Height() > nMax )
        {
            if( pOrig->Top() > nMax )
                pOrig->Top( nMax );
            pOrig->Height( nMax - pOrig->Top() );
        }
        if ( pCMS && pCMS->m_bRealHeight && pCMS->m_aRealHeight.Y() >= 0 )
        {
            tools::Long nTmp = pCMS->m_aRealHeight.X() + pOrig->Top();
            if( nTmp >= nMax )
            {
                pCMS->m_aRealHeight.setX( nMax - pOrig->Top() );
                pCMS->m_aRealHeight.setY( 0 );
            }
            else if( nTmp + pCMS->m_aRealHeight.Y() > nMax )
                pCMS->m_aRealHeight.setY( nMax - nTmp );
        }
    }
}

/**
 * Determines if SwTextCursor::GetModelPositionForViewPoint() should consider the next portion when calculating the
 * doc model position from a Point.
 */

static bool ConsiderNextPortionForCursorOffset(const SwLinePortion* pPor, SwTwips nWidth30, sal_uInt16 nX)
{
    if (!pPor->GetNextPortion() || pPor->IsBreakPortion())
    {
        return false;
    }

    // tdf#138592: consider all following zero-width text portions of current text portion,
    // like combining characters.
    if (nWidth30 == nX && pPor->IsTextPortion() && pPor->GetNextPortion()->IsTextPortion()
        && pPor->GetNextPortion()->Width() == 0)
        return true;

    // If we're past the target position, stop the iteration in general.
    // Exception: don't stop the iteration between as-char fly portions and their comments.
    if (nWidth30 >= nX && (!pPor->IsFlyCntPortion() || !pPor->GetNextPortion()->IsPostItsPortion()))
    {
        // Normally returns false.

        // Another exception: If the cursor is at the very end of the portion, and the next portion is a comment,
        // then place the cursor after the zero-width comment. This is primarily to benefit the very end of a line.
        return nWidth30 == nX && pPor->GetNextPortion()->IsPostItsPortion();
    }

    return true;
}

static auto SearchLine(SwLineLayout const*const pLineOfFoundPor,
    SwLinePortion const*const pFoundPor,
    int & rLines, std::vector<SwFieldPortion const*> & rPortions,
    SwLineLayout const*const pLine) -> bool
{
    for (SwLinePortion const* pLP = pLine; pLP; pLP = pLP->GetNextPortion())
    {
        if (pLP == pFoundPor)
        {
            return true;
        }
        if (pLP->InFieldGrp())
        {
            SwFieldPortion const* pField(static_cast<SwFieldPortion const*>(pLP));
            if (!pField->IsFollow())
            {
                rLines = 0;
                rPortions.clear();
            }
            if (pLine == pLineOfFoundPor)
            {
                rPortions.emplace_back(pField);
            }
        }
        else if (pLP->IsMultiPortion())
        {
            SwMultiPortion const*const pMulti(static_cast<SwMultiPortion const*>(pLP));
            for (SwLineLayout const* pMLine = &pMulti->GetRoot();
                    pMLine; pMLine = pMLine->GetNext())
            {
                if (SearchLine(pLineOfFoundPor, pFoundPor, rLines, rPortions, pMLine))
                {
                    return true;
                }
            }
        }
    }
    return (pLine == pLineOfFoundPor);
}

// Return: Offset in String
TextFrameIndex SwTextCursor::GetModelPositionForViewPoint( SwPosition *pPos, const Point &rPoint,
                                    bool bChgNode, SwCursorMoveState* pCMS ) const
{
    // If necessary, as catch up, do the adjustment
    GetAdjusted();

    const OUString &rText = GetInfo().GetText();
    TextFrameIndex nOffset(0);

    // x is the horizontal offset within the line.
    SwTwips x = rPoint.X();
    const SwTwips nLeftMargin  = GetLineStart();
    SwTwips nRightMargin = GetLineEnd() +
        ( GetCurr()->IsHanging() ? GetCurr()->GetHangingMargin() : 0 );
    if( nRightMargin == nLeftMargin )
        nRightMargin += 30;

    const bool bLeftOver = x < nLeftMargin;
    if( bLeftOver )
        x = nLeftMargin;
    const bool bRightOver = x > nRightMargin;
    const bool bRightAllowed = pCMS && ( pCMS->m_eState == CursorMoveState::NONE );

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

    SwTwips nX = x;

    // If there are attribute changes in the line, search for the paragraph,
    // in which nX is situated.
    SwLinePortion *pPor = m_pCurr->GetFirstPortion();
    TextFrameIndex nCurrStart = m_nStart;
    bool bLastHyph = false;

    std::deque<sal_uInt16> *pKanaComp = m_pCurr->GetpKanaComp();
    TextFrameIndex const nOldIdx = GetInfo().GetIdx();
    sal_uInt16 nSpaceIdx = 0;
    size_t nKanaIdx = 0;
    tools::Long nSpaceAdd = m_pCurr->IsSpaceAdd() ? m_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.
    // tdf#16342 In the case of shrunk lines with a single portion,
    // adjust the line width to move the cursor to the click position
    SwTwips nWidth =
        ( std::abs( m_pCurr->Width() - pPor->Width() ) <= 1 && m_pCurr->ExtraShrunkWidth() > 0 )
            ? m_pCurr->ExtraShrunkWidth()
            :  pPor->Width();
    if ( m_pCurr->IsSpaceAdd() || pKanaComp )
    {
        if ( pPor->InSpaceGrp() && nSpaceAdd )
        {
            const_cast<SwTextSizeInfo&>(GetInfo()).SetIdx( nCurrStart );
            nWidth += pPor->CalcSpacing( nSpaceAdd, GetInfo() );
        }
        if( ( pPor->InFixMargGrp() && ! pPor->IsMarginPortion() ) ||
            ( pPor->IsMultiPortion() && static_cast<SwMultiPortion*>(pPor)->HasTabulator() )
          )
        {
            if ( m_pCurr->IsSpaceAdd() )
            {
                if ( ++nSpaceIdx < m_pCurr->GetLLSpaceAddCount() )
                    nSpaceAdd = m_pCurr->GetLLSpaceAdd( nSpaceIdx );
                else
                    nSpaceAdd = 0;
            }

            if( pKanaComp )
            {
                if ( nKanaIdx + 1 < pKanaComp->size() )
                    nKanaComp = (*pKanaComp)[++nKanaIdx];
                else
                    nKanaComp = 0;
            }
        }
    }

    SwTwips nWidth30;
    if ( pPor->IsPostItsPortion() )
        nWidth30 = 0;
    else
        nWidth30 = ! nWidth && pPor->GetLen() && pPor->InToxRefOrFieldGrp() ?
                     30 :
                     nWidth;

    while (ConsiderNextPortionForCursorOffset(pPor, nWidth30, nX))
    {
        nX -= nWidth;
        nCurrStart += pPor->GetLen();
        pPor = pPor->GetNextPortion();
        nWidth = pPor->Width();
        if ( m_pCurr->IsSpaceAdd() || pKanaComp )
        {
            if ( pPor->InSpaceGrp() && nSpaceAdd )
            {
                const_cast<SwTextSizeInfo&>(GetInfo()).SetIdx( nCurrStart );
                nWidth += pPor->CalcSpacing( nSpaceAdd, GetInfo() );
            }

            if( ( pPor->InFixMargGrp() && ! pPor->IsMarginPortion() ) ||
                ( pPor->IsMultiPortion() && static_cast<SwMultiPortion*>(pPor)->HasTabulator() )
              )
            {
                if ( m_pCurr->IsSpaceAdd() )
                {
                    if ( ++nSpaceIdx < m_pCurr->GetLLSpaceAddCount() )
                        nSpaceAdd = m_pCurr->GetLLSpaceAdd( nSpaceIdx );
                    else
                        nSpaceAdd = 0;
                }

                if ( pKanaComp )
                {
                    if( nKanaIdx + 1 < pKanaComp->size() )
                        nKanaComp = (*pKanaComp)[++nKanaIdx];
                    else
                        nKanaComp = 0;
                }
            }
        }

        if ( pPor->IsPostItsPortion() )
            nWidth30 = 0;
        else
            nWidth30 = ! nWidth && pPor->GetLen() && pPor->InToxRefOrFieldGrp() ?
                         30 :
                         nWidth;
        if( !pPor->IsFlyPortion() && !pPor->IsMarginPortion() )
            bLastHyph = pPor->InHyphGrp();
    }

    const bool bLastPortion = (nullptr == pPor->GetNextPortion());

    if( nX==nWidth )
    {
        SwLinePortion *pNextPor = pPor->GetNextPortion();
        while( pNextPor && pNextPor->InFieldGrp() && !pNextPor->Width() )
        {
            nCurrStart += pPor->GetLen();
            pPor = pNextPor;
            if( !pPor->IsFlyPortion() && !pPor->IsMarginPortion() )
                bLastHyph = pPor->InHyphGrp();
            pNextPor = pPor->GetNextPortion();
        }
    }

    const_cast<SwTextSizeInfo&>(GetInfo()).SetIdx( nOldIdx );

    TextFrameIndex nLength = pPor->GetLen();

    const bool bFieldInfo = pCMS && pCMS->m_bFieldInfo;

    if( bFieldInfo && ( nWidth30 < nX || bRightOver || bLeftOver ||
        ( pPor->InNumberGrp() && !pPor->IsFootnoteNumPortion() ) ||
        ( pPor->IsMarginPortion() && nWidth > nX + 30 ) ) )
        pCMS->m_bPosCorr = true;

    // #i27615#
    if (pCMS && pCMS->m_bInFrontOfLabel)
    {
        if (2 * nX >= nWidth || !pPor->InNumberGrp() || pPor->IsFootnoteNumPortion())
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=94 H=94 G=93

¤ Dauer der Verarbeitung: 0.10 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge