summaryrefslogtreecommitdiffstats
path: root/editeng/source/outliner/outliner.cxx
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--editeng/source/outliner/outliner.cxx2170
1 files changed, 2170 insertions, 0 deletions
diff --git a/editeng/source/outliner/outliner.cxx b/editeng/source/outliner/outliner.cxx
new file mode 100644
index 000000000..13e490f87
--- /dev/null
+++ b/editeng/source/outliner/outliner.cxx
@@ -0,0 +1,2170 @@
+/* -*- 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 <osl/diagnose.h>
+
+#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->nFlags = ParaFlag::SETBULLETTEXT;
+ 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, true, false );
+ ParagraphInsertedHdl(pPara);
+ }
+ }
+}
+
+void Outliner::ParagraphDeleted( sal_Int32 nPara )
+{
+
+ if ( nBlockInsCallback || ( nPara == EE_PARA_ALL ) )
+ 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, true, false );
+ // Search for next on the this level ...
+ while ( pPara && pPara->GetDepth() > nDepth )
+ pPara = pParaList->GetParagraph( ++nPara );
+ }
+
+ if ( pPara && ( pPara->GetDepth() == nDepth ) )
+ ImplCalcBulletText( nPara, true, false );
+}
+
+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, false, false );
+
+ 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", "<Outliner::GetBulletsNumberingStatus> - 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_uLong>(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 )
+{
+ DBG_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 );
+ pPara->nFlags &= ~ParaFlag::HOLDDEPTH;
+ }
+ }
+ 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( "heading" );
+ OUString aNumber_US( "Numbering" );
+ 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_APPEND, 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 )
+{
+ 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.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 );
+ pPara->nFlags |= ParaFlag::SETBULLETTEXT;
+ 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, false, false );
+
+ 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 );
+ DBG_ASSERT( pFmt && ( pFmt->GetNumberingType() != SVX_NUM_BITMAP ) && ( pFmt->GetNumberingType() != SVX_NUM_NUMBER_NONE ), "ImpCalcBulletFont: Missing or BitmapBullet!" );
+
+ vcl::Font aStdFont;
+ if ( !pEditEngine->IsFlatMode() )
+ {
+ ESelection aSel( nPara, 0, 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...
+ sal_uInt16 nStretchX, nStretchY;
+ GetGlobalCharStretching(nStretchX, nStretchY);
+
+ sal_uInt16 nScale = pFmt->GetBulletRelSize() * nStretchY / 100;
+ tools::Long nScaledLineHeight = aStdFont.GetFontSize().Height();
+ nScaledLineHeight *= nScale*10;
+ nScaledLineHeight /= 1000;
+
+ aBulletFont.SetAlignment( ALIGN_BOTTOM );
+ aBulletFont.SetFontSize( Size( 0, nScaledLineHeight ) );
+ 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)
+{
+
+ 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, true, false ) );
+ sal_uInt16 nStretchX, nStretchY;
+ GetGlobalCharStretching(nStretchX, nStretchY);
+ aBulletArea = tools::Rectangle( Point(aBulletArea.Left()*nStretchX/100,
+ aBulletArea.Top()),
+ Size(aBulletArea.GetWidth()*nStretchX/100,
+ 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 ...
+ double nRealOrientation = toRadians(nOrientation);
+ double nCos = cos( nRealOrientation );
+ double nSin = sin( nRealOrientation );
+ Point aRotatedPos;
+ // Translation...
+ aTextPos -= rOrigin;
+ // Rotation...
+ aRotatedPos.setX(static_cast<tools::Long>(nCos*aTextPos.X() + nSin*aTextPos.Y()) );
+ aRotatedPos.setY(static_cast<tools::Long>(- (nSin*aTextPos.X() - nCos*aTextPos.Y())) );
+ aTextPos = aRotatedPos;
+ // Translation...
+ aTextPos += rOrigin;
+ vcl::Font aRotatedFont( 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(bStrippingPortions)
+ {
+ const vcl::Font& aSvxFont(rOutDev.GetFont());
+ std::vector<sal_Int32> aBuf;
+ rOutDev.GetTextArray( pPara->GetText(), &aBuf );
+
+ if(bSymbol)
+ {
+ // aTextPos is Bottom, go to Baseline
+ FontMetric aMetric(rOutDev.GetFontMetric());
+ aTextPos.AdjustY( -(aMetric.GetDescent()) );
+ }
+
+ DrawingText(aTextPos, pPara->GetText(), 0, pPara->GetText().getLength(), aBuf,
+ aSvxFont, nPara, bRightToLeftPara ? 1 : 0, nullptr, nullptr, false, false, true, nullptr, Color(), Color());
+ }
+ 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(bStrippingPortions)
+ {
+ if(aDrawBulletHdl.IsSet())
+ {
+ // 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);
+
+ aDrawBulletHdl.Call(&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) ||
+ bStrippingPortions || 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 );
+}
+
+SfxUndoManager& Outliner::GetUndoManager()
+{
+ return pEditEngine->GetUndoManager();
+}
+
+SfxUndoManager* Outliner::SetUndoManager(SfxUndoManager* 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)
+ : mnFirstSelPage(0)
+ , nDepthChangedHdlPrevDepth(0)
+ , nMaxDepth(9)
+ , bFirstParaIsEmpty(true)
+ , nBlockInsCallback(0)
+ , bStrippingPortions(false)
+ , bPasting(false)
+{
+
+ pParaList.reset( new ParagraphList );
+ pParaList->SetVisibleStateChangedHdl( LINK( this, Outliner, ParaVisibleStateChangedHdl ) );
+ std::unique_ptr<Paragraph> pPara(new Paragraph( 0 ));
+ pParaList->Append(std::move(pPara));
+
+ pEditEngine.reset( new OutlinerEditEng( this, pPool ) );
+ 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 );
+ DBG_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, false, false );
+ }
+ }
+}
+
+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, true, true);
+}
+
+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, false, false );
+ // EditEngine formats changed paragraphs before calling this method,
+ // so they are not reformatted now and use wrong bullet indent
+ pEditEngine->QuickMarkInvalid( ESelection( nPara, 0, 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.GetTextLeft() + rLR.GetTextFirstLineOffset() + nSpaceBefore );
+
+ tools::Long nBulletWidth = std::max( static_cast<tools::Long>(-rLR.GetTextFirstLineOffset()), 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, true, true );
+ }
+
+ 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()
+{
+ bStrippingPortions = true;
+ pEditEngine->StripPortions();
+ bStrippingPortions = false;
+}
+
+void Outliner::DrawingText( const Point& rStartPos, const OUString& rText, sal_Int32 nTextStart,
+ sal_Int32 nTextLen, o3tl::span<const sal_Int32> pDXArray,const SvxFont& rFont,
+ sal_Int32 nPara, sal_uInt8 nRightToLeft,
+ const EEngineData::WrongSpellVector* pWrongSpellVector,
+ const SvxFieldData* pFieldData,
+ bool bEndOfLine,
+ bool bEndOfParagraph,
+ bool bEndOfBullet,
+ const css::lang::Locale* pLocale,
+ const Color& rOverlineColor,
+ const Color& rTextLineColor)
+{
+ if(aDrawPortionHdl.IsSet())
+ {
+ DrawPortionInfo aInfo( rStartPos, rText, nTextStart, nTextLen, rFont, nPara, pDXArray, pWrongSpellVector,
+ pFieldData, pLocale, rOverlineColor, rTextLineColor, nRightToLeft, false, 0, bEndOfLine, bEndOfParagraph, bEndOfBullet);
+
+ aDrawPortionHdl.Call( &aInfo );
+ }
+}
+
+void Outliner::DrawingTab( const Point& rStartPos, tools::Long nWidth, const OUString& rChar, const SvxFont& rFont,
+ sal_Int32 nPara, sal_uInt8 nRightToLeft, bool bEndOfLine, bool bEndOfParagraph,
+ const Color& rOverlineColor, const Color& rTextLineColor)
+{
+ if(aDrawPortionHdl.IsSet())
+ {
+ DrawPortionInfo aInfo( rStartPos, rChar, 0, rChar.getLength(), rFont, nPara, {}, nullptr,
+ nullptr, nullptr, rOverlineColor, rTextLineColor, nRightToLeft, true, nWidth, bEndOfLine, bEndOfParagraph, false);
+
+ aDrawPortionHdl.Call( &aInfo );
+ }
+}
+
+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, false, false );
+
+ 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 );
+
+ pPara->nFlags &= ~ParaFlag::SETBULLETTEXT;
+
+ 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)
+ {
+ // Enable optimization again ...
+// if( pPara->nFlags & ParaFlag::SETBULLETTEXT )
+ ImplCalcBulletText( nPara, false, false );
+ 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(!pEditEngine->aNotifyCache.empty())
+ {
+ EENotify aNotify(pEditEngine->aNotifyCache.front());
+ // Remove from list before calling, maybe we enter LeaveBlockNotifications while calling the handler...
+ pEditEngine->aNotifyCache.erase(pEditEngine->aNotifyCache.begin());
+ pEditEngine->aOutlinerNotifyHdl.Call( aNotify );
+ }
+ }
+ }
+}
+
+IMPL_LINK( Outliner, EditEngineNotifyHdl, EENotify&, rNotify, void )
+{
+ if ( !nBlockInsCallback )
+ pEditEngine->aOutlinerNotifyHdl.Call( rNotify );
+ else
+ pEditEngine->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(0,0,0,0);
+ //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;
+
+ const sal_Int32 nEndPara = GetParagraphCount()-1;
+ const sal_Int32 nEndPos = pEditEngine->GetTextLen(nEndPara);
+
+ if (nLen == 0) {
+ // XXX: What happens inside this case might be dependent on the joining paragraph or not-thingy
+ // Overflowing paragraph is empty or first line overflowing: it's not "Non-Overflowing" text then
+ sal_Int32 nParaLen = GetText(GetParagraph(nOverflowingPara-1)).getLength();
+ aOverflowingTextSelection =
+ ESelection(nOverflowingPara-1, nParaLen, nEndPara, nEndPos);
+ } else {
+ // We take until we have to from the overflowing paragraph
+ aOverflowingTextSelection =
+ ESelection(nOverflowingPara, nLen, nEndPara, nEndPos);
+ }
+ //EditTextObject *pTObj = pEditEngine->CreateTextObject(aNonOverflowingTextSelection);
+
+ //sal_Int32 nLastLine = GetLineCount(nOverflowingPara)-1;
+ bool bLastParaInterrupted =
+ pEditEngine->GetOverflowingLineNum() > 0;
+
+ return NonOverflowingText(aOverflowingTextSelection, bLastParaInterrupted);
+ }
+}
+
+OutlinerParaObject Outliner::GetEmptyParaObject() const
+{
+ std::unique_ptr<EditTextObject> pEmptyText = pEditEngine->GetEmptyTextObject();
+ OutlinerParaObject aPObj( std::move(pEmptyText) );
+ aPObj.SetOutlinerMode(GetOutlinerMode());
+ return aPObj;
+}
+
+std::optional<OverflowingText> Outliner::GetOverflowingText() const
+{
+ if ( pEditEngine->GetOverflowingParaNum() < 0)
+ return {};
+
+
+ // Defensive check: overflowing para index beyond actual # of paragraphs?
+ if ( pEditEngine->GetOverflowingParaNum() > GetParagraphCount()-1) {
+ SAL_INFO("editeng.chaining",
+ "[Overflowing] Ops, trying to retrieve para "
+ << pEditEngine->GetOverflowingParaNum() << " when max index is "
+ << GetParagraphCount()-1 );
+ return {};
+ }
+
+ 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);
+ return OverflowingText(pEditEngine->CreateTransferable(aOverflowingTextSel));
+
+}
+
+void Outliner::ClearOverflowingParaNum()
+{
+ pEditEngine->ClearOverflowingParaNum();
+}
+
+void Outliner::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ bool bOwns = false;
+ if (!pWriter)
+ {
+ pWriter = xmlNewTextWriterFilename("outliner.xml", 0);
+ xmlTextWriterSetIndent(pWriter,1);
+ (void)xmlTextWriterSetIndentString(pWriter, BAD_CAST(" "));
+ (void)xmlTextWriterStartDocument(pWriter, nullptr, nullptr, nullptr);
+ bOwns = true;
+ }
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("Outliner"));
+ pParaList->dumpAsXml(pWriter);
+ (void)xmlTextWriterEndElement(pWriter);
+
+ if (bOwns)
+ {
+ (void)xmlTextWriterEndDocument(pWriter);
+ xmlFreeTextWriter(pWriter);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */