summaryrefslogtreecommitdiffstats
path: root/editeng/source/outliner
diff options
context:
space:
mode:
Diffstat (limited to 'editeng/source/outliner')
-rw-r--r--editeng/source/outliner/outleeng.cxx192
-rw-r--r--editeng/source/outliner/outleeng.hxx87
-rw-r--r--editeng/source/outliner/outlin2.cxx590
-rw-r--r--editeng/source/outliner/outliner.cxx2184
-rw-r--r--editeng/source/outliner/outlobj.cxx248
-rw-r--r--editeng/source/outliner/outlundo.cxx164
-rw-r--r--editeng/source/outliner/outlundo.hxx121
-rw-r--r--editeng/source/outliner/outlvw.cxx1481
-rw-r--r--editeng/source/outliner/overflowingtxt.cxx226
-rw-r--r--editeng/source/outliner/paralist.cxx256
-rw-r--r--editeng/source/outliner/paralist.hxx85
11 files changed, 5634 insertions, 0 deletions
diff --git a/editeng/source/outliner/outleeng.cxx b/editeng/source/outliner/outleeng.cxx
new file mode 100644
index 000000000..7358a7c6a
--- /dev/null
+++ b/editeng/source/outliner/outleeng.cxx
@@ -0,0 +1,192 @@
+/* -*- 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 <editeng/editeng.hxx>
+#include <editeng/eerdll.hxx>
+
+#include <editeng/outliner.hxx>
+#include "outleeng.hxx"
+#include "paralist.hxx"
+#include <editeng/editrids.hrc>
+#include <svl/itemset.hxx>
+#include <editeng/editstat.hxx>
+#include "outlundo.hxx"
+
+OutlinerEditEng::OutlinerEditEng( Outliner* pEngOwner, SfxItemPool* pPool )
+ : EditEngine( pPool )
+{
+ pOwner = pEngOwner;
+}
+
+OutlinerEditEng::~OutlinerEditEng()
+{
+}
+
+void OutlinerEditEng::PaintingFirstLine( sal_Int32 nPara, const Point& rStartPos, long /*nBaseLineY*/, const Point& rOrigin, short nOrientation, OutputDevice* pOutDev )
+{
+ if( GetControlWord() & EEControlBits::OUTLINER )
+ {
+ PaintFirstLineInfo aInfo( nPara, rStartPos, pOutDev );
+ pOwner->maPaintFirstLineHdl.Call( &aInfo );
+ }
+
+ pOwner->PaintBullet( nPara, rStartPos, rOrigin, nOrientation, pOutDev );
+}
+
+const SvxNumberFormat* OutlinerEditEng::GetNumberFormat( sal_Int32 nPara ) const
+{
+ const SvxNumberFormat* pFmt = nullptr;
+ if (pOwner)
+ pFmt = pOwner->GetNumberFormat( nPara );
+ return pFmt;
+}
+
+
+tools::Rectangle OutlinerEditEng::GetBulletArea( sal_Int32 nPara )
+{
+ tools::Rectangle aBulletArea { Point(), Point() };
+ if ( nPara < pOwner->pParaList->GetParagraphCount() )
+ {
+ if ( pOwner->ImplHasNumberFormat( nPara ) )
+ aBulletArea = pOwner->ImpCalcBulletArea( nPara, false, false );
+ }
+ return aBulletArea;
+}
+
+void OutlinerEditEng::ParagraphInserted( sal_Int32 nNewParagraph )
+{
+ pOwner->ParagraphInserted( nNewParagraph );
+
+ EditEngine::ParagraphInserted( nNewParagraph );
+}
+
+void OutlinerEditEng::ParagraphDeleted( sal_Int32 nDeletedParagraph )
+{
+ pOwner->ParagraphDeleted( nDeletedParagraph );
+
+ EditEngine::ParagraphDeleted( nDeletedParagraph );
+}
+
+void OutlinerEditEng::ParagraphConnected( sal_Int32 /*nLeftParagraph*/, sal_Int32 nRightParagraph )
+{
+ if( pOwner && pOwner->IsUndoEnabled() && !pOwner->GetEditEngine().IsInUndo() )
+ {
+ Paragraph* pPara = pOwner->GetParagraph( nRightParagraph );
+ if( pPara && Outliner::HasParaFlag( pPara, ParaFlag::ISPAGE ) )
+ {
+ pOwner->InsertUndo( std::make_unique<OutlinerUndoChangeParaFlags>( pOwner, nRightParagraph, ParaFlag::ISPAGE, ParaFlag::NONE ) );
+ }
+ }
+}
+
+
+void OutlinerEditEng::StyleSheetChanged( SfxStyleSheet* pStyle )
+{
+ pOwner->StyleSheetChanged( pStyle );
+}
+
+void OutlinerEditEng::ParaAttribsChanged( sal_Int32 nPara )
+{
+ pOwner->ParaAttribsChanged( nPara );
+}
+
+bool OutlinerEditEng::SpellNextDocument()
+{
+ return pOwner->SpellNextDocument();
+}
+
+bool OutlinerEditEng::ConvertNextDocument()
+{
+ return pOwner->ConvertNextDocument();
+}
+
+OUString OutlinerEditEng::GetUndoComment( sal_uInt16 nUndoId ) const
+{
+ switch( nUndoId )
+ {
+ case OLUNDO_DEPTH:
+ return EditResId(RID_OUTLUNDO_DEPTH);
+
+ case OLUNDO_EXPAND:
+ return EditResId(RID_OUTLUNDO_EXPAND);
+
+ case OLUNDO_COLLAPSE:
+ return EditResId(RID_OUTLUNDO_COLLAPSE);
+
+ case OLUNDO_ATTR:
+ return EditResId(RID_OUTLUNDO_ATTR);
+
+ case OLUNDO_INSERT:
+ return EditResId(RID_OUTLUNDO_INSERT);
+
+ default:
+ return EditEngine::GetUndoComment( nUndoId );
+ }
+}
+
+void OutlinerEditEng::DrawingText( const Point& rStartPos, const OUString& rText, sal_Int32 nTextStart, sal_Int32 nTextLen,
+ const long* pDXArray, const SvxFont& rFont, sal_Int32 nPara, sal_uInt8 nRightToLeft,
+ const EEngineData::WrongSpellVector* pWrongSpellVector,
+ const SvxFieldData* pFieldData,
+ bool bEndOfLine,
+ bool bEndOfParagraph,
+ const css::lang::Locale* pLocale,
+ const Color& rOverlineColor,
+ const Color& rTextLineColor)
+{
+ pOwner->DrawingText(rStartPos,rText,nTextStart,nTextLen,pDXArray,rFont,nPara,nRightToLeft,
+ pWrongSpellVector, pFieldData, bEndOfLine, bEndOfParagraph, false/*bEndOfBullet*/, pLocale, rOverlineColor, rTextLineColor);
+}
+
+void OutlinerEditEng::DrawingTab( const Point& rStartPos, long nWidth, const OUString& rChar,
+ const SvxFont& rFont, sal_Int32 nPara, sal_uInt8 nRightToLeft,
+ bool bEndOfLine, bool bEndOfParagraph,
+ const Color& rOverlineColor, const Color& rTextLineColor)
+{
+ pOwner->DrawingTab(rStartPos, nWidth, rChar, rFont, nPara, nRightToLeft,
+ bEndOfLine, bEndOfParagraph, rOverlineColor, rTextLineColor );
+}
+
+OUString OutlinerEditEng::CalcFieldValue( const SvxFieldItem& rField, sal_Int32 nPara, sal_Int32 nPos, std::optional<Color>& rpTxtColor, std::optional<Color>& rpFldColor )
+{
+ return pOwner->CalcFieldValue( rField, nPara, nPos, rpTxtColor, rpFldColor );
+}
+
+void OutlinerEditEng::SetParaAttribs( sal_Int32 nPara, const SfxItemSet& rSet )
+{
+ Paragraph* pPara = pOwner->pParaList->GetParagraph( nPara );
+ if( !pPara )
+ return;
+
+ if ( !IsInUndo() && IsUndoEnabled() )
+ pOwner->UndoActionStart( OLUNDO_ATTR );
+
+ EditEngine::SetParaAttribs( nPara, rSet );
+
+ pOwner->ImplCheckNumBulletItem( nPara );
+ // #i100014#
+ // It is not a good idea to subtract 1 from a count and cast the result
+ // to sal_uInt16 without check, if the count is 0.
+ pOwner->ImplCheckParagraphs( nPara, pOwner->pParaList->GetParagraphCount() );
+
+ if ( !IsInUndo() && IsUndoEnabled() )
+ pOwner->UndoActionEnd();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/outliner/outleeng.hxx b/editeng/source/outliner/outleeng.hxx
new file mode 100644
index 000000000..2d2119593
--- /dev/null
+++ b/editeng/source/outliner/outleeng.hxx
@@ -0,0 +1,87 @@
+/* -*- 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 .
+ */
+#ifndef INCLUDED_EDITENG_SOURCE_OUTLINER_OUTLEENG_HXX
+#define INCLUDED_EDITENG_SOURCE_OUTLINER_OUTLEENG_HXX
+
+#include <editeng/outliner.hxx>
+#include <editeng/editeng.hxx>
+
+typedef std::vector<EENotify> NotifyList;
+
+class OutlinerEditEng : public EditEngine
+{
+ Outliner* pOwner;
+
+protected:
+
+ // derived from EditEngine. Allows Outliner objects to provide
+ // bullet access to the EditEngine.
+ virtual const SvxNumberFormat* GetNumberFormat( sal_Int32 nPara ) const override;
+
+public:
+ OutlinerEditEng( Outliner* pOwner, SfxItemPool* pPool );
+ virtual ~OutlinerEditEng() override;
+
+ virtual void PaintingFirstLine( sal_Int32 nPara, const Point& rStartPos, long nBaseLineY, const Point& rOrigin, short nOrientation, OutputDevice* pOutDev ) override;
+
+ virtual void ParagraphInserted( sal_Int32 nNewParagraph ) override;
+ virtual void ParagraphDeleted( sal_Int32 nDeletedParagraph ) override;
+ virtual void ParagraphConnected( sal_Int32 nLeftParagraph, sal_Int32 nRightParagraph ) override;
+
+ virtual void DrawingText( const Point& rStartPos, const OUString& rText, sal_Int32 nTextStart,
+ sal_Int32 nTextLen, const long* pDXArray, const SvxFont& rFont,
+ sal_Int32 nPara, sal_uInt8 nRightToLeft,
+ const EEngineData::WrongSpellVector* pWrongSpellVector,
+ const SvxFieldData* pFieldData,
+ bool bEndOfLine,
+ bool bEndOfParagraph,
+ const css::lang::Locale* pLocale,
+ const Color& rOverlineColor,
+ const Color& rTextLineColor) override;
+
+ virtual void DrawingTab(
+ const Point& rStartPos, long nWidth, const OUString& rChar,
+ const SvxFont& rFont, sal_Int32 nPara, sal_uInt8 nRightToLeft,
+ bool bEndOfLine,
+ bool bEndOfParagraph,
+ const Color& rOverlineColor,
+ const Color& rTextLineColor) override;
+
+ virtual void StyleSheetChanged( SfxStyleSheet* pStyle ) override;
+ virtual void ParaAttribsChanged( sal_Int32 nPara ) override;
+ virtual bool SpellNextDocument() override;
+ virtual OUString GetUndoComment( sal_uInt16 nUndoId ) const override;
+
+ // for text conversion
+ virtual bool ConvertNextDocument() override;
+
+ virtual OUString CalcFieldValue( const SvxFieldItem& rField, sal_Int32 nPara, sal_Int32 nPos, std::optional<Color>& rTxtColor, std::optional<Color>& rFldColor ) override;
+
+ virtual tools::Rectangle GetBulletArea( sal_Int32 nPara ) override;
+
+ virtual void SetParaAttribs( sal_Int32 nPara, const SfxItemSet& rSet ) override;
+
+ // belongs into class Outliner, move there before incompatible update!
+ Link<EENotify&,void> aOutlinerNotifyHdl;
+ NotifyList aNotifyCache;
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/outliner/outlin2.cxx b/editeng/source/outliner/outlin2.cxx
new file mode 100644
index 000000000..ff3d1583a
--- /dev/null
+++ b/editeng/source/outliner/outlin2.cxx
@@ -0,0 +1,590 @@
+/* -*- 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 <editeng/editeng.hxx>
+#include <editeng/editview.hxx>
+#include <editeng/editdata.hxx>
+#include <editeng/editund2.hxx>
+
+#include <svl/style.hxx>
+#include <vcl/mapmod.hxx>
+
+#include <editeng/forbiddencharacterstable.hxx>
+
+#include <editeng/outliner.hxx>
+#include "paralist.hxx"
+#include "outleeng.hxx"
+#include <editeng/editstat.hxx>
+
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::linguistic2;
+
+
+// ====================== Simple pass-through =======================
+
+
+void Outliner::SetUpdateMode( bool bUpdate )
+{
+ pEditEngine->SetUpdateMode( bUpdate );
+}
+
+
+bool Outliner::GetUpdateMode() const
+{
+ return pEditEngine->GetUpdateMode();
+}
+
+const SfxItemSet& Outliner::GetEmptyItemSet() const
+{
+ return pEditEngine->GetEmptyItemSet();
+}
+
+void Outliner::EnableUndo( bool bEnable )
+{
+ pEditEngine->EnableUndo( bEnable );
+}
+
+bool Outliner::IsUndoEnabled() const
+{
+ return pEditEngine->IsUndoEnabled();
+}
+
+MapMode const & Outliner::GetRefMapMode() const
+{
+ return pEditEngine->GetRefMapMode();
+}
+
+void Outliner::SetRefMapMode( const MapMode& rMMode )
+{
+ pEditEngine->SetRefMapMode( rMMode );
+}
+
+void Outliner::SetBackgroundColor( const Color& rColor )
+{
+ pEditEngine->SetBackgroundColor( rColor );
+}
+
+Color const & Outliner::GetBackgroundColor() const
+{
+ return pEditEngine->GetBackgroundColor();
+}
+
+
+void Outliner::ClearModifyFlag()
+{
+ pEditEngine->ClearModifyFlag();
+}
+
+bool Outliner::IsModified() const
+{
+ return pEditEngine->IsModified();
+}
+
+sal_uLong Outliner::GetTextHeight() const
+{
+ return pEditEngine->GetTextHeight();
+}
+
+void Outliner::SetModifyHdl( const Link<LinkParamNone*,void>& rLink )
+{
+ pEditEngine->SetModifyHdl( rLink );
+}
+
+Link<LinkParamNone*,void> const & Outliner::GetModifyHdl() const
+{
+ return pEditEngine->GetModifyHdl();
+}
+
+void Outliner::SetNotifyHdl( const Link<EENotify&,void>& rLink )
+{
+ pEditEngine->aOutlinerNotifyHdl = rLink;
+
+ if ( rLink.IsSet() )
+ pEditEngine->SetNotifyHdl( LINK( this, Outliner, EditEngineNotifyHdl ) );
+ else
+ pEditEngine->SetNotifyHdl( Link<EENotify&,void>() );
+}
+
+void Outliner::SetStatusEventHdl( const Link<EditStatus&, void>& rLink )
+{
+ pEditEngine->SetStatusEventHdl( rLink );
+}
+
+Link<EditStatus&, void> const & Outliner::GetStatusEventHdl() const
+{
+ return pEditEngine->GetStatusEventHdl();
+}
+
+void Outliner::SetDefTab( sal_uInt16 nTab )
+{
+ pEditEngine->SetDefTab( nTab );
+}
+
+bool Outliner::IsFlatMode() const
+{
+ return pEditEngine->IsFlatMode();
+}
+
+bool Outliner::UpdateFields()
+{
+ return pEditEngine->UpdateFields();
+}
+
+void Outliner::RemoveFields( const std::function<bool ( const SvxFieldData* )>& isFieldData )
+{
+ pEditEngine->RemoveFields( isFieldData );
+}
+
+void Outliner::SetWordDelimiters( const OUString& rDelimiters )
+{
+ pEditEngine->SetWordDelimiters( rDelimiters );
+}
+
+OUString const & Outliner::GetWordDelimiters() const
+{
+ return pEditEngine->GetWordDelimiters();
+}
+
+OUString Outliner::GetWord( sal_Int32 nPara, sal_Int32 nIndex )
+{
+ return pEditEngine->GetWord( nPara, nIndex );
+}
+
+void Outliner::Draw( OutputDevice* pOutDev, const tools::Rectangle& rOutRect )
+{
+ pEditEngine->Draw( pOutDev, rOutRect );
+}
+
+void Outliner::Draw( OutputDevice* pOutDev, const Point& rStartPos )
+{
+ pEditEngine->Draw( pOutDev, rStartPos );
+}
+
+void Outliner::SetPaperSize( const Size& rSize )
+{
+ pEditEngine->SetPaperSize( rSize );
+}
+
+const Size& Outliner::GetPaperSize() const
+{
+ return pEditEngine->GetPaperSize();
+}
+
+void Outliner::SetPolygon( const basegfx::B2DPolyPolygon& rPolyPolygon )
+{
+ pEditEngine->SetPolygon( rPolyPolygon );
+}
+
+void Outliner::SetPolygon( const basegfx::B2DPolyPolygon& rPolyPolygon, const basegfx::B2DPolyPolygon* pLinePolyPolygon)
+{
+ pEditEngine->SetPolygon( rPolyPolygon, pLinePolyPolygon);
+}
+
+void Outliner::ClearPolygon()
+{
+ pEditEngine->ClearPolygon();
+}
+
+const Size& Outliner::GetMinAutoPaperSize() const
+{
+ return pEditEngine->GetMinAutoPaperSize();
+}
+
+void Outliner::SetMinAutoPaperSize( const Size& rSz )
+{
+ pEditEngine->SetMinAutoPaperSize( rSz );
+}
+
+const Size& Outliner::GetMaxAutoPaperSize() const
+{
+ return pEditEngine->GetMaxAutoPaperSize();
+}
+
+void Outliner::SetMaxAutoPaperSize( const Size& rSz )
+{
+ pEditEngine->SetMaxAutoPaperSize( rSz );
+}
+
+bool Outliner::IsExpanded( Paragraph const * pPara ) const
+{
+ return pParaList->HasVisibleChildren( pPara );
+}
+
+Paragraph* Outliner::GetParent( Paragraph const * pParagraph ) const
+{
+ return pParaList->GetParent( pParagraph );
+}
+
+sal_Int32 Outliner::GetChildCount( Paragraph const * pParent ) const
+{
+ return pParaList->GetChildCount( pParent );
+}
+
+Size Outliner::CalcTextSize()
+{
+ return Size(pEditEngine->CalcTextWidth(),pEditEngine->GetTextHeight());
+}
+
+Size Outliner::CalcTextSizeNTP()
+{
+ return Size(pEditEngine->CalcTextWidth(),pEditEngine->GetTextHeightNTP());
+}
+
+void Outliner::SetStyleSheetPool( SfxStyleSheetPool* pSPool )
+{
+ pEditEngine->SetStyleSheetPool( pSPool );
+}
+
+SfxStyleSheetPool* Outliner::GetStyleSheetPool()
+{
+ return pEditEngine->GetStyleSheetPool();
+}
+
+SfxStyleSheet* Outliner::GetStyleSheet( sal_Int32 nPara )
+{
+ return pEditEngine->GetStyleSheet( nPara );
+}
+
+bool Outliner::IsInSelectionMode() const
+{
+ return pEditEngine->IsInSelectionMode();
+}
+
+void Outliner::SetControlWord( EEControlBits nWord )
+{
+ pEditEngine->SetControlWord( nWord );
+}
+
+EEControlBits Outliner::GetControlWord() const
+{
+ return pEditEngine->GetControlWord();
+}
+
+void Outliner::SetAsianCompressionMode( CharCompressType n )
+{
+ pEditEngine->SetAsianCompressionMode( n );
+}
+
+void Outliner::SetKernAsianPunctuation( bool b )
+{
+ pEditEngine->SetKernAsianPunctuation( b );
+}
+
+void Outliner::SetAddExtLeading( bool bExtLeading )
+{
+ pEditEngine->SetAddExtLeading( bExtLeading );
+}
+
+void Outliner::UndoActionStart( sal_uInt16 nId )
+{
+ pEditEngine->UndoActionStart( nId );
+}
+
+void Outliner::UndoActionEnd()
+{
+ pEditEngine->UndoActionEnd();
+}
+
+void Outliner::InsertUndo( std::unique_ptr<EditUndo> pUndo )
+{
+ pEditEngine->GetUndoManager().AddUndoAction( std::move(pUndo) );
+}
+
+bool Outliner::IsInUndo() const
+{
+ return pEditEngine->IsInUndo();
+}
+
+sal_uLong Outliner::GetLineCount( sal_Int32 nParagraph ) const
+{
+ return pEditEngine->GetLineCount( nParagraph );
+}
+
+sal_Int32 Outliner::GetLineLen( sal_Int32 nParagraph, sal_Int32 nLine ) const
+{
+ return pEditEngine->GetLineLen( nParagraph, nLine );
+}
+
+sal_uLong Outliner::GetLineHeight( sal_Int32 nParagraph )
+{
+ return pEditEngine->GetLineHeight( nParagraph );
+}
+
+void Outliner::RemoveCharAttribs( sal_Int32 nPara, sal_uInt16 nWhich )
+{
+ pEditEngine->RemoveCharAttribs( nPara, nWhich );
+}
+
+EESpellState Outliner::HasSpellErrors()
+{
+ return pEditEngine->HasSpellErrors();
+}
+
+bool Outliner::HasConvertibleTextPortion( LanguageType nLang )
+{
+ return pEditEngine->HasConvertibleTextPortion( nLang );
+}
+
+bool Outliner::ConvertNextDocument()
+{
+ return false;
+}
+
+void Outliner::SetDefaultLanguage( LanguageType eLang )
+{
+ pEditEngine->SetDefaultLanguage( eLang );
+}
+
+void Outliner::CompleteOnlineSpelling()
+{
+ pEditEngine->CompleteOnlineSpelling();
+}
+
+bool Outliner::HasText( const SvxSearchItem& rSearchItem )
+{
+ return pEditEngine->HasText( rSearchItem );
+}
+
+void Outliner::SetEditTextObjectPool( SfxItemPool* pPool )
+{
+ pEditEngine->SetEditTextObjectPool( pPool );
+}
+
+SfxItemPool* Outliner::GetEditTextObjectPool() const
+{
+ return pEditEngine->GetEditTextObjectPool();
+}
+
+bool Outliner::SpellNextDocument()
+{
+ return false;
+}
+
+
+void Outliner::SetSpeller( Reference< XSpellChecker1 > const &xSpeller )
+{
+ pEditEngine->SetSpeller( xSpeller );
+}
+
+Reference< XSpellChecker1 > const & Outliner::GetSpeller()
+{
+ return pEditEngine->GetSpeller();
+}
+
+void Outliner::SetForbiddenCharsTable(const std::shared_ptr<SvxForbiddenCharactersTable>& xForbiddenChars)
+{
+ EditEngine::SetForbiddenCharsTable(xForbiddenChars);
+}
+
+void Outliner::SetHyphenator( Reference< XHyphenator > const & xHyph )
+{
+ pEditEngine->SetHyphenator( xHyph );
+}
+
+OutputDevice* Outliner::GetRefDevice() const
+{
+ return pEditEngine->GetRefDevice();
+}
+
+tools::Rectangle Outliner::GetParaBounds( sal_Int32 nParagraph ) const
+{
+ return pEditEngine->GetParaBounds(nParagraph );
+}
+
+Point Outliner::GetDocPos( const Point& rPaperPos ) const
+{
+ return pEditEngine->GetDocPos( rPaperPos );
+}
+
+bool Outliner::IsTextPos( const Point& rPaperPos, sal_uInt16 nBorder )
+{
+ return IsTextPos( rPaperPos, nBorder, nullptr );
+}
+
+bool Outliner::IsTextPos( const Point& rPaperPos, sal_uInt16 nBorder, bool* pbBullet )
+{
+ if ( pbBullet)
+ *pbBullet = false;
+ bool bTextPos = pEditEngine->IsTextPos( rPaperPos, nBorder );
+ if ( !bTextPos )
+ {
+ Point aDocPos = GetDocPos( rPaperPos );
+ sal_Int32 nPara = pEditEngine->FindParagraph( aDocPos.Y() );
+ if ( ( nPara != EE_PARA_NOT_FOUND ) && ImplHasNumberFormat( nPara ) )
+ {
+ tools::Rectangle aBulArea = ImpCalcBulletArea( nPara, true, true );
+ if ( aBulArea.IsInside( rPaperPos ) )
+ {
+ bTextPos = true;
+ if ( pbBullet)
+ *pbBullet = true;
+ }
+ }
+ }
+
+ return bTextPos;
+}
+
+void Outliner::QuickSetAttribs( const SfxItemSet& rSet, const ESelection& rSel )
+{
+ pEditEngine->QuickSetAttribs( rSet, rSel );
+}
+
+void Outliner::QuickInsertText( const OUString& rText, const ESelection& rSel )
+{
+ bFirstParaIsEmpty = false;
+ pEditEngine->QuickInsertText( rText, rSel );
+}
+
+void Outliner::QuickDelete( const ESelection& rSel )
+{
+ bFirstParaIsEmpty = false;
+ pEditEngine->QuickDelete( rSel );
+}
+
+void Outliner::QuickInsertField( const SvxFieldItem& rFld, const ESelection& rSel )
+{
+ bFirstParaIsEmpty = false;
+ pEditEngine->QuickInsertField( rFld, rSel );
+}
+
+void Outliner::QuickInsertLineBreak( const ESelection& rSel )
+{
+ bFirstParaIsEmpty = false;
+ pEditEngine->QuickInsertLineBreak( rSel );
+}
+
+void Outliner::QuickFormatDoc()
+{
+ pEditEngine->QuickFormatDoc();
+}
+
+void Outliner::SetGlobalCharStretching( sal_uInt16 nX, sal_uInt16 nY )
+{
+
+ // reset bullet size
+ sal_Int32 nParagraphs = pParaList->GetParagraphCount();
+ for ( sal_Int32 nPara = 0; nPara < nParagraphs; nPara++ )
+ {
+ Paragraph* pPara = pParaList->GetParagraph( nPara );
+ if ( pPara )
+ pPara->aBulSize.setWidth( -1 );
+ }
+
+ pEditEngine->SetGlobalCharStretching( nX, nY );
+}
+
+void Outliner::GetGlobalCharStretching( sal_uInt16& rX, sal_uInt16& rY ) const
+{
+ pEditEngine->GetGlobalCharStretching( rX, rY );
+}
+
+void Outliner::EraseVirtualDevice()
+{
+ pEditEngine->EraseVirtualDevice();
+}
+
+bool Outliner::ShouldCreateBigTextObject() const
+{
+ return pEditEngine->ShouldCreateBigTextObject();
+}
+
+const EditEngine& Outliner::GetEditEngine() const
+{
+ return *pEditEngine;
+}
+
+void Outliner::SetVertical(bool bVertical)
+{
+ pEditEngine->SetVertical(bVertical);
+}
+
+void Outliner::SetRotation(TextRotation nRotation)
+{
+ pEditEngine->SetRotation(nRotation);
+}
+
+bool Outliner::IsVertical() const
+{
+ return pEditEngine->IsVertical();
+}
+
+bool Outliner::IsTopToBottom() const
+{
+ return pEditEngine->IsTopToBottom();
+}
+
+void Outliner::SetFixedCellHeight( bool bUseFixedCellHeight )
+{
+ pEditEngine->SetFixedCellHeight( bUseFixedCellHeight );
+}
+
+void Outliner::SetDefaultHorizontalTextDirection( EEHorizontalTextDirection eHTextDir )
+{
+ pEditEngine->SetDefaultHorizontalTextDirection( eHTextDir );
+}
+
+EEHorizontalTextDirection Outliner::GetDefaultHorizontalTextDirection() const
+{
+ return pEditEngine->GetDefaultHorizontalTextDirection();
+}
+
+LanguageType Outliner::GetLanguage( sal_Int32 nPara, sal_Int32 nPos ) const
+{
+ return pEditEngine->GetLanguage( nPara, nPos );
+}
+
+void Outliner::RemoveAttribs( const ESelection& rSelection, bool bRemoveParaAttribs, sal_uInt16 nWhich )
+{
+ pEditEngine->RemoveAttribs( rSelection, bRemoveParaAttribs, nWhich );
+}
+
+void Outliner::EnableAutoColor( bool b )
+{
+ pEditEngine->EnableAutoColor( b );
+}
+
+void Outliner::ForceAutoColor( bool b )
+{
+ pEditEngine->ForceAutoColor( b );
+}
+
+bool Outliner::IsForceAutoColor() const
+{
+ return pEditEngine->IsForceAutoColor();
+}
+
+bool Outliner::SpellSentence(EditView const & rEditView, svx::SpellPortions& rToFill )
+{
+ return pEditEngine->SpellSentence(rEditView, rToFill );
+}
+
+void Outliner::PutSpellingToSentenceStart( EditView const & rEditView )
+{
+ pEditEngine->PutSpellingToSentenceStart( rEditView );
+}
+
+void Outliner::ApplyChangedSentence(EditView const & rEditView, const svx::SpellPortions& rNewPortions, bool bRecheck )
+{
+ pEditEngine->ApplyChangedSentence( rEditView, rNewPortions, bRecheck );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/outliner/outliner.cxx b/editeng/source/outliner/outliner.cxx
new file mode 100644
index 000000000..8cf7132c5
--- /dev/null
+++ b/editeng/source/outliner/outliner.cxx
@@ -0,0 +1,2184 @@
+/* -*- 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/window.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 <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->GetUpdateMode();
+ pEditEngine->SetUpdateMode( 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->SetUpdateMode( 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 ( ImplGetOutlinerMode() )
+ {
+ 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 ( ImplGetOutlinerMode() == OutlinerMode::OutlineObject )
+ ImplSetLevelDependentStyleSheet( nPara );
+
+ DepthChangedHdl(pPara, nPrevFlags);
+}
+
+sal_Int16 Outliner::GetNumberingStartValue( sal_Int32 nPara )
+{
+ 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 )
+{
+ 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::unique_ptr<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 nullptr;
+
+ std::unique_ptr<EditTextObject> pText = pEditEngine->CreateTextObject( nStartPara, nCount );
+ const bool bIsEditDoc(OutlinerMode::TextObject == ImplGetOutlinerMode());
+ ParagraphDataVector aParagraphDataVector(nCount);
+ const sal_Int32 nLastPara(nStartPara + nCount - 1);
+
+ for(sal_Int32 nPara(nStartPara); nPara <= nLastPara; nPara++)
+ {
+ aParagraphDataVector[nPara-nStartPara] = *GetParagraph(nPara);
+ }
+
+ std::unique_ptr<OutlinerParaObject> pPObj(new OutlinerParaObject(*pText, aParagraphDataVector, bIsEditDoc));
+ pPObj->SetOutlinerMode(GetMode());
+
+ return pPObj;
+}
+
+void Outliner::SetToEmptyText()
+{
+ std::unique_ptr<OutlinerParaObject> pEmptyTxt = GetEmptyParaObject();
+ SetText(*pEmptyTxt);
+}
+
+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->GetUpdateMode();
+ pEditEngine->SetUpdateMode( 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() )
+ {
+ OUString aStr = aText.getToken( 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( ( ImplGetOutlinerMode() == OutlinerMode::OutlineObject ) ||
+ ( ImplGetOutlinerMode() == OutlinerMode::OutlineView ) )
+ {
+ // Extract Tabs
+ sal_Int32 nTabs = 0;
+ while ( ( nTabs < aStr.getLength() ) && ( aStr[nTabs] == '\t' ) )
+ nTabs++;
+ if ( nTabs )
+ aStr = aStr.copy(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, aStr );
+ ParagraphInsertedHdl(pPara);
+ }
+ else
+ {
+ nInsPos--;
+ pEditEngine->SetText( nInsPos, aStr );
+ }
+ ImplInitDepth( nInsPos, nCurDepth, false );
+ nInsPos++;
+ nPos++;
+ }
+ }
+
+ DBG_ASSERT(pParaList->GetParagraphCount()==pEditEngine->GetParagraphCount(),"SetText failed!");
+ bFirstParaIsEmpty = false;
+ ImplBlockInsertionCallbacks( false );
+ // Restore the update mode.
+ pEditEngine->SetUpdateMode(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 aHeading_US( "heading" );
+ OUString aNumber_US( "Numbering" );
+
+ 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 )
+ {
+ 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;
+ OUString aLevel = comphelper::string::stripStart(aName.copy(nPos), ' ');
+ nTabs = aLevel.toInt32();
+ 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->GetUpdateMode();
+ pEditEngine->SetUpdateMode( 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->SetUpdateMode( 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->GetUpdateMode();
+ pEditEngine->SetUpdateMode( 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->SetUpdateMode( 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( ( ImplGetOutlinerMode() == OutlinerMode::OutlineObject ) || ( ImplGetOutlinerMode() == 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.copy( 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->GetUpdateMode();
+ pEditEngine->SetUpdateMode( 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->SetUpdateMode( 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 ) )
+ {
+ 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;
+ }
+ return false;
+}
+
+bool Outliner::Collapse( Paragraph const * pPara )
+{
+ if ( pParaList->HasVisibleChildren( pPara ) ) // expanded
+ {
+ 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;
+ }
+ return false;
+}
+
+
+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;
+ const vcl::Font *pSourceFont = nullptr;
+ 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;
+ sal_uLong 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( 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, short nOrientation, OutputDevice* pOutDev )
+{
+
+ 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 = pOutDev->GetFont();
+ pOutDev->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 = nOrientation*F_PI1800;
+ double nCos = cos( nRealOrientation );
+ double nSin = sin( nRealOrientation );
+ Point aRotatedPos;
+ // Translation...
+ aTextPos -= rOrigin;
+ // Rotation...
+ aRotatedPos.setX(static_cast<long>(nCos*aTextPos.X() + nSin*aTextPos.Y()) );
+ aRotatedPos.setY(static_cast<long>(- (nSin*aTextPos.X() - nCos*aTextPos.Y())) );
+ aTextPos = aRotatedPos;
+ // Translation...
+ aTextPos += rOrigin;
+ vcl::Font aRotatedFont( aBulletFont );
+ aRotatedFont.SetOrientation( nOrientation );
+ pOutDev->SetFont( aRotatedFont );
+ }
+
+ // VCL will take care of brackets and so on...
+ ComplexTextLayoutFlags nLayoutMode = pOutDev->GetLayoutMode();
+ nLayoutMode &= ~ComplexTextLayoutFlags(ComplexTextLayoutFlags::BiDiRtl|ComplexTextLayoutFlags::BiDiStrong);
+ if ( bRightToLeftPara )
+ nLayoutMode |= ComplexTextLayoutFlags::BiDiRtl | ComplexTextLayoutFlags::TextOriginLeft | ComplexTextLayoutFlags::BiDiStrong;
+ pOutDev->SetLayoutMode( nLayoutMode );
+
+ if(bStrippingPortions)
+ {
+ const vcl::Font& aSvxFont(pOutDev->GetFont());
+ std::unique_ptr<long[]> pBuf(new long[ pPara->GetText().getLength() ]);
+ pOutDev->GetTextArray( pPara->GetText(), pBuf.get() );
+
+ if(bSymbol)
+ {
+ // aTextPos is Bottom, go to Baseline
+ FontMetric aMetric(pOutDev->GetFontMetric());
+ aTextPos.AdjustY( -(aMetric.GetDescent()) );
+ }
+
+ DrawingText(aTextPos, pPara->GetText(), 0, pPara->GetText().getLength(), pBuf.get(),
+ aSvxFont, nPara, bRightToLeftPara ? 1 : 0, nullptr, nullptr, false, false, true, nullptr, Color(), Color());
+ }
+ else
+ {
+ pOutDev->DrawText( aTextPos, pPara->GetText() );
+ }
+
+ pOutDev->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
+ {
+ // Remove CAST when KA made the Draw-Method const
+ const_cast<GraphicObject*>(pFmt->GetBrush()->GetGraphicObject())->Draw( pOutDev, aBulletPos, pPara->aBulSize );
+ }
+ }
+ }
+ }
+
+ // In case of collapsed subparagraphs paint a line before the text.
+ if( !(pParaList->HasChildren(pPara) && !pParaList->HasVisibleChildren(pPara) &&
+ !bStrippingPortions && !nOrientation) )
+ return;
+
+ long nWidth = pOutDev->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 = pOutDev->GetLineColor();
+ pOutDev->SetLineColor( COL_BLACK );
+ pOutDev->DrawLine( aStartPos, aEndPos );
+ pOutDev->SetLineColor( rOldLineColor );
+}
+
+void Outliner::InvalidateBullet(sal_Int32 nPara)
+{
+ long nLineHeight = static_cast<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->GetWindow()->Invalidate( aRect );
+ }
+}
+
+ErrCode Outliner::Read( SvStream& rInput, const OUString& rBaseURL, EETextFormat eFormat, SvKeyValueIterator* pHTTPHeaderAttrs )
+{
+
+ bool bOldUndo = pEditEngine->IsUndoEnabled();
+ EnableUndo( false );
+
+ bool bUpdate = pEditEngine->GetUpdateMode();
+ pEditEngine->SetUpdateMode( 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->SetUpdateMode( bUpdate );
+ EnableUndo( bOldUndo );
+
+ return nRet;
+}
+
+
+void Outliner::ImpFilterIndents( sal_Int32 nFirstPara, sal_Int32 nLastPara )
+{
+
+ bool bUpdate = pEditEngine->GetUpdateMode();
+ pEditEngine->SetUpdateMode( 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->SetUpdateMode( 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->GetUpdateMode();
+ pEditEngine->SetUpdateMode( false );
+
+ const sal_Int32 nStart = nStartPara;
+
+ Paragraph* pPara = pParaList->GetParagraph( nStartPara );
+
+ while( nCount && pPara )
+ {
+ if( ImplGetOutlinerMode() != 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->SetUpdateMode( 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 ) && ( ImplGetOutlinerMode() != 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 )
+{
+ 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 );
+
+ long nBulletWidth = std::max( static_cast<long>(-rLR.GetTextFirstLineOffset()), static_cast<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, const long* 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, 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, 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 )
+{
+ 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 )
+ {
+ aBulletText += OUStringChar(pFmt->GetBulletChar());
+ }
+ 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::unique_ptr<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 nullptr;
+
+ // 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 nullptr;
+ }
+
+ if (nCount < 0)
+ {
+ SAL_INFO("editeng.chaining",
+ "[Overflowing] No Overflowing text but GetNonOverflowinText called?!");
+ return nullptr;
+ }
+
+ // 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 std::make_unique<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 std::make_unique<NonOverflowingText>(aOverflowingTextSelection, bLastParaInterrupted);
+ }
+}
+
+std::unique_ptr<OutlinerParaObject> Outliner::GetEmptyParaObject() const
+{
+ std::unique_ptr<EditTextObject> pEmptyText = pEditEngine->GetEmptyTextObject();
+ std::unique_ptr<OutlinerParaObject> pPObj( new OutlinerParaObject( std::move(pEmptyText) ));
+ pPObj->SetOutlinerMode(GetMode());
+ return pPObj;
+}
+
+std::unique_ptr<OverflowingText> Outliner::GetOverflowingText() const
+{
+ if ( pEditEngine->GetOverflowingParaNum() < 0)
+ return nullptr;
+
+
+ // 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 nullptr;
+ }
+
+ 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 std::make_unique<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);
+ xmlTextWriterSetIndentString(pWriter, BAD_CAST(" "));
+ xmlTextWriterStartDocument(pWriter, nullptr, nullptr, nullptr);
+ bOwns = true;
+ }
+
+ xmlTextWriterStartElement(pWriter, BAD_CAST("Outliner"));
+ pParaList->dumpAsXml(pWriter);
+ xmlTextWriterEndElement(pWriter);
+
+ if (bOwns)
+ {
+ xmlTextWriterEndDocument(pWriter);
+ xmlFreeTextWriter(pWriter);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/outliner/outlobj.cxx b/editeng/source/outliner/outlobj.cxx
new file mode 100644
index 000000000..48c7aa02a
--- /dev/null
+++ b/editeng/source/outliner/outlobj.cxx
@@ -0,0 +1,248 @@
+/* -*- 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 <editeng/editdata.hxx>
+
+#include <editeng/outliner.hxx>
+#include <editeng/outlobj.hxx>
+#include <editeng/editobj.hxx>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+
+#include <o3tl/cow_wrapper.hxx>
+#include <o3tl/safeint.hxx>
+#include <libxml/xmlwriter.h>
+
+OutlinerParaObjData::OutlinerParaObjData( std::unique_ptr<EditTextObject> pEditTextObject, const ParagraphDataVector& rParagraphDataVector, bool bIsEditDoc ) :
+ mpEditTextObject(std::move(pEditTextObject)),
+ maParagraphDataVector(rParagraphDataVector),
+ mbIsEditDoc(bIsEditDoc)
+{
+ if( maParagraphDataVector.empty() && (mpEditTextObject->GetParagraphCount() != 0) )
+ maParagraphDataVector.resize(mpEditTextObject->GetParagraphCount());
+}
+
+OutlinerParaObjData::OutlinerParaObjData( const OutlinerParaObjData& r ):
+ mpEditTextObject(r.mpEditTextObject->Clone()),
+ maParagraphDataVector(r.maParagraphDataVector),
+ mbIsEditDoc(r.mbIsEditDoc)
+{
+}
+
+OutlinerParaObjData::~OutlinerParaObjData()
+{
+}
+
+bool OutlinerParaObjData::operator==(const OutlinerParaObjData& rCandidate) const
+{
+ return (*mpEditTextObject == *rCandidate.mpEditTextObject
+ && maParagraphDataVector == rCandidate.maParagraphDataVector
+ && mbIsEditDoc == rCandidate.mbIsEditDoc);
+}
+
+bool OutlinerParaObjData::isWrongListEqual(const OutlinerParaObjData& rCompare) const
+{
+ return mpEditTextObject->isWrongListEqual(*rCompare.mpEditTextObject);
+}
+
+OutlinerParaObject::OutlinerParaObject(
+ const EditTextObject& rTextObj, const ParagraphDataVector& rParagraphDataVector, bool bIsEditDoc ) :
+ mpImpl(OutlinerParaObjData(rTextObj.Clone(), rParagraphDataVector, bIsEditDoc))
+{
+}
+
+OutlinerParaObject::OutlinerParaObject( const EditTextObject& rTextObj ) :
+ mpImpl(OutlinerParaObjData(rTextObj.Clone(), ParagraphDataVector(), true))
+{
+}
+
+OutlinerParaObject::OutlinerParaObject( std::unique_ptr<EditTextObject> pTextObj ) :
+ mpImpl(OutlinerParaObjData(std::move(pTextObj), ParagraphDataVector(), true))
+{
+}
+
+OutlinerParaObject::OutlinerParaObject( const OutlinerParaObject& r ) :
+ mpImpl(r.mpImpl)
+{
+}
+
+OutlinerParaObject::~OutlinerParaObject()
+{
+}
+
+OutlinerParaObject& OutlinerParaObject::operator=( const OutlinerParaObject& r )
+{
+ mpImpl = r.mpImpl;
+ return *this;
+}
+
+bool OutlinerParaObject::operator==( const OutlinerParaObject& r ) const
+{
+ return r.mpImpl == mpImpl;
+}
+
+// #i102062#
+bool OutlinerParaObject::isWrongListEqual( const OutlinerParaObject& r ) const
+{
+ if (r.mpImpl.same_object(mpImpl))
+ {
+ return true;
+ }
+
+ return mpImpl->isWrongListEqual(*r.mpImpl);
+}
+
+OutlinerMode OutlinerParaObject::GetOutlinerMode() const
+{
+ return mpImpl->mpEditTextObject->GetUserType();
+}
+
+void OutlinerParaObject::SetOutlinerMode(OutlinerMode nNew)
+{
+ // create a const pointer to avoid an early call to
+ // make_unique() in the dereference of mpImpl
+ const ::o3tl::cow_wrapper< OutlinerParaObjData >* pImpl = &mpImpl;
+ if ( ( *pImpl )->mpEditTextObject->GetUserType() != nNew )
+ {
+ mpImpl->mpEditTextObject->SetUserType(nNew);
+ }
+}
+
+bool OutlinerParaObject::IsVertical() const
+{
+ return mpImpl->mpEditTextObject->IsVertical();
+}
+
+bool OutlinerParaObject::GetDirectVertical() const
+{
+ return mpImpl->mpEditTextObject->GetDirectVertical();
+}
+
+bool OutlinerParaObject::IsTopToBottom() const
+{
+ return mpImpl->mpEditTextObject->IsTopToBottom();
+}
+
+void OutlinerParaObject::SetVertical(bool bNew)
+{
+ const ::o3tl::cow_wrapper< OutlinerParaObjData >* pImpl = &mpImpl;
+ if ( ( *pImpl )->mpEditTextObject->IsVertical() != bNew)
+ {
+ mpImpl->mpEditTextObject->SetVertical(bNew);
+ }
+}
+void OutlinerParaObject::SetRotation(TextRotation nRotation)
+{
+ mpImpl->mpEditTextObject->SetRotation(nRotation);
+}
+
+TextRotation OutlinerParaObject::GetRotation() const
+{
+ return mpImpl->mpEditTextObject->GetRotation();
+}
+
+sal_Int32 OutlinerParaObject::Count() const
+{
+ size_t nSize = mpImpl->maParagraphDataVector.size();
+ if (nSize > EE_PARA_MAX_COUNT)
+ {
+ SAL_WARN( "editeng", "OutlinerParaObject::Count - overflow " << nSize);
+ return EE_PARA_MAX_COUNT;
+ }
+ return static_cast<sal_Int32>(nSize);
+}
+
+sal_Int16 OutlinerParaObject::GetDepth(sal_Int32 nPara) const
+{
+ if(0 <= nPara && o3tl::make_unsigned(nPara) < mpImpl->maParagraphDataVector.size())
+ {
+ return mpImpl->maParagraphDataVector[nPara].getDepth();
+ }
+ else
+ {
+ return -1;
+ }
+}
+
+const EditTextObject& OutlinerParaObject::GetTextObject() const
+{
+ return *mpImpl->mpEditTextObject;
+}
+
+const ParagraphData& OutlinerParaObject::GetParagraphData(sal_Int32 nIndex) const
+{
+ if(0 <= nIndex && o3tl::make_unsigned(nIndex) < mpImpl->maParagraphDataVector.size())
+ {
+ return mpImpl->maParagraphDataVector[nIndex];
+ }
+ else
+ {
+ OSL_FAIL("OutlinerParaObject::GetParagraphData: Access out of range (!)");
+ static ParagraphData aEmptyParagraphData;
+ return aEmptyParagraphData;
+ }
+}
+
+void OutlinerParaObject::ClearPortionInfo()
+{
+ mpImpl->mpEditTextObject->ClearPortionInfo();
+}
+
+bool OutlinerParaObject::ChangeStyleSheets(const OUString& rOldName,
+ SfxStyleFamily eOldFamily, const OUString& rNewName, SfxStyleFamily eNewFamily)
+{
+ return mpImpl->mpEditTextObject->ChangeStyleSheets(rOldName, eOldFamily, rNewName, eNewFamily);
+}
+
+void OutlinerParaObject::ChangeStyleSheetName(SfxStyleFamily eFamily,
+ const OUString& rOldName, const OUString& rNewName)
+{
+ mpImpl->mpEditTextObject->ChangeStyleSheetName(eFamily, rOldName, rNewName);
+}
+
+void OutlinerParaObject::SetStyleSheets(sal_uInt16 nLevel, const OUString& rNewName,
+ const SfxStyleFamily& rNewFamily)
+{
+ const sal_Int32 nCount(Count());
+
+ if(nCount)
+ {
+ sal_Int32 nDecrementer(nCount);
+
+ while(nDecrementer > 0)
+ {
+ if(GetDepth(--nDecrementer) == nLevel)
+ {
+ mpImpl->mpEditTextObject->SetStyleSheet(nDecrementer, rNewName, rNewFamily);
+ }
+ }
+ }
+}
+
+void OutlinerParaObject::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ xmlTextWriterStartElement(pWriter, BAD_CAST("OutlinerParaObject"));
+ xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
+ mpImpl->mpEditTextObject->dumpAsXml(pWriter);
+ for (ParagraphData const & p : mpImpl->maParagraphDataVector)
+ Paragraph(p).dumpAsXml(pWriter);
+ xmlTextWriterEndElement(pWriter);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/outliner/outlundo.cxx b/editeng/source/outliner/outlundo.cxx
new file mode 100644
index 000000000..c2db1a77f
--- /dev/null
+++ b/editeng/source/outliner/outlundo.cxx
@@ -0,0 +1,164 @@
+/* -*- 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 <editeng/outliner.hxx>
+#include <tools/debug.hxx>
+#include "outlundo.hxx"
+
+
+OutlinerUndoBase::OutlinerUndoBase( sal_uInt16 _nId, Outliner* pOutliner )
+ : EditUndo( _nId, nullptr )
+{
+ DBG_ASSERT( pOutliner, "Undo: Outliner?!" );
+ mpOutliner = pOutliner;
+}
+
+OutlinerUndoChangeParaFlags::OutlinerUndoChangeParaFlags( Outliner* pOutliner, sal_Int32 nPara, ParaFlag nOldFlags, ParaFlag nNewFlags )
+: OutlinerUndoBase( OLUNDO_DEPTH, pOutliner ), mnPara(nPara), mnOldFlags(nOldFlags), mnNewFlags(nNewFlags)
+{
+}
+
+void OutlinerUndoChangeParaFlags::Undo()
+{
+ ImplChangeFlags( mnOldFlags );
+}
+
+void OutlinerUndoChangeParaFlags::Redo()
+{
+ ImplChangeFlags( mnNewFlags );
+}
+
+void OutlinerUndoChangeParaFlags::ImplChangeFlags( ParaFlag nFlags )
+{
+ Outliner* pOutliner = GetOutliner();
+ Paragraph* pPara = pOutliner->GetParagraph( mnPara );
+ if( pPara )
+ {
+ pOutliner->nDepthChangedHdlPrevDepth = pPara->GetDepth();
+ ParaFlag nPrevFlags = pPara->nFlags;
+
+ pPara->nFlags = nFlags;
+ pOutliner->DepthChangedHdl(pPara, nPrevFlags);
+ }
+}
+
+OutlinerUndoChangeParaNumberingRestart::OutlinerUndoChangeParaNumberingRestart( Outliner* pOutliner, sal_Int32 nPara,
+ sal_Int16 nOldNumberingStartValue, sal_Int16 nNewNumberingStartValue,
+ bool bOldParaIsNumberingRestart, bool bNewParaIsNumberingRestart )
+: OutlinerUndoBase( OLUNDO_DEPTH, pOutliner ), mnPara(nPara)
+{
+ maUndoData.mnNumberingStartValue = nOldNumberingStartValue;
+ maUndoData.mbParaIsNumberingRestart = bOldParaIsNumberingRestart;
+ maRedoData.mnNumberingStartValue = nNewNumberingStartValue;
+ maRedoData.mbParaIsNumberingRestart = bNewParaIsNumberingRestart;
+}
+
+void OutlinerUndoChangeParaNumberingRestart::Undo()
+{
+ ImplApplyData( maUndoData );
+}
+
+void OutlinerUndoChangeParaNumberingRestart::Redo()
+{
+ ImplApplyData( maRedoData );
+}
+
+void OutlinerUndoChangeParaNumberingRestart::ImplApplyData( const ParaRestartData& rData )
+{
+ Outliner* pOutliner = GetOutliner();
+ pOutliner->SetNumberingStartValue( mnPara, rData.mnNumberingStartValue );
+ pOutliner->SetParaIsNumberingRestart( mnPara, rData.mbParaIsNumberingRestart );
+}
+
+OutlinerUndoChangeDepth::OutlinerUndoChangeDepth( Outliner* pOutliner, sal_Int32 nPara, sal_Int16 nOldDepth, sal_Int16 nNewDepth )
+ : OutlinerUndoBase( OLUNDO_DEPTH, pOutliner ), mnPara(nPara), mnOldDepth(nOldDepth), mnNewDepth(nNewDepth)
+{
+}
+
+void OutlinerUndoChangeDepth::Undo()
+{
+ GetOutliner()->ImplInitDepth( mnPara, mnOldDepth, false );
+}
+
+void OutlinerUndoChangeDepth::Redo()
+{
+ GetOutliner()->ImplInitDepth( mnPara, mnNewDepth, false );
+}
+
+OutlinerUndoCheckPara::OutlinerUndoCheckPara( Outliner* pOutliner, sal_Int32 nPara )
+ : OutlinerUndoBase( OLUNDO_DEPTH, pOutliner ), mnPara(nPara)
+{
+}
+
+void OutlinerUndoCheckPara::Undo()
+{
+ Paragraph* pPara = GetOutliner()->GetParagraph( mnPara );
+ pPara->Invalidate();
+ GetOutliner()->ImplCalcBulletText( mnPara, false, false );
+}
+
+void OutlinerUndoCheckPara::Redo()
+{
+ Paragraph* pPara = GetOutliner()->GetParagraph( mnPara );
+ pPara->Invalidate();
+ GetOutliner()->ImplCalcBulletText( mnPara, false, false );
+}
+
+OLUndoExpand::OLUndoExpand(Outliner* pOut, sal_uInt16 _nId )
+ : EditUndo( _nId, nullptr ), pOutliner(pOut), nCount(0)
+{
+ DBG_ASSERT(pOut,"Undo:No Outliner");
+}
+
+
+OLUndoExpand::~OLUndoExpand()
+{
+}
+
+
+void OLUndoExpand::Restore( bool bUndo )
+{
+ DBG_ASSERT(pOutliner,"Undo:No Outliner");
+ DBG_ASSERT(pOutliner->pEditEngine,"Outliner already deleted");
+ Paragraph* pPara;
+
+ bool bExpand = false;
+ sal_uInt16 _nId = GetId();
+ if((_nId == OLUNDO_EXPAND && !bUndo) || (_nId == OLUNDO_COLLAPSE && bUndo))
+ bExpand = true;
+
+ pPara = pOutliner->GetParagraph( nCount );
+ if( bExpand )
+ pOutliner->Expand( pPara );
+ else
+ pOutliner->Collapse( pPara );
+}
+
+void OLUndoExpand::Undo()
+{
+ Restore( true );
+}
+
+void OLUndoExpand::Redo()
+{
+ Restore( false );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/outliner/outlundo.hxx b/editeng/source/outliner/outlundo.hxx
new file mode 100644
index 000000000..68b518474
--- /dev/null
+++ b/editeng/source/outliner/outlundo.hxx
@@ -0,0 +1,121 @@
+/* -*- 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 .
+ */
+
+#ifndef INCLUDED_EDITENG_SOURCE_OUTLINER_OUTLUNDO_HXX
+#define INCLUDED_EDITENG_SOURCE_OUTLINER_OUTLUNDO_HXX
+
+#include <editeng/outliner.hxx>
+#include <editeng/editund2.hxx>
+
+class OutlinerUndoBase : public EditUndo
+{
+private:
+ Outliner* mpOutliner;
+
+public:
+ OutlinerUndoBase( sal_uInt16 nId, Outliner* pOutliner );
+
+ Outliner* GetOutliner() const { return mpOutliner; }
+};
+
+class OutlinerUndoChangeParaFlags : public OutlinerUndoBase
+{
+private:
+ sal_Int32 mnPara;
+ ParaFlag mnOldFlags;
+ ParaFlag mnNewFlags;
+
+ void ImplChangeFlags( ParaFlag nFlags );
+
+public:
+ OutlinerUndoChangeParaFlags( Outliner* pOutliner, sal_Int32 nPara, ParaFlag nOldFlags, ParaFlag nNewFlags );
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+};
+
+class OutlinerUndoChangeParaNumberingRestart : public OutlinerUndoBase
+{
+private:
+ sal_Int32 mnPara;
+
+ struct ParaRestartData
+ {
+ sal_Int16 mnNumberingStartValue;
+ bool mbParaIsNumberingRestart;
+ };
+
+ ParaRestartData maUndoData;
+ ParaRestartData maRedoData;
+
+ void ImplApplyData( const ParaRestartData& rData );
+public:
+ OutlinerUndoChangeParaNumberingRestart( Outliner* pOutliner, sal_Int32 nPara,
+ sal_Int16 nOldNumberingStartValue, sal_Int16 mnNewNumberingStartValue,
+ bool bOldbParaIsNumberingRestart, bool bNewParaIsNumberingRestart );
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+};
+
+class OutlinerUndoChangeDepth : public OutlinerUndoBase
+{
+private:
+ sal_Int32 mnPara;
+ sal_Int16 mnOldDepth;
+ sal_Int16 mnNewDepth;
+
+public:
+ OutlinerUndoChangeDepth( Outliner* pOutliner, sal_Int32 nPara, sal_Int16 nOldDepth, sal_Int16 nNewDepth );
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+};
+
+// Help-Undo: If it does not exist an OutlinerUndoAction for a certain action
+// because this is handled by the EditEngine, but for example the bullet has
+// to be recalculated.
+class OutlinerUndoCheckPara : public OutlinerUndoBase
+{
+private:
+ sal_Int32 mnPara;
+
+public:
+ OutlinerUndoCheckPara( Outliner* pOutliner, sal_Int32 nPara );
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+};
+
+class OLUndoExpand : public EditUndo
+{
+ void Restore( bool bUndo );
+public:
+ OLUndoExpand( Outliner* pOut, sal_uInt16 nId );
+ virtual ~OLUndoExpand() override;
+ virtual void Undo() override;
+ virtual void Redo() override;
+
+ Outliner* pOutliner;
+ sal_Int32 nCount;
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/outliner/outlvw.cxx b/editeng/source/outliner/outlvw.cxx
new file mode 100644
index 000000000..8d1127e15
--- /dev/null
+++ b/editeng/source/outliner/outlvw.cxx
@@ -0,0 +1,1481 @@
+/* -*- 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 <memory>
+#include <com/sun/star/i18n/WordType.hpp>
+
+#include <svl/itempool.hxx>
+#include <editeng/editeng.hxx>
+#include <editeng/editview.hxx>
+#include <editeng/editdata.hxx>
+
+#include <svl/style.hxx>
+#include <svl/languageoptions.hxx>
+#include <i18nlangtag/languagetag.hxx>
+
+#include <editeng/outliner.hxx>
+#include "outleeng.hxx"
+#include "paralist.hxx"
+#include "outlundo.hxx"
+#include <editeng/outlobj.hxx>
+#include <editeng/flditem.hxx>
+#include <editeng/eeitem.hxx>
+#include <editeng/numitem.hxx>
+#include <vcl/window.hxx>
+#include <vcl/event.hxx>
+#include <vcl/ptrstyle.hxx>
+#include <svl/itemset.hxx>
+#include <svl/eitem.hxx>
+#include <editeng/editstat.hxx>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+#include <tools/debug.hxx>
+
+using namespace ::com::sun::star;
+
+
+OutlinerView::OutlinerView( Outliner* pOut, vcl::Window* pWin )
+{
+ pOwner = pOut;
+ pEditView.reset( new EditView( pOut->pEditEngine.get(), pWin ) );
+}
+
+OutlinerView::~OutlinerView()
+{
+}
+
+void OutlinerView::Paint( const tools::Rectangle& rRect, OutputDevice* pTargetDevice )
+{
+ // For the first Paint/KeyInput/Drop an empty Outliner is turned into
+ // an Outliner with exactly one paragraph.
+ if( pOwner->bFirstParaIsEmpty )
+ pOwner->Insert( OUString() );
+
+ pEditView->Paint( rRect, pTargetDevice );
+}
+
+bool OutlinerView::PostKeyEvent( const KeyEvent& rKEvt, vcl::Window const * pFrameWin )
+{
+ // For the first Paint/KeyInput/Drop an empty Outliner is turned into
+ // an Outliner with exactly one paragraph.
+ if( pOwner->bFirstParaIsEmpty )
+ pOwner->Insert( OUString() );
+
+ bool bKeyProcessed = false;
+ ESelection aSel( pEditView->GetSelection() );
+ bool bSelection = aSel.HasRange();
+ vcl::KeyCode aKeyCode = rKEvt.GetKeyCode();
+ KeyFuncType eFunc = aKeyCode.GetFunction();
+ sal_uInt16 nCode = aKeyCode.GetCode();
+ bool bReadOnly = IsReadOnly();
+
+ if( bSelection && ( nCode != KEY_TAB ) && EditEngine::DoesKeyChangeText( rKEvt ) )
+ {
+ if ( ImpCalcSelectedPages( false ) && !pOwner->ImpCanDeleteSelectedPages( this ) )
+ return true;
+ }
+
+ if ( eFunc != KeyFuncType::DONTKNOW )
+ {
+ switch ( eFunc )
+ {
+ case KeyFuncType::CUT:
+ {
+ if ( !bReadOnly )
+ {
+ Cut();
+ bKeyProcessed = true;
+ }
+ }
+ break;
+ case KeyFuncType::COPY:
+ {
+ Copy();
+ bKeyProcessed = true;
+ }
+ break;
+ case KeyFuncType::PASTE:
+ {
+ if ( !bReadOnly )
+ {
+ PasteSpecial();
+ bKeyProcessed = true;
+ }
+ }
+ break;
+ case KeyFuncType::DELETE:
+ {
+ if( !bReadOnly && !bSelection && ( pOwner->ImplGetOutlinerMode() != OutlinerMode::TextObject ) )
+ {
+ if( aSel.nEndPos == pOwner->pEditEngine->GetTextLen( aSel.nEndPara ) )
+ {
+ Paragraph* pNext = pOwner->pParaList->GetParagraph( aSel.nEndPara+1 );
+ if( pNext && pNext->HasFlag(ParaFlag::ISPAGE) )
+ {
+ if( !pOwner->ImpCanDeleteSelectedPages( this, aSel.nEndPara, 1 ) )
+ return false;
+ }
+ }
+ }
+ }
+ break;
+ default: // is then possibly edited below.
+ eFunc = KeyFuncType::DONTKNOW;
+ }
+ }
+ if ( eFunc == KeyFuncType::DONTKNOW )
+ {
+ switch ( nCode )
+ {
+ case KEY_TAB:
+ {
+ if ( !bReadOnly && !aKeyCode.IsMod1() && !aKeyCode.IsMod2() )
+ {
+ if ( ( pOwner->ImplGetOutlinerMode() != OutlinerMode::TextObject ) &&
+ ( pOwner->ImplGetOutlinerMode() != OutlinerMode::TitleObject ) &&
+ ( bSelection || !aSel.nStartPos ) )
+ {
+ Indent( aKeyCode.IsShift() ? -1 : +1 );
+ bKeyProcessed = true;
+ }
+ else if ( ( pOwner->ImplGetOutlinerMode() == OutlinerMode::TextObject ) &&
+ !bSelection && !aSel.nEndPos && pOwner->ImplHasNumberFormat( aSel.nEndPara ) )
+ {
+ Indent( aKeyCode.IsShift() ? -1 : +1 );
+ bKeyProcessed = true;
+ }
+ }
+ }
+ break;
+ case KEY_BACKSPACE:
+ {
+ if( !bReadOnly && !bSelection && aSel.nEndPara && !aSel.nEndPos )
+ {
+ Paragraph* pPara = pOwner->pParaList->GetParagraph( aSel.nEndPara );
+ Paragraph* pPrev = pOwner->pParaList->GetParagraph( aSel.nEndPara-1 );
+ if( !pPrev->IsVisible() )
+ return true;
+ if( !pPara->GetDepth() )
+ {
+ if(!pOwner->ImpCanDeleteSelectedPages(this, aSel.nEndPara , 1 ) )
+ return true;
+ }
+ }
+ }
+ break;
+ case KEY_RETURN:
+ {
+ if ( !bReadOnly )
+ {
+ // Special treatment: hard return at the end of a paragraph,
+ // which has collapsed subparagraphs.
+ Paragraph* pPara = pOwner->pParaList->GetParagraph( aSel.nEndPara );
+
+ if( !aKeyCode.IsShift() )
+ {
+ // ImpGetCursor again???
+ if( !bSelection &&
+ aSel.nEndPos == pOwner->pEditEngine->GetTextLen( aSel.nEndPara ) )
+ {
+ sal_Int32 nChildren = pOwner->pParaList->GetChildCount(pPara);
+ if( nChildren && !pOwner->pParaList->HasVisibleChildren(pPara))
+ {
+ pOwner->UndoActionStart( OLUNDO_INSERT );
+ sal_Int32 nTemp = aSel.nEndPara;
+ nTemp += nChildren;
+ nTemp++; // insert above next Non-Child
+ SAL_WARN_IF( nTemp < 0, "editeng", "OutlinerView::PostKeyEvent - overflow");
+ if (nTemp >= 0)
+ {
+ pOwner->Insert( OUString(),nTemp,pPara->GetDepth());
+ // Position the cursor
+ ESelection aTmpSel(nTemp,0,nTemp,0);
+ pEditView->SetSelection( aTmpSel );
+ }
+ pEditView->ShowCursor();
+ pOwner->UndoActionEnd();
+ bKeyProcessed = true;
+ }
+ }
+ }
+ if( !bKeyProcessed && !bSelection &&
+ !aKeyCode.IsShift() && aKeyCode.IsMod1() &&
+ ( aSel.nEndPos == pOwner->pEditEngine->GetTextLen(aSel.nEndPara) ) )
+ {
+ pOwner->UndoActionStart( OLUNDO_INSERT );
+ sal_Int32 nTemp = aSel.nEndPara;
+ nTemp++;
+ pOwner->Insert( OUString(), nTemp, pPara->GetDepth()+1 );
+
+ // Position the cursor
+ ESelection aTmpSel(nTemp,0,nTemp,0);
+ pEditView->SetSelection( aTmpSel );
+ pEditView->ShowCursor();
+ pOwner->UndoActionEnd();
+ bKeyProcessed = true;
+ }
+ }
+ }
+ break;
+ }
+ }
+
+ return bKeyProcessed || pEditView->PostKeyEvent( rKEvt, pFrameWin );
+}
+
+sal_Int32 OutlinerView::ImpCheckMousePos(const Point& rPosPix, MouseTarget& reTarget)
+{
+ sal_Int32 nPara = EE_PARA_NOT_FOUND;
+
+ Point aMousePosWin = pEditView->GetWindow()->PixelToLogic( rPosPix );
+ if( !pEditView->GetOutputArea().IsInside( aMousePosWin ) )
+ {
+ reTarget = MouseTarget::Outside;
+ }
+ else
+ {
+ reTarget = MouseTarget::Text;
+
+ Point aPaperPos( aMousePosWin );
+ tools::Rectangle aOutArea = pEditView->GetOutputArea();
+ tools::Rectangle aVisArea = pEditView->GetVisArea();
+ aPaperPos.AdjustX( -(aOutArea.Left()) );
+ aPaperPos.AdjustX(aVisArea.Left() );
+ aPaperPos.AdjustY( -(aOutArea.Top()) );
+ aPaperPos.AdjustY(aVisArea.Top() );
+
+ bool bBullet;
+ if ( pOwner->IsTextPos( aPaperPos, 0, &bBullet ) )
+ {
+ Point aDocPos = pOwner->GetDocPos( aPaperPos );
+ nPara = pOwner->pEditEngine->FindParagraph( aDocPos.Y() );
+
+ if ( bBullet )
+ {
+ reTarget = MouseTarget::Bullet;
+ }
+ else
+ {
+ // Check for hyperlink
+ const SvxFieldItem* pFieldItem = pEditView->GetField( aMousePosWin );
+ if ( pFieldItem && pFieldItem->GetField() && dynamic_cast< const SvxURLField* >(pFieldItem->GetField()) != nullptr )
+ reTarget = MouseTarget::Hypertext;
+ }
+ }
+ }
+ return nPara;
+}
+
+bool OutlinerView::MouseMove( const MouseEvent& rMEvt )
+{
+ if( ( pOwner->ImplGetOutlinerMode() == OutlinerMode::TextObject ) || pEditView->GetEditEngine()->IsInSelectionMode())
+ return pEditView->MouseMove( rMEvt );
+
+ Point aMousePosWin( pEditView->GetWindow()->PixelToLogic( rMEvt.GetPosPixel() ) );
+ if( !pEditView->GetOutputArea().IsInside( aMousePosWin ) )
+ return false;
+
+ PointerStyle aPointer = GetPointer( rMEvt.GetPosPixel() );
+ pEditView->GetWindow()->SetPointer( aPointer );
+ return pEditView->MouseMove( rMEvt );
+}
+
+
+bool OutlinerView::MouseButtonDown( const MouseEvent& rMEvt )
+{
+ if ( ( pOwner->ImplGetOutlinerMode() == OutlinerMode::TextObject ) || pEditView->GetEditEngine()->IsInSelectionMode() )
+ return pEditView->MouseButtonDown( rMEvt );
+
+ Point aMousePosWin( pEditView->GetWindow()->PixelToLogic( rMEvt.GetPosPixel() ) );
+ if( !pEditView->GetOutputArea().IsInside( aMousePosWin ) )
+ return false;
+
+ PointerStyle aPointer = GetPointer( rMEvt.GetPosPixel() );
+ pEditView->GetWindow()->SetPointer( aPointer );
+
+ MouseTarget eTarget;
+ sal_Int32 nPara = ImpCheckMousePos( rMEvt.GetPosPixel(), eTarget );
+ if ( eTarget == MouseTarget::Bullet )
+ {
+ Paragraph* pPara = pOwner->pParaList->GetParagraph( nPara );
+ bool bHasChildren = (pPara && pOwner->pParaList->HasChildren(pPara));
+ if( rMEvt.GetClicks() == 1 )
+ {
+ sal_Int32 nEndPara = nPara;
+ if ( bHasChildren && pOwner->pParaList->HasVisibleChildren(pPara) )
+ nEndPara += pOwner->pParaList->GetChildCount( pPara );
+ // The selection is inverted, so that EditEngine does not scroll
+ ESelection aSel(nEndPara, EE_TEXTPOS_ALL, nPara, 0 );
+ pEditView->SetSelection( aSel );
+ }
+ else if( rMEvt.GetClicks() == 2 && bHasChildren )
+ ImpToggleExpand( pPara );
+
+ return true;
+ }
+
+ // special case for outliner view in impress, check if double click hits the page icon for toggle
+ if( (nPara == EE_PARA_NOT_FOUND) && (pOwner->ImplGetOutlinerMode() == OutlinerMode::OutlineView) && (eTarget == MouseTarget::Text) && (rMEvt.GetClicks() == 2) )
+ {
+ ESelection aSel( pEditView->GetSelection() );
+ nPara = aSel.nStartPara;
+ Paragraph* pPara = pOwner->pParaList->GetParagraph( nPara );
+ if( (pPara && pOwner->pParaList->HasChildren(pPara)) && pPara->HasFlag(ParaFlag::ISPAGE) )
+ {
+ ImpToggleExpand( pPara );
+ }
+ }
+ return pEditView->MouseButtonDown( rMEvt );
+}
+
+
+bool OutlinerView::MouseButtonUp( const MouseEvent& rMEvt )
+{
+ if ( ( pOwner->ImplGetOutlinerMode() == OutlinerMode::TextObject ) || pEditView->GetEditEngine()->IsInSelectionMode() )
+ return pEditView->MouseButtonUp( rMEvt );
+
+ Point aMousePosWin( pEditView->GetWindow()->PixelToLogic( rMEvt.GetPosPixel() ) );
+ if( !pEditView->GetOutputArea().IsInside( aMousePosWin ) )
+ return false;
+
+ PointerStyle aPointer = GetPointer( rMEvt.GetPosPixel() );
+ pEditView->GetWindow()->SetPointer( aPointer );
+
+ return pEditView->MouseButtonUp( rMEvt );
+}
+
+void OutlinerView::ReleaseMouse()
+{
+ pEditView->ReleaseMouse();
+}
+
+void OutlinerView::ImpToggleExpand( Paragraph const * pPara )
+{
+ sal_Int32 nPara = pOwner->pParaList->GetAbsPos( pPara );
+ pEditView->SetSelection( ESelection( nPara, 0, nPara, 0 ) );
+ ImplExpandOrCollaps( nPara, nPara, !pOwner->pParaList->HasVisibleChildren( pPara ) );
+ pEditView->ShowCursor();
+}
+
+void OutlinerView::Select( Paragraph const * pParagraph, bool bSelect )
+{
+ sal_Int32 nPara = pOwner->pParaList->GetAbsPos( pParagraph );
+ sal_Int32 nEnd = 0;
+ if ( bSelect )
+ nEnd = SAL_MAX_INT32;
+
+ ESelection aSel( nPara, 0, nPara, nEnd );
+ pEditView->SetSelection( aSel );
+}
+
+
+void OutlinerView::SetAttribs( const SfxItemSet& rAttrs )
+{
+ bool bUpdate = pOwner->pEditEngine->GetUpdateMode();
+ pOwner->pEditEngine->SetUpdateMode( false );
+
+ if( !pOwner->IsInUndo() && pOwner->IsUndoEnabled() )
+ pOwner->UndoActionStart( OLUNDO_ATTR );
+
+ ParaRange aSel = ImpGetSelectedParagraphs( false );
+
+ pEditView->SetAttribs( rAttrs );
+
+ // Update Bullet text
+ for( sal_Int32 nPara= aSel.nStartPara; nPara <= aSel.nEndPara; nPara++ )
+ {
+ pOwner->ImplCheckNumBulletItem( nPara );
+ pOwner->ImplCalcBulletText( nPara, false, false );
+
+ if( !pOwner->IsInUndo() && pOwner->IsUndoEnabled() )
+ pOwner->InsertUndo( std::make_unique<OutlinerUndoCheckPara>( pOwner, nPara ) );
+ }
+
+ if( !pOwner->IsInUndo() && pOwner->IsUndoEnabled() )
+ pOwner->UndoActionEnd();
+
+ pEditView->SetEditEngineUpdateMode( bUpdate );
+}
+
+ParaRange OutlinerView::ImpGetSelectedParagraphs( bool bIncludeHiddenChildren )
+{
+ ESelection aSel = pEditView->GetSelection();
+ ParaRange aParas( aSel.nStartPara, aSel.nEndPara );
+ aParas.Adjust();
+
+ // Record the invisible Children of the last Parents in the selection
+ if ( bIncludeHiddenChildren )
+ {
+ Paragraph* pLast = pOwner->pParaList->GetParagraph( aParas.nEndPara );
+ if ( pOwner->pParaList->HasHiddenChildren( pLast ) )
+ aParas.nEndPara = aParas.nEndPara + pOwner->pParaList->GetChildCount( pLast );
+ }
+ return aParas;
+}
+
+// TODO: Name should be changed!
+void OutlinerView::AdjustDepth( short nDX )
+{
+ Indent( nDX );
+}
+
+void OutlinerView::Indent( short nDiff )
+{
+ if( !nDiff || ( ( nDiff > 0 ) && ImpCalcSelectedPages( true ) && !pOwner->ImpCanIndentSelectedPages( this ) ) )
+ return;
+
+ const bool bOutlinerView = bool(pOwner->pEditEngine->GetControlWord() & EEControlBits::OUTLINER);
+ bool bUpdate = pOwner->pEditEngine->GetUpdateMode();
+ pOwner->pEditEngine->SetUpdateMode( false );
+
+ bool bUndo = !pOwner->IsInUndo() && pOwner->IsUndoEnabled();
+
+ if( bUndo )
+ pOwner->UndoActionStart( OLUNDO_DEPTH );
+
+ sal_Int16 nMinDepth = -1; // Optimization: avoid recalculate too many paragraphs if not really needed.
+
+ ParaRange aSel = ImpGetSelectedParagraphs( true );
+ for ( sal_Int32 nPara = aSel.nStartPara; nPara <= aSel.nEndPara; nPara++ )
+ {
+ Paragraph* pPara = pOwner->pParaList->GetParagraph( nPara );
+
+ sal_Int16 nOldDepth = pPara->GetDepth();
+ sal_Int16 nNewDepth = nOldDepth + nDiff;
+
+ if( bOutlinerView && nPara )
+ {
+ const bool bPage = pPara->HasFlag(ParaFlag::ISPAGE);
+ if( (bPage && (nDiff == +1)) || (!bPage && (nDiff == -1) && (nOldDepth <= 0)) )
+ {
+ // Notify App
+ pOwner->nDepthChangedHdlPrevDepth = nOldDepth;
+ ParaFlag nPrevFlags = pPara->nFlags;
+
+ if( bPage )
+ pPara->RemoveFlag( ParaFlag::ISPAGE );
+ else
+ pPara->SetFlag( ParaFlag::ISPAGE );
+
+ pOwner->DepthChangedHdl(pPara, nPrevFlags);
+ pOwner->pEditEngine->QuickMarkInvalid( ESelection( nPara, 0, nPara, 0 ) );
+
+ if( bUndo )
+ pOwner->InsertUndo( std::make_unique<OutlinerUndoChangeParaFlags>( pOwner, nPara, nPrevFlags, pPara->nFlags ) );
+
+ continue;
+ }
+ }
+
+ // do not switch off numeration with tab
+ if( (nOldDepth == 0) && (nNewDepth == -1) )
+ continue;
+
+ // do not indent if there is no numeration enabled
+ if( nOldDepth == -1 )
+ continue;
+
+ if ( nNewDepth < Outliner::gnMinDepth )
+ nNewDepth = Outliner::gnMinDepth;
+ if ( nNewDepth > pOwner->nMaxDepth )
+ nNewDepth = pOwner->nMaxDepth;
+
+ if( nOldDepth < nMinDepth )
+ nMinDepth = nOldDepth;
+ if( nNewDepth < nMinDepth )
+ nMinDepth = nNewDepth;
+
+ if( nOldDepth != nNewDepth )
+ {
+ if ( ( nPara == aSel.nStartPara ) && aSel.nStartPara && ( pOwner->ImplGetOutlinerMode() != OutlinerMode::TextObject ))
+ {
+ // Special case: the predecessor of an indented paragraph is
+ // invisible and is now on the same level as the visible
+ // paragraph. In this case, the next visible paragraph is
+ // searched for and fluffed.
+#ifdef DBG_UTIL
+ Paragraph* _pPara = pOwner->pParaList->GetParagraph( aSel.nStartPara );
+ DBG_ASSERT(_pPara->IsVisible(),"Selected Paragraph invisible ?!");
+#endif
+ Paragraph* pPrev= pOwner->pParaList->GetParagraph( aSel.nStartPara-1 );
+
+ if( !pPrev->IsVisible() && ( pPrev->GetDepth() == nNewDepth ) )
+ {
+ // Predecessor is collapsed and is on the same level
+ // => find next visible paragraph and expand it
+ pPrev = pOwner->pParaList->GetParent( pPrev );
+ while( !pPrev->IsVisible() )
+ pPrev = pOwner->pParaList->GetParent( pPrev );
+
+ pOwner->Expand( pPrev );
+ pOwner->InvalidateBullet(pOwner->pParaList->GetAbsPos(pPrev));
+ }
+ }
+
+ pOwner->nDepthChangedHdlPrevDepth = nOldDepth;
+ ParaFlag nPrevFlags = pPara->nFlags;
+
+ pOwner->ImplInitDepth( nPara, nNewDepth, true );
+ pOwner->ImplCalcBulletText( nPara, false, false );
+
+ if ( pOwner->ImplGetOutlinerMode() == OutlinerMode::OutlineObject )
+ pOwner->ImplSetLevelDependentStyleSheet( nPara );
+
+ // Notify App
+ pOwner->DepthChangedHdl(pPara, nPrevFlags);
+ }
+ else
+ {
+ // Needs at least a repaint...
+ pOwner->pEditEngine->QuickMarkInvalid( ESelection( nPara, 0, nPara, 0 ) );
+ }
+ }
+
+ sal_Int32 nParas = pOwner->pParaList->GetParagraphCount();
+ for ( sal_Int32 n = aSel.nEndPara+1; n < nParas; n++ )
+ {
+ Paragraph* pPara = pOwner->pParaList->GetParagraph( n );
+ if ( pPara->GetDepth() < nMinDepth )
+ break;
+ pOwner->ImplCalcBulletText( n, false, false );
+ }
+
+ if ( bUpdate )
+ {
+ pEditView->SetEditEngineUpdateMode( true );
+ pEditView->ShowCursor();
+ }
+
+ if( bUndo )
+ pOwner->UndoActionEnd();
+}
+
+void OutlinerView::AdjustHeight( long nDY )
+{
+ pEditView->MoveParagraphs( nDY );
+}
+
+tools::Rectangle const & OutlinerView::GetVisArea() const
+{
+ return pEditView->GetVisArea();
+}
+
+void OutlinerView::Expand()
+{
+ ParaRange aParas = ImpGetSelectedParagraphs( false );
+ ImplExpandOrCollaps( aParas.nStartPara, aParas.nEndPara, true );
+}
+
+
+void OutlinerView::Collapse()
+{
+ ParaRange aParas = ImpGetSelectedParagraphs( false );
+ ImplExpandOrCollaps( aParas.nStartPara, aParas.nEndPara, false );
+}
+
+
+void OutlinerView::ExpandAll()
+{
+ ImplExpandOrCollaps( 0, pOwner->pParaList->GetParagraphCount()-1, true );
+}
+
+
+void OutlinerView::CollapseAll()
+{
+ ImplExpandOrCollaps( 0, pOwner->pParaList->GetParagraphCount()-1, false );
+}
+
+void OutlinerView::ImplExpandOrCollaps( sal_Int32 nStartPara, sal_Int32 nEndPara, bool bExpand )
+{
+ bool bUpdate = pOwner->GetUpdateMode();
+ pOwner->SetUpdateMode( false );
+
+ bool bUndo = !pOwner->IsInUndo() && pOwner->IsUndoEnabled();
+ if( bUndo )
+ pOwner->UndoActionStart( bExpand ? OLUNDO_EXPAND : OLUNDO_COLLAPSE );
+
+ for ( sal_Int32 nPara = nStartPara; nPara <= nEndPara; nPara++ )
+ {
+ Paragraph* pPara = pOwner->pParaList->GetParagraph( nPara );
+ bool bDone = bExpand ? pOwner->Expand( pPara ) : pOwner->Collapse( pPara );
+ if( bDone )
+ {
+ // The line under the paragraph should disappear ...
+ pOwner->pEditEngine->QuickMarkToBeRepainted( nPara );
+ }
+ }
+
+ if( bUndo )
+ pOwner->UndoActionEnd();
+
+ if ( bUpdate )
+ {
+ pOwner->SetUpdateMode( true );
+ pEditView->ShowCursor();
+ }
+}
+
+void OutlinerView::InsertText( const OutlinerParaObject& rParaObj )
+{
+ // Like Paste, only EditView::Insert, instead of EditView::Paste.
+ // Actually not quite true that possible indentations must be corrected,
+ // but that comes later by a universal import. The indentation level is
+ // then determined right in the Inserted method.
+ // Possible structure:
+ // pImportInfo with DestPara, DestPos, nFormat, pParaObj...
+ // Possibly problematic:
+ // EditEngine, RTF => Splitting the area, later join together.
+
+ if ( ImpCalcSelectedPages( false ) && !pOwner->ImpCanDeleteSelectedPages( this ) )
+ return;
+
+ pOwner->UndoActionStart( OLUNDO_INSERT );
+
+ pOwner->pEditEngine->SetUpdateMode( false );
+ sal_Int32 nStart, nParaCount;
+ nParaCount = pOwner->pEditEngine->GetParagraphCount();
+ sal_uInt16 nSize = ImpInitPaste( nStart );
+ pEditView->InsertText( rParaObj.GetTextObject() );
+ ImpPasted( nStart, nParaCount, nSize);
+ pEditView->SetEditEngineUpdateMode( true );
+
+ pOwner->UndoActionEnd();
+
+ pEditView->ShowCursor();
+}
+
+
+void OutlinerView::Cut()
+{
+ if ( !ImpCalcSelectedPages( false ) || pOwner->ImpCanDeleteSelectedPages( this ) ) {
+ pEditView->Cut();
+ // Chaining handling
+ aEndCutPasteLink.Call(nullptr);
+ }
+}
+
+void OutlinerView::PasteSpecial()
+{
+ Paste( true );
+}
+
+void OutlinerView::Paste( bool bUseSpecial )
+{
+ if ( !(!ImpCalcSelectedPages( false ) || pOwner->ImpCanDeleteSelectedPages( this )) )
+ return;
+
+ pOwner->UndoActionStart( OLUNDO_INSERT );
+
+ pOwner->pEditEngine->SetUpdateMode( false );
+ pOwner->bPasting = true;
+
+ if ( bUseSpecial )
+ pEditView->PasteSpecial();
+ else
+ pEditView->Paste();
+
+ if ( pOwner->ImplGetOutlinerMode() == OutlinerMode::OutlineObject )
+ {
+ const sal_Int32 nParaCount = pOwner->pEditEngine->GetParagraphCount();
+
+ for( sal_Int32 nPara = 0; nPara < nParaCount; nPara++ )
+ pOwner->ImplSetLevelDependentStyleSheet( nPara );
+ }
+
+ pEditView->SetEditEngineUpdateMode( true );
+ pOwner->UndoActionEnd();
+ pEditView->ShowCursor();
+
+ // Chaining handling
+ // NOTE: We need to do this last because it pEditView may be deleted if a switch of box occurs
+ aEndCutPasteLink.Call(nullptr);
+}
+
+void OutlinerView::CreateSelectionList (std::vector<Paragraph*> &aSelList)
+{
+ ParaRange aParas = ImpGetSelectedParagraphs( true );
+
+ for ( sal_Int32 nPara = aParas.nStartPara; nPara <= aParas.nEndPara; nPara++ )
+ {
+ Paragraph* pPara = pOwner->pParaList->GetParagraph( nPara );
+ aSelList.push_back(pPara);
+ }
+}
+
+const SfxStyleSheet* OutlinerView::GetStyleSheet() const
+{
+ return pEditView->GetStyleSheet();
+}
+
+SfxStyleSheet* OutlinerView::GetStyleSheet()
+{
+ return pEditView->GetStyleSheet();
+}
+
+PointerStyle OutlinerView::GetPointer( const Point& rPosPixel )
+{
+ MouseTarget eTarget;
+ ImpCheckMousePos( rPosPixel, eTarget );
+
+ PointerStyle ePointerStyle = PointerStyle::Arrow;
+ if ( eTarget == MouseTarget::Text )
+ {
+ ePointerStyle = GetOutliner()->IsVertical() ? PointerStyle::TextVertical : PointerStyle::Text;
+ }
+ else if ( eTarget == MouseTarget::Hypertext )
+ {
+ ePointerStyle = PointerStyle::RefHand;
+ }
+ else if ( eTarget == MouseTarget::Bullet )
+ {
+ ePointerStyle = PointerStyle::Move;
+ }
+
+ return ePointerStyle;
+}
+
+
+sal_Int32 OutlinerView::ImpInitPaste( sal_Int32& rStart )
+{
+ pOwner->bPasting = true;
+ ESelection aSelection( pEditView->GetSelection() );
+ aSelection.Adjust();
+ rStart = aSelection.nStartPara;
+ sal_Int32 nSize = aSelection.nEndPara - aSelection.nStartPara + 1;
+ return nSize;
+}
+
+
+void OutlinerView::ImpPasted( sal_Int32 nStart, sal_Int32 nPrevParaCount, sal_Int32 nSize)
+{
+ pOwner->bPasting = false;
+ sal_Int32 nCurParaCount = pOwner->pEditEngine->GetParagraphCount();
+ if( nCurParaCount < nPrevParaCount )
+ nSize = nSize - ( nPrevParaCount - nCurParaCount );
+ else
+ nSize = nSize + ( nCurParaCount - nPrevParaCount );
+ pOwner->ImpTextPasted( nStart, nSize );
+}
+
+
+void OutlinerView::Command( const CommandEvent& rCEvt )
+{
+ pEditView->Command( rCEvt );
+}
+
+
+void OutlinerView::SelectRange( sal_Int32 nFirst, sal_Int32 nCount )
+{
+ sal_Int32 nLast = nFirst+nCount;
+ nCount = pOwner->pParaList->GetParagraphCount();
+ if( nLast <= nCount )
+ nLast = nCount - 1;
+ ESelection aSel( nFirst, 0, nLast, EE_TEXTPOS_ALL );
+ pEditView->SetSelection( aSel );
+}
+
+
+sal_Int32 OutlinerView::ImpCalcSelectedPages( bool bIncludeFirstSelected )
+{
+ ESelection aSel( pEditView->GetSelection() );
+ aSel.Adjust();
+
+ sal_Int32 nPages = 0;
+ sal_Int32 nFirstPage = EE_PARA_MAX_COUNT;
+ sal_Int32 nStartPara = aSel.nStartPara;
+ if ( !bIncludeFirstSelected )
+ nStartPara++; // All paragraphs after StartPara will be deleted
+ for ( sal_Int32 nPara = nStartPara; nPara <= aSel.nEndPara; nPara++ )
+ {
+ Paragraph* pPara = pOwner->pParaList->GetParagraph( nPara );
+ DBG_ASSERT(pPara, "ImpCalcSelectedPages: invalid Selection? ");
+ if( pPara->HasFlag(ParaFlag::ISPAGE) )
+ {
+ nPages++;
+ if( nFirstPage == EE_PARA_MAX_COUNT )
+ nFirstPage = nPara;
+ }
+ }
+
+ if( nPages )
+ {
+ pOwner->nDepthChangedHdlPrevDepth = nPages;
+ pOwner->mnFirstSelPage = nFirstPage;
+ }
+
+ return nPages;
+}
+
+
+void OutlinerView::ToggleBullets()
+{
+ pOwner->UndoActionStart( OLUNDO_DEPTH );
+
+ ESelection aSel( pEditView->GetSelection() );
+ aSel.Adjust();
+
+ const bool bUpdate = pOwner->pEditEngine->GetUpdateMode();
+ pOwner->pEditEngine->SetUpdateMode( false );
+
+ sal_Int16 nNewDepth = -2;
+ const SvxNumRule* pDefaultBulletNumRule = nullptr;
+
+ for ( sal_Int32 nPara = aSel.nStartPara; nPara <= aSel.nEndPara; nPara++ )
+ {
+ Paragraph* pPara = pOwner->pParaList->GetParagraph( nPara );
+ DBG_ASSERT(pPara, "OutlinerView::ToggleBullets(), illegal selection?");
+
+ if( pPara )
+ {
+ if( nNewDepth == -2 )
+ {
+ nNewDepth = (pOwner->GetDepth(nPara) == -1) ? 0 : -1;
+ if ( nNewDepth == 0 )
+ {
+ // determine default numbering rule for bullets
+ const ESelection aSelection(nPara, 0);
+ const SfxItemSet aTmpSet(pOwner->pEditEngine->GetAttribs(aSelection));
+ const SfxPoolItem& rPoolItem = aTmpSet.GetPool()->GetDefaultItem( EE_PARA_NUMBULLET );
+ const SvxNumBulletItem* pNumBulletItem = dynamic_cast< const SvxNumBulletItem* >(&rPoolItem);
+ pDefaultBulletNumRule = pNumBulletItem ? pNumBulletItem->GetNumRule() : nullptr;
+ }
+ }
+
+ pOwner->SetDepth( pPara, nNewDepth );
+
+ if( nNewDepth == -1 )
+ {
+ const SfxItemSet& rAttrs = pOwner->GetParaAttribs( nPara );
+ if ( rAttrs.GetItemState( EE_PARA_BULLETSTATE ) == SfxItemState::SET )
+ {
+ SfxItemSet aAttrs(rAttrs);
+ aAttrs.ClearItem( EE_PARA_BULLETSTATE );
+ pOwner->SetParaAttribs( nPara, aAttrs );
+ }
+ }
+ else
+ {
+ if ( pDefaultBulletNumRule )
+ {
+ const SvxNumberFormat* pFmt = pOwner ->GetNumberFormat( nPara );
+ if ( !pFmt
+ || ( pFmt->GetNumberingType() != SVX_NUM_BITMAP
+ && pFmt->GetNumberingType() != SVX_NUM_CHAR_SPECIAL ) )
+ {
+ SfxItemSet aAttrs( pOwner->GetParaAttribs( nPara ) );
+ SvxNumRule aNewNumRule( *pDefaultBulletNumRule );
+ aAttrs.Put( SvxNumBulletItem( aNewNumRule, EE_PARA_NUMBULLET ) );
+ pOwner->SetParaAttribs( nPara, aAttrs );
+ }
+ }
+ }
+ }
+ }
+
+ const sal_Int32 nParaCount = pOwner->pParaList->GetParagraphCount();
+ pOwner->ImplCheckParagraphs( aSel.nStartPara, nParaCount );
+
+ sal_Int32 nEndPara = (nParaCount > 0) ? nParaCount-1 : nParaCount;
+ pOwner->pEditEngine->QuickMarkInvalid( ESelection( aSel.nStartPara, 0, nEndPara, 0 ) );
+
+ pOwner->pEditEngine->SetUpdateMode( bUpdate );
+
+ pOwner->UndoActionEnd();
+}
+
+
+void OutlinerView::ToggleBulletsNumbering(
+ const bool bToggle,
+ const bool bHandleBullets,
+ const SvxNumRule* pNumRule )
+{
+ ESelection aSel( pEditView->GetSelection() );
+ aSel.Adjust();
+
+ bool bToggleOn = true;
+ if ( bToggle )
+ {
+ bToggleOn = false;
+ const sal_Int16 nBulletNumberingStatus( pOwner->GetBulletsNumberingStatus( aSel.nStartPara, aSel.nEndPara ) );
+ if ( nBulletNumberingStatus != 0 && bHandleBullets )
+ {
+ // not all paragraphs have bullets and method called to toggle bullets --> bullets on
+ bToggleOn = true;
+ }
+ else if ( nBulletNumberingStatus != 1 && !bHandleBullets )
+ {
+ // not all paragraphs have numbering and method called to toggle numberings --> numberings on
+ bToggleOn = true;
+ }
+ }
+ if ( bToggleOn )
+ {
+ // apply bullets/numbering for selected paragraphs
+ ApplyBulletsNumbering( bHandleBullets, pNumRule, bToggle, true );
+ }
+ else
+ {
+ // switch off bullets/numbering for selected paragraphs
+ SwitchOffBulletsNumbering( true );
+ }
+}
+
+
+void OutlinerView::ApplyBulletsNumbering(
+ const bool bHandleBullets,
+ const SvxNumRule* pNewNumRule,
+ const bool bCheckCurrentNumRuleBeforeApplyingNewNumRule,
+ const bool bAtSelection )
+{
+ if (!pOwner || !pOwner->pEditEngine || !pOwner->pParaList)
+ {
+ return;
+ }
+
+ pOwner->UndoActionStart(OLUNDO_DEPTH);
+ const bool bUpdate = pOwner->pEditEngine->GetUpdateMode();
+ pOwner->pEditEngine->SetUpdateMode(false);
+
+ sal_Int32 nStartPara = 0;
+ sal_Int32 nEndPara = 0;
+ if ( bAtSelection )
+ {
+ ESelection aSel( pEditView->GetSelection() );
+ aSel.Adjust();
+ nStartPara = aSel.nStartPara;
+ nEndPara = aSel.nEndPara;
+ }
+ else
+ {
+ nStartPara = 0;
+ nEndPara = pOwner->pParaList->GetParagraphCount() - 1;
+ }
+
+ for (sal_Int32 nPara = nStartPara; nPara <= nEndPara; ++nPara)
+ {
+ Paragraph* pPara = pOwner->pParaList->GetParagraph(nPara);
+ DBG_ASSERT(pPara, "OutlinerView::ApplyBulletsNumbering(..), illegal selection?");
+
+ if (pPara)
+ {
+ const sal_Int16 nDepth = pOwner->GetDepth(nPara);
+ if ( nDepth == -1 )
+ {
+ pOwner->SetDepth( pPara, 0 );
+ }
+
+ const SfxItemSet& rAttrs = pOwner->GetParaAttribs(nPara);
+ SfxItemSet aAttrs(rAttrs);
+ aAttrs.Put(SfxBoolItem(EE_PARA_BULLETSTATE, true));
+
+ // apply new numbering rule
+ if ( pNewNumRule )
+ {
+ bool bApplyNumRule = false;
+ if ( !bCheckCurrentNumRuleBeforeApplyingNewNumRule )
+ {
+ bApplyNumRule = true;
+ }
+ else
+ {
+ const SvxNumberFormat* pFmt = pOwner ->GetNumberFormat(nPara);
+ if (!pFmt)
+ {
+ bApplyNumRule = true;
+ }
+ else
+ {
+ sal_Int16 nNumType = pFmt->GetNumberingType();
+ if ( bHandleBullets
+ && nNumType != SVX_NUM_BITMAP && nNumType != SVX_NUM_CHAR_SPECIAL)
+ {
+ // Set to Normal bullet, old bullet type is Numbering bullet.
+ bApplyNumRule = true;
+ }
+ else if ( !bHandleBullets
+ && (nNumType == SVX_NUM_BITMAP || nNumType == SVX_NUM_CHAR_SPECIAL))
+ {
+ // Set to Numbering bullet, old bullet type is Normal bullet.
+ bApplyNumRule = true;
+ }
+ }
+ }
+
+ if ( bApplyNumRule )
+ {
+ SvxNumRule aNewRule(*pNewNumRule);
+
+ // Get old bullet space.
+ {
+ const SfxPoolItem* pPoolItem=nullptr;
+ SfxItemState eState = rAttrs.GetItemState(EE_PARA_NUMBULLET, false, &pPoolItem);
+ if (eState != SfxItemState::SET)
+ {
+ // Use default value when has not contain bullet item.
+ ESelection aSelection(nPara, 0);
+ SfxItemSet aTmpSet(pOwner->pEditEngine->GetAttribs(aSelection));
+ pPoolItem = aTmpSet.GetItem(EE_PARA_NUMBULLET);
+ }
+
+ const SvxNumBulletItem* pNumBulletItem = dynamic_cast< const SvxNumBulletItem* >(pPoolItem);
+ if (pNumBulletItem)
+ {
+ const sal_uInt16 nLevelCnt = std::min(pNumBulletItem->GetNumRule()->GetLevelCount(), aNewRule.GetLevelCount());
+ for ( sal_uInt16 nLevel = 0; nLevel < nLevelCnt; ++nLevel )
+ {
+ const SvxNumberFormat* pOldFmt = pNumBulletItem->GetNumRule()->Get(nLevel);
+ const SvxNumberFormat* pNewFmt = aNewRule.Get(nLevel);
+ if (pOldFmt && pNewFmt && (pOldFmt->GetFirstLineOffset() != pNewFmt->GetFirstLineOffset() || pOldFmt->GetAbsLSpace() != pNewFmt->GetAbsLSpace()))
+ {
+ std::unique_ptr<SvxNumberFormat> pNewFmtClone(new SvxNumberFormat(*pNewFmt));
+ pNewFmtClone->SetFirstLineOffset(pOldFmt->GetFirstLineOffset());
+ pNewFmtClone->SetAbsLSpace(pOldFmt->GetAbsLSpace());
+ aNewRule.SetLevel(nLevel, pNewFmtClone.get());
+ }
+ }
+ }
+ }
+
+ aAttrs.Put(SvxNumBulletItem(aNewRule, EE_PARA_NUMBULLET));
+ }
+ }
+ pOwner->SetParaAttribs(nPara, aAttrs);
+ }
+ }
+
+ const sal_uInt16 nParaCount = static_cast<sal_uInt16>(pOwner->pParaList->GetParagraphCount());
+ pOwner->ImplCheckParagraphs( nStartPara, nParaCount );
+ pOwner->pEditEngine->QuickMarkInvalid( ESelection( nStartPara, 0, nParaCount, 0 ) );
+
+ pOwner->pEditEngine->SetUpdateMode( bUpdate );
+
+ pOwner->UndoActionEnd();
+}
+
+
+void OutlinerView::SwitchOffBulletsNumbering(
+ const bool bAtSelection )
+{
+ sal_Int32 nStartPara = 0;
+ sal_Int32 nEndPara = 0;
+ if ( bAtSelection )
+ {
+ ESelection aSel( pEditView->GetSelection() );
+ aSel.Adjust();
+ nStartPara = aSel.nStartPara;
+ nEndPara = aSel.nEndPara;
+ }
+ else
+ {
+ nStartPara = 0;
+ nEndPara = pOwner->pParaList->GetParagraphCount() - 1;
+ }
+
+ pOwner->UndoActionStart( OLUNDO_DEPTH );
+ const bool bUpdate = pOwner->pEditEngine->GetUpdateMode();
+ pOwner->pEditEngine->SetUpdateMode( false );
+
+ for ( sal_Int32 nPara = nStartPara; nPara <= nEndPara; ++nPara )
+ {
+ Paragraph* pPara = pOwner->pParaList->GetParagraph( nPara );
+ DBG_ASSERT(pPara, "OutlinerView::SwitchOffBulletsNumbering(...), illegal paragraph index?");
+
+ if( pPara )
+ {
+ pOwner->SetDepth( pPara, -1 );
+
+ const SfxItemSet& rAttrs = pOwner->GetParaAttribs( nPara );
+ if (rAttrs.GetItemState( EE_PARA_BULLETSTATE ) == SfxItemState::SET)
+ {
+ SfxItemSet aAttrs(rAttrs);
+ aAttrs.ClearItem( EE_PARA_BULLETSTATE );
+ pOwner->SetParaAttribs( nPara, aAttrs );
+ }
+ }
+ }
+
+ const sal_uInt16 nParaCount = static_cast<sal_uInt16>(pOwner->pParaList->GetParagraphCount());
+ pOwner->ImplCheckParagraphs( nStartPara, nParaCount );
+ pOwner->pEditEngine->QuickMarkInvalid( ESelection( nStartPara, 0, nParaCount, 0 ) );
+
+ pOwner->pEditEngine->SetUpdateMode( bUpdate );
+ pOwner->UndoActionEnd();
+}
+
+
+void OutlinerView::RemoveAttribsKeepLanguages( bool bRemoveParaAttribs )
+{
+ RemoveAttribs( bRemoveParaAttribs, true /*keep language attribs*/ );
+}
+
+void OutlinerView::RemoveAttribs( bool bRemoveParaAttribs, bool bKeepLanguages )
+{
+ bool bUpdate = pOwner->GetUpdateMode();
+ pOwner->SetUpdateMode( false );
+ pOwner->UndoActionStart( OLUNDO_ATTR );
+ if (bKeepLanguages)
+ pEditView->RemoveAttribsKeepLanguages( bRemoveParaAttribs );
+ else
+ pEditView->RemoveAttribs( bRemoveParaAttribs );
+ if ( bRemoveParaAttribs )
+ {
+ // Loop through all paragraphs and set indentation and level
+ ESelection aSel = pEditView->GetSelection();
+ aSel.Adjust();
+ for ( sal_Int32 nPara = aSel.nStartPara; nPara <= aSel.nEndPara; nPara++ )
+ {
+ Paragraph* pPara = pOwner->pParaList->GetParagraph( nPara );
+ pOwner->ImplInitDepth( nPara, pPara->GetDepth(), false );
+ }
+ }
+ pOwner->UndoActionEnd();
+ pOwner->SetUpdateMode( bUpdate );
+}
+
+
+// ====================== Simple pass-through =======================
+
+
+void OutlinerView::InsertText( const OUString& rNew, bool bSelect )
+{
+ if( pOwner->bFirstParaIsEmpty )
+ pOwner->Insert( OUString() );
+ pEditView->InsertText( rNew, bSelect );
+}
+
+void OutlinerView::SetVisArea( const tools::Rectangle& rRect )
+{
+ pEditView->SetVisArea( rRect );
+}
+
+
+void OutlinerView::SetSelection( const ESelection& rSel )
+{
+ pEditView->SetSelection( rSel );
+}
+
+void OutlinerView::GetSelectionRectangles(std::vector<tools::Rectangle>& rLogicRects) const
+{
+ pEditView->GetSelectionRectangles(rLogicRects);
+}
+
+void OutlinerView::SetReadOnly( bool bReadOnly )
+{
+ pEditView->SetReadOnly( bReadOnly );
+}
+
+bool OutlinerView::IsReadOnly() const
+{
+ return pEditView->IsReadOnly();
+}
+
+bool OutlinerView::HasSelection() const
+{
+ return pEditView->HasSelection();
+}
+
+void OutlinerView::ShowCursor( bool bGotoCursor, bool bActivate )
+{
+ pEditView->ShowCursor( bGotoCursor, /*bForceVisCursor=*/true, bActivate );
+}
+
+void OutlinerView::HideCursor(bool bDeactivate)
+{
+ pEditView->HideCursor(bDeactivate);
+}
+
+void OutlinerView::SetWindow( vcl::Window* pWin )
+{
+ pEditView->SetWindow( pWin );
+}
+
+vcl::Window* OutlinerView::GetWindow() const
+{
+ return pEditView->GetWindow();
+}
+
+void OutlinerView::SetOutputArea( const tools::Rectangle& rRect )
+{
+ pEditView->SetOutputArea( rRect );
+}
+
+tools::Rectangle const & OutlinerView::GetOutputArea() const
+{
+ return pEditView->GetOutputArea();
+}
+
+OUString OutlinerView::GetSelected() const
+{
+ return pEditView->GetSelected();
+}
+
+void OutlinerView::StartSpeller()
+{
+ pEditView->StartSpeller();
+}
+
+EESpellState OutlinerView::StartThesaurus()
+{
+ return pEditView->StartThesaurus();
+}
+
+void OutlinerView::StartTextConversion(
+ LanguageType nSrcLang, LanguageType nDestLang, const vcl::Font *pDestFont,
+ sal_Int32 nOptions, bool bIsInteractive, bool bMultipleDoc )
+{
+ if (
+ (LANGUAGE_KOREAN == nSrcLang && LANGUAGE_KOREAN == nDestLang) ||
+ (LANGUAGE_CHINESE_SIMPLIFIED == nSrcLang && LANGUAGE_CHINESE_TRADITIONAL == nDestLang) ||
+ (LANGUAGE_CHINESE_TRADITIONAL == nSrcLang && LANGUAGE_CHINESE_SIMPLIFIED == nDestLang)
+ )
+ {
+ pEditView->StartTextConversion( nSrcLang, nDestLang, pDestFont, nOptions, bIsInteractive, bMultipleDoc );
+ }
+ else
+ {
+ OSL_FAIL( "unexpected language" );
+ }
+}
+
+
+sal_Int32 OutlinerView::StartSearchAndReplace( const SvxSearchItem& rSearchItem )
+{
+ return pEditView->StartSearchAndReplace( rSearchItem );
+}
+
+void OutlinerView::TransliterateText( TransliterationFlags nTransliterationMode )
+{
+ pEditView->TransliterateText( nTransliterationMode );
+}
+
+ESelection OutlinerView::GetSelection() const
+{
+ return pEditView->GetSelection();
+}
+
+
+void OutlinerView::Scroll( long nHorzScroll, long nVertScroll )
+{
+ pEditView->Scroll( nHorzScroll, nVertScroll );
+}
+
+void OutlinerView::SetControlWord( EVControlBits nWord )
+{
+ pEditView->SetControlWord( nWord );
+}
+
+EVControlBits OutlinerView::GetControlWord() const
+{
+ return pEditView->GetControlWord();
+}
+
+void OutlinerView::SetAnchorMode( EEAnchorMode eMode )
+{
+ pEditView->SetAnchorMode( eMode );
+}
+
+EEAnchorMode OutlinerView::GetAnchorMode() const
+{
+ return pEditView->GetAnchorMode();
+}
+
+void OutlinerView::Copy()
+{
+ pEditView->Copy();
+}
+
+void OutlinerView::InsertField( const SvxFieldItem& rFld )
+{
+ pEditView->InsertField( rFld );
+}
+
+const SvxFieldItem* OutlinerView::GetFieldUnderMousePointer() const
+{
+ return pEditView->GetFieldUnderMousePointer();
+}
+
+const SvxFieldItem* OutlinerView::GetFieldAtSelection() const
+{
+ return pEditView->GetFieldAtSelection();
+}
+
+const SvxFieldData* OutlinerView::GetFieldAtCursor() const
+{
+ return pEditView->GetFieldAtCursor();
+}
+
+void OutlinerView::SelectFieldAtCursor()
+{
+ pEditView->SelectFieldAtCursor();
+}
+
+void OutlinerView::SetInvalidateMore( sal_uInt16 nPixel )
+{
+ pEditView->SetInvalidateMore( nPixel );
+}
+
+
+sal_uInt16 OutlinerView::GetInvalidateMore() const
+{
+ return pEditView->GetInvalidateMore();
+}
+
+
+bool OutlinerView::IsCursorAtWrongSpelledWord()
+{
+ return pEditView->IsCursorAtWrongSpelledWord();
+}
+
+
+bool OutlinerView::IsWrongSpelledWordAtPos( const Point& rPosPixel, bool bMarkIfWrong )
+{
+ return pEditView->IsWrongSpelledWordAtPos( rPosPixel, bMarkIfWrong );
+}
+
+void OutlinerView::ExecuteSpellPopup( const Point& rPosPixel, Link<SpellCallbackInfo&,void> const * pStartDlg )
+{
+ pEditView->ExecuteSpellPopup( rPosPixel, pStartDlg );
+}
+
+void OutlinerView::Read( SvStream& rInput, EETextFormat eFormat, SvKeyValueIterator* pHTTPHeaderAttrs )
+{
+ sal_Int32 nOldParaCount = pEditView->GetEditEngine()->GetParagraphCount();
+ ESelection aOldSel = pEditView->GetSelection();
+ aOldSel.Adjust();
+
+ pEditView->Read( rInput, eFormat, pHTTPHeaderAttrs );
+
+ long nParaDiff = pEditView->GetEditEngine()->GetParagraphCount() - nOldParaCount;
+ sal_Int32 nChangesStart = aOldSel.nStartPara;
+ sal_Int32 nChangesEnd = nChangesStart + nParaDiff + (aOldSel.nEndPara-aOldSel.nStartPara);
+
+ for ( sal_Int32 n = nChangesStart; n <= nChangesEnd; n++ )
+ {
+ if ( pOwner->ImplGetOutlinerMode() == OutlinerMode::OutlineObject )
+ pOwner->ImplSetLevelDependentStyleSheet( n );
+ }
+
+ pOwner->ImpFilterIndents( nChangesStart, nChangesEnd );
+}
+
+void OutlinerView::SetBackgroundColor( const Color& rColor )
+{
+ pEditView->SetBackgroundColor( rColor );
+}
+
+void OutlinerView::RegisterViewShell(OutlinerViewShell* pViewShell)
+{
+ pEditView->RegisterViewShell(pViewShell);
+}
+
+Color const & OutlinerView::GetBackgroundColor() const
+{
+ return pEditView->GetBackgroundColor();
+}
+
+SfxItemSet OutlinerView::GetAttribs()
+{
+ return pEditView->GetAttribs();
+}
+
+SvtScriptType OutlinerView::GetSelectedScriptType() const
+{
+ return pEditView->GetSelectedScriptType();
+}
+
+OUString OutlinerView::GetSurroundingText() const
+{
+ return pEditView->GetSurroundingText();
+}
+
+Selection OutlinerView::GetSurroundingTextSelection() const
+{
+ return pEditView->GetSurroundingTextSelection();
+}
+
+// ===== some code for thesaurus sub menu within context menu
+
+
+namespace {
+
+bool isSingleScriptType( SvtScriptType nScriptType )
+{
+ sal_uInt8 nScriptCount = 0;
+
+ if (nScriptType & SvtScriptType::LATIN)
+ ++nScriptCount;
+ if (nScriptType & SvtScriptType::ASIAN)
+ ++nScriptCount;
+ if (nScriptType & SvtScriptType::COMPLEX)
+ ++nScriptCount;
+
+ return nScriptCount == 1;
+}
+
+}
+
+// returns: true if a word for thesaurus look-up was found at the current cursor position.
+// The status string will be word + iso language string (e.g. "light#en-US")
+bool GetStatusValueForThesaurusFromContext(
+ OUString &rStatusVal,
+ LanguageType &rLang,
+ const EditView &rEditView )
+{
+ // get text and locale for thesaurus look up
+ OUString aText;
+ EditEngine *pEditEngine = rEditView.GetEditEngine();
+ ESelection aTextSel( rEditView.GetSelection() );
+ if (!aTextSel.HasRange())
+ aTextSel = pEditEngine->GetWord( aTextSel, i18n::WordType::DICTIONARY_WORD );
+ aText = pEditEngine->GetText( aTextSel );
+ aTextSel.Adjust();
+
+ if (!isSingleScriptType(pEditEngine->GetScriptType(aTextSel)))
+ return false;
+
+ LanguageType nLang = pEditEngine->GetLanguage( aTextSel.nStartPara, aTextSel.nStartPos );
+ OUString aLangText( LanguageTag::convertToBcp47( nLang ) );
+
+ // set word and locale to look up as status value
+ rStatusVal = aText + "#" + aLangText;
+ rLang = nLang;
+
+ return aText.getLength() > 0;
+}
+
+
+void ReplaceTextWithSynonym( EditView &rEditView, const OUString &rSynonmText )
+{
+ // get selection to use
+ ESelection aCurSel( rEditView.GetSelection() );
+ if (!rEditView.HasSelection())
+ {
+ // select the same word that was used in GetStatusValueForThesaurusFromContext by calling GetWord.
+ // (In the end both functions will call ImpEditEngine::SelectWord)
+ rEditView.SelectCurrentWord( i18n::WordType::DICTIONARY_WORD );
+ aCurSel = rEditView.GetSelection();
+ }
+
+ // replace word ...
+ rEditView.InsertText( rSynonmText );
+ rEditView.ShowCursor( true, false );
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/outliner/overflowingtxt.cxx b/editeng/source/outliner/overflowingtxt.cxx
new file mode 100644
index 000000000..16be74813
--- /dev/null
+++ b/editeng/source/outliner/overflowingtxt.cxx
@@ -0,0 +1,226 @@
+/* -*- 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 <rtl/ustring.hxx>
+#include <sal/log.hxx>
+
+#include <editeng/overflowingtxt.hxx>
+#include <editeng/outliner.hxx>
+#include <editeng/outlobj.hxx>
+#include <editeng/editeng.hxx>
+#include <editeng/editobj.hxx>
+#include <editeng/editdata.hxx>
+
+#include <editdoc.hxx>
+
+
+std::unique_ptr<OutlinerParaObject> TextChainingUtils::JuxtaposeParaObject(
+ css::uno::Reference< css::datatransfer::XTransferable > const & xOverflowingContent,
+ Outliner *pOutl,
+ OutlinerParaObject const *pNextPObj)
+{
+ if (!pNextPObj) {
+ pOutl->SetToEmptyText();
+ } else {
+ pOutl->SetText(*pNextPObj);
+ }
+
+ // Special case: if only empty text remove it at the end
+ bool bOnlyOneEmptyPara = !pNextPObj ||
+ (pOutl->GetParagraphCount() == 1 &&
+ pNextPObj->GetTextObject().GetText(0).isEmpty());
+
+ EditEngine &rEditEngine = const_cast<EditEngine &>(pOutl->GetEditEngine());
+
+ // XXX: this code should be moved in Outliner directly
+ // creating Outliner::InsertText(...transferable...)
+ EditSelection aStartSel(rEditEngine.CreateSelection(ESelection(0,0)));
+ EditSelection aNewSel = rEditEngine.InsertText(xOverflowingContent,
+ OUString(),
+ aStartSel.Min(),
+ true);
+
+ if (!bOnlyOneEmptyPara) {
+ // Separate Paragraphs
+ rEditEngine.InsertParaBreak(aNewSel);
+ }
+
+
+ return pOutl->CreateParaObject();
+}
+
+std::unique_ptr<OutlinerParaObject> TextChainingUtils::DeeplyMergeParaObject(
+ css::uno::Reference< css::datatransfer::XTransferable > const & xOverflowingContent,
+ Outliner *pOutl,
+ OutlinerParaObject const *pNextPObj)
+{
+ if (!pNextPObj) {
+ pOutl->SetToEmptyText();
+ } else {
+ pOutl->SetText(*pNextPObj);
+ }
+
+ EditEngine &rEditEngine = const_cast<EditEngine &>(pOutl->GetEditEngine());
+
+ // XXX: this code should be moved in Outliner directly
+ // creating Outliner::InsertText(...transferable...)
+ EditSelection aStartSel(rEditEngine.CreateSelection(ESelection(0,0)));
+ // We don't need to mark the selection
+ // EditSelection aNewSel =
+ rEditEngine.InsertText(xOverflowingContent,
+ OUString(),
+ aStartSel.Min(),
+ true);
+
+ return pOutl->CreateParaObject();
+}
+
+css::uno::Reference< css::datatransfer::XTransferable > TextChainingUtils::CreateTransferableFromText(Outliner const *pOutl)
+{
+ const EditEngine &rEditEngine = pOutl->GetEditEngine();
+ sal_Int32 nLastPara = pOutl->GetParagraphCount()-1;
+ ESelection aWholeTextSel(0, 0, nLastPara, rEditEngine.GetTextLen(nLastPara));
+
+ return rEditEngine.CreateTransferable(aWholeTextSel);
+}
+
+
+
+OverflowingText::OverflowingText(css::uno::Reference< css::datatransfer::XTransferable > const & xOverflowingContent) :
+ mxOverflowingContent(xOverflowingContent)
+{
+
+}
+
+
+
+NonOverflowingText::NonOverflowingText(const ESelection &aSel, bool bLastParaInterrupted)
+ : maContentSel(aSel)
+ , mbLastParaInterrupted(bLastParaInterrupted)
+{
+}
+
+bool NonOverflowingText::IsLastParaInterrupted() const
+{
+ return mbLastParaInterrupted;
+}
+
+
+std::unique_ptr<OutlinerParaObject> NonOverflowingText::RemoveOverflowingText(Outliner *pOutliner) const
+{
+ pOutliner->QuickDelete(maContentSel);
+ SAL_INFO("editeng.chaining", "Deleting selection from (Para: " << maContentSel.nStartPara
+ << ", Pos: " << maContentSel.nStartPos << ") to (Para: " << maContentSel.nEndPara
+ << ", Pos: " << maContentSel.nEndPos << ")");
+ return pOutliner->CreateParaObject();
+}
+
+ESelection NonOverflowingText::GetOverflowPointSel() const
+{
+ //return getLastPositionSel(mpContentTextObj);
+
+ // return the starting point of the selection we are removing
+ return ESelection(maContentSel.nStartPara, maContentSel.nStartPos); //XXX
+}
+
+// The equivalent of ToParaObject for OverflowingText. Here we are prepending the overflowing text to the old dest box's text
+// XXX: In a sense a better name for OverflowingText and NonOverflowingText are respectively DestLinkText and SourceLinkText
+std::unique_ptr<OutlinerParaObject> OverflowingText::JuxtaposeParaObject(Outliner *pOutl, OutlinerParaObject const *pNextPObj)
+{
+ return TextChainingUtils::JuxtaposeParaObject(mxOverflowingContent, pOutl, pNextPObj);
+}
+
+std::unique_ptr<OutlinerParaObject> OverflowingText::DeeplyMergeParaObject(Outliner *pOutl, OutlinerParaObject const *pNextPObj)
+{
+ return TextChainingUtils::DeeplyMergeParaObject(mxOverflowingContent, pOutl, pNextPObj);
+}
+
+
+OFlowChainedText::OFlowChainedText(Outliner const *pOutl, bool bIsDeepMerge)
+{
+ mpOverflowingTxt = pOutl->GetOverflowingText();
+ mpNonOverflowingTxt = pOutl->GetNonOverflowingText();
+
+ mbIsDeepMerge = bIsDeepMerge;
+}
+
+OFlowChainedText::~OFlowChainedText()
+{
+}
+
+
+ESelection OFlowChainedText::GetOverflowPointSel() const
+{
+ return mpNonOverflowingTxt->GetOverflowPointSel();
+}
+
+std::unique_ptr<OutlinerParaObject> OFlowChainedText::InsertOverflowingText(Outliner *pOutliner, OutlinerParaObject const *pTextToBeMerged)
+{
+ // Just return the roughly merged paras for now
+ if (mpOverflowingTxt == nullptr)
+ return nullptr;
+
+ if (mbIsDeepMerge) {
+ SAL_INFO("editeng.chaining", "[TEXTCHAINFLOW - OF] Deep merging paras" );
+ return mpOverflowingTxt->DeeplyMergeParaObject(pOutliner, pTextToBeMerged );
+ } else {
+ SAL_INFO("editeng.chaining", "[TEXTCHAINFLOW - OF] Juxtaposing paras" );
+ return mpOverflowingTxt->JuxtaposeParaObject(pOutliner, pTextToBeMerged );
+ }
+}
+
+
+std::unique_ptr<OutlinerParaObject> OFlowChainedText::RemoveOverflowingText(Outliner *pOutliner)
+{
+ if (mpNonOverflowingTxt == nullptr)
+ return nullptr;
+
+ return mpNonOverflowingTxt->RemoveOverflowingText(pOutliner);
+}
+
+bool OFlowChainedText::IsLastParaInterrupted() const
+{
+ return mpNonOverflowingTxt->IsLastParaInterrupted();
+}
+
+
+
+UFlowChainedText::UFlowChainedText(Outliner const *pOutl, bool bIsDeepMerge)
+{
+ mxUnderflowingTxt = TextChainingUtils::CreateTransferableFromText(pOutl);
+ mbIsDeepMerge = bIsDeepMerge;
+}
+
+std::unique_ptr<OutlinerParaObject> UFlowChainedText::CreateMergedUnderflowParaObject(Outliner *pOutl, OutlinerParaObject const *pNextLinkWholeText)
+{
+ std::unique_ptr<OutlinerParaObject> pNewText;
+
+ if (mbIsDeepMerge) {
+ SAL_INFO("editeng.chaining", "[TEXTCHAINFLOW - UF] Deep merging paras" );
+ pNewText = TextChainingUtils::DeeplyMergeParaObject(mxUnderflowingTxt, pOutl, pNextLinkWholeText);
+ } else {
+ // NewTextForCurBox = Txt(CurBox) ++ Txt(NextBox)
+ SAL_INFO("editeng.chaining", "[TEXTCHAINFLOW - UF] Juxtaposing paras" );
+ pNewText = TextChainingUtils::JuxtaposeParaObject(mxUnderflowingTxt, pOutl, pNextLinkWholeText);
+ }
+
+ return pNewText;
+
+}
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/outliner/paralist.cxx b/editeng/source/outliner/paralist.cxx
new file mode 100644
index 000000000..4e03e24c2
--- /dev/null
+++ b/editeng/source/outliner/paralist.cxx
@@ -0,0 +1,256 @@
+/* -*- 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 "paralist.hxx"
+
+#include <editeng/outliner.hxx>
+#include <editeng/numdef.hxx>
+#include <o3tl/safeint.hxx>
+#include <osl/diagnose.h>
+#include <sal/log.hxx>
+#include <tools/debug.hxx>
+#include <libxml/xmlwriter.h>
+
+ParagraphData::ParagraphData()
+: nDepth( -1 )
+, mnNumberingStartValue( -1 )
+, mbParaIsNumberingRestart( false )
+{
+}
+
+bool ParagraphData::operator==(const ParagraphData& rCandidate) const
+{
+ return (nDepth == rCandidate.nDepth
+ && mnNumberingStartValue == rCandidate.mnNumberingStartValue
+ && mbParaIsNumberingRestart == rCandidate.mbParaIsNumberingRestart);
+}
+
+Paragraph::Paragraph( sal_Int16 nDDepth )
+: aBulSize( -1, -1)
+{
+
+ DBG_ASSERT( ( nDDepth >= -1 ) && ( nDDepth < SVX_MAX_NUM ), "Paragraph-CTOR: nDepth invalid!" );
+
+ nDepth = nDDepth;
+ nFlags = ParaFlag::NONE;
+ bVisible = true;
+}
+
+Paragraph::Paragraph( const ParagraphData& rData )
+: nFlags( ParaFlag::NONE )
+, aBulSize( -1, -1)
+, bVisible( true )
+{
+ nDepth = rData.nDepth;
+ mnNumberingStartValue = rData.mnNumberingStartValue;
+ mbParaIsNumberingRestart = rData.mbParaIsNumberingRestart;
+}
+
+Paragraph::~Paragraph()
+{
+}
+
+void Paragraph::SetNumberingStartValue( sal_Int16 nNumberingStartValue )
+{
+ mnNumberingStartValue = nNumberingStartValue;
+ if( mnNumberingStartValue != -1 )
+ mbParaIsNumberingRestart = true;
+}
+
+void Paragraph::SetParaIsNumberingRestart( bool bParaIsNumberingRestart )
+{
+ mbParaIsNumberingRestart = bParaIsNumberingRestart;
+ if( !mbParaIsNumberingRestart )
+ mnNumberingStartValue = -1;
+}
+
+void Paragraph::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ xmlTextWriterStartElement(pWriter, BAD_CAST("Paragraph"));
+ xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("nDepth"), "%" SAL_PRIdINT32, static_cast<sal_Int32>(nDepth));
+ xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("mnNumberingStartValue"), "%" SAL_PRIdINT32, static_cast<sal_Int32>(mnNumberingStartValue));
+ xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("mbParaIsNumberingRestart"), "%" SAL_PRIdINT32, static_cast<sal_Int32>(mbParaIsNumberingRestart));
+ xmlTextWriterEndElement(pWriter);
+}
+
+void ParagraphList::Clear()
+{
+ maEntries.clear();
+}
+
+void ParagraphList::Append( std::unique_ptr<Paragraph> pPara)
+{
+ SAL_WARN_IF( maEntries.size() >= EE_PARA_MAX_COUNT, "editeng", "ParagraphList::Append - overflow");
+ maEntries.push_back(std::move(pPara));
+}
+
+void ParagraphList::Insert( std::unique_ptr<Paragraph> pPara, sal_Int32 nAbsPos)
+{
+ SAL_WARN_IF( nAbsPos < 0 || (maEntries.size() < o3tl::make_unsigned(nAbsPos) && nAbsPos != EE_PARA_APPEND),
+ "editeng", "ParagraphList::Insert - bad insert position " << nAbsPos);
+ SAL_WARN_IF( maEntries.size() >= EE_PARA_MAX_COUNT, "editeng", "ParagraphList::Insert - overflow");
+
+ if (nAbsPos < 0 || maEntries.size() <= o3tl::make_unsigned(nAbsPos))
+ Append( std::move(pPara) );
+ else
+ maEntries.insert(maEntries.begin()+nAbsPos, std::move(pPara));
+}
+
+void ParagraphList::Remove( sal_Int32 nPara )
+{
+ if (nPara < 0 || maEntries.size() <= o3tl::make_unsigned(nPara))
+ {
+ SAL_WARN( "editeng", "ParagraphList::Remove - out of bounds " << nPara);
+ return;
+ }
+
+ maEntries.erase(maEntries.begin() + nPara );
+}
+
+void ParagraphList::MoveParagraphs( sal_Int32 nStart, sal_Int32 nDest, sal_Int32 _nCount )
+{
+ OSL_ASSERT(o3tl::make_unsigned(nStart) < maEntries.size() && o3tl::make_unsigned(nDest) < maEntries.size());
+
+ if ( (( nDest < nStart ) || ( nDest >= ( nStart + _nCount ) )) && nStart >= 0 && nDest >= 0 && _nCount >= 0 )
+ {
+ std::vector<std::unique_ptr<Paragraph>> aParas;
+ auto iterBeg = maEntries.begin() + nStart;
+ auto iterEnd = iterBeg + _nCount;
+
+ for (auto it = iterBeg; it != iterEnd; ++it)
+ aParas.push_back(std::move(*it));
+
+ maEntries.erase(iterBeg,iterEnd);
+
+ if ( nDest > nStart )
+ nDest -= _nCount;
+
+ for (auto & i : aParas)
+ {
+ maEntries.insert(maEntries.begin() + nDest, std::move(i));
+ ++nDest;
+ }
+ }
+ else
+ {
+ OSL_FAIL( "MoveParagraphs: Invalid Parameters" );
+ }
+}
+
+bool ParagraphList::HasChildren( Paragraph const * pParagraph ) const
+{
+ sal_Int32 n = GetAbsPos( pParagraph );
+ Paragraph* pNext = GetParagraph( ++n );
+ return pNext && ( pNext->GetDepth() > pParagraph->GetDepth() );
+}
+
+bool ParagraphList::HasHiddenChildren( Paragraph const * pParagraph ) const
+{
+ sal_Int32 n = GetAbsPos( pParagraph );
+ Paragraph* pNext = GetParagraph( ++n );
+ return pNext && ( pNext->GetDepth() > pParagraph->GetDepth() ) && !pNext->IsVisible();
+}
+
+bool ParagraphList::HasVisibleChildren( Paragraph const * pParagraph ) const
+{
+ sal_Int32 n = GetAbsPos( pParagraph );
+ Paragraph* pNext = GetParagraph( ++n );
+ return pNext && ( pNext->GetDepth() > pParagraph->GetDepth() ) && pNext->IsVisible();
+}
+
+sal_Int32 ParagraphList::GetChildCount( Paragraph const * pParent ) const
+{
+ sal_Int32 nChildCount = 0;
+ sal_Int32 n = GetAbsPos( pParent );
+ Paragraph* pPara = GetParagraph( ++n );
+ while ( pPara && ( pPara->GetDepth() > pParent->GetDepth() ) )
+ {
+ nChildCount++;
+ pPara = GetParagraph( ++n );
+ }
+ return nChildCount;
+}
+
+Paragraph* ParagraphList::GetParent( Paragraph const * pParagraph ) const
+{
+ sal_Int32 n = GetAbsPos( pParagraph );
+ Paragraph* pPrev = GetParagraph( --n );
+ while ( pPrev && ( pPrev->GetDepth() >= pParagraph->GetDepth() ) )
+ {
+ pPrev = GetParagraph( --n );
+ }
+
+ return pPrev;
+}
+
+void ParagraphList::Expand( Paragraph const * pParent )
+{
+ sal_Int32 nChildCount = GetChildCount( pParent );
+ sal_Int32 nPos = GetAbsPos( pParent );
+
+ for ( sal_Int32 n = 1; n <= nChildCount; n++ )
+ {
+ Paragraph* pPara = GetParagraph( nPos+n );
+ if ( !( pPara->IsVisible() ) )
+ {
+ pPara->bVisible = true;
+ aVisibleStateChangedHdl.Call( *pPara );
+ }
+ }
+}
+
+void ParagraphList::Collapse( Paragraph const * pParent )
+{
+ sal_Int32 nChildCount = GetChildCount( pParent );
+ sal_Int32 nPos = GetAbsPos( pParent );
+
+ for ( sal_Int32 n = 1; n <= nChildCount; n++ )
+ {
+ Paragraph* pPara = GetParagraph( nPos+n );
+ if ( pPara->IsVisible() )
+ {
+ pPara->bVisible = false;
+ aVisibleStateChangedHdl.Call( *pPara );
+ }
+ }
+}
+
+sal_Int32 ParagraphList::GetAbsPos( Paragraph const * pParent ) const
+{
+ sal_Int32 pos = 0;
+ for (auto const& entry : maEntries)
+ {
+ if (entry.get() == pParent)
+ return pos;
+ ++pos;
+ }
+
+ return EE_PARA_NOT_FOUND;
+}
+
+void ParagraphList::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ xmlTextWriterStartElement(pWriter, BAD_CAST("ParagraphList"));
+ for (auto const & pParagraph : maEntries)
+ pParagraph->dumpAsXml(pWriter);
+ xmlTextWriterEndElement(pWriter);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/outliner/paralist.hxx b/editeng/source/outliner/paralist.hxx
new file mode 100644
index 000000000..0b60ac78e
--- /dev/null
+++ b/editeng/source/outliner/paralist.hxx
@@ -0,0 +1,85 @@
+/* -*- 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 .
+ */
+
+#ifndef INCLUDED_EDITENG_SOURCE_OUTLINER_PARALIST_HXX
+#define INCLUDED_EDITENG_SOURCE_OUTLINER_PARALIST_HXX
+
+#include <sal/config.h>
+#include <sal/log.hxx>
+
+#include <memory>
+#include <vector>
+
+#include <editeng/outliner.hxx>
+#include <o3tl/safeint.hxx>
+#include <tools/link.hxx>
+
+class Paragraph;
+typedef struct _xmlTextWriter* xmlTextWriterPtr;
+
+class ParagraphList
+{
+public:
+ void Clear();
+
+ sal_Int32 GetParagraphCount() const
+ {
+ size_t nSize = maEntries.size();
+ if (nSize > SAL_MAX_INT32)
+ {
+ SAL_WARN( "editeng", "ParagraphList::GetParagraphCount - overflow " << nSize);
+ return SAL_MAX_INT32;
+ }
+ return nSize;
+ }
+
+ Paragraph* GetParagraph( sal_Int32 nPos ) const
+ {
+ return 0 <= nPos && o3tl::make_unsigned(nPos) < maEntries.size() ? maEntries[nPos].get() : nullptr;
+ }
+
+ sal_Int32 GetAbsPos( Paragraph const * pParent ) const;
+
+ void Append( std::unique_ptr<Paragraph> pPara);
+ void Insert( std::unique_ptr<Paragraph> pPara, sal_Int32 nAbsPos);
+ void Remove( sal_Int32 nPara );
+ void MoveParagraphs( sal_Int32 nStart, sal_Int32 nDest, sal_Int32 nCount );
+
+ Paragraph* GetParent( Paragraph const * pParagraph ) const;
+ bool HasChildren( Paragraph const * pParagraph ) const;
+ bool HasHiddenChildren( Paragraph const * pParagraph ) const;
+ bool HasVisibleChildren( Paragraph const * pParagraph ) const;
+ sal_Int32 GetChildCount( Paragraph const * pParagraph ) const;
+
+ void Expand( Paragraph const * pParent );
+ void Collapse( Paragraph const * pParent );
+
+ void SetVisibleStateChangedHdl( const Link<Paragraph&,void>& rLink ) { aVisibleStateChangedHdl = rLink; }
+
+ void dumpAsXml(xmlTextWriterPtr pWriter) const;
+
+private:
+
+ Link<Paragraph&,void> aVisibleStateChangedHdl;
+ std::vector<std::unique_ptr<Paragraph>> maEntries;
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */