Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/LibreOffice/editeng/source/outliner/   (Office von Apache Version 25.8.3.2©)  Datei vom 5.10.2025 mit Größe 67 kB image not shown  

Quelle  outliner.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 <comphelper/string.hxx>
#include <svl/eitem.hxx>
#include <svl/intitem.hxx>
#include <editeng/editeng.hxx>
#include <editeng/editview.hxx>
#include <editeng/editdata.hxx>
#include <editeng/lrspitem.hxx>

#include <math.h>
#include <svl/style.hxx>
#include <editeng/outliner.hxx>
#include "paralist.hxx"
#include <editeng/outlobj.hxx>
#include <outleeng.hxx>
#include "outlundo.hxx"
#include <editeng/eeitem.hxx>
#include <editeng/editstat.hxx>
#include <editeng/overflowingtxt.hxx>
#include <editeng/editobj.hxx>
#include <svl/itemset.hxx>
#include <vcl/metric.hxx>
#include <editeng/numitem.hxx>
#include <editeng/adjustitem.hxx>
#include <vcl/GraphicObject.hxx>
#include <editeng/svxfont.hxx>
#include <editeng/brushitem.hxx>
#include <svl/itempool.hxx>
#include <libxml/xmlwriter.h>
#include <sal/log.hxx>
#include <o3tl/safeint.hxx>
#include <o3tl/string_view.hxx>
#include <o3tl/temporary.hxx>
#include <osl/diagnose.h>
#include <editeng/StripPortionsHelper.hxx>

#include <memory>
using std::advance;


// Outliner


void Outliner::ImplCheckDepth( sal_Int16& rnDepth ) const
{
    if( rnDepth < gnMinDepth )
        rnDepth = gnMinDepth;
    else if( rnDepth > nMaxDepth )
        rnDepth = nMaxDepth;
}

Paragraph* Outliner::Insert(const OUString& rText, sal_Int32 nAbsPos, sal_Int16 nDepth)
{
    DBG_ASSERT(pParaList->GetParagraphCount(),"Insert:No Paras");

    Paragraph* pPara;

    ImplCheckDepth( nDepth );

    sal_Int32 nParagraphCount = pParaList->GetParagraphCount();
    if( nAbsPos > nParagraphCount )
        nAbsPos = nParagraphCount;

    if( bFirstParaIsEmpty )
    {
        pPara = pParaList->GetParagraph( 0 );
        if( pPara->GetDepth() != nDepth )
        {
            nDepthChangedHdlPrevDepth = pPara->GetDepth();
            ParaFlag nPrevFlags = pPara->nFlags;
            pPara->SetDepth( nDepth );
            DepthChangedHdl(pPara, nPrevFlags);
        }
        pPara->nFlags |= ParaFlag::HOLDDEPTH;
        SetText( rText, pPara );
    }
    else
    {
        bool bUpdate = pEditEngine->SetUpdateLayout( false );
        ImplBlockInsertionCallbacks( true );
        pPara = new Paragraph( nDepth );
        pParaList->Insert( std::unique_ptr<Paragraph>(pPara), nAbsPos );
        pEditEngine->InsertParagraph( nAbsPos, OUString() );
        DBG_ASSERT(pPara==pParaList->GetParagraph(nAbsPos),"Insert:Failed");
        ImplInitDepth( nAbsPos, nDepth, false );
        ParagraphInsertedHdl(pPara);
        pPara->nFlags |= ParaFlag::HOLDDEPTH;
        SetText( rText, pPara );
        ImplBlockInsertionCallbacks( false );
        pEditEngine->SetUpdateLayout( bUpdate );
    }
    bFirstParaIsEmpty = false;
    DBG_ASSERT(pEditEngine->GetParagraphCount()==pParaList->GetParagraphCount(),"SetText failed");
    return pPara;
}


void Outliner::ParagraphInserted( sal_Int32 nPara )
{

    if ( nBlockInsCallback )
        return;

    if( bPasting || pEditEngine->IsInUndo() )
    {
        Paragraph* pPara = new Paragraph( -1 );
        pParaList->Insert( std::unique_ptr<Paragraph>(pPara), nPara );
        if( pEditEngine->IsInUndo() )
        {
            pPara->bVisible = true;
            const SfxInt16Item& rLevel = pEditEngine->GetParaAttrib( nPara, EE_PARA_OUTLLEVEL );
            pPara->SetDepth( rLevel.GetValue() );
        }
    }
    else
    {
        sal_Int16 nDepth = -1;
        Paragraph* pParaBefore = pParaList->GetParagraph( nPara-1 );
        if ( pParaBefore )
            nDepth = pParaBefore->GetDepth();

        Paragraph* pPara = new Paragraph( nDepth );
        pParaList->Insert( std::unique_ptr<Paragraph>(pPara), nPara );

        if( !pEditEngine->IsInUndo() )
        {
            ImplCalcBulletText( nPara, truefalse );
            ParagraphInsertedHdl(pPara);
        }
    }
}

void Outliner::ParagraphDeleted( sal_Int32 nPara )
{

    if (nBlockInsCallback || (nPara == EE_PARA_MAX))
        return;

    Paragraph* pPara = pParaList->GetParagraph( nPara );
    if (!pPara)
        return;

    sal_Int16 nDepth = pPara->GetDepth();

    if( !pEditEngine->IsInUndo() )
    {
        aParaRemovingHdl.Call( { this, pPara } );
    }

    pParaList->Remove( nPara );

    if( pEditEngine->IsInUndo() || bPasting )
        return;

    pPara = pParaList->GetParagraph( nPara );
    if ( pPara && ( pPara->GetDepth() > nDepth ) )
    {
        ImplCalcBulletText( nPara, truefalse );
        // Search for next on the this level ...
        while ( pPara && pPara->GetDepth() > nDepth )
            pPara = pParaList->GetParagraph( ++nPara );
    }

    if ( pPara && ( pPara->GetDepth() == nDepth ) )
        ImplCalcBulletText( nPara, truefalse );
}

void Outliner::Init( OutlinerMode nMode )
{
    nOutlinerMode = nMode;

    Clear();

    EEControlBits nCtrl = pEditEngine->GetControlWord();
    nCtrl &= ~EEControlBits(EEControlBits::OUTLINER|EEControlBits::OUTLINER2);

    SetMaxDepth( 9 );

    switch ( GetOutlinerMode() )
    {
        case OutlinerMode::TextObject:
        case OutlinerMode::TitleObject:
            break;

        case OutlinerMode::OutlineObject:
            nCtrl |= EEControlBits::OUTLINER2;
            break;
        case OutlinerMode::OutlineView:
            nCtrl |= EEControlBits::OUTLINER;
            break;

        default: OSL_FAIL( "Outliner::Init - Invalid Mode!" );
    }

    pEditEngine->SetControlWord( nCtrl );

    const bool bWasUndoEnabled(IsUndoEnabled());
    EnableUndo(false);
    ImplInitDepth( 0, -1, false );
    GetUndoManager().Clear();
    EnableUndo(bWasUndoEnabled);
}

void Outliner::SetMaxDepth( sal_Int16 nDepth )
{
    if( nMaxDepth != nDepth )
    {
        nMaxDepth = std::min( nDepth, sal_Int16(SVX_MAX_NUM-1) );
    }
}

sal_Int16 Outliner::GetDepth( sal_Int32 nPara ) const
{
    Paragraph* pPara = pParaList->GetParagraph( nPara );
    DBG_ASSERT( pPara, "Outliner::GetDepth - Paragraph not found!" );
    return pPara ? pPara->GetDepth() : -1;
}

void Outliner::SetDepth( Paragraph* pPara, sal_Int16 nNewDepth )
{

    ImplCheckDepth( nNewDepth );

    if ( nNewDepth == pPara->GetDepth() )
        return;

    nDepthChangedHdlPrevDepth = pPara->GetDepth();
    ParaFlag nPrevFlags = pPara->nFlags;

    sal_Int32 nPara = GetAbsPos( pPara );
    ImplInitDepth( nPara, nNewDepth, true );
    ImplCalcBulletText( nPara, falsefalse );

    if ( GetOutlinerMode() == OutlinerMode::OutlineObject )
        ImplSetLevelDependentStyleSheet( nPara );

    DepthChangedHdl(pPara, nPrevFlags);
}

sal_Int16 Outliner::GetNumberingStartValue( sal_Int32 nPara ) const
{
    Paragraph* pPara = pParaList->GetParagraph( nPara );
    DBG_ASSERT( pPara, "Outliner::GetNumberingStartValue - Paragraph not found!" );
    return pPara ? pPara->GetNumberingStartValue() : -1;
}

void Outliner::SetNumberingStartValue( sal_Int32 nPara, sal_Int16 nNumberingStartValue )
{
    Paragraph* pPara = pParaList->GetParagraph( nPara );
    DBG_ASSERT( pPara, "Outliner::GetNumberingStartValue - Paragraph not found!" );
    if( pPara && pPara->GetNumberingStartValue() != nNumberingStartValue )
    {
        if( IsUndoEnabled() && !IsInUndo() )
            InsertUndo( std::make_unique<OutlinerUndoChangeParaNumberingRestart>( this, nPara,
                pPara->GetNumberingStartValue(), nNumberingStartValue,
                pPara->IsParaIsNumberingRestart(), pPara->IsParaIsNumberingRestart() ) );

        pPara->SetNumberingStartValue( nNumberingStartValue );
        ImplCheckParagraphs( nPara, pParaList->GetParagraphCount() );
        pEditEngine->SetModified();
    }
}

bool Outliner::IsParaIsNumberingRestart( sal_Int32 nPara ) const
{
    Paragraph* pPara = pParaList->GetParagraph( nPara );
    DBG_ASSERT( pPara, "Outliner::IsParaIsNumberingRestart - Paragraph not found!" );
    return pPara && pPara->IsParaIsNumberingRestart();
}

void Outliner::SetParaIsNumberingRestart( sal_Int32 nPara, bool bParaIsNumberingRestart )
{
    Paragraph* pPara = pParaList->GetParagraph( nPara );
    DBG_ASSERT( pPara, "Outliner::SetParaIsNumberingRestart - Paragraph not found!" );
    if( pPara && (pPara->IsParaIsNumberingRestart() != bParaIsNumberingRestart) )
    {
        if( IsUndoEnabled() && !IsInUndo() )
            InsertUndo( std::make_unique<OutlinerUndoChangeParaNumberingRestart>( this, nPara,
                pPara->GetNumberingStartValue(), pPara->GetNumberingStartValue(),
                pPara->IsParaIsNumberingRestart(), bParaIsNumberingRestart ) );

        pPara->SetParaIsNumberingRestart( bParaIsNumberingRestart );
        ImplCheckParagraphs( nPara, pParaList->GetParagraphCount() );
        pEditEngine->SetModified();
    }
}

sal_Int32 Outliner::GetBulletsNumberingStatus(
    const sal_Int32 nParaStart,
    const sal_Int32 nParaEnd ) const
{
    if ( nParaStart > nParaEnd
         || nParaEnd >= pParaList->GetParagraphCount() )
    {
        SAL_WARN("editeng"" - unexpected parameter values" );
        return 2;
    }

    sal_Int32 nBulletsCount = 0;
    sal_Int32 nNumberingCount = 0;
    for (sal_Int32 nPara = nParaStart; nPara <= nParaEnd; ++nPara)
    {
        if ( !pParaList->GetParagraph(nPara) )
        {
            break;
        }
        const SvxNumberFormat* pFmt = GetNumberFormat(nPara);
        if (!pFmt)
        {
            // At least, exists one paragraph that has no Bullets/Numbering.
            break;
        }
        else if ((pFmt->GetNumberingType() == SVX_NUM_BITMAP) || (pFmt->GetNumberingType() == SVX_NUM_CHAR_SPECIAL))
        {
            // Having Bullets in this paragraph.
            nBulletsCount++;
        }
        else
        {
            // Having Numbering in this paragraph.
            nNumberingCount++;
        }
    }

    const sal_Int32 nParaCount = nParaEnd - nParaStart + 1;
    if ( nBulletsCount == nParaCount )
    {
        return 0;
    }
    else if ( nNumberingCount == nParaCount )
    {
        return 1;
    }
    return 2;
}

sal_Int32 Outliner::GetBulletsNumberingStatus() const
{
    return pParaList->GetParagraphCount() > 0
           ? GetBulletsNumberingStatus( 0, pParaList->GetParagraphCount()-1 )
           : 2;
}

std::optional<OutlinerParaObject> Outliner::CreateParaObject( sal_Int32 nStartPara, sal_Int32 nCount ) const
{
    if ( static_cast<sal_uInt64>(nStartPara) + nCount >
            o3tl::make_unsigned(pParaList->GetParagraphCount()) )
        nCount = pParaList->GetParagraphCount() - nStartPara;

    // When a new OutlinerParaObject is created because a paragraph is just being deleted,
    // it can happen that the ParaList is not updated yet...
    if ( ( nStartPara + nCount ) > pEditEngine->GetParagraphCount() )
        nCount = pEditEngine->GetParagraphCount() - nStartPara;

    if (nCount <= 0)
        return std::nullopt;

    std::unique_ptr<EditTextObject> xText = pEditEngine->CreateTextObject( nStartPara, nCount );
    const bool bIsEditDoc(OutlinerMode::TextObject == GetOutlinerMode());
    ParagraphDataVector aParagraphDataVector(nCount);
    const sal_Int32 nLastPara(nStartPara + nCount - 1);

    for(sal_Int32 nPara(nStartPara); nPara <= nLastPara; nPara++)
    {
        aParagraphDataVector[nPara-nStartPara] = *GetParagraph(nPara);
    }

    xText->ClearPortionInfo(); // tdf#147166 the PortionInfo is unwanted here
    OutlinerParaObject aPObj(std::move(xText), std::move(aParagraphDataVector), bIsEditDoc);
    aPObj.SetOutlinerMode(GetOutlinerMode());

    return aPObj;
}

void Outliner::SetToEmptyText()
{
    SetText(GetEmptyParaObject());
}

void Outliner::SetText( const OUString& rText, Paragraph* pPara )
{
    assert(pPara && "SetText:No Para");

    const sal_Int32 nPara = pParaList->GetAbsPos( pPara );

    if (pEditEngine->GetText( nPara ) == rText)
    {
        // short-circuit logic to improve performance
        bFirstParaIsEmpty = false;
        return;
    }

    const bool bUpdate = pEditEngine->SetUpdateLayout( false );
    ImplBlockInsertionCallbacks( true );

    if (rText.isEmpty())
    {
        pEditEngine->SetText( nPara, rText );
        ImplInitDepth( nPara, pPara->GetDepth(), false );
    }
    else
    {
        const OUString aText(convertLineEnd(rText, LINEEND_LF));

        sal_Int32 nPos = 0;
        sal_Int32 nInsPos = nPara+1;
        sal_Int32 nIdx {0};
        // Loop over all tokens, but ignore the last one if empty
        // (i.e. if strings ends with the delimiter, detected by
        // checking nIdx against string length). This check also
        // handle empty strings.
        while( nIdx>=0 && nIdx<aText.getLength() )
        {
            std::u16string_view aStr = o3tl::getToken(aText, 0, '\x0A', nIdx );

            sal_Int16 nCurDepth;
            if( nPos )
            {
                pPara = new Paragraph( -1 );
                nCurDepth = -1;
            }
            else
                nCurDepth = pPara->GetDepth();

            // In the outliner mode, filter the tabs and set the indentation
            // about a LRSpaceItem. In EditEngine mode intend over old tabs
            if( ( GetOutlinerMode() == OutlinerMode::OutlineObject ) ||
                ( GetOutlinerMode() == OutlinerMode::OutlineView ) )
            {
                // Extract Tabs
                size_t nTabs = 0;
                while ( ( nTabs < aStr.size() ) && ( aStr[nTabs] == '\t' ) )
                    nTabs++;
                if ( nTabs )
                    aStr = aStr.substr(nTabs);

                // Keep depth?  (see Outliner::Insert)
                if( !(pPara->nFlags & ParaFlag::HOLDDEPTH) )
                {
                    nCurDepth = nTabs-1; //TODO: sal_Int32 -> sal_Int16!
                    ImplCheckDepth( nCurDepth );
                    pPara->SetDepth( nCurDepth );
                }
            }
            if( nPos ) // not with the first paragraph
            {
                pParaList->Insert( std::unique_ptr<Paragraph>(pPara), nInsPos );
                pEditEngine->InsertParagraph( nInsPos, OUString(aStr) );
                ParagraphInsertedHdl(pPara);
            }
            else
            {
                nInsPos--;
                pEditEngine->SetText( nInsPos, OUString(aStr) );
            }
            ImplInitDepth( nInsPos, nCurDepth, false );
            nInsPos++;
            nPos++;
        }
    }

    DBG_ASSERT(pParaList->GetParagraphCount()==pEditEngine->GetParagraphCount(),"SetText failed!");
    bFirstParaIsEmpty = false;
    ImplBlockInsertionCallbacks( false );
    // Restore the update mode.
    pEditEngine->SetUpdateLayout(bUpdate, /*bRestoring=*/true);
}

// pView == 0 -> Ignore tabs

bool Outliner::ImpConvertEdtToOut( sal_Int32 nPara )
{

    bool bConverted = false;
    sal_Int32 nTabs = 0;
    ESelection aDelSel;

    OUString aName;

    OUString aStr( pEditEngine->GetText( nPara ) );
    const sal_Unicode* pPtr = aStr.getStr();

    sal_Int32 nHeadingNumberStart = 0;
    sal_Int32 nNumberingNumberStart = 0;
    SfxStyleSheet* pStyle= pEditEngine->GetStyleSheet( nPara );
    if( pStyle )
    {
        OUString aHeading_US( u"heading"_ustr );
        OUString aNumber_US( u"Numbering"_ustr );
        aName = pStyle->GetName();
        sal_Int32 nSearch;
        if ( ( nSearch = aName.indexOf( aHeading_US ) ) != -1 )
            nHeadingNumberStart = nSearch + aHeading_US.getLength();
        else if ( ( nSearch = aName.indexOf( aNumber_US ) ) != -1 )
            nNumberingNumberStart = nSearch + aNumber_US.getLength();
    }

    if ( nHeadingNumberStart || nNumberingNumberStart )
    {
        // PowerPoint import ?
        if( nHeadingNumberStart && ( aStr.getLength() >= 2 ) &&
                ( pPtr[0] != '\t' ) && ( pPtr[1] == '\t' ) )
        {
            // Extract Bullet and Tab
            aDelSel = ESelection( nPara, 0, nPara, 2 );
        }

        sal_Int32 nPos = nHeadingNumberStart ? nHeadingNumberStart : nNumberingNumberStart;
        std::u16string_view aLevel = comphelper::string::stripStart(aName.subView(nPos), ' ');
        nTabs = o3tl::toInt32(aLevel);
        if( nTabs )
            nTabs--; // Level 0 = "heading 1"
        bConverted = true;
    }
    else
    {
        // filter leading tabs
        while( *pPtr == '\t' )
        {
            pPtr++;
            nTabs++;
        }
        // Remove tabs from the text
        if( nTabs )
            aDelSel = ESelection( nPara, 0, nPara, nTabs );
    }

    if ( aDelSel.HasRange() )
    {
        pEditEngine->QuickDelete( aDelSel );
    }

    const SfxInt16Item& rLevel = pEditEngine->GetParaAttrib( nPara, EE_PARA_OUTLLEVEL );
    sal_Int16 nOutlLevel = rLevel.GetValue();

    ImplCheckDepth( nOutlLevel );
    ImplInitDepth( nPara, nOutlLevel, false );

    return bConverted;
}

void Outliner::SetText( const OutlinerParaObject& rPObj )
{
    bool bUpdate = pEditEngine->SetUpdateLayout( false );

    bool bUndo = pEditEngine->IsUndoEnabled();
    EnableUndo( false );

    Init( rPObj.GetOutlinerMode() );

    ImplBlockInsertionCallbacks( true );
    pEditEngine->SetText(rPObj.GetTextObject());

    bFirstParaIsEmpty = false;

    pParaList->Clear();
    for( sal_Int32 nCurPara = 0; nCurPara < rPObj.Count(); nCurPara++ )
    {
        std::unique_ptr<Paragraph> pPara(new Paragraph( rPObj.GetParagraphData(nCurPara)));
        ImplCheckDepth( pPara->nDepth );

        pParaList->Append(std::move(pPara));
        ImplCheckNumBulletItem( nCurPara );
    }

    ImplCheckParagraphs( 0, pParaList->GetParagraphCount() );

    EnableUndo( bUndo );
    ImplBlockInsertionCallbacks( false );
    pEditEngine->SetUpdateLayout( bUpdate );

    DBG_ASSERT( pParaList->GetParagraphCount()==rPObj.Count(),"SetText failed");
    DBG_ASSERT( pEditEngine->GetParagraphCount()==rPObj.Count(),"SetText failed");
}

void Outliner::AddText( const OutlinerParaObject& rPObj, bool bAppend )
{
    bool bUpdate = pEditEngine->SetUpdateLayout( false );

    ImplBlockInsertionCallbacks( true );
    sal_Int32 nPara;
    if( bFirstParaIsEmpty )
    {
        pParaList->Clear();
        pEditEngine->SetText(rPObj.GetTextObject());
        nPara = 0;
        bAppend = false;
    }
    else
    {
        nPara = pParaList->GetParagraphCount();
        pEditEngine->InsertParagraph(EE_PARA_MAX, rPObj.GetTextObject(), bAppend);
    }
    bFirstParaIsEmpty = false;

    for( sal_Int32 n = 0; n < rPObj.Count(); n++ )
    {
        if ( n == 0 && bAppend )
        {
            // This first "paragraph" was just appended to an existing (incomplete) paragraph.
            // Since no new paragraph will be added, the assumed increase-by-1 also won't happen.
            --nPara;
            continue;
        }

        Paragraph* pPara = new Paragraph( rPObj.GetParagraphData(n) );
        pParaList->Append(std::unique_ptr<Paragraph>(pPara));
        sal_Int32 nP = nPara+n;
        DBG_ASSERT(pParaList->GetAbsPos(pPara)==nP,"AddText:Out of sync");
        ImplInitDepth( nP, pPara->GetDepth(), false );
    }
    DBG_ASSERT( pEditEngine->GetParagraphCount()==pParaList->GetParagraphCount(), "SetText: OutOfSync" );

    ImplCheckParagraphs( nPara, pParaList->GetParagraphCount() );

    ImplBlockInsertionCallbacks( false );
    pEditEngine->SetUpdateLayout( bUpdate );
}

OUString Outliner::CalcFieldValue( const SvxFieldItem& rField, sal_Int32 nPara, sal_Int32 nPos, std::optional<Color>& rpTxtColor, std::optional<Color>& rpFldColor, std::optional<FontLineStyle>& rpFldLineStyle )
{
    if ( !aCalcFieldValueHdl.IsSet() )
        return OUString( ' ' );

    EditFieldInfo aFldInfo( this, rField, nPara, nPos );
    // The FldColor is preset with COL_LIGHTGRAY.
    if ( rpFldColor )
        aFldInfo.SetFieldColor( *rpFldColor );

    aCalcFieldValueHdl.Call( &aFldInfo );
    if ( aFldInfo.GetTextColor() )
    {
        rpTxtColor = *aFldInfo.GetTextColor();
    }

    if ( aFldInfo.GetFontLineStyle() )
    {
        rpFldLineStyle = *aFldInfo.GetFontLineStyle();
    }

    if (aFldInfo.GetFieldColor())
        rpFldColor = *aFldInfo.GetFieldColor();
    else
        rpFldColor.reset();

    return aFldInfo.GetRepresentation();
}

void Outliner::SetStyleSheet( sal_Int32 nPara, SfxStyleSheet* pStyle )
{
    Paragraph* pPara = pParaList->GetParagraph( nPara );
    if (pPara)
    {
        pEditEngine->SetStyleSheet( nPara, pStyle );
        ImplCheckNumBulletItem(  nPara );
    }
}

void Outliner::ImplCheckNumBulletItem( sal_Int32 nPara )
{
    Paragraph* pPara = pParaList->GetParagraph( nPara );
    if (pPara)
        pPara->aBulSize.setWidth( -1 );
}

void Outliner::ImplSetLevelDependentStyleSheet( sal_Int32 nPara )
{

    DBG_ASSERT( ( GetOutlinerMode() == OutlinerMode::OutlineObject ) || ( GetOutlinerMode() == OutlinerMode::OutlineView ), "SetLevelDependentStyleSheet: Wrong Mode!" );

    SfxStyleSheet* pStyle = GetStyleSheet( nPara );

    if ( !pStyle )
        return;

    sal_Int16 nDepth = GetDepth( nPara );
    if( nDepth < 0 )
        nDepth = 0;

    OUString aNewStyleSheetName( pStyle->GetName() );
    aNewStyleSheetName = aNewStyleSheetName.subView( 0, aNewStyleSheetName.getLength()-1 ) +
        OUString::number( nDepth+1 );
    SfxStyleSheet* pNewStyle = static_cast<SfxStyleSheet*>(GetStyleSheetPool()->Find( aNewStyleSheetName, pStyle->GetFamily() ));
    DBG_ASSERT( pNewStyle, "AutoStyleSheetName - Style not found!" );
    if ( pNewStyle && ( pNewStyle != GetStyleSheet( nPara ) ) )
    {
        SfxItemSet aOldAttrs( GetParaAttribs( nPara ) );
        SetStyleSheet( nPara, pNewStyle );
        if ( aOldAttrs.GetItemState( EE_PARA_NUMBULLET ) == SfxItemState::SET )
        {
            SfxItemSet aAttrs( GetParaAttribs( nPara ) );
            aAttrs.Put( aOldAttrs.Get( EE_PARA_NUMBULLET ) );
            SetParaAttribs( nPara, aAttrs );
        }
    }
}

void Outliner::ImplInitDepth( sal_Int32 nPara, sal_Int16 nDepth, bool bCreateUndo )
{

    DBG_ASSERT( ( nDepth >= gnMinDepth ) && ( nDepth <= nMaxDepth ), "ImplInitDepth - Depth is invalid!" );

    Paragraph* pPara = pParaList->GetParagraph( nPara );
    if (!pPara)
        return;
    sal_Int16 nOldDepth = pPara->GetDepth();
    pPara->SetDepth( nDepth );

    // For IsInUndo attributes and style do not have to be set, there
    // the old values are restored by the EditEngine.
    if( IsInUndo() )
        return;

    bool bUpdate = pEditEngine->SetUpdateLayout( false );

    bool bUndo = bCreateUndo && IsUndoEnabled();

    SfxItemSet aAttrs( pEditEngine->GetParaAttribs( nPara ) );
    aAttrs.Put( SfxInt16Item( EE_PARA_OUTLLEVEL, nDepth ) );
    pEditEngine->SetParaAttribs( nPara, aAttrs );
    ImplCheckNumBulletItem( nPara );
    ImplCalcBulletText( nPara, falsefalse );

    if ( bUndo )
    {
        InsertUndo( std::make_unique<OutlinerUndoChangeDepth>( this, nPara, nOldDepth, nDepth ) );
    }

    pEditEngine->SetUpdateLayout( bUpdate );
}

void Outliner::SetParaAttribs( sal_Int32 nPara, const SfxItemSet& rSet )
{

    pEditEngine->SetParaAttribs( nPara, rSet );
}

void Outliner::SetCharAttribs(sal_Int32 nPara, const SfxItemSet& rSet)
{
    pEditEngine->SetCharAttribs(nPara, rSet);
}

bool Outliner::Expand( Paragraph const * pPara )
{
    if ( !pParaList->HasHiddenChildren( pPara ) )
        return false;

    std::unique_ptr<OLUndoExpand> pUndo;
    bool bUndo = IsUndoEnabled() && !IsInUndo();
    if( bUndo )
    {
        UndoActionStart( OLUNDO_EXPAND );
        pUndo.reset( new OLUndoExpand( this, OLUNDO_EXPAND ) );
        pUndo->nCount = pParaList->GetAbsPos( pPara );
    }
    pParaList->Expand( pPara );
    InvalidateBullet(pParaList->GetAbsPos(pPara));
    if( bUndo )
    {
        InsertUndo( std::move(pUndo) );
        UndoActionEnd();
    }
    return true;
}

bool Outliner::Collapse( Paragraph const * pPara )
{
    if ( !pParaList->HasVisibleChildren( pPara ) ) // collapsed
        return false;

    std::unique_ptr<OLUndoExpand> pUndo;
    bool bUndo = false;

    if( !IsInUndo() && IsUndoEnabled() )
        bUndo = true;
    if( bUndo )
    {
        UndoActionStart( OLUNDO_COLLAPSE );
        pUndo.reset( new OLUndoExpand( this, OLUNDO_COLLAPSE ) );
        pUndo->nCount = pParaList->GetAbsPos( pPara );
    }

    pParaList->Collapse( pPara );
    InvalidateBullet(pParaList->GetAbsPos(pPara));
    if( bUndo )
    {
        InsertUndo( std::move(pUndo) );
        UndoActionEnd();
    }
    return true;
}

vcl::Font Outliner::ImpCalcBulletFont( sal_Int32 nPara ) const
{
    const SvxNumberFormat* pFmt = GetNumberFormat( nPara );
    assert(pFmt && "ImpCalcBulletFont: Missing!");
    DBG_ASSERT(( pFmt->GetNumberingType() != SVX_NUM_BITMAP ) && ( pFmt->GetNumberingType() != SVX_NUM_NUMBER_NONE ), "ImpCalcBulletFont: BitmapBullet!" );

    vcl::Font aStdFont;
    if ( !pEditEngine->IsFlatMode() )
    {
        ESelection aSel(nPara, 0);
        aStdFont = EditEngine::CreateFontFromItemSet( pEditEngine->GetAttribs( aSel ), pEditEngine->GetScriptType( aSel ) );
    }
    else
    {
        aStdFont = pEditEngine->GetStandardFont( nPara );
    }

    vcl::Font aBulletFont;
    std::optional<vcl::Font> pSourceFont;
    if ( pFmt->GetNumberingType() == SVX_NUM_CHAR_SPECIAL )
    {
        pSourceFont = pFmt->GetBulletFont();
    }

    if (pSourceFont)
    {
        aBulletFont = *pSourceFont;
    }
    else
    {
        aBulletFont = aStdFont;
        aBulletFont.SetUnderline( LINESTYLE_NONE );
        aBulletFont.SetOverline( LINESTYLE_NONE );
        aBulletFont.SetStrikeout( STRIKEOUT_NONE );
        aBulletFont.SetEmphasisMark( FontEmphasisMark::NONE );
        aBulletFont.SetRelief( FontRelief::NONE );
    }

    // Use original scale...

    double fFontScaleY = pFmt->GetBulletRelSize() / 100.0 * getScalingParameters().fFontY;
    double fScaledLineHeight = aStdFont.GetFontSize().Height() * fFontScaleY;

    aBulletFont.SetAlignment( ALIGN_BOTTOM );
    aBulletFont.SetFontSize(Size(0, basegfx::fround(fScaledLineHeight)));
    bool bVertical = IsVertical();
    aBulletFont.SetVertical( bVertical );
    aBulletFont.SetOrientation( Degree10(bVertical ? (IsTopToBottom() ? 2700 : 900) : 0) );

    Color aColor( COL_AUTO );
    if( !pEditEngine->IsFlatMode() && !( pEditEngine->GetControlWord() & EEControlBits::NOCOLORS ) )
    {
        aColor = pFmt->GetBulletColor();
    }

    if ( ( aColor == COL_AUTO ) || ( IsForceAutoColor() ) )
        aColor = pEditEngine->GetAutoColor();

    aBulletFont.SetColor( aColor );
    return aBulletFont;
}

void Outliner::PaintBullet(
    sal_Int32 nPara, const Point& rStartPos, const Point& rOrigin,
    Degree10 nOrientation, OutputDevice& rOutDev,
    const std::function<void(const DrawPortionInfo&)>& rDrawPortion,
    const std::function<void(const DrawBulletInfo&)>& rDrawBullet)
{

    bool bDrawBullet = false;
    if (pEditEngine)
    {
        const SfxBoolItem& rBulletState = pEditEngine->GetParaAttrib( nPara, EE_PARA_BULLETSTATE );
        bDrawBullet = rBulletState.GetValue();
    }

    if (!(bDrawBullet && ImplHasNumberFormat(nPara)))
        return;

    bool bVertical = IsVertical();
    bool bTopToBottom = IsTopToBottom();

    bool bRightToLeftPara = pEditEngine->IsRightToLeft( nPara );

    tools::Rectangle aBulletArea( ImpCalcBulletArea( nPara, truefalse ) );

    double fSpacingFactorX = getScalingParameters().fSpacingX;

    tools::Long nStretchBulletX = basegfx::fround<tools::Long>(double(aBulletArea.Left()) * fSpacingFactorX);
    tools::Long nStretchBulletWidth = basegfx::fround<tools::Long>(double(aBulletArea.GetWidth()) * fSpacingFactorX);
    aBulletArea = tools::Rectangle(Point(nStretchBulletX, aBulletArea.Top()),
                             Size(nStretchBulletWidth, aBulletArea.GetHeight()) );

    Paragraph* pPara = pParaList->GetParagraph( nPara );
    const SvxNumberFormat* pFmt = GetNumberFormat( nPara );
    if ( pFmt && ( pFmt->GetNumberingType() != SVX_NUM_NUMBER_NONE ) )
    {
        if( pFmt->GetNumberingType() != SVX_NUM_BITMAP )
        {
            vcl::Font aBulletFont( ImpCalcBulletFont( nPara ) );
            // Use baseline
            bool bSymbol = pFmt->GetNumberingType() == SVX_NUM_CHAR_SPECIAL;
            aBulletFont.SetAlignment( bSymbol ? ALIGN_BOTTOM : ALIGN_BASELINE );
            vcl::Font aOldFont = rOutDev.GetFont();
            rOutDev.SetFont( aBulletFont );

            ParagraphInfos  aParaInfos = pEditEngine->GetParagraphInfos( nPara );
            Point aTextPos;
            if ( !bVertical )
            {
//                  aTextPos.Y() = rStartPos.Y() + aBulletArea.Bottom();
                aTextPos.setY( rStartPos.Y() + ( bSymbol ? aBulletArea.Bottom() : aParaInfos.nFirstLineMaxAscent ) );
                if ( !bRightToLeftPara )
                    aTextPos.setX( rStartPos.X() + aBulletArea.Left() );
                else
                    aTextPos.setX( rStartPos.X() + GetPaperSize().Width() - aBulletArea.Right() );
            }
            else
            {
                if (bTopToBottom)
                {
//                      aTextPos.X() = rStartPos.X() - aBulletArea.Bottom();
                    aTextPos.setX( rStartPos.X() - (bSymbol ? aBulletArea.Bottom() : aParaInfos.nFirstLineMaxAscent) );
                    aTextPos.setY( rStartPos.Y() + aBulletArea.Left() );
                }
                else
                {
                    aTextPos.setX( rStartPos.X() + (bSymbol ? aBulletArea.Bottom() : aParaInfos.nFirstLineMaxAscent) );
                    aTextPos.setY( rStartPos.Y() + aBulletArea.Left() );
                }
            }

            if ( nOrientation )
            {
                // Both TopLeft and bottom left is not quite correct,
                // since in EditEngine baseline ...
                rOrigin.RotateAround(aTextPos, nOrientation);

                vcl::Font aRotatedFont( std::move(aBulletFont) );
                aRotatedFont.SetOrientation( nOrientation );
                rOutDev.SetFont( aRotatedFont );
            }

            // VCL will take care of brackets and so on...
            vcl::text::ComplexTextLayoutFlags nLayoutMode = rOutDev.GetLayoutMode();
            nLayoutMode &= ~vcl::text::ComplexTextLayoutFlags(vcl::text::ComplexTextLayoutFlags::BiDiRtl|vcl::text::ComplexTextLayoutFlags::BiDiStrong);
            if ( bRightToLeftPara )
                nLayoutMode |= vcl::text::ComplexTextLayoutFlags::BiDiRtl | vcl::text::ComplexTextLayoutFlags::TextOriginLeft | vcl::text::ComplexTextLayoutFlags::BiDiStrong;
            rOutDev.SetLayoutMode( nLayoutMode );

            if(rDrawPortion)
            {
                const SvxFont aSvxFont(rOutDev.GetFont());
                KernArray aBuf;
                rOutDev.GetTextArray( pPara->GetText(), &aBuf );

                if(bSymbol)
                {
                    // aTextPos is Bottom, go to Baseline
                    FontMetric aMetric(rOutDev.GetFontMetric());
                    aTextPos.AdjustY( -(aMetric.GetDescent()) );
                }

                const DrawPortionInfo aInfo(
                    aTextPos, pPara->GetText(), 0, pPara->GetText().getLength(), aBuf, {},
                    aSvxFont, nPara, bRightToLeftPara ? 1 : 0, nullptr, nullptr, falsefalsetrue, nullptr, Color(), Color());
                rDrawPortion(aInfo);
            }
            else
            {
                rOutDev.DrawText( aTextPos, pPara->GetText() );
            }

            rOutDev.SetFont( aOldFont );
        }
        else
        {
            if ( pFmt->GetBrush()->GetGraphicObject() )
            {
                Point aBulletPos;
                if ( !bVertical )
                {
                    aBulletPos.setY( rStartPos.Y() + aBulletArea.Top() );
                    if ( !bRightToLeftPara )
                        aBulletPos.setX( rStartPos.X() + aBulletArea.Left() );
                    else
                        aBulletPos.setX( rStartPos.X() + GetPaperSize().Width() - aBulletArea.Right() );
                }
                else
                {
                    if (bTopToBottom)
                    {
                        aBulletPos.setX( rStartPos.X() - aBulletArea.Bottom() );
                        aBulletPos.setY( rStartPos.Y() + aBulletArea.Left() );
                    }
                    else
                    {
                        aBulletPos.setX( rStartPos.X() + aBulletArea.Top() );
                        aBulletPos.setY( rStartPos.Y() - aBulletArea.Right() );
                    }
                }

                if(rDrawBullet)
                {
                    // call something analog to aDrawPortionHdl (if set) and feed it something
                    // analog to DrawPortionInfo...
                    // created aDrawBulletHdl, Set/GetDrawBulletHdl.
                    // created DrawBulletInfo and added handling to sdrtextdecomposition.cxx
                    DrawBulletInfo aDrawBulletInfo(
                        *pFmt->GetBrush()->GetGraphicObject(),
                        aBulletPos,
                        pPara->aBulSize);
                    rDrawBullet(aDrawBulletInfo);
                }
                else
                {
                    pFmt->GetBrush()->GetGraphicObject()->Draw(rOutDev, aBulletPos, pPara->aBulSize);
                }
            }
        }
    }

    // In case of collapsed subparagraphs paint a line before the text.
    if( !pParaList->HasChildren(pPara) || pParaList->HasVisibleChildren(pPara) || rDrawPortion || nOrientation )
        return;

    tools::Long nWidth = rOutDev.PixelToLogic( Size( 10, 0 ) ).Width();

    Point aStartPos, aEndPos;
    if ( !bVertical )
    {
        aStartPos.setY( rStartPos.Y() + aBulletArea.Bottom() );
        if ( !bRightToLeftPara )
            aStartPos.setX( rStartPos.X() + aBulletArea.Right() );
        else
            aStartPos.setX( rStartPos.X() + GetPaperSize().Width() - aBulletArea.Left() );
        aEndPos = aStartPos;
        aEndPos.AdjustX(nWidth );
    }
    else
    {
        aStartPos.setX( rStartPos.X() - aBulletArea.Bottom() );
        aStartPos.setY( rStartPos.Y() + aBulletArea.Right() );
        aEndPos = aStartPos;
        aEndPos.AdjustY(nWidth );
    }

    const Color& rOldLineColor = rOutDev.GetLineColor();
    rOutDev.SetLineColor( COL_BLACK );
    rOutDev.DrawLine( aStartPos, aEndPos );
    rOutDev.SetLineColor( rOldLineColor );
}

void Outliner::InvalidateBullet(sal_Int32 nPara)
{
    tools::Long nLineHeight = static_cast<tools::Long>(pEditEngine->GetLineHeight(nPara ));
    for (OutlinerView* pView : aViewList)
    {
        Point aPos( pView->pEditView->GetWindowPosTopLeft(nPara ) );
        tools::Rectangle aRect( pView->GetOutputArea() );
        aRect.SetRight( aPos.X() );
        aRect.SetTop( aPos.Y() );
        aRect.SetBottom( aPos.Y() );
        aRect.AdjustBottom(nLineHeight );

        pView->pEditView->InvalidateWindow(aRect);
    }
}

ErrCode Outliner::Read( SvStream& rInput, const OUString& rBaseURL, EETextFormat eFormat, SvKeyValueIterator* pHTTPHeaderAttrs )
{

    bool bOldUndo = pEditEngine->IsUndoEnabled();
    EnableUndo( false );

    bool bUpdate = pEditEngine->SetUpdateLayout( false );

    Clear();

    ImplBlockInsertionCallbacks( true );
    ErrCode nRet = pEditEngine->Read( rInput, rBaseURL, eFormat, pHTTPHeaderAttrs );

    bFirstParaIsEmpty = false;

    sal_Int32 nParas = pEditEngine->GetParagraphCount();
    pParaList->Clear();
    for ( sal_Int32 n = 0; n < nParas; n++ )
    {
        std::unique_ptr<Paragraph> pPara(new Paragraph( 0 ));
        pParaList->Append(std::move(pPara));
    }

    ImpFilterIndents( 0, nParas-1 );

    ImplBlockInsertionCallbacks( false );
    pEditEngine->SetUpdateLayout( bUpdate );
    EnableUndo( bOldUndo );

    return nRet;
}


void Outliner::ImpFilterIndents( sal_Int32 nFirstPara, sal_Int32 nLastPara )
{
    bool bUpdate = pEditEngine->SetUpdateLayout( false );

    Paragraph* pLastConverted = nullptr;
    for( sal_Int32 nPara = nFirstPara; nPara <= nLastPara; nPara++ )
    {
        Paragraph* pPara = pParaList->GetParagraph( nPara );
        if (pPara)
        {
                    if( ImpConvertEdtToOut( nPara ) )
                    {
                            pLastConverted = pPara;
                    }
                    else if ( pLastConverted )
                    {
                            // Arrange normal paragraphs below the heading ...
                            pPara->SetDepth( pLastConverted->GetDepth() );
                    }

                    ImplInitDepth( nPara, pPara->GetDepth(), false );
        }
    }

    pEditEngine->SetUpdateLayout( bUpdate );
}

EditUndoManager& Outliner::GetUndoManager()
{
    return pEditEngine->GetUndoManager();
}

EditUndoManager* Outliner::SetUndoManager(EditUndoManager* pNew)
{
    return pEditEngine->SetUndoManager(pNew);
}

void Outliner::ImpTextPasted( sal_Int32 nStartPara, sal_Int32 nCount )
{
    bool bUpdate = pEditEngine->SetUpdateLayout( false );

    const sal_Int32 nStart = nStartPara;

    Paragraph* pPara = pParaList->GetParagraph( nStartPara );

    while( nCount && pPara )
    {
        if( GetOutlinerMode() != OutlinerMode::TextObject )
        {
            nDepthChangedHdlPrevDepth = pPara->GetDepth();
            ParaFlag nPrevFlags = pPara->nFlags;

            ImpConvertEdtToOut( nStartPara );

            if( nStartPara == nStart )
            {
                // the existing paragraph has changed depth or flags
                if( (pPara->GetDepth() != nDepthChangedHdlPrevDepth) || (pPara->nFlags != nPrevFlags) )
                    DepthChangedHdl(pPara, nPrevFlags);
            }
        }
        else // EditEngine mode
        {
            sal_Int16 nDepth = -1;
            const SfxItemSet& rAttrs = pEditEngine->GetParaAttribs( nStartPara );
            if ( rAttrs.GetItemState( EE_PARA_OUTLLEVEL ) == SfxItemState::SET )
            {
                const SfxInt16Item& rLevel = rAttrs.Get( EE_PARA_OUTLLEVEL );
                nDepth = rLevel.GetValue();
            }
            if ( nDepth != GetDepth( nStartPara ) )
                ImplInitDepth( nStartPara, nDepth, false );
        }

        nCount--;
        nStartPara++;
        pPara = pParaList->GetParagraph( nStartPara );
    }

    pEditEngine->SetUpdateLayout( bUpdate );

    DBG_ASSERT(pParaList->GetParagraphCount()==pEditEngine->GetParagraphCount(),"ImpTextPasted failed");
}

bool Outliner::IndentingPagesHdl( OutlinerView* pView )
{
    if( !aIndentingPagesHdl.IsSet() )
        return true;
    return aIndentingPagesHdl.Call( pView );
}

bool Outliner::ImpCanIndentSelectedPages( OutlinerView* pCurView )
{
    // The selected pages must already be set in advance through
    // ImpCalcSelectedPages

    // If the first paragraph is on level 0 it can not indented in any case,
    // possible there might be indentations in the following on the 0 level.
    if ( ( mnFirstSelPage == 0 ) && ( GetOutlinerMode() != OutlinerMode::TextObject ) )
    {
        if ( nDepthChangedHdlPrevDepth == 1 )   // is the only page
            return false;
        else
            (void)pCurView->ImpCalcSelectedPages( false );  // without the first
    }
    return IndentingPagesHdl( pCurView );
}


bool Outliner::ImpCanDeleteSelectedPages( OutlinerView* pCurView )
{
    // The selected pages must already be set in advance through
    // ImpCalcSelectedPages
    return RemovingPagesHdl( pCurView );
}

Outliner::Outliner(SfxItemPool* pPool, OutlinerMode nMode)
    : pEditEngine(new OutlinerEditEng(this, pPool))
    , pParaList(new ParagraphList)
    , mnFirstSelPage(0)
    , nDepthChangedHdlPrevDepth(0)
    , nMaxDepth(9)
    , bFirstParaIsEmpty(true)
    , nBlockInsCallback(0)
    , bPasting(false)
{
    pParaList->SetVisibleStateChangedHdl( LINK( this, Outliner, ParaVisibleStateChangedHdl ) );
    std::unique_ptr<Paragraph> pPara(new Paragraph( 0 ));
    pParaList->Append(std::move(pPara));

    pEditEngine->SetBeginMovingParagraphsHdl( LINK( this, Outliner, BeginMovingParagraphsHdl ) );
    pEditEngine->SetEndMovingParagraphsHdl( LINK( this, Outliner, EndMovingParagraphsHdl ) );
    pEditEngine->SetBeginPasteOrDropHdl( LINK( this, Outliner, BeginPasteOrDropHdl ) );
    pEditEngine->SetEndPasteOrDropHdl( LINK( this, Outliner, EndPasteOrDropHdl ) );

    Init( nMode );
}

Outliner::~Outliner()
{
    pParaList->Clear();
    pParaList.reset();
    pEditEngine.reset();
}

size_t Outliner::InsertView( OutlinerView* pView, size_t nIndex )
{
    size_t ActualIndex;

    if ( nIndex >= aViewList.size() )
    {
        aViewList.push_back( pView );
        ActualIndex = aViewList.size() - 1;
    }
    else
    {
        ViewList::iterator it = aViewList.begin();
        advance( it, nIndex );
        ActualIndex = nIndex;
    }
    pEditEngine->InsertView(  pView->pEditView.get(), nIndex );
    return ActualIndex;
}

void Outliner::RemoveView( OutlinerView const * pView )
{
    ViewList::iterator it = std::find(aViewList.begin(), aViewList.end(), pView);
    if (it != aViewList.end())
    {
        pView->pEditView->HideCursor(); // HACK
        pEditEngine->RemoveView(  pView->pEditView.get() );
        aViewList.erase( it );
    }
}

void Outliner::RemoveView( size_t nIndex )
{
    EditView* pEditView = pEditEngine->GetView( nIndex );
    pEditView->HideCursor(); // HACK

    pEditEngine->RemoveView( nIndex );

    {
        ViewList::iterator it = aViewList.begin();
        advance( it, nIndex );
        aViewList.erase( it );
    }
}


OutlinerView* Outliner::GetView( size_t nIndex ) const
{
    return ( nIndex >= aViewList.size() ) ? nullptr : aViewList[ nIndex ];
}

size_t Outliner::GetViewCount() const
{
    return aViewList.size();
}

void Outliner::ParagraphInsertedHdl(Paragraph* pPara)
{
    if( !IsInUndo() )
        aParaInsertedHdl.Call( { this, pPara } );
}


void Outliner::DepthChangedHdl(Paragraph* pPara, ParaFlag nPrevFlags)
{
    if( !IsInUndo() )
        aDepthChangedHdl.Call( { this, pPara, nPrevFlags } );
}


sal_Int32 Outliner::GetAbsPos( Paragraph const * pPara ) const
{
    DBG_ASSERT(pPara,"GetAbsPos:No Para");
    return pParaList->GetAbsPos( pPara );
}

sal_Int32 Outliner::GetParagraphCount() const
{
    return pParaList->GetParagraphCount();
}

Paragraph* Outliner::GetParagraph( sal_Int32 nAbsPos ) const
{
    return pParaList->GetParagraph( nAbsPos );
}

bool Outliner::HasChildren( Paragraph const * pParagraph ) const
{
    return pParaList->HasChildren( pParagraph );
}

bool Outliner::ImplHasNumberFormat( sal_Int32 nPara ) const
{
    return GetNumberFormat(nPara) != nullptr;
}

const SvxNumberFormat* Outliner::GetNumberFormat( sal_Int32 nPara ) const
{
    const SvxNumberFormat* pFmt = nullptr;

    Paragraph* pPara = pParaList->GetParagraph( nPara );
    if (!pPara)
        return nullptr;

    sal_Int16 nDepth = pPara->GetDepth();

    if( nDepth >= 0 )
    {
        const SvxNumBulletItem& rNumBullet = pEditEngine->GetParaAttrib( nPara, EE_PARA_NUMBULLET );
        if ( rNumBullet.GetNumRule().GetLevelCount() > nDepth )
            pFmt = rNumBullet.GetNumRule().Get( nDepth );
    }

    return pFmt;
}

Size Outliner::ImplGetBulletSize( sal_Int32 nPara )
{
    Paragraph* pPara = pParaList->GetParagraph( nPara );
    if (!pPara)
        return Size();

    if( pPara->aBulSize.Width() == -1 )
    {
        const SvxNumberFormat* pFmt = GetNumberFormat( nPara );
        assert(pFmt && "ImplGetBulletSize - no Bullet!");

        if ( pFmt->GetNumberingType() == SVX_NUM_NUMBER_NONE )
        {
            pPara->aBulSize = Size( 0, 0 );
        }
        else if( pFmt->GetNumberingType() != SVX_NUM_BITMAP )
        {
            OUString aBulletText = ImplGetBulletText( nPara );
            OutputDevice* pRefDev = pEditEngine->GetRefDevice();
            vcl::Font aBulletFont( ImpCalcBulletFont( nPara ) );
            vcl::Font aRefFont( pRefDev->GetFont());
            pRefDev->SetFont( aBulletFont );
            pPara->aBulSize.setWidth( pRefDev->GetTextWidth( aBulletText ) );
            pPara->aBulSize.setHeight( pRefDev->GetTextHeight() );
            pRefDev->SetFont( aRefFont );
        }
        else
        {
            pPara->aBulSize = OutputDevice::LogicToLogic(pFmt->GetGraphicSize(),
                    MapMode(MapUnit::Map100thMM),
                    pEditEngine->GetRefDevice()->GetMapMode());
        }
    }

    return pPara->aBulSize;
}

void Outliner::ImplCheckParagraphs( sal_Int32 nStart, sal_Int32 nEnd )
{

    for ( sal_Int32 n = nStart; n < nEnd; n++ )
    {
        Paragraph* pPara = pParaList->GetParagraph( n );
        if (pPara)
        {
            pPara->Invalidate();
            ImplCalcBulletText( n, falsefalse );
        }
    }
}

void Outliner::SetRefDevice( OutputDevice* pRefDev )
{
    pEditEngine->SetRefDevice( pRefDev );
    for ( sal_Int32 n = pParaList->GetParagraphCount(); n; )
    {
        Paragraph* pPara = pParaList->GetParagraph( --n );
        pPara->Invalidate();
    }
}

void Outliner::ParaAttribsChanged( sal_Int32 nPara )
{
    // The Outliner does not have an undo of its own, when paragraphs are
    // separated/merged. When ParagraphInserted the attribute EE_PARA_OUTLLEVEL
    // may not be set, this is however needed when the depth of the paragraph
    // is to be determined.
    if (!pEditEngine->IsInUndo())
        return;
    if (pParaList->GetParagraphCount() != pEditEngine->GetParagraphCount())
        return;
    Paragraph* pPara = pParaList->GetParagraph(nPara);
    if (!pPara)
        return;
    // tdf#100734: force update of bullet
    pPara->Invalidate();
    const SfxInt16Item& rLevel = pEditEngine->GetParaAttrib( nPara, EE_PARA_OUTLLEVEL );
    if (pPara->GetDepth() == rLevel.GetValue())
        return;
    pPara->SetDepth(rLevel.GetValue());
    ImplCalcBulletText(nPara, truetrue);
}

void Outliner::StyleSheetChanged( SfxStyleSheet const * pStyle )
{

    // The EditEngine calls StyleSheetChanged also for derived styles.
    // Here all the paragraphs, which had the said template, used to be
    // hunted by an ImpRecalcParaAttribs, why?
    // => only the Bullet-representation can really change...
    sal_Int32 nParas = pParaList->GetParagraphCount();
    for( sal_Int32 nPara = 0; nPara < nParas; nPara++ )
    {
        if ( pEditEngine->GetStyleSheet( nPara ) == pStyle )
        {
            ImplCheckNumBulletItem( nPara );
            ImplCalcBulletText( nPara, falsefalse );
            // EditEngine formats changed paragraphs before calling this method,
            // so they are not reformatted now and use wrong bullet indent
            pEditEngine->QuickMarkInvalid(ESelection(nPara, 0));
        }
    }
}

tools::Rectangle Outliner::ImpCalcBulletArea( sal_Int32 nPara, bool bAdjust, bool bReturnPaperPos )
{
    // Bullet area within the paragraph ...
    tools::Rectangle aBulletArea;

    const SvxNumberFormat* pFmt = GetNumberFormat( nPara );
    if ( pFmt )
    {
        Point aTopLeft;
        Size aBulletSize( ImplGetBulletSize( nPara ) );

        bool bOutlineMode = bool( pEditEngine->GetControlWord() & EEControlBits::OUTLINER );

        // the ODF attribute text:space-before which holds the spacing to add to the left of the label
        const auto nSpaceBefore = pFmt->GetAbsLSpace() + pFmt->GetFirstLineOffset();

        const SvxLRSpaceItem& rLR = pEditEngine->GetParaAttrib( nPara, bOutlineMode ? EE_PARA_OUTLLRSPACE : EE_PARA_LRSPACE );
        aTopLeft.setX(rLR.ResolveTextLeft({}) + rLR.ResolveTextFirstLineOffset({}) + nSpaceBefore);

        tools::Long nBulletWidth = std::max(
            static_cast<tools::Long>(-rLR.ResolveTextFirstLineOffset({})),
            static_cast<tools::Long>((-pFmt->GetFirstLineOffset()) + pFmt->GetCharTextDistance()));
        if ( nBulletWidth < aBulletSize.Width() )   // The Bullet creates its space
            nBulletWidth = aBulletSize.Width();

        if ( bAdjust && !bOutlineMode )
        {
            // Adjust when centered or align right
            const SvxAdjustItem& rItem = pEditEngine->GetParaAttrib( nPara, EE_PARA_JUST );
            if ( ( !pEditEngine->IsRightToLeft( nPara ) && ( rItem.GetAdjust() != SvxAdjust::Left ) ) ||
                 ( pEditEngine->IsRightToLeft( nPara ) && ( rItem.GetAdjust() != SvxAdjust::Right ) ) )
            {
                aTopLeft.setX( pEditEngine->GetFirstLineStartX( nPara ) - nBulletWidth );
            }
        }

        // Vertical:
        ParagraphInfos aInfos = pEditEngine->GetParagraphInfos( nPara );
        if ( aInfos.bValid )
        {
            aTopLeft.setY( /* aInfos.nFirstLineOffset + */ // nFirstLineOffset is already added to the StartPos (PaintBullet) from the EditEngine
                            aInfos.nFirstLineHeight - aInfos.nFirstLineTextHeight
                            + aInfos.nFirstLineTextHeight / 2
                            - aBulletSize.Height() / 2 );
            // may prefer to print out on the baseline ...
            if( ( pFmt->GetNumberingType() != SVX_NUM_NUMBER_NONE ) && ( pFmt->GetNumberingType() != SVX_NUM_BITMAP ) && ( pFmt->GetNumberingType() != SVX_NUM_CHAR_SPECIAL ) )
            {
                vcl::Font aBulletFont( ImpCalcBulletFont( nPara ) );
                if ( aBulletFont.GetCharSet() != RTL_TEXTENCODING_SYMBOL )
                {
                    OutputDevice* pRefDev = pEditEngine->GetRefDevice();
                    vcl::Font aOldFont = pRefDev->GetFont();
                    pRefDev->SetFont( aBulletFont );
                    FontMetric aMetric( pRefDev->GetFontMetric() );
                    // Leading on the first line ...
                    aTopLeft.setY( /* aInfos.nFirstLineOffset + */ aInfos.nFirstLineMaxAscent );
                    aTopLeft.AdjustY( -(aMetric.GetAscent()) );
                    pRefDev->SetFont( aOldFont );
                }
            }
        }

        // Horizontal:
        if( pFmt->GetNumAdjust() == SvxAdjust::Right )
        {
            aTopLeft.AdjustX(nBulletWidth - aBulletSize.Width() );
        }
        else if( pFmt->GetNumAdjust() == SvxAdjust::Center )
        {
            aTopLeft.AdjustX(( nBulletWidth - aBulletSize.Width() ) / 2 );
        }

        if ( aTopLeft.X() < 0 )     // then push
            aTopLeft.setX( 0 );

        aBulletArea = tools::Rectangle( aTopLeft, aBulletSize );
    }
    if ( bReturnPaperPos )
    {
        Size aBulletSize( aBulletArea.GetSize() );
        Point aBulletDocPos( aBulletArea.TopLeft() );
        aBulletDocPos.AdjustY(pEditEngine->GetDocPosTopLeft( nPara ).Y() );
        Point aBulletPos( aBulletDocPos );

        if ( IsVertical() )
        {
            aBulletPos.setY( aBulletDocPos.X() );
            aBulletPos.setX( GetPaperSize().Width() - aBulletDocPos.Y() );
            // Rotate:
            aBulletPos.AdjustX( -(aBulletSize.Height()) );
            Size aSz( aBulletSize );
            aBulletSize.setWidth( aSz.Height() );
            aBulletSize.setHeight( aSz.Width() );
        }
        else if ( pEditEngine->IsRightToLeft( nPara ) )
        {
            aBulletPos.setX( GetPaperSize().Width() - aBulletDocPos.X() - aBulletSize.Width() );
        }

        aBulletArea = tools::Rectangle( aBulletPos, aBulletSize );
    }
    return aBulletArea;
}

EBulletInfo Outliner::GetBulletInfo( sal_Int32 nPara )
{
    EBulletInfo aInfo;

    aInfo.nParagraph = nPara;
    aInfo.bVisible = ImplHasNumberFormat( nPara );

    const SvxNumberFormat* pFmt = GetNumberFormat( nPara );
    aInfo.nType = pFmt ? pFmt->GetNumberingType() : 0;

    if( pFmt )
    {
        if( pFmt->GetNumberingType() != SVX_NUM_BITMAP )
        {
            aInfo.aText = ImplGetBulletText( nPara );

            if( pFmt->GetBulletFont() )
                aInfo.aFont = *pFmt->GetBulletFont();
        }
    }

    if ( aInfo.bVisible )
    {
        aInfo.aBounds = ImpCalcBulletArea( nPara, truetrue );
    }

    return aInfo;
}

OUString Outliner::GetText( Paragraph const * pParagraph, sal_Int32 nCount ) const
{

    OUStringBuffer aText(128);
    sal_Int32 nStartPara = pParaList->GetAbsPos( pParagraph );
    for ( sal_Int32 n = 0; n < nCount; n++ )
    {
        aText.append(pEditEngine->GetText( nStartPara + n ));
        if ( (n+1) < nCount )
            aText.append("\n");
    }
    return aText.makeStringAndClear();
}

void Outliner::Remove( Paragraph const * pPara, sal_Int32 nParaCount )
{

    sal_Int32 nPos = pParaList->GetAbsPos( pPara );
    if( !nPos && ( nParaCount >= pParaList->GetParagraphCount() ) )
    {
        Clear();
    }
    else
    {
        for( sal_Int32 n = 0; n < nParaCount; n++ )
            pEditEngine->RemoveParagraph( nPos );
    }
}

void Outliner::StripPortions(
    const std::function<void(const DrawPortionInfo&)>& rDrawPortion,
    const std::function<void(const DrawBulletInfo&)>& rDrawBullet)
{
    pEditEngine->StripPortions(rDrawPortion, rDrawBullet);
}

bool Outliner::RemovingPagesHdl( OutlinerView* pView )
{
    return !aRemovingPagesHdl.IsSet() || aRemovingPagesHdl.Call( pView );
}

bool Outliner::ImpCanDeleteSelectedPages( OutlinerView* pCurView, sal_Int32 _nFirstPage, sal_Int32 nPages )
{

    nDepthChangedHdlPrevDepth = nPages;
    mnFirstSelPage = _nFirstPage;
    return RemovingPagesHdl( pCurView );
}

SfxItemSet const & Outliner::GetParaAttribs( sal_Int32 nPara ) const
{
    return pEditEngine->GetParaAttribs( nPara );
}

IMPL_LINK( Outliner, ParaVisibleStateChangedHdl, Paragraph&, rPara, void )
{
    sal_Int32 nPara = pParaList->GetAbsPos( &rPara );
    pEditEngine->ShowParagraph( nPara, rPara.IsVisible() );
}

IMPL_LINK_NOARG(Outliner, BeginMovingParagraphsHdl, MoveParagraphsInfo&, void)
{
    if( !IsInUndo() )
        aBeginMovingHdl.Call( this );
}

IMPL_LINK( Outliner, BeginPasteOrDropHdl, PasteOrDropInfos&, rInfos, void )
{
    UndoActionStart( EDITUNDO_DRAGANDDROP );
    maBeginPasteOrDropHdl.Call(&rInfos);
}

IMPL_LINK( Outliner, EndPasteOrDropHdl, PasteOrDropInfos&, rInfos, void )
{
    bPasting = false;
    ImpTextPasted( rInfos.nStartPara, rInfos.nEndPara - rInfos.nStartPara + 1 );
    maEndPasteOrDropHdl.Call( &rInfos );
    UndoActionEnd();
}

IMPL_LINK( Outliner, EndMovingParagraphsHdl, MoveParagraphsInfo&, rInfos, void )
{
    pParaList->MoveParagraphs( rInfos.nStartPara, rInfos.nDestPara, rInfos.nEndPara - rInfos.nStartPara + 1 );
    sal_Int32 nChangesStart = std::min( rInfos.nStartPara, rInfos.nDestPara );
    sal_Int32 nParas = pParaList->GetParagraphCount();
    for ( sal_Int32 n = nChangesStart; n < nParas; n++ )
        ImplCalcBulletText( n, falsefalse );

    if( !IsInUndo() )
        aEndMovingHdl.Call( this );
}

static bool isSameNumbering( const SvxNumberFormat& rN1, const SvxNumberFormat& rN2 )
{
    if( rN1.GetNumberingType() != rN2.GetNumberingType() )
        return false;

    if( rN1.GetNumStr(1) != rN2.GetNumStr(1) )
        return false;

    if( (rN1.GetPrefix() != rN2.GetPrefix()) || (rN1.GetSuffix() != rN2.GetSuffix()) )
        return false;

    return true;
}

sal_uInt16 Outliner::ImplGetNumbering( sal_Int32 nPara, const SvxNumberFormat* pParaFmt )
{
    sal_uInt16 nNumber = pParaFmt->GetStart() - 1;

    Paragraph* pPara = pParaList->GetParagraph( nPara );
    const sal_Int16 nParaDepth = pPara->GetDepth();

    do
    {
        pPara = pParaList->GetParagraph( nPara );
        const sal_Int16 nDepth = pPara->GetDepth();

        // ignore paragraphs that are below our paragraph or have no numbering
        if( (nDepth > nParaDepth) || (nDepth == -1) )
            continue;

        // stop on paragraphs that are above our paragraph
        if( nDepth < nParaDepth )
            break;

        const SvxNumberFormat* pFmt = GetNumberFormat( nPara );

        if( pFmt == nullptr )
            continue// ignore paragraphs without bullets

        // check if numbering less than or equal to pParaFmt
        if( !isSameNumbering( *pFmt, *pParaFmt ) || ( pFmt->GetStart() < pParaFmt->GetStart() ) )
            break;

        if (  pFmt->GetStart() > pParaFmt->GetStart() )
        {
           nNumber += pFmt->GetStart() - pParaFmt->GetStart();
           pParaFmt = pFmt;
        }

        const SfxBoolItem& rBulletState = pEditEngine->GetParaAttrib( nPara, EE_PARA_BULLETSTATE );

        if( rBulletState.GetValue() )
            nNumber += 1;

        // same depth, same number format, check for restart
        const sal_Int16 nNumberingStartValue = pPara->GetNumberingStartValue();
        if( (nNumberingStartValue != -1) || pPara->IsParaIsNumberingRestart() )
        {
            if( nNumberingStartValue != -1 )
                nNumber += nNumberingStartValue - 1;
            break;
        }
    }
    while( nPara-- );

    return nNumber;
}

void Outliner::ImplCalcBulletText( sal_Int32 nPara, bool bRecalcLevel, bool bRecalcChildren )
{

    Paragraph* pPara = pParaList->GetParagraph( nPara );

    while ( pPara )
    {
        OUString aBulletText;
        const SvxNumberFormat* pFmt = GetNumberFormat( nPara );
        if( pFmt && ( pFmt->GetNumberingType() != SVX_NUM_BITMAP ) )
        {
            aBulletText += pFmt->GetPrefix();
            if( pFmt->GetNumberingType() == SVX_NUM_CHAR_SPECIAL )
            {
                sal_UCS4 cChar = pFmt->GetBulletChar();
                aBulletText += OUString(&cChar, 1);
            }
            else if( pFmt->GetNumberingType() != SVX_NUM_NUMBER_NONE )
            {
                aBulletText += pFmt->GetNumStr( ImplGetNumbering( nPara, pFmt ) );
            }
            aBulletText += pFmt->GetSuffix();
        }

        if (pPara->GetText() != aBulletText)
            pPara->SetText( aBulletText );

        if ( bRecalcLevel )
        {
            sal_Int16 nDepth = pPara->GetDepth();
            pPara = pParaList->GetParagraph( ++nPara );
            if ( !bRecalcChildren )
            {
                while ( pPara && ( pPara->GetDepth() > nDepth ) )
                    pPara = pParaList->GetParagraph( ++nPara );
            }

            if ( pPara && ( pPara->GetDepth() < nDepth ) )
                pPara = nullptr;
        }
        else
        {
            pPara = nullptr;
        }
    }
}

void Outliner::Clear()
{

    if( !bFirstParaIsEmpty )
    {
        ImplBlockInsertionCallbacks( true );
        pEditEngine->Clear();
        pParaList->Clear();
        pParaList->Append( std::unique_ptr<Paragraph>(new Paragraph( gnMinDepth )));
        bFirstParaIsEmpty = true;
        ImplBlockInsertionCallbacks( false );
    }
    else
    {
            Paragraph* pPara = pParaList->GetParagraph( 0 );
            if(pPara)
                pPara->SetDepth( gnMinDepth );
    }
}

void Outliner::SetFlatMode( bool bFlat )
{

    if( bFlat != pEditEngine->IsFlatMode() )
    {
        for ( sal_Int32 nPara = pParaList->GetParagraphCount(); nPara; )
            pParaList->GetParagraph( --nPara )->aBulSize.setWidth( -1 );

        pEditEngine->SetFlatMode( bFlat );
    }
}

OUString Outliner::ImplGetBulletText( sal_Int32 nPara )
{
    OUString aRes;
    Paragraph* pPara = pParaList->GetParagraph( nPara );
    if (pPara)
    {
        ImplCalcBulletText( nPara, falsefalse );
        aRes = pPara->GetText();
    }
    return aRes;
}

// this is needed for StarOffice Api
void Outliner::SetLevelDependentStyleSheet( sal_Int32 nPara )
{
    SfxItemSet aOldAttrs( pEditEngine->GetParaAttribs( nPara ) );
    ImplSetLevelDependentStyleSheet( nPara );
    pEditEngine->SetParaAttribs( nPara, aOldAttrs );
}

void Outliner::ImplBlockInsertionCallbacks( bool b )
{
    if ( b )
    {
        nBlockInsCallback++;
    }
    else
    {
        DBG_ASSERT( nBlockInsCallback, "ImplBlockInsertionCallbacks ?!" );
        nBlockInsCallback--;
        if ( !nBlockInsCallback )
        {
            // Call blocked notify events...
            while(!aNotifyCache.empty())
            {
                EENotify aNotify(aNotifyCache.front());
                // Remove from list before calling, maybe we enter LeaveBlockNotifications while calling the handler...
                aNotifyCache.erase(aNotifyCache.begin());
                aOutlinerNotifyHdl.Call( aNotify );
            }
        }
    }
}

IMPL_LINK( Outliner, EditEngineNotifyHdl, EENotify&, rNotify, void )
{
    if ( !nBlockInsCallback )
        aOutlinerNotifyHdl.Call( rNotify );
    else
        aNotifyCache.push_back(rNotify);
}

/** sets a link that is called at the beginning of a drag operation at an edit view */
void Outliner::SetBeginDropHdl( const Link<EditView*,void>& rLink )
{
    pEditEngine->SetBeginDropHdl( rLink );
}

/** sets a link that is called at the end of a drag operation at an edit view */
void Outliner::SetEndDropHdl( const Link<EditView*,void>& rLink )
{
    pEditEngine->SetEndDropHdl( rLink );
}

/** sets a link that is called before a drop or paste operation. */
void Outliner::SetBeginPasteOrDropHdl( const Link<PasteOrDropInfos*,void>& rLink )
{
    maBeginPasteOrDropHdl = rLink;
}

/** sets a link that is called after a drop or paste operation. */
void Outliner::SetEndPasteOrDropHdl( const Link<PasteOrDropInfos*,void>& rLink )
{
    maEndPasteOrDropHdl = rLink;
}

void Outliner::SetParaFlag( Paragraph* pPara,  ParaFlag nFlag )
{
    if( pPara && !pPara->HasFlag( nFlag ) )
    {
        if( IsUndoEnabled() && !IsInUndo() )
            InsertUndo( std::make_unique<OutlinerUndoChangeParaFlags>( this, GetAbsPos( pPara ), pPara->nFlags, pPara->nFlags|nFlag ) );

        pPara->SetFlag( nFlag );
    }
}

bool Outliner::HasParaFlag( const Paragraph* pPara, ParaFlag nFlag )
{
    return pPara && pPara->HasFlag( nFlag );
}


bool Outliner::IsPageOverflow()
{
    return pEditEngine->IsPageOverflow();
}

std::optional<NonOverflowingText> Outliner::GetNonOverflowingText() const
{
    /* XXX:
     * nCount should be the number of paragraphs of the non overflowing text
     * nStart should be the starting paragraph of the non overflowing text (XXX: Always 0?)
    */


    if ( GetParagraphCount() < 1 )
        return {};

    // last non-overflowing paragraph is before the first overflowing one
    sal_Int32 nCount = pEditEngine->GetOverflowingParaNum();
    sal_Int32 nOverflowLine = pEditEngine->GetOverflowingLineNum(); // XXX: Unused for now

    // Defensive check: overflowing para index beyond actual # of paragraphs?
    if ( nCount > GetParagraphCount()-1) {
        SAL_INFO("editeng.chaining",
                 "[Overflowing] Ops, trying to retrieve para "
                 << nCount << " when max index is " << GetParagraphCount()-1 );
        return {};
    }

    if (nCount < 0)
    {
        SAL_INFO("editeng.chaining",
                 "[Overflowing] No Overflowing text but GetNonOverflowinText called?!");
        return {};
    }

    // NOTE: We want the selection of the overflowing text from here
    //       At the same time we may want to consider the beginning of such text
    //       in a more fine grained way (i.e. as GetNonOverflowingText did)

/*
    sal_Int32 nHeadPara = pEditEngine->GetOverflowingParaNum();
    sal_uInt32 nParaCount = GetParagraphCount();

    sal_uInt32 nLen = 0;
    for ( sal_Int32 nLine = 0;
          nLine < pEditEngine->GetOverflowingLineNum();
          nLine++) {
        nLen += GetLineLen(nHeadPara, nLine);
    }

    sal_uInt32 nOverflowingPara = pEditEngine->GetOverflowingParaNum();
    ESelection aOverflowingTextSel;
    sal_Int32 nLastPara = nParaCount-1;
    sal_Int32 nLastParaLen = GetText(GetParagraph(nLastPara)).getLength();
    aOverflowingTextSel = ESelection(nOverflowingPara, nLen,
                                     nLastPara, nLastParaLen);
    bool bLastParaInterrupted =
            pEditEngine->GetOverflowingLineNum() > 0;

    return new NonOverflowingText(aOverflowingTextSel, bLastParaInterrupted);
    **/



    // Only overflowing text, i.e. 1st line of 1st paragraph overflowing
    bool bItAllOverflew = nCount == 0 && nOverflowLine == 0;
    if ( bItAllOverflew )
    {
        ESelection aEmptySel;
        //EditTextObject *pTObj = pEditEngine->CreateTextObject(aEmptySel);
        bool const bLastParaInterrupted = true// Last Para was interrupted since everything overflew
        return NonOverflowingText(aEmptySel, bLastParaInterrupted);
    } else { // Get the lines that of the overflowing para fit in the box

        sal_Int32 nOverflowingPara = nCount;
        sal_uInt32 nLen = 0;

        for ( sal_Int32 nLine = 0;
              nLine < pEditEngine->GetOverflowingLineNum();
              nLine++)
        {
            nLen += GetLineLen(nOverflowingPara, nLine);
        }

        //sal_Int32 nStartPara = 0;
        //sal_Int32 nStartPos = 0;
        ESelection aOverflowingTextSelection;

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

--> maximum size reached

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

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

¤ Dauer der Verarbeitung: 0.12 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.