diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
commit | ed5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch) | |
tree | 7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /editeng/source/outliner | |
parent | Initial commit. (diff) | |
download | libreoffice-upstream.tar.xz libreoffice-upstream.zip |
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'editeng/source/outliner')
-rw-r--r-- | editeng/source/outliner/outleeng.cxx | 192 | ||||
-rw-r--r-- | editeng/source/outliner/outleeng.hxx | 84 | ||||
-rw-r--r-- | editeng/source/outliner/outlin2.cxx | 600 | ||||
-rw-r--r-- | editeng/source/outliner/outliner.cxx | 2170 | ||||
-rw-r--r-- | editeng/source/outliner/outlobj.cxx | 254 | ||||
-rw-r--r-- | editeng/source/outliner/outlundo.cxx | 164 | ||||
-rw-r--r-- | editeng/source/outliner/outlundo.hxx | 118 | ||||
-rw-r--r-- | editeng/source/outliner/outlvw.cxx | 1503 | ||||
-rw-r--r-- | editeng/source/outliner/overflowingtxt.cxx | 227 | ||||
-rw-r--r-- | editeng/source/outliner/paralist.cxx | 256 | ||||
-rw-r--r-- | editeng/source/outliner/paralist.hxx | 82 |
11 files changed, 5650 insertions, 0 deletions
diff --git a/editeng/source/outliner/outleeng.cxx b/editeng/source/outliner/outleeng.cxx new file mode 100644 index 000000000..e4fc414ad --- /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, const Point& rOrigin, Degree10 nOrientation, OutputDevice& rOutDev) +{ + if( GetControlWord() & EEControlBits::OUTLINER ) + { + PaintFirstLineInfo aInfo(nPara, rStartPos, &rOutDev); + pOwner->maPaintFirstLineHdl.Call( &aInfo ); + } + + pOwner->PaintBullet(nPara, rStartPos, rOrigin, nOrientation, rOutDev); +} + +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, + o3tl::span<const sal_Int32> pDXArray, const SvxFont& rFont, sal_Int32 nPara, sal_uInt8 nRightToLeft, + const EEngineData::WrongSpellVector* pWrongSpellVector, + const SvxFieldData* pFieldData, + bool bEndOfLine, + bool bEndOfParagraph, + 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, tools::Long nWidth, const OUString& rChar, + const SvxFont& rFont, sal_Int32 nPara, sal_uInt8 nRightToLeft, + bool bEndOfLine, bool bEndOfParagraph, + const Color& rOverlineColor, const Color& rTextLineColor) +{ + 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..963e74582 --- /dev/null +++ b/editeng/source/outliner/outleeng.hxx @@ -0,0 +1,84 @@ +/* -*- 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 . + */ +#pragma once + +#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, const Point& rOrigin, Degree10 nOrientation, OutputDevice& rOutDev) 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, o3tl::span<const sal_Int32> pDXArray, const SvxFont& rFont, + sal_Int32 nPara, sal_uInt8 nRightToLeft, + const EEngineData::WrongSpellVector* pWrongSpellVector, + const SvxFieldData* pFieldData, + bool bEndOfLine, + bool bEndOfParagraph, + const css::lang::Locale* pLocale, + const Color& rOverlineColor, + const Color& rTextLineColor) override; + + virtual void DrawingTab( + const Point& rStartPos, tools::Long nWidth, const OUString& rChar, + const SvxFont& rFont, sal_Int32 nPara, sal_uInt8 nRightToLeft, + bool bEndOfLine, + bool bEndOfParagraph, + const Color& rOverlineColor, + const Color& rTextLineColor) 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; +}; + +/* 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..951f69235 --- /dev/null +++ b/editeng/source/outliner/outlin2.cxx @@ -0,0 +1,600 @@ +/* -*- 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 ======================= + + +bool Outliner::SetUpdateLayout( bool bUpdate ) +{ + return pEditEngine->SetUpdateLayout( bUpdate ); +} + + +bool Outliner::IsUpdateLayout() const +{ + return pEditEngine->IsUpdateLayout(); +} + +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_uInt32 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& rOutDev, const tools::Rectangle& rOutRect ) +{ + pEditEngine->Draw( rOutDev, rOutRect ); +} + +void Outliner::Draw( OutputDevice& rOutDev, const Point& rStartPos ) +{ + pEditEngine->Draw( rOutDev, 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 ); +} + +void Outliner::SetMinColumnWrapHeight(tools::Long nVal) +{ + pEditEngine->SetMinColumnWrapHeight(nVal); +} + +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_uInt32 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_uInt32 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.Contains( 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->IsEffectivelyVertical(); +} + +bool Outliner::IsTopToBottom() const +{ + return pEditEngine->IsTopToBottom(); +} + +void Outliner::SetTextColumns(sal_Int16 nColumns, sal_Int32 nSpacing) +{ + pEditEngine->SetTextColumns(nColumns, nSpacing); +} + +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 ).nLang; +} + +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..13e490f87 --- /dev/null +++ b/editeng/source/outliner/outliner.cxx @@ -0,0 +1,2170 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <comphelper/string.hxx> +#include <svl/eitem.hxx> +#include <svl/intitem.hxx> +#include <editeng/editeng.hxx> +#include <editeng/editview.hxx> +#include <editeng/editdata.hxx> +#include <editeng/lrspitem.hxx> + +#include <math.h> +#include <svl/style.hxx> +#include <editeng/outliner.hxx> +#include "paralist.hxx" +#include <editeng/outlobj.hxx> +#include "outleeng.hxx" +#include "outlundo.hxx" +#include <editeng/eeitem.hxx> +#include <editeng/editstat.hxx> +#include <editeng/overflowingtxt.hxx> +#include <editeng/editobj.hxx> +#include <svl/itemset.hxx> +#include <vcl/metric.hxx> +#include <editeng/numitem.hxx> +#include <editeng/adjustitem.hxx> +#include <vcl/GraphicObject.hxx> +#include <editeng/svxfont.hxx> +#include <editeng/brushitem.hxx> +#include <svl/itempool.hxx> +#include <libxml/xmlwriter.h> +#include <sal/log.hxx> +#include <o3tl/safeint.hxx> +#include <o3tl/string_view.hxx> +#include <osl/diagnose.h> + +#include <memory> +using std::advance; + + +// Outliner + + +void Outliner::ImplCheckDepth( sal_Int16& rnDepth ) const +{ + if( rnDepth < gnMinDepth ) + rnDepth = gnMinDepth; + else if( rnDepth > nMaxDepth ) + rnDepth = nMaxDepth; +} + +Paragraph* Outliner::Insert(const OUString& rText, sal_Int32 nAbsPos, sal_Int16 nDepth) +{ + DBG_ASSERT(pParaList->GetParagraphCount(),"Insert:No Paras"); + + Paragraph* pPara; + + ImplCheckDepth( nDepth ); + + sal_Int32 nParagraphCount = pParaList->GetParagraphCount(); + if( nAbsPos > nParagraphCount ) + nAbsPos = nParagraphCount; + + if( bFirstParaIsEmpty ) + { + pPara = pParaList->GetParagraph( 0 ); + if( pPara->GetDepth() != nDepth ) + { + nDepthChangedHdlPrevDepth = pPara->GetDepth(); + ParaFlag nPrevFlags = pPara->nFlags; + pPara->SetDepth( nDepth ); + DepthChangedHdl(pPara, nPrevFlags); + } + pPara->nFlags |= ParaFlag::HOLDDEPTH; + SetText( rText, pPara ); + } + else + { + bool bUpdate = pEditEngine->SetUpdateLayout( false ); + ImplBlockInsertionCallbacks( true ); + pPara = new Paragraph( nDepth ); + pParaList->Insert( std::unique_ptr<Paragraph>(pPara), nAbsPos ); + pEditEngine->InsertParagraph( nAbsPos, OUString() ); + DBG_ASSERT(pPara==pParaList->GetParagraph(nAbsPos),"Insert:Failed"); + ImplInitDepth( nAbsPos, nDepth, false ); + ParagraphInsertedHdl(pPara); + pPara->nFlags |= ParaFlag::HOLDDEPTH; + SetText( rText, pPara ); + ImplBlockInsertionCallbacks( false ); + pEditEngine->SetUpdateLayout( bUpdate ); + } + bFirstParaIsEmpty = false; + DBG_ASSERT(pEditEngine->GetParagraphCount()==pParaList->GetParagraphCount(),"SetText failed"); + return pPara; +} + + +void Outliner::ParagraphInserted( sal_Int32 nPara ) +{ + + if ( nBlockInsCallback ) + return; + + if( bPasting || pEditEngine->IsInUndo() ) + { + Paragraph* pPara = new Paragraph( -1 ); + pParaList->Insert( std::unique_ptr<Paragraph>(pPara), nPara ); + if( pEditEngine->IsInUndo() ) + { + pPara->nFlags = ParaFlag::SETBULLETTEXT; + pPara->bVisible = true; + const SfxInt16Item& rLevel = pEditEngine->GetParaAttrib( nPara, EE_PARA_OUTLLEVEL ); + pPara->SetDepth( rLevel.GetValue() ); + } + } + else + { + sal_Int16 nDepth = -1; + Paragraph* pParaBefore = pParaList->GetParagraph( nPara-1 ); + if ( pParaBefore ) + nDepth = pParaBefore->GetDepth(); + + Paragraph* pPara = new Paragraph( nDepth ); + pParaList->Insert( std::unique_ptr<Paragraph>(pPara), nPara ); + + if( !pEditEngine->IsInUndo() ) + { + ImplCalcBulletText( nPara, true, false ); + ParagraphInsertedHdl(pPara); + } + } +} + +void Outliner::ParagraphDeleted( sal_Int32 nPara ) +{ + + if ( nBlockInsCallback || ( nPara == EE_PARA_ALL ) ) + return; + + Paragraph* pPara = pParaList->GetParagraph( nPara ); + if (!pPara) + return; + + sal_Int16 nDepth = pPara->GetDepth(); + + if( !pEditEngine->IsInUndo() ) + { + aParaRemovingHdl.Call( { this, pPara } ); + } + + pParaList->Remove( nPara ); + + if( pEditEngine->IsInUndo() || bPasting ) + return; + + pPara = pParaList->GetParagraph( nPara ); + if ( pPara && ( pPara->GetDepth() > nDepth ) ) + { + ImplCalcBulletText( nPara, true, false ); + // Search for next on the this level ... + while ( pPara && pPara->GetDepth() > nDepth ) + pPara = pParaList->GetParagraph( ++nPara ); + } + + if ( pPara && ( pPara->GetDepth() == nDepth ) ) + ImplCalcBulletText( nPara, true, false ); +} + +void Outliner::Init( OutlinerMode nMode ) +{ + nOutlinerMode = nMode; + + Clear(); + + EEControlBits nCtrl = pEditEngine->GetControlWord(); + nCtrl &= ~EEControlBits(EEControlBits::OUTLINER|EEControlBits::OUTLINER2); + + SetMaxDepth( 9 ); + + switch ( GetOutlinerMode() ) + { + case OutlinerMode::TextObject: + case OutlinerMode::TitleObject: + break; + + case OutlinerMode::OutlineObject: + nCtrl |= EEControlBits::OUTLINER2; + break; + case OutlinerMode::OutlineView: + nCtrl |= EEControlBits::OUTLINER; + break; + + default: OSL_FAIL( "Outliner::Init - Invalid Mode!" ); + } + + pEditEngine->SetControlWord( nCtrl ); + + const bool bWasUndoEnabled(IsUndoEnabled()); + EnableUndo(false); + ImplInitDepth( 0, -1, false ); + GetUndoManager().Clear(); + EnableUndo(bWasUndoEnabled); +} + +void Outliner::SetMaxDepth( sal_Int16 nDepth ) +{ + if( nMaxDepth != nDepth ) + { + nMaxDepth = std::min( nDepth, sal_Int16(SVX_MAX_NUM-1) ); + } +} + +sal_Int16 Outliner::GetDepth( sal_Int32 nPara ) const +{ + Paragraph* pPara = pParaList->GetParagraph( nPara ); + DBG_ASSERT( pPara, "Outliner::GetDepth - Paragraph not found!" ); + return pPara ? pPara->GetDepth() : -1; +} + +void Outliner::SetDepth( Paragraph* pPara, sal_Int16 nNewDepth ) +{ + + ImplCheckDepth( nNewDepth ); + + if ( nNewDepth == pPara->GetDepth() ) + return; + + nDepthChangedHdlPrevDepth = pPara->GetDepth(); + ParaFlag nPrevFlags = pPara->nFlags; + + sal_Int32 nPara = GetAbsPos( pPara ); + ImplInitDepth( nPara, nNewDepth, true ); + ImplCalcBulletText( nPara, false, false ); + + if ( GetOutlinerMode() == OutlinerMode::OutlineObject ) + ImplSetLevelDependentStyleSheet( nPara ); + + DepthChangedHdl(pPara, nPrevFlags); +} + +sal_Int16 Outliner::GetNumberingStartValue( sal_Int32 nPara ) const +{ + Paragraph* pPara = pParaList->GetParagraph( nPara ); + DBG_ASSERT( pPara, "Outliner::GetNumberingStartValue - Paragraph not found!" ); + return pPara ? pPara->GetNumberingStartValue() : -1; +} + +void Outliner::SetNumberingStartValue( sal_Int32 nPara, sal_Int16 nNumberingStartValue ) +{ + Paragraph* pPara = pParaList->GetParagraph( nPara ); + DBG_ASSERT( pPara, "Outliner::GetNumberingStartValue - Paragraph not found!" ); + if( pPara && pPara->GetNumberingStartValue() != nNumberingStartValue ) + { + if( IsUndoEnabled() && !IsInUndo() ) + InsertUndo( std::make_unique<OutlinerUndoChangeParaNumberingRestart>( this, nPara, + pPara->GetNumberingStartValue(), nNumberingStartValue, + pPara->IsParaIsNumberingRestart(), pPara->IsParaIsNumberingRestart() ) ); + + pPara->SetNumberingStartValue( nNumberingStartValue ); + ImplCheckParagraphs( nPara, pParaList->GetParagraphCount() ); + pEditEngine->SetModified(); + } +} + +bool Outliner::IsParaIsNumberingRestart( sal_Int32 nPara ) const +{ + Paragraph* pPara = pParaList->GetParagraph( nPara ); + DBG_ASSERT( pPara, "Outliner::IsParaIsNumberingRestart - Paragraph not found!" ); + return pPara && pPara->IsParaIsNumberingRestart(); +} + +void Outliner::SetParaIsNumberingRestart( sal_Int32 nPara, bool bParaIsNumberingRestart ) +{ + Paragraph* pPara = pParaList->GetParagraph( nPara ); + DBG_ASSERT( pPara, "Outliner::SetParaIsNumberingRestart - Paragraph not found!" ); + if( pPara && (pPara->IsParaIsNumberingRestart() != bParaIsNumberingRestart) ) + { + if( IsUndoEnabled() && !IsInUndo() ) + InsertUndo( std::make_unique<OutlinerUndoChangeParaNumberingRestart>( this, nPara, + pPara->GetNumberingStartValue(), pPara->GetNumberingStartValue(), + pPara->IsParaIsNumberingRestart(), bParaIsNumberingRestart ) ); + + pPara->SetParaIsNumberingRestart( bParaIsNumberingRestart ); + ImplCheckParagraphs( nPara, pParaList->GetParagraphCount() ); + pEditEngine->SetModified(); + } +} + +sal_Int32 Outliner::GetBulletsNumberingStatus( + const sal_Int32 nParaStart, + const sal_Int32 nParaEnd ) const +{ + if ( nParaStart > nParaEnd + || nParaEnd >= pParaList->GetParagraphCount() ) + { + SAL_WARN("editeng", "<Outliner::GetBulletsNumberingStatus> - unexpected parameter values" ); + return 2; + } + + sal_Int32 nBulletsCount = 0; + sal_Int32 nNumberingCount = 0; + for (sal_Int32 nPara = nParaStart; nPara <= nParaEnd; ++nPara) + { + if ( !pParaList->GetParagraph(nPara) ) + { + break; + } + const SvxNumberFormat* pFmt = GetNumberFormat(nPara); + if (!pFmt) + { + // At least, exists one paragraph that has no Bullets/Numbering. + break; + } + else if ((pFmt->GetNumberingType() == SVX_NUM_BITMAP) || (pFmt->GetNumberingType() == SVX_NUM_CHAR_SPECIAL)) + { + // Having Bullets in this paragraph. + nBulletsCount++; + } + else + { + // Having Numbering in this paragraph. + nNumberingCount++; + } + } + + const sal_Int32 nParaCount = nParaEnd - nParaStart + 1; + if ( nBulletsCount == nParaCount ) + { + return 0; + } + else if ( nNumberingCount == nParaCount ) + { + return 1; + } + return 2; +} + +sal_Int32 Outliner::GetBulletsNumberingStatus() const +{ + return pParaList->GetParagraphCount() > 0 + ? GetBulletsNumberingStatus( 0, pParaList->GetParagraphCount()-1 ) + : 2; +} + +std::optional<OutlinerParaObject> Outliner::CreateParaObject( sal_Int32 nStartPara, sal_Int32 nCount ) const +{ + if ( static_cast<sal_uLong>(nStartPara) + nCount > + o3tl::make_unsigned(pParaList->GetParagraphCount()) ) + nCount = pParaList->GetParagraphCount() - nStartPara; + + // When a new OutlinerParaObject is created because a paragraph is just being deleted, + // it can happen that the ParaList is not updated yet... + if ( ( nStartPara + nCount ) > pEditEngine->GetParagraphCount() ) + nCount = pEditEngine->GetParagraphCount() - nStartPara; + + if (nCount <= 0) + return std::nullopt; + + std::unique_ptr<EditTextObject> xText = pEditEngine->CreateTextObject( nStartPara, nCount ); + const bool bIsEditDoc(OutlinerMode::TextObject == GetOutlinerMode()); + ParagraphDataVector aParagraphDataVector(nCount); + const sal_Int32 nLastPara(nStartPara + nCount - 1); + + for(sal_Int32 nPara(nStartPara); nPara <= nLastPara; nPara++) + { + aParagraphDataVector[nPara-nStartPara] = *GetParagraph(nPara); + } + + xText->ClearPortionInfo(); // tdf#147166 the PortionInfo is unwanted here + OutlinerParaObject aPObj(std::move(xText), std::move(aParagraphDataVector), bIsEditDoc); + aPObj.SetOutlinerMode(GetOutlinerMode()); + + return aPObj; +} + +void Outliner::SetToEmptyText() +{ + SetText(GetEmptyParaObject()); +} + +void Outliner::SetText( const OUString& rText, Paragraph* pPara ) +{ + DBG_ASSERT(pPara,"SetText:No Para"); + + const sal_Int32 nPara = pParaList->GetAbsPos( pPara ); + + if (pEditEngine->GetText( nPara ) == rText) + { + // short-circuit logic to improve performance + bFirstParaIsEmpty = false; + return; + } + + const bool bUpdate = pEditEngine->SetUpdateLayout( false ); + ImplBlockInsertionCallbacks( true ); + + if (rText.isEmpty()) + { + pEditEngine->SetText( nPara, rText ); + ImplInitDepth( nPara, pPara->GetDepth(), false ); + } + else + { + const OUString aText(convertLineEnd(rText, LINEEND_LF)); + + sal_Int32 nPos = 0; + sal_Int32 nInsPos = nPara+1; + sal_Int32 nIdx {0}; + // Loop over all tokens, but ignore the last one if empty + // (i.e. if strings ends with the delimiter, detected by + // checking nIdx against string length). This check also + // handle empty strings. + while( nIdx>=0 && nIdx<aText.getLength() ) + { + std::u16string_view aStr = o3tl::getToken(aText, 0, '\x0A', nIdx ); + + sal_Int16 nCurDepth; + if( nPos ) + { + pPara = new Paragraph( -1 ); + nCurDepth = -1; + } + else + nCurDepth = pPara->GetDepth(); + + // In the outliner mode, filter the tabs and set the indentation + // about a LRSpaceItem. In EditEngine mode intend over old tabs + if( ( GetOutlinerMode() == OutlinerMode::OutlineObject ) || + ( GetOutlinerMode() == OutlinerMode::OutlineView ) ) + { + // Extract Tabs + size_t nTabs = 0; + while ( ( nTabs < aStr.size() ) && ( aStr[nTabs] == '\t' ) ) + nTabs++; + if ( nTabs ) + aStr = aStr.substr(nTabs); + + // Keep depth? (see Outliner::Insert) + if( !(pPara->nFlags & ParaFlag::HOLDDEPTH) ) + { + nCurDepth = nTabs-1; //TODO: sal_Int32 -> sal_Int16! + ImplCheckDepth( nCurDepth ); + pPara->SetDepth( nCurDepth ); + pPara->nFlags &= ~ParaFlag::HOLDDEPTH; + } + } + if( nPos ) // not with the first paragraph + { + pParaList->Insert( std::unique_ptr<Paragraph>(pPara), nInsPos ); + pEditEngine->InsertParagraph( nInsPos, OUString(aStr) ); + ParagraphInsertedHdl(pPara); + } + else + { + nInsPos--; + pEditEngine->SetText( nInsPos, OUString(aStr) ); + } + ImplInitDepth( nInsPos, nCurDepth, false ); + nInsPos++; + nPos++; + } + } + + DBG_ASSERT(pParaList->GetParagraphCount()==pEditEngine->GetParagraphCount(),"SetText failed!"); + bFirstParaIsEmpty = false; + ImplBlockInsertionCallbacks( false ); + // Restore the update mode. + pEditEngine->SetUpdateLayout(bUpdate, /*bRestoring=*/true); +} + +// pView == 0 -> Ignore tabs + +bool Outliner::ImpConvertEdtToOut( sal_Int32 nPara ) +{ + + bool bConverted = false; + sal_Int32 nTabs = 0; + ESelection aDelSel; + + OUString aName; + + OUString aStr( pEditEngine->GetText( nPara ) ); + const sal_Unicode* pPtr = aStr.getStr(); + + sal_Int32 nHeadingNumberStart = 0; + sal_Int32 nNumberingNumberStart = 0; + SfxStyleSheet* pStyle= pEditEngine->GetStyleSheet( nPara ); + if( pStyle ) + { + OUString aHeading_US( "heading" ); + OUString aNumber_US( "Numbering" ); + aName = pStyle->GetName(); + sal_Int32 nSearch; + if ( ( nSearch = aName.indexOf( aHeading_US ) ) != -1 ) + nHeadingNumberStart = nSearch + aHeading_US.getLength(); + else if ( ( nSearch = aName.indexOf( aNumber_US ) ) != -1 ) + nNumberingNumberStart = nSearch + aNumber_US.getLength(); + } + + if ( nHeadingNumberStart || nNumberingNumberStart ) + { + // PowerPoint import ? + if( nHeadingNumberStart && ( aStr.getLength() >= 2 ) && + ( pPtr[0] != '\t' ) && ( pPtr[1] == '\t' ) ) + { + // Extract Bullet and Tab + aDelSel = ESelection( nPara, 0, nPara, 2 ); + } + + sal_Int32 nPos = nHeadingNumberStart ? nHeadingNumberStart : nNumberingNumberStart; + std::u16string_view aLevel = comphelper::string::stripStart(aName.subView(nPos), ' '); + nTabs = o3tl::toInt32(aLevel); + if( nTabs ) + nTabs--; // Level 0 = "heading 1" + bConverted = true; + } + else + { + // filter leading tabs + while( *pPtr == '\t' ) + { + pPtr++; + nTabs++; + } + // Remove tabs from the text + if( nTabs ) + aDelSel = ESelection( nPara, 0, nPara, nTabs ); + } + + if ( aDelSel.HasRange() ) + { + pEditEngine->QuickDelete( aDelSel ); + } + + const SfxInt16Item& rLevel = pEditEngine->GetParaAttrib( nPara, EE_PARA_OUTLLEVEL ); + sal_Int16 nOutlLevel = rLevel.GetValue(); + + ImplCheckDepth( nOutlLevel ); + ImplInitDepth( nPara, nOutlLevel, false ); + + return bConverted; +} + +void Outliner::SetText( const OutlinerParaObject& rPObj ) +{ + bool bUpdate = pEditEngine->SetUpdateLayout( false ); + + bool bUndo = pEditEngine->IsUndoEnabled(); + EnableUndo( false ); + + Init( rPObj.GetOutlinerMode() ); + + ImplBlockInsertionCallbacks( true ); + pEditEngine->SetText(rPObj.GetTextObject()); + + bFirstParaIsEmpty = false; + + pParaList->Clear(); + for( sal_Int32 nCurPara = 0; nCurPara < rPObj.Count(); nCurPara++ ) + { + std::unique_ptr<Paragraph> pPara(new Paragraph( rPObj.GetParagraphData(nCurPara))); + ImplCheckDepth( pPara->nDepth ); + + pParaList->Append(std::move(pPara)); + ImplCheckNumBulletItem( nCurPara ); + } + + ImplCheckParagraphs( 0, pParaList->GetParagraphCount() ); + + EnableUndo( bUndo ); + ImplBlockInsertionCallbacks( false ); + pEditEngine->SetUpdateLayout( bUpdate ); + + DBG_ASSERT( pParaList->GetParagraphCount()==rPObj.Count(),"SetText failed"); + DBG_ASSERT( pEditEngine->GetParagraphCount()==rPObj.Count(),"SetText failed"); +} + +void Outliner::AddText( const OutlinerParaObject& rPObj, bool bAppend ) +{ + bool bUpdate = pEditEngine->SetUpdateLayout( false ); + + ImplBlockInsertionCallbacks( true ); + sal_Int32 nPara; + if( bFirstParaIsEmpty ) + { + pParaList->Clear(); + pEditEngine->SetText(rPObj.GetTextObject()); + nPara = 0; + bAppend = false; + } + else + { + nPara = pParaList->GetParagraphCount(); + pEditEngine->InsertParagraph( EE_PARA_APPEND, rPObj.GetTextObject(), bAppend ); + } + bFirstParaIsEmpty = false; + + for( sal_Int32 n = 0; n < rPObj.Count(); n++ ) + { + if ( n == 0 && bAppend ) + { + // This first "paragraph" was just appended to an existing (incomplete) paragraph. + // Since no new paragraph will be added, the assumed increase-by-1 also won't happen. + --nPara; + continue; + } + + Paragraph* pPara = new Paragraph( rPObj.GetParagraphData(n) ); + pParaList->Append(std::unique_ptr<Paragraph>(pPara)); + sal_Int32 nP = nPara+n; + DBG_ASSERT(pParaList->GetAbsPos(pPara)==nP,"AddText:Out of sync"); + ImplInitDepth( nP, pPara->GetDepth(), false ); + } + DBG_ASSERT( pEditEngine->GetParagraphCount()==pParaList->GetParagraphCount(), "SetText: OutOfSync" ); + + ImplCheckParagraphs( nPara, pParaList->GetParagraphCount() ); + + ImplBlockInsertionCallbacks( false ); + pEditEngine->SetUpdateLayout( bUpdate ); +} + +OUString Outliner::CalcFieldValue( const SvxFieldItem& rField, sal_Int32 nPara, sal_Int32 nPos, std::optional<Color>& rpTxtColor, std::optional<Color>& rpFldColor ) +{ + if ( !aCalcFieldValueHdl.IsSet() ) + return OUString( ' ' ); + + EditFieldInfo aFldInfo( this, rField, nPara, nPos ); + // The FldColor is preset with COL_LIGHTGRAY. + if ( rpFldColor ) + aFldInfo.SetFieldColor( *rpFldColor ); + + aCalcFieldValueHdl.Call( &aFldInfo ); + if ( aFldInfo.GetTextColor() ) + { + rpTxtColor = *aFldInfo.GetTextColor(); + } + + if (aFldInfo.GetFieldColor()) + rpFldColor = *aFldInfo.GetFieldColor(); + else + rpFldColor.reset(); + + return aFldInfo.GetRepresentation(); +} + +void Outliner::SetStyleSheet( sal_Int32 nPara, SfxStyleSheet* pStyle ) +{ + Paragraph* pPara = pParaList->GetParagraph( nPara ); + if (pPara) + { + pEditEngine->SetStyleSheet( nPara, pStyle ); + pPara->nFlags |= ParaFlag::SETBULLETTEXT; + ImplCheckNumBulletItem( nPara ); + } +} + +void Outliner::ImplCheckNumBulletItem( sal_Int32 nPara ) +{ + Paragraph* pPara = pParaList->GetParagraph( nPara ); + if (pPara) + pPara->aBulSize.setWidth( -1 ); +} + +void Outliner::ImplSetLevelDependentStyleSheet( sal_Int32 nPara ) +{ + + DBG_ASSERT( ( GetOutlinerMode() == OutlinerMode::OutlineObject ) || ( GetOutlinerMode() == OutlinerMode::OutlineView ), "SetLevelDependentStyleSheet: Wrong Mode!" ); + + SfxStyleSheet* pStyle = GetStyleSheet( nPara ); + + if ( !pStyle ) + return; + + sal_Int16 nDepth = GetDepth( nPara ); + if( nDepth < 0 ) + nDepth = 0; + + OUString aNewStyleSheetName( pStyle->GetName() ); + aNewStyleSheetName = aNewStyleSheetName.subView( 0, aNewStyleSheetName.getLength()-1 ) + + OUString::number( nDepth+1 ); + SfxStyleSheet* pNewStyle = static_cast<SfxStyleSheet*>(GetStyleSheetPool()->Find( aNewStyleSheetName, pStyle->GetFamily() )); + DBG_ASSERT( pNewStyle, "AutoStyleSheetName - Style not found!" ); + if ( pNewStyle && ( pNewStyle != GetStyleSheet( nPara ) ) ) + { + SfxItemSet aOldAttrs( GetParaAttribs( nPara ) ); + SetStyleSheet( nPara, pNewStyle ); + if ( aOldAttrs.GetItemState( EE_PARA_NUMBULLET ) == SfxItemState::SET ) + { + SfxItemSet aAttrs( GetParaAttribs( nPara ) ); + aAttrs.Put( aOldAttrs.Get( EE_PARA_NUMBULLET ) ); + SetParaAttribs( nPara, aAttrs ); + } + } +} + +void Outliner::ImplInitDepth( sal_Int32 nPara, sal_Int16 nDepth, bool bCreateUndo ) +{ + + DBG_ASSERT( ( nDepth >= gnMinDepth ) && ( nDepth <= nMaxDepth ), "ImplInitDepth - Depth is invalid!" ); + + Paragraph* pPara = pParaList->GetParagraph( nPara ); + if (!pPara) + return; + sal_Int16 nOldDepth = pPara->GetDepth(); + pPara->SetDepth( nDepth ); + + // For IsInUndo attributes and style do not have to be set, there + // the old values are restored by the EditEngine. + if( IsInUndo() ) + return; + + bool bUpdate = pEditEngine->SetUpdateLayout( false ); + + bool bUndo = bCreateUndo && IsUndoEnabled(); + + SfxItemSet aAttrs( pEditEngine->GetParaAttribs( nPara ) ); + aAttrs.Put( SfxInt16Item( EE_PARA_OUTLLEVEL, nDepth ) ); + pEditEngine->SetParaAttribs( nPara, aAttrs ); + ImplCheckNumBulletItem( nPara ); + ImplCalcBulletText( nPara, false, false ); + + if ( bUndo ) + { + InsertUndo( std::make_unique<OutlinerUndoChangeDepth>( this, nPara, nOldDepth, nDepth ) ); + } + + pEditEngine->SetUpdateLayout( bUpdate ); +} + +void Outliner::SetParaAttribs( sal_Int32 nPara, const SfxItemSet& rSet ) +{ + + pEditEngine->SetParaAttribs( nPara, rSet ); +} + +void Outliner::SetCharAttribs(sal_Int32 nPara, const SfxItemSet& rSet) +{ + pEditEngine->SetCharAttribs(nPara, rSet); +} + +bool Outliner::Expand( Paragraph const * pPara ) +{ + if ( !pParaList->HasHiddenChildren( pPara ) ) + return false; + + std::unique_ptr<OLUndoExpand> pUndo; + bool bUndo = IsUndoEnabled() && !IsInUndo(); + if( bUndo ) + { + UndoActionStart( OLUNDO_EXPAND ); + pUndo.reset( new OLUndoExpand( this, OLUNDO_EXPAND ) ); + pUndo->nCount = pParaList->GetAbsPos( pPara ); + } + pParaList->Expand( pPara ); + InvalidateBullet(pParaList->GetAbsPos(pPara)); + if( bUndo ) + { + InsertUndo( std::move(pUndo) ); + UndoActionEnd(); + } + return true; +} + +bool Outliner::Collapse( Paragraph const * pPara ) +{ + if ( !pParaList->HasVisibleChildren( pPara ) ) // collapsed + return false; + + std::unique_ptr<OLUndoExpand> pUndo; + bool bUndo = false; + + if( !IsInUndo() && IsUndoEnabled() ) + bUndo = true; + if( bUndo ) + { + UndoActionStart( OLUNDO_COLLAPSE ); + pUndo.reset( new OLUndoExpand( this, OLUNDO_COLLAPSE ) ); + pUndo->nCount = pParaList->GetAbsPos( pPara ); + } + + pParaList->Collapse( pPara ); + InvalidateBullet(pParaList->GetAbsPos(pPara)); + if( bUndo ) + { + InsertUndo( std::move(pUndo) ); + UndoActionEnd(); + } + return true; +} + + +vcl::Font Outliner::ImpCalcBulletFont( sal_Int32 nPara ) const +{ + const SvxNumberFormat* pFmt = GetNumberFormat( nPara ); + DBG_ASSERT( pFmt && ( pFmt->GetNumberingType() != SVX_NUM_BITMAP ) && ( pFmt->GetNumberingType() != SVX_NUM_NUMBER_NONE ), "ImpCalcBulletFont: Missing or BitmapBullet!" ); + + vcl::Font aStdFont; + if ( !pEditEngine->IsFlatMode() ) + { + ESelection aSel( nPara, 0, nPara, 0 ); + aStdFont = EditEngine::CreateFontFromItemSet( pEditEngine->GetAttribs( aSel ), pEditEngine->GetScriptType( aSel ) ); + } + else + { + aStdFont = pEditEngine->GetStandardFont( nPara ); + } + + vcl::Font aBulletFont; + std::optional<vcl::Font> pSourceFont; + if ( pFmt->GetNumberingType() == SVX_NUM_CHAR_SPECIAL ) + { + pSourceFont = pFmt->GetBulletFont(); + } + + if (pSourceFont) + { + aBulletFont = *pSourceFont; + } + else + { + aBulletFont = aStdFont; + aBulletFont.SetUnderline( LINESTYLE_NONE ); + aBulletFont.SetOverline( LINESTYLE_NONE ); + aBulletFont.SetStrikeout( STRIKEOUT_NONE ); + aBulletFont.SetEmphasisMark( FontEmphasisMark::NONE ); + aBulletFont.SetRelief( FontRelief::NONE ); + } + + // Use original scale... + sal_uInt16 nStretchX, nStretchY; + GetGlobalCharStretching(nStretchX, nStretchY); + + sal_uInt16 nScale = pFmt->GetBulletRelSize() * nStretchY / 100; + tools::Long nScaledLineHeight = aStdFont.GetFontSize().Height(); + nScaledLineHeight *= nScale*10; + nScaledLineHeight /= 1000; + + aBulletFont.SetAlignment( ALIGN_BOTTOM ); + aBulletFont.SetFontSize( Size( 0, nScaledLineHeight ) ); + bool bVertical = IsVertical(); + aBulletFont.SetVertical( bVertical ); + aBulletFont.SetOrientation( Degree10(bVertical ? (IsTopToBottom() ? 2700 : 900) : 0) ); + + Color aColor( COL_AUTO ); + if( !pEditEngine->IsFlatMode() && !( pEditEngine->GetControlWord() & EEControlBits::NOCOLORS ) ) + { + aColor = pFmt->GetBulletColor(); + } + + if ( ( aColor == COL_AUTO ) || ( IsForceAutoColor() ) ) + aColor = pEditEngine->GetAutoColor(); + + aBulletFont.SetColor( aColor ); + return aBulletFont; +} + +void Outliner::PaintBullet(sal_Int32 nPara, const Point& rStartPos, const Point& rOrigin, + Degree10 nOrientation, OutputDevice& rOutDev) +{ + + bool bDrawBullet = false; + if (pEditEngine) + { + const SfxBoolItem& rBulletState = pEditEngine->GetParaAttrib( nPara, EE_PARA_BULLETSTATE ); + bDrawBullet = rBulletState.GetValue(); + } + + if (!(bDrawBullet && ImplHasNumberFormat(nPara))) + return; + + bool bVertical = IsVertical(); + bool bTopToBottom = IsTopToBottom(); + + bool bRightToLeftPara = pEditEngine->IsRightToLeft( nPara ); + + tools::Rectangle aBulletArea( ImpCalcBulletArea( nPara, true, false ) ); + sal_uInt16 nStretchX, nStretchY; + GetGlobalCharStretching(nStretchX, nStretchY); + aBulletArea = tools::Rectangle( Point(aBulletArea.Left()*nStretchX/100, + aBulletArea.Top()), + Size(aBulletArea.GetWidth()*nStretchX/100, + aBulletArea.GetHeight()) ); + + Paragraph* pPara = pParaList->GetParagraph( nPara ); + const SvxNumberFormat* pFmt = GetNumberFormat( nPara ); + if ( pFmt && ( pFmt->GetNumberingType() != SVX_NUM_NUMBER_NONE ) ) + { + if( pFmt->GetNumberingType() != SVX_NUM_BITMAP ) + { + vcl::Font aBulletFont( ImpCalcBulletFont( nPara ) ); + // Use baseline + bool bSymbol = pFmt->GetNumberingType() == SVX_NUM_CHAR_SPECIAL; + aBulletFont.SetAlignment( bSymbol ? ALIGN_BOTTOM : ALIGN_BASELINE ); + vcl::Font aOldFont = rOutDev.GetFont(); + rOutDev.SetFont( aBulletFont ); + + ParagraphInfos aParaInfos = pEditEngine->GetParagraphInfos( nPara ); + Point aTextPos; + if ( !bVertical ) + { +// aTextPos.Y() = rStartPos.Y() + aBulletArea.Bottom(); + aTextPos.setY( rStartPos.Y() + ( bSymbol ? aBulletArea.Bottom() : aParaInfos.nFirstLineMaxAscent ) ); + if ( !bRightToLeftPara ) + aTextPos.setX( rStartPos.X() + aBulletArea.Left() ); + else + aTextPos.setX( rStartPos.X() + GetPaperSize().Width() - aBulletArea.Right() ); + } + else + { + if (bTopToBottom) + { +// aTextPos.X() = rStartPos.X() - aBulletArea.Bottom(); + aTextPos.setX( rStartPos.X() - (bSymbol ? aBulletArea.Bottom() : aParaInfos.nFirstLineMaxAscent) ); + aTextPos.setY( rStartPos.Y() + aBulletArea.Left() ); + } + else + { + aTextPos.setX( rStartPos.X() + (bSymbol ? aBulletArea.Bottom() : aParaInfos.nFirstLineMaxAscent) ); + aTextPos.setY( rStartPos.Y() + aBulletArea.Left() ); + } + } + + if ( nOrientation ) + { + // Both TopLeft and bottom left is not quite correct, + // since in EditEngine baseline ... + double nRealOrientation = toRadians(nOrientation); + double nCos = cos( nRealOrientation ); + double nSin = sin( nRealOrientation ); + Point aRotatedPos; + // Translation... + aTextPos -= rOrigin; + // Rotation... + aRotatedPos.setX(static_cast<tools::Long>(nCos*aTextPos.X() + nSin*aTextPos.Y()) ); + aRotatedPos.setY(static_cast<tools::Long>(- (nSin*aTextPos.X() - nCos*aTextPos.Y())) ); + aTextPos = aRotatedPos; + // Translation... + aTextPos += rOrigin; + vcl::Font aRotatedFont( aBulletFont ); + aRotatedFont.SetOrientation( nOrientation ); + rOutDev.SetFont( aRotatedFont ); + } + + // VCL will take care of brackets and so on... + vcl::text::ComplexTextLayoutFlags nLayoutMode = rOutDev.GetLayoutMode(); + nLayoutMode &= ~vcl::text::ComplexTextLayoutFlags(vcl::text::ComplexTextLayoutFlags::BiDiRtl|vcl::text::ComplexTextLayoutFlags::BiDiStrong); + if ( bRightToLeftPara ) + nLayoutMode |= vcl::text::ComplexTextLayoutFlags::BiDiRtl | vcl::text::ComplexTextLayoutFlags::TextOriginLeft | vcl::text::ComplexTextLayoutFlags::BiDiStrong; + rOutDev.SetLayoutMode( nLayoutMode ); + + if(bStrippingPortions) + { + const vcl::Font& aSvxFont(rOutDev.GetFont()); + std::vector<sal_Int32> aBuf; + rOutDev.GetTextArray( pPara->GetText(), &aBuf ); + + if(bSymbol) + { + // aTextPos is Bottom, go to Baseline + FontMetric aMetric(rOutDev.GetFontMetric()); + aTextPos.AdjustY( -(aMetric.GetDescent()) ); + } + + DrawingText(aTextPos, pPara->GetText(), 0, pPara->GetText().getLength(), aBuf, + aSvxFont, nPara, bRightToLeftPara ? 1 : 0, nullptr, nullptr, false, false, true, nullptr, Color(), Color()); + } + else + { + rOutDev.DrawText( aTextPos, pPara->GetText() ); + } + + rOutDev.SetFont( aOldFont ); + } + else + { + if ( pFmt->GetBrush()->GetGraphicObject() ) + { + Point aBulletPos; + if ( !bVertical ) + { + aBulletPos.setY( rStartPos.Y() + aBulletArea.Top() ); + if ( !bRightToLeftPara ) + aBulletPos.setX( rStartPos.X() + aBulletArea.Left() ); + else + aBulletPos.setX( rStartPos.X() + GetPaperSize().Width() - aBulletArea.Right() ); + } + else + { + if (bTopToBottom) + { + aBulletPos.setX( rStartPos.X() - aBulletArea.Bottom() ); + aBulletPos.setY( rStartPos.Y() + aBulletArea.Left() ); + } + else + { + aBulletPos.setX( rStartPos.X() + aBulletArea.Top() ); + aBulletPos.setY( rStartPos.Y() - aBulletArea.Right() ); + } + } + + if(bStrippingPortions) + { + if(aDrawBulletHdl.IsSet()) + { + // call something analog to aDrawPortionHdl (if set) and feed it something + // analog to DrawPortionInfo... + // created aDrawBulletHdl, Set/GetDrawBulletHdl. + // created DrawBulletInfo and added handling to sdrtextdecomposition.cxx + DrawBulletInfo aDrawBulletInfo( + *pFmt->GetBrush()->GetGraphicObject(), + aBulletPos, + pPara->aBulSize); + + aDrawBulletHdl.Call(&aDrawBulletInfo); + } + } + else + { + pFmt->GetBrush()->GetGraphicObject()->Draw(rOutDev, aBulletPos, pPara->aBulSize); + } + } + } + } + + // In case of collapsed subparagraphs paint a line before the text. + if( !pParaList->HasChildren(pPara) || pParaList->HasVisibleChildren(pPara) || + bStrippingPortions || nOrientation ) + return; + + tools::Long nWidth = rOutDev.PixelToLogic( Size( 10, 0 ) ).Width(); + + Point aStartPos, aEndPos; + if ( !bVertical ) + { + aStartPos.setY( rStartPos.Y() + aBulletArea.Bottom() ); + if ( !bRightToLeftPara ) + aStartPos.setX( rStartPos.X() + aBulletArea.Right() ); + else + aStartPos.setX( rStartPos.X() + GetPaperSize().Width() - aBulletArea.Left() ); + aEndPos = aStartPos; + aEndPos.AdjustX(nWidth ); + } + else + { + aStartPos.setX( rStartPos.X() - aBulletArea.Bottom() ); + aStartPos.setY( rStartPos.Y() + aBulletArea.Right() ); + aEndPos = aStartPos; + aEndPos.AdjustY(nWidth ); + } + + const Color& rOldLineColor = rOutDev.GetLineColor(); + rOutDev.SetLineColor( COL_BLACK ); + rOutDev.DrawLine( aStartPos, aEndPos ); + rOutDev.SetLineColor( rOldLineColor ); +} + +void Outliner::InvalidateBullet(sal_Int32 nPara) +{ + tools::Long nLineHeight = static_cast<tools::Long>(pEditEngine->GetLineHeight(nPara )); + for (OutlinerView* pView : aViewList) + { + Point aPos( pView->pEditView->GetWindowPosTopLeft(nPara ) ); + tools::Rectangle aRect( pView->GetOutputArea() ); + aRect.SetRight( aPos.X() ); + aRect.SetTop( aPos.Y() ); + aRect.SetBottom( aPos.Y() ); + aRect.AdjustBottom(nLineHeight ); + + pView->pEditView->InvalidateWindow(aRect); + } +} + +ErrCode Outliner::Read( SvStream& rInput, const OUString& rBaseURL, EETextFormat eFormat, SvKeyValueIterator* pHTTPHeaderAttrs ) +{ + + bool bOldUndo = pEditEngine->IsUndoEnabled(); + EnableUndo( false ); + + bool bUpdate = pEditEngine->SetUpdateLayout( false ); + + Clear(); + + ImplBlockInsertionCallbacks( true ); + ErrCode nRet = pEditEngine->Read( rInput, rBaseURL, eFormat, pHTTPHeaderAttrs ); + + bFirstParaIsEmpty = false; + + sal_Int32 nParas = pEditEngine->GetParagraphCount(); + pParaList->Clear(); + for ( sal_Int32 n = 0; n < nParas; n++ ) + { + std::unique_ptr<Paragraph> pPara(new Paragraph( 0 )); + pParaList->Append(std::move(pPara)); + } + + ImpFilterIndents( 0, nParas-1 ); + + ImplBlockInsertionCallbacks( false ); + pEditEngine->SetUpdateLayout( bUpdate ); + EnableUndo( bOldUndo ); + + return nRet; +} + + +void Outliner::ImpFilterIndents( sal_Int32 nFirstPara, sal_Int32 nLastPara ) +{ + bool bUpdate = pEditEngine->SetUpdateLayout( false ); + + Paragraph* pLastConverted = nullptr; + for( sal_Int32 nPara = nFirstPara; nPara <= nLastPara; nPara++ ) + { + Paragraph* pPara = pParaList->GetParagraph( nPara ); + if (pPara) + { + if( ImpConvertEdtToOut( nPara ) ) + { + pLastConverted = pPara; + } + else if ( pLastConverted ) + { + // Arrange normal paragraphs below the heading ... + pPara->SetDepth( pLastConverted->GetDepth() ); + } + + ImplInitDepth( nPara, pPara->GetDepth(), false ); + } + } + + pEditEngine->SetUpdateLayout( bUpdate ); +} + +SfxUndoManager& Outliner::GetUndoManager() +{ + return pEditEngine->GetUndoManager(); +} + +SfxUndoManager* Outliner::SetUndoManager(SfxUndoManager* pNew) +{ + return pEditEngine->SetUndoManager(pNew); +} + +void Outliner::ImpTextPasted( sal_Int32 nStartPara, sal_Int32 nCount ) +{ + bool bUpdate = pEditEngine->SetUpdateLayout( false ); + + const sal_Int32 nStart = nStartPara; + + Paragraph* pPara = pParaList->GetParagraph( nStartPara ); + + while( nCount && pPara ) + { + if( GetOutlinerMode() != OutlinerMode::TextObject ) + { + nDepthChangedHdlPrevDepth = pPara->GetDepth(); + ParaFlag nPrevFlags = pPara->nFlags; + + ImpConvertEdtToOut( nStartPara ); + + if( nStartPara == nStart ) + { + // the existing paragraph has changed depth or flags + if( (pPara->GetDepth() != nDepthChangedHdlPrevDepth) || (pPara->nFlags != nPrevFlags) ) + DepthChangedHdl(pPara, nPrevFlags); + } + } + else // EditEngine mode + { + sal_Int16 nDepth = -1; + const SfxItemSet& rAttrs = pEditEngine->GetParaAttribs( nStartPara ); + if ( rAttrs.GetItemState( EE_PARA_OUTLLEVEL ) == SfxItemState::SET ) + { + const SfxInt16Item& rLevel = rAttrs.Get( EE_PARA_OUTLLEVEL ); + nDepth = rLevel.GetValue(); + } + if ( nDepth != GetDepth( nStartPara ) ) + ImplInitDepth( nStartPara, nDepth, false ); + } + + nCount--; + nStartPara++; + pPara = pParaList->GetParagraph( nStartPara ); + } + + pEditEngine->SetUpdateLayout( bUpdate ); + + DBG_ASSERT(pParaList->GetParagraphCount()==pEditEngine->GetParagraphCount(),"ImpTextPasted failed"); +} + +bool Outliner::IndentingPagesHdl( OutlinerView* pView ) +{ + if( !aIndentingPagesHdl.IsSet() ) + return true; + return aIndentingPagesHdl.Call( pView ); +} + +bool Outliner::ImpCanIndentSelectedPages( OutlinerView* pCurView ) +{ + // The selected pages must already be set in advance through + // ImpCalcSelectedPages + + // If the first paragraph is on level 0 it can not indented in any case, + // possible there might be indentations in the following on the 0 level. + if ( ( mnFirstSelPage == 0 ) && ( GetOutlinerMode() != OutlinerMode::TextObject ) ) + { + if ( nDepthChangedHdlPrevDepth == 1 ) // is the only page + return false; + else + (void)pCurView->ImpCalcSelectedPages( false ); // without the first + } + return IndentingPagesHdl( pCurView ); +} + + +bool Outliner::ImpCanDeleteSelectedPages( OutlinerView* pCurView ) +{ + // The selected pages must already be set in advance through + // ImpCalcSelectedPages + return RemovingPagesHdl( pCurView ); +} + +Outliner::Outliner(SfxItemPool* pPool, OutlinerMode nMode) + : mnFirstSelPage(0) + , nDepthChangedHdlPrevDepth(0) + , nMaxDepth(9) + , bFirstParaIsEmpty(true) + , nBlockInsCallback(0) + , bStrippingPortions(false) + , bPasting(false) +{ + + pParaList.reset( new ParagraphList ); + pParaList->SetVisibleStateChangedHdl( LINK( this, Outliner, ParaVisibleStateChangedHdl ) ); + std::unique_ptr<Paragraph> pPara(new Paragraph( 0 )); + pParaList->Append(std::move(pPara)); + + pEditEngine.reset( new OutlinerEditEng( this, pPool ) ); + pEditEngine->SetBeginMovingParagraphsHdl( LINK( this, Outliner, BeginMovingParagraphsHdl ) ); + pEditEngine->SetEndMovingParagraphsHdl( LINK( this, Outliner, EndMovingParagraphsHdl ) ); + pEditEngine->SetBeginPasteOrDropHdl( LINK( this, Outliner, BeginPasteOrDropHdl ) ); + pEditEngine->SetEndPasteOrDropHdl( LINK( this, Outliner, EndPasteOrDropHdl ) ); + + Init( nMode ); +} + +Outliner::~Outliner() +{ + pParaList->Clear(); + pParaList.reset(); + pEditEngine.reset(); +} + +size_t Outliner::InsertView( OutlinerView* pView, size_t nIndex ) +{ + size_t ActualIndex; + + if ( nIndex >= aViewList.size() ) + { + aViewList.push_back( pView ); + ActualIndex = aViewList.size() - 1; + } + else + { + ViewList::iterator it = aViewList.begin(); + advance( it, nIndex ); + ActualIndex = nIndex; + } + pEditEngine->InsertView( pView->pEditView.get(), nIndex ); + return ActualIndex; +} + +void Outliner::RemoveView( OutlinerView const * pView ) +{ + ViewList::iterator it = std::find(aViewList.begin(), aViewList.end(), pView); + if (it != aViewList.end()) + { + pView->pEditView->HideCursor(); // HACK + pEditEngine->RemoveView( pView->pEditView.get() ); + aViewList.erase( it ); + } +} + +void Outliner::RemoveView( size_t nIndex ) +{ + EditView* pEditView = pEditEngine->GetView( nIndex ); + pEditView->HideCursor(); // HACK + + pEditEngine->RemoveView( nIndex ); + + { + ViewList::iterator it = aViewList.begin(); + advance( it, nIndex ); + aViewList.erase( it ); + } +} + + +OutlinerView* Outliner::GetView( size_t nIndex ) const +{ + return ( nIndex >= aViewList.size() ) ? nullptr : aViewList[ nIndex ]; +} + +size_t Outliner::GetViewCount() const +{ + return aViewList.size(); +} + +void Outliner::ParagraphInsertedHdl(Paragraph* pPara) +{ + if( !IsInUndo() ) + aParaInsertedHdl.Call( { this, pPara } ); +} + + +void Outliner::DepthChangedHdl(Paragraph* pPara, ParaFlag nPrevFlags) +{ + if( !IsInUndo() ) + aDepthChangedHdl.Call( { this, pPara, nPrevFlags } ); +} + + +sal_Int32 Outliner::GetAbsPos( Paragraph const * pPara ) const +{ + DBG_ASSERT(pPara,"GetAbsPos:No Para"); + return pParaList->GetAbsPos( pPara ); +} + +sal_Int32 Outliner::GetParagraphCount() const +{ + return pParaList->GetParagraphCount(); +} + +Paragraph* Outliner::GetParagraph( sal_Int32 nAbsPos ) const +{ + return pParaList->GetParagraph( nAbsPos ); +} + +bool Outliner::HasChildren( Paragraph const * pParagraph ) const +{ + return pParaList->HasChildren( pParagraph ); +} + +bool Outliner::ImplHasNumberFormat( sal_Int32 nPara ) const +{ + return GetNumberFormat(nPara) != nullptr; +} + +const SvxNumberFormat* Outliner::GetNumberFormat( sal_Int32 nPara ) const +{ + const SvxNumberFormat* pFmt = nullptr; + + Paragraph* pPara = pParaList->GetParagraph( nPara ); + if (!pPara) + return nullptr; + + sal_Int16 nDepth = pPara->GetDepth(); + + if( nDepth >= 0 ) + { + const SvxNumBulletItem& rNumBullet = pEditEngine->GetParaAttrib( nPara, EE_PARA_NUMBULLET ); + if ( rNumBullet.GetNumRule().GetLevelCount() > nDepth ) + pFmt = rNumBullet.GetNumRule().Get( nDepth ); + } + + return pFmt; +} + +Size Outliner::ImplGetBulletSize( sal_Int32 nPara ) +{ + Paragraph* pPara = pParaList->GetParagraph( nPara ); + if (!pPara) + return Size(); + + if( pPara->aBulSize.Width() == -1 ) + { + const SvxNumberFormat* pFmt = GetNumberFormat( nPara ); + DBG_ASSERT( pFmt, "ImplGetBulletSize - no Bullet!" ); + + if ( pFmt->GetNumberingType() == SVX_NUM_NUMBER_NONE ) + { + pPara->aBulSize = Size( 0, 0 ); + } + else if( pFmt->GetNumberingType() != SVX_NUM_BITMAP ) + { + OUString aBulletText = ImplGetBulletText( nPara ); + OutputDevice* pRefDev = pEditEngine->GetRefDevice(); + vcl::Font aBulletFont( ImpCalcBulletFont( nPara ) ); + vcl::Font aRefFont( pRefDev->GetFont()); + pRefDev->SetFont( aBulletFont ); + pPara->aBulSize.setWidth( pRefDev->GetTextWidth( aBulletText ) ); + pPara->aBulSize.setHeight( pRefDev->GetTextHeight() ); + pRefDev->SetFont( aRefFont ); + } + else + { + pPara->aBulSize = OutputDevice::LogicToLogic(pFmt->GetGraphicSize(), + MapMode(MapUnit::Map100thMM), + pEditEngine->GetRefDevice()->GetMapMode()); + } + } + + return pPara->aBulSize; +} + +void Outliner::ImplCheckParagraphs( sal_Int32 nStart, sal_Int32 nEnd ) +{ + + for ( sal_Int32 n = nStart; n < nEnd; n++ ) + { + Paragraph* pPara = pParaList->GetParagraph( n ); + if (pPara) + { + pPara->Invalidate(); + ImplCalcBulletText( n, false, false ); + } + } +} + +void Outliner::SetRefDevice( OutputDevice* pRefDev ) +{ + pEditEngine->SetRefDevice( pRefDev ); + for ( sal_Int32 n = pParaList->GetParagraphCount(); n; ) + { + Paragraph* pPara = pParaList->GetParagraph( --n ); + pPara->Invalidate(); + } +} + +void Outliner::ParaAttribsChanged( sal_Int32 nPara ) +{ + // The Outliner does not have an undo of its own, when paragraphs are + // separated/merged. When ParagraphInserted the attribute EE_PARA_OUTLLEVEL + // may not be set, this is however needed when the depth of the paragraph + // is to be determined. + if (!pEditEngine->IsInUndo()) + return; + if (pParaList->GetParagraphCount() != pEditEngine->GetParagraphCount()) + return; + Paragraph* pPara = pParaList->GetParagraph(nPara); + if (!pPara) + return; + // tdf#100734: force update of bullet + pPara->Invalidate(); + const SfxInt16Item& rLevel = pEditEngine->GetParaAttrib( nPara, EE_PARA_OUTLLEVEL ); + if (pPara->GetDepth() == rLevel.GetValue()) + return; + pPara->SetDepth(rLevel.GetValue()); + ImplCalcBulletText(nPara, true, true); +} + +void Outliner::StyleSheetChanged( SfxStyleSheet const * pStyle ) +{ + + // The EditEngine calls StyleSheetChanged also for derived styles. + // Here all the paragraphs, which had the said template, used to be + // hunted by an ImpRecalcParaAttribs, why? + // => only the Bullet-representation can really change... + sal_Int32 nParas = pParaList->GetParagraphCount(); + for( sal_Int32 nPara = 0; nPara < nParas; nPara++ ) + { + if ( pEditEngine->GetStyleSheet( nPara ) == pStyle ) + { + ImplCheckNumBulletItem( nPara ); + ImplCalcBulletText( nPara, false, false ); + // EditEngine formats changed paragraphs before calling this method, + // so they are not reformatted now and use wrong bullet indent + pEditEngine->QuickMarkInvalid( ESelection( nPara, 0, nPara, 0 ) ); + } + } +} + +tools::Rectangle Outliner::ImpCalcBulletArea( sal_Int32 nPara, bool bAdjust, bool bReturnPaperPos ) +{ + // Bullet area within the paragraph ... + tools::Rectangle aBulletArea; + + const SvxNumberFormat* pFmt = GetNumberFormat( nPara ); + if ( pFmt ) + { + Point aTopLeft; + Size aBulletSize( ImplGetBulletSize( nPara ) ); + + bool bOutlineMode = bool( pEditEngine->GetControlWord() & EEControlBits::OUTLINER ); + + // the ODF attribute text:space-before which holds the spacing to add to the left of the label + const auto nSpaceBefore = pFmt->GetAbsLSpace() + pFmt->GetFirstLineOffset(); + + const SvxLRSpaceItem& rLR = pEditEngine->GetParaAttrib( nPara, bOutlineMode ? EE_PARA_OUTLLRSPACE : EE_PARA_LRSPACE ); + aTopLeft.setX( rLR.GetTextLeft() + rLR.GetTextFirstLineOffset() + nSpaceBefore ); + + tools::Long nBulletWidth = std::max( static_cast<tools::Long>(-rLR.GetTextFirstLineOffset()), static_cast<tools::Long>((-pFmt->GetFirstLineOffset()) + pFmt->GetCharTextDistance()) ); + if ( nBulletWidth < aBulletSize.Width() ) // The Bullet creates its space + nBulletWidth = aBulletSize.Width(); + + if ( bAdjust && !bOutlineMode ) + { + // Adjust when centered or align right + const SvxAdjustItem& rItem = pEditEngine->GetParaAttrib( nPara, EE_PARA_JUST ); + if ( ( !pEditEngine->IsRightToLeft( nPara ) && ( rItem.GetAdjust() != SvxAdjust::Left ) ) || + ( pEditEngine->IsRightToLeft( nPara ) && ( rItem.GetAdjust() != SvxAdjust::Right ) ) ) + { + aTopLeft.setX( pEditEngine->GetFirstLineStartX( nPara ) - nBulletWidth ); + } + } + + // Vertical: + ParagraphInfos aInfos = pEditEngine->GetParagraphInfos( nPara ); + if ( aInfos.bValid ) + { + aTopLeft.setY( /* aInfos.nFirstLineOffset + */ // nFirstLineOffset is already added to the StartPos (PaintBullet) from the EditEngine + aInfos.nFirstLineHeight - aInfos.nFirstLineTextHeight + + aInfos.nFirstLineTextHeight / 2 + - aBulletSize.Height() / 2 ); + // may prefer to print out on the baseline ... + if( ( pFmt->GetNumberingType() != SVX_NUM_NUMBER_NONE ) && ( pFmt->GetNumberingType() != SVX_NUM_BITMAP ) && ( pFmt->GetNumberingType() != SVX_NUM_CHAR_SPECIAL ) ) + { + vcl::Font aBulletFont( ImpCalcBulletFont( nPara ) ); + if ( aBulletFont.GetCharSet() != RTL_TEXTENCODING_SYMBOL ) + { + OutputDevice* pRefDev = pEditEngine->GetRefDevice(); + vcl::Font aOldFont = pRefDev->GetFont(); + pRefDev->SetFont( aBulletFont ); + FontMetric aMetric( pRefDev->GetFontMetric() ); + // Leading on the first line ... + aTopLeft.setY( /* aInfos.nFirstLineOffset + */ aInfos.nFirstLineMaxAscent ); + aTopLeft.AdjustY( -(aMetric.GetAscent()) ); + pRefDev->SetFont( aOldFont ); + } + } + } + + // Horizontal: + if( pFmt->GetNumAdjust() == SvxAdjust::Right ) + { + aTopLeft.AdjustX(nBulletWidth - aBulletSize.Width() ); + } + else if( pFmt->GetNumAdjust() == SvxAdjust::Center ) + { + aTopLeft.AdjustX(( nBulletWidth - aBulletSize.Width() ) / 2 ); + } + + if ( aTopLeft.X() < 0 ) // then push + aTopLeft.setX( 0 ); + + aBulletArea = tools::Rectangle( aTopLeft, aBulletSize ); + } + if ( bReturnPaperPos ) + { + Size aBulletSize( aBulletArea.GetSize() ); + Point aBulletDocPos( aBulletArea.TopLeft() ); + aBulletDocPos.AdjustY(pEditEngine->GetDocPosTopLeft( nPara ).Y() ); + Point aBulletPos( aBulletDocPos ); + + if ( IsVertical() ) + { + aBulletPos.setY( aBulletDocPos.X() ); + aBulletPos.setX( GetPaperSize().Width() - aBulletDocPos.Y() ); + // Rotate: + aBulletPos.AdjustX( -(aBulletSize.Height()) ); + Size aSz( aBulletSize ); + aBulletSize.setWidth( aSz.Height() ); + aBulletSize.setHeight( aSz.Width() ); + } + else if ( pEditEngine->IsRightToLeft( nPara ) ) + { + aBulletPos.setX( GetPaperSize().Width() - aBulletDocPos.X() - aBulletSize.Width() ); + } + + aBulletArea = tools::Rectangle( aBulletPos, aBulletSize ); + } + return aBulletArea; +} + +EBulletInfo Outliner::GetBulletInfo( sal_Int32 nPara ) +{ + EBulletInfo aInfo; + + aInfo.nParagraph = nPara; + aInfo.bVisible = ImplHasNumberFormat( nPara ); + + const SvxNumberFormat* pFmt = GetNumberFormat( nPara ); + aInfo.nType = pFmt ? pFmt->GetNumberingType() : 0; + + if( pFmt ) + { + if( pFmt->GetNumberingType() != SVX_NUM_BITMAP ) + { + aInfo.aText = ImplGetBulletText( nPara ); + + if( pFmt->GetBulletFont() ) + aInfo.aFont = *pFmt->GetBulletFont(); + } + } + + if ( aInfo.bVisible ) + { + aInfo.aBounds = ImpCalcBulletArea( nPara, true, true ); + } + + return aInfo; +} + +OUString Outliner::GetText( Paragraph const * pParagraph, sal_Int32 nCount ) const +{ + + OUStringBuffer aText(128); + sal_Int32 nStartPara = pParaList->GetAbsPos( pParagraph ); + for ( sal_Int32 n = 0; n < nCount; n++ ) + { + aText.append(pEditEngine->GetText( nStartPara + n )); + if ( (n+1) < nCount ) + aText.append("\n"); + } + return aText.makeStringAndClear(); +} + +void Outliner::Remove( Paragraph const * pPara, sal_Int32 nParaCount ) +{ + + sal_Int32 nPos = pParaList->GetAbsPos( pPara ); + if( !nPos && ( nParaCount >= pParaList->GetParagraphCount() ) ) + { + Clear(); + } + else + { + for( sal_Int32 n = 0; n < nParaCount; n++ ) + pEditEngine->RemoveParagraph( nPos ); + } +} + +void Outliner::StripPortions() +{ + bStrippingPortions = true; + pEditEngine->StripPortions(); + bStrippingPortions = false; +} + +void Outliner::DrawingText( const Point& rStartPos, const OUString& rText, sal_Int32 nTextStart, + sal_Int32 nTextLen, o3tl::span<const sal_Int32> pDXArray,const SvxFont& rFont, + sal_Int32 nPara, sal_uInt8 nRightToLeft, + const EEngineData::WrongSpellVector* pWrongSpellVector, + const SvxFieldData* pFieldData, + bool bEndOfLine, + bool bEndOfParagraph, + bool bEndOfBullet, + const css::lang::Locale* pLocale, + const Color& rOverlineColor, + const Color& rTextLineColor) +{ + if(aDrawPortionHdl.IsSet()) + { + DrawPortionInfo aInfo( rStartPos, rText, nTextStart, nTextLen, rFont, nPara, pDXArray, pWrongSpellVector, + pFieldData, pLocale, rOverlineColor, rTextLineColor, nRightToLeft, false, 0, bEndOfLine, bEndOfParagraph, bEndOfBullet); + + aDrawPortionHdl.Call( &aInfo ); + } +} + +void Outliner::DrawingTab( const Point& rStartPos, tools::Long nWidth, const OUString& rChar, const SvxFont& rFont, + sal_Int32 nPara, sal_uInt8 nRightToLeft, bool bEndOfLine, bool bEndOfParagraph, + const Color& rOverlineColor, const Color& rTextLineColor) +{ + if(aDrawPortionHdl.IsSet()) + { + DrawPortionInfo aInfo( rStartPos, rChar, 0, rChar.getLength(), rFont, nPara, {}, nullptr, + nullptr, nullptr, rOverlineColor, rTextLineColor, nRightToLeft, true, nWidth, bEndOfLine, bEndOfParagraph, false); + + aDrawPortionHdl.Call( &aInfo ); + } +} + +bool Outliner::RemovingPagesHdl( OutlinerView* pView ) +{ + return !aRemovingPagesHdl.IsSet() || aRemovingPagesHdl.Call( pView ); +} + +bool Outliner::ImpCanDeleteSelectedPages( OutlinerView* pCurView, sal_Int32 _nFirstPage, sal_Int32 nPages ) +{ + + nDepthChangedHdlPrevDepth = nPages; + mnFirstSelPage = _nFirstPage; + return RemovingPagesHdl( pCurView ); +} + +SfxItemSet const & Outliner::GetParaAttribs( sal_Int32 nPara ) const +{ + return pEditEngine->GetParaAttribs( nPara ); +} + +IMPL_LINK( Outliner, ParaVisibleStateChangedHdl, Paragraph&, rPara, void ) +{ + sal_Int32 nPara = pParaList->GetAbsPos( &rPara ); + pEditEngine->ShowParagraph( nPara, rPara.IsVisible() ); +} + +IMPL_LINK_NOARG(Outliner, BeginMovingParagraphsHdl, MoveParagraphsInfo&, void) +{ + if( !IsInUndo() ) + aBeginMovingHdl.Call( this ); +} + +IMPL_LINK( Outliner, BeginPasteOrDropHdl, PasteOrDropInfos&, rInfos, void ) +{ + UndoActionStart( EDITUNDO_DRAGANDDROP ); + maBeginPasteOrDropHdl.Call(&rInfos); +} + +IMPL_LINK( Outliner, EndPasteOrDropHdl, PasteOrDropInfos&, rInfos, void ) +{ + bPasting = false; + ImpTextPasted( rInfos.nStartPara, rInfos.nEndPara - rInfos.nStartPara + 1 ); + maEndPasteOrDropHdl.Call( &rInfos ); + UndoActionEnd(); +} + +IMPL_LINK( Outliner, EndMovingParagraphsHdl, MoveParagraphsInfo&, rInfos, void ) +{ + pParaList->MoveParagraphs( rInfos.nStartPara, rInfos.nDestPara, rInfos.nEndPara - rInfos.nStartPara + 1 ); + sal_Int32 nChangesStart = std::min( rInfos.nStartPara, rInfos.nDestPara ); + sal_Int32 nParas = pParaList->GetParagraphCount(); + for ( sal_Int32 n = nChangesStart; n < nParas; n++ ) + ImplCalcBulletText( n, false, false ); + + if( !IsInUndo() ) + aEndMovingHdl.Call( this ); +} + +static bool isSameNumbering( const SvxNumberFormat& rN1, const SvxNumberFormat& rN2 ) +{ + if( rN1.GetNumberingType() != rN2.GetNumberingType() ) + return false; + + if( rN1.GetNumStr(1) != rN2.GetNumStr(1) ) + return false; + + if( (rN1.GetPrefix() != rN2.GetPrefix()) || (rN1.GetSuffix() != rN2.GetSuffix()) ) + return false; + + return true; +} + +sal_uInt16 Outliner::ImplGetNumbering( sal_Int32 nPara, const SvxNumberFormat* pParaFmt ) +{ + sal_uInt16 nNumber = pParaFmt->GetStart() - 1; + + Paragraph* pPara = pParaList->GetParagraph( nPara ); + const sal_Int16 nParaDepth = pPara->GetDepth(); + + do + { + pPara = pParaList->GetParagraph( nPara ); + const sal_Int16 nDepth = pPara->GetDepth(); + + // ignore paragraphs that are below our paragraph or have no numbering + if( (nDepth > nParaDepth) || (nDepth == -1) ) + continue; + + // stop on paragraphs that are above our paragraph + if( nDepth < nParaDepth ) + break; + + const SvxNumberFormat* pFmt = GetNumberFormat( nPara ); + + if( pFmt == nullptr ) + continue; // ignore paragraphs without bullets + + // check if numbering less than or equal to pParaFmt + if( !isSameNumbering( *pFmt, *pParaFmt ) || ( pFmt->GetStart() < pParaFmt->GetStart() ) ) + break; + + if ( pFmt->GetStart() > pParaFmt->GetStart() ) + { + nNumber += pFmt->GetStart() - pParaFmt->GetStart(); + pParaFmt = pFmt; + } + + const SfxBoolItem& rBulletState = pEditEngine->GetParaAttrib( nPara, EE_PARA_BULLETSTATE ); + + if( rBulletState.GetValue() ) + nNumber += 1; + + // same depth, same number format, check for restart + const sal_Int16 nNumberingStartValue = pPara->GetNumberingStartValue(); + if( (nNumberingStartValue != -1) || pPara->IsParaIsNumberingRestart() ) + { + if( nNumberingStartValue != -1 ) + nNumber += nNumberingStartValue - 1; + break; + } + } + while( nPara-- ); + + return nNumber; +} + +void Outliner::ImplCalcBulletText( sal_Int32 nPara, bool bRecalcLevel, bool bRecalcChildren ) +{ + + Paragraph* pPara = pParaList->GetParagraph( nPara ); + + while ( pPara ) + { + OUString aBulletText; + const SvxNumberFormat* pFmt = GetNumberFormat( nPara ); + if( pFmt && ( pFmt->GetNumberingType() != SVX_NUM_BITMAP ) ) + { + aBulletText += pFmt->GetPrefix(); + if( pFmt->GetNumberingType() == SVX_NUM_CHAR_SPECIAL ) + { + sal_UCS4 cChar = pFmt->GetBulletChar(); + aBulletText += OUString(&cChar, 1); + } + else if( pFmt->GetNumberingType() != SVX_NUM_NUMBER_NONE ) + { + aBulletText += pFmt->GetNumStr( ImplGetNumbering( nPara, pFmt ) ); + } + aBulletText += pFmt->GetSuffix(); + } + + if (pPara->GetText() != aBulletText) + pPara->SetText( aBulletText ); + + pPara->nFlags &= ~ParaFlag::SETBULLETTEXT; + + if ( bRecalcLevel ) + { + sal_Int16 nDepth = pPara->GetDepth(); + pPara = pParaList->GetParagraph( ++nPara ); + if ( !bRecalcChildren ) + { + while ( pPara && ( pPara->GetDepth() > nDepth ) ) + pPara = pParaList->GetParagraph( ++nPara ); + } + + if ( pPara && ( pPara->GetDepth() < nDepth ) ) + pPara = nullptr; + } + else + { + pPara = nullptr; + } + } +} + +void Outliner::Clear() +{ + + if( !bFirstParaIsEmpty ) + { + ImplBlockInsertionCallbacks( true ); + pEditEngine->Clear(); + pParaList->Clear(); + pParaList->Append( std::unique_ptr<Paragraph>(new Paragraph( gnMinDepth ))); + bFirstParaIsEmpty = true; + ImplBlockInsertionCallbacks( false ); + } + else + { + Paragraph* pPara = pParaList->GetParagraph( 0 ); + if(pPara) + pPara->SetDepth( gnMinDepth ); + } +} + +void Outliner::SetFlatMode( bool bFlat ) +{ + + if( bFlat != pEditEngine->IsFlatMode() ) + { + for ( sal_Int32 nPara = pParaList->GetParagraphCount(); nPara; ) + pParaList->GetParagraph( --nPara )->aBulSize.setWidth( -1 ); + + pEditEngine->SetFlatMode( bFlat ); + } +} + +OUString Outliner::ImplGetBulletText( sal_Int32 nPara ) +{ + OUString aRes; + Paragraph* pPara = pParaList->GetParagraph( nPara ); + if (pPara) + { + // Enable optimization again ... +// if( pPara->nFlags & ParaFlag::SETBULLETTEXT ) + ImplCalcBulletText( nPara, false, false ); + aRes = pPara->GetText(); + } + return aRes; +} + +// this is needed for StarOffice Api +void Outliner::SetLevelDependentStyleSheet( sal_Int32 nPara ) +{ + SfxItemSet aOldAttrs( pEditEngine->GetParaAttribs( nPara ) ); + ImplSetLevelDependentStyleSheet( nPara ); + pEditEngine->SetParaAttribs( nPara, aOldAttrs ); +} + +void Outliner::ImplBlockInsertionCallbacks( bool b ) +{ + if ( b ) + { + nBlockInsCallback++; + } + else + { + DBG_ASSERT( nBlockInsCallback, "ImplBlockInsertionCallbacks ?!" ); + nBlockInsCallback--; + if ( !nBlockInsCallback ) + { + // Call blocked notify events... + while(!pEditEngine->aNotifyCache.empty()) + { + EENotify aNotify(pEditEngine->aNotifyCache.front()); + // Remove from list before calling, maybe we enter LeaveBlockNotifications while calling the handler... + pEditEngine->aNotifyCache.erase(pEditEngine->aNotifyCache.begin()); + pEditEngine->aOutlinerNotifyHdl.Call( aNotify ); + } + } + } +} + +IMPL_LINK( Outliner, EditEngineNotifyHdl, EENotify&, rNotify, void ) +{ + if ( !nBlockInsCallback ) + pEditEngine->aOutlinerNotifyHdl.Call( rNotify ); + else + pEditEngine->aNotifyCache.push_back(rNotify); +} + +/** sets a link that is called at the beginning of a drag operation at an edit view */ +void Outliner::SetBeginDropHdl( const Link<EditView*,void>& rLink ) +{ + pEditEngine->SetBeginDropHdl( rLink ); +} + +/** sets a link that is called at the end of a drag operation at an edit view */ +void Outliner::SetEndDropHdl( const Link<EditView*,void>& rLink ) +{ + pEditEngine->SetEndDropHdl( rLink ); +} + +/** sets a link that is called before a drop or paste operation. */ +void Outliner::SetBeginPasteOrDropHdl( const Link<PasteOrDropInfos*,void>& rLink ) +{ + maBeginPasteOrDropHdl = rLink; +} + +/** sets a link that is called after a drop or paste operation. */ +void Outliner::SetEndPasteOrDropHdl( const Link<PasteOrDropInfos*,void>& rLink ) +{ + maEndPasteOrDropHdl = rLink; +} + +void Outliner::SetParaFlag( Paragraph* pPara, ParaFlag nFlag ) +{ + if( pPara && !pPara->HasFlag( nFlag ) ) + { + if( IsUndoEnabled() && !IsInUndo() ) + InsertUndo( std::make_unique<OutlinerUndoChangeParaFlags>( this, GetAbsPos( pPara ), pPara->nFlags, pPara->nFlags|nFlag ) ); + + pPara->SetFlag( nFlag ); + } +} + +bool Outliner::HasParaFlag( const Paragraph* pPara, ParaFlag nFlag ) +{ + return pPara && pPara->HasFlag( nFlag ); +} + + +bool Outliner::IsPageOverflow() +{ + return pEditEngine->IsPageOverflow(); +} + +std::optional<NonOverflowingText> Outliner::GetNonOverflowingText() const +{ + /* XXX: + * nCount should be the number of paragraphs of the non overflowing text + * nStart should be the starting paragraph of the non overflowing text (XXX: Always 0?) + */ + + if ( GetParagraphCount() < 1 ) + return {}; + + // last non-overflowing paragraph is before the first overflowing one + sal_Int32 nCount = pEditEngine->GetOverflowingParaNum(); + sal_Int32 nOverflowLine = pEditEngine->GetOverflowingLineNum(); // XXX: Unused for now + + // Defensive check: overflowing para index beyond actual # of paragraphs? + if ( nCount > GetParagraphCount()-1) { + SAL_INFO("editeng.chaining", + "[Overflowing] Ops, trying to retrieve para " + << nCount << " when max index is " << GetParagraphCount()-1 ); + return {}; + } + + if (nCount < 0) + { + SAL_INFO("editeng.chaining", + "[Overflowing] No Overflowing text but GetNonOverflowinText called?!"); + return {}; + } + + // NOTE: We want the selection of the overflowing text from here + // At the same time we may want to consider the beginning of such text + // in a more fine grained way (i.e. as GetNonOverflowingText did) + +/* + sal_Int32 nHeadPara = pEditEngine->GetOverflowingParaNum(); + sal_uInt32 nParaCount = GetParagraphCount(); + + sal_uInt32 nLen = 0; + for ( sal_Int32 nLine = 0; + nLine < pEditEngine->GetOverflowingLineNum(); + nLine++) { + nLen += GetLineLen(nHeadPara, nLine); + } + + sal_uInt32 nOverflowingPara = pEditEngine->GetOverflowingParaNum(); + ESelection aOverflowingTextSel; + sal_Int32 nLastPara = nParaCount-1; + sal_Int32 nLastParaLen = GetText(GetParagraph(nLastPara)).getLength(); + aOverflowingTextSel = ESelection(nOverflowingPara, nLen, + nLastPara, nLastParaLen); + bool bLastParaInterrupted = + pEditEngine->GetOverflowingLineNum() > 0; + + return new NonOverflowingText(aOverflowingTextSel, bLastParaInterrupted); + **/ + + + // Only overflowing text, i.e. 1st line of 1st paragraph overflowing + bool bItAllOverflew = nCount == 0 && nOverflowLine == 0; + if ( bItAllOverflew ) + { + ESelection aEmptySel(0,0,0,0); + //EditTextObject *pTObj = pEditEngine->CreateTextObject(aEmptySel); + bool const bLastParaInterrupted = true; // Last Para was interrupted since everything overflew + return NonOverflowingText(aEmptySel, bLastParaInterrupted); + } else { // Get the lines that of the overflowing para fit in the box + + sal_Int32 nOverflowingPara = nCount; + sal_uInt32 nLen = 0; + + for ( sal_Int32 nLine = 0; + nLine < pEditEngine->GetOverflowingLineNum(); + nLine++) + { + nLen += GetLineLen(nOverflowingPara, nLine); + } + + //sal_Int32 nStartPara = 0; + //sal_Int32 nStartPos = 0; + ESelection aOverflowingTextSelection; + + const sal_Int32 nEndPara = GetParagraphCount()-1; + const sal_Int32 nEndPos = pEditEngine->GetTextLen(nEndPara); + + if (nLen == 0) { + // XXX: What happens inside this case might be dependent on the joining paragraph or not-thingy + // Overflowing paragraph is empty or first line overflowing: it's not "Non-Overflowing" text then + sal_Int32 nParaLen = GetText(GetParagraph(nOverflowingPara-1)).getLength(); + aOverflowingTextSelection = + ESelection(nOverflowingPara-1, nParaLen, nEndPara, nEndPos); + } else { + // We take until we have to from the overflowing paragraph + aOverflowingTextSelection = + ESelection(nOverflowingPara, nLen, nEndPara, nEndPos); + } + //EditTextObject *pTObj = pEditEngine->CreateTextObject(aNonOverflowingTextSelection); + + //sal_Int32 nLastLine = GetLineCount(nOverflowingPara)-1; + bool bLastParaInterrupted = + pEditEngine->GetOverflowingLineNum() > 0; + + return NonOverflowingText(aOverflowingTextSelection, bLastParaInterrupted); + } +} + +OutlinerParaObject Outliner::GetEmptyParaObject() const +{ + std::unique_ptr<EditTextObject> pEmptyText = pEditEngine->GetEmptyTextObject(); + OutlinerParaObject aPObj( std::move(pEmptyText) ); + aPObj.SetOutlinerMode(GetOutlinerMode()); + return aPObj; +} + +std::optional<OverflowingText> Outliner::GetOverflowingText() const +{ + if ( pEditEngine->GetOverflowingParaNum() < 0) + return {}; + + + // Defensive check: overflowing para index beyond actual # of paragraphs? + if ( pEditEngine->GetOverflowingParaNum() > GetParagraphCount()-1) { + SAL_INFO("editeng.chaining", + "[Overflowing] Ops, trying to retrieve para " + << pEditEngine->GetOverflowingParaNum() << " when max index is " + << GetParagraphCount()-1 ); + return {}; + } + + sal_Int32 nHeadPara = pEditEngine->GetOverflowingParaNum(); + sal_uInt32 nParaCount = GetParagraphCount(); + + sal_uInt32 nLen = 0; + for ( sal_Int32 nLine = 0; + nLine < pEditEngine->GetOverflowingLineNum(); + nLine++) { + nLen += GetLineLen(nHeadPara, nLine); + } + + sal_uInt32 nOverflowingPara = pEditEngine->GetOverflowingParaNum(); + ESelection aOverflowingTextSel; + sal_Int32 nLastPara = nParaCount-1; + sal_Int32 nLastParaLen = GetText(GetParagraph(nLastPara)).getLength(); + aOverflowingTextSel = ESelection(nOverflowingPara, nLen, + nLastPara, nLastParaLen); + return OverflowingText(pEditEngine->CreateTransferable(aOverflowingTextSel)); + +} + +void Outliner::ClearOverflowingParaNum() +{ + pEditEngine->ClearOverflowingParaNum(); +} + +void Outliner::dumpAsXml(xmlTextWriterPtr pWriter) const +{ + bool bOwns = false; + if (!pWriter) + { + pWriter = xmlNewTextWriterFilename("outliner.xml", 0); + xmlTextWriterSetIndent(pWriter,1); + (void)xmlTextWriterSetIndentString(pWriter, BAD_CAST(" ")); + (void)xmlTextWriterStartDocument(pWriter, nullptr, nullptr, nullptr); + bOwns = true; + } + + (void)xmlTextWriterStartElement(pWriter, BAD_CAST("Outliner")); + pParaList->dumpAsXml(pWriter); + (void)xmlTextWriterEndElement(pWriter); + + if (bOwns) + { + (void)xmlTextWriterEndDocument(pWriter); + xmlFreeTextWriter(pWriter); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/editeng/source/outliner/outlobj.cxx b/editeng/source/outliner/outlobj.cxx new file mode 100644 index 000000000..e6dc6e691 --- /dev/null +++ b/editeng/source/outliner/outlobj.cxx @@ -0,0 +1,254 @@ +/* -*- 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, ParagraphDataVector&& rParagraphDataVector, bool bIsEditDoc ) : + mpEditTextObject(std::move(pEditTextObject)), + maParagraphDataVector(std::move(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( + std::unique_ptr<EditTextObject> xTextObj, ParagraphDataVector&& rParagraphDataVector, bool bIsEditDoc ) : + mpImpl(OutlinerParaObjData(std::move(xTextObj), std::move(rParagraphDataVector), bIsEditDoc)) +{ +} + +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&& r ) noexcept : + mpImpl(std::move(r.mpImpl)) +{ +} + +OutlinerParaObject::~OutlinerParaObject() +{ +} + +OutlinerParaObject& OutlinerParaObject::operator=( const OutlinerParaObject& r ) +{ + mpImpl = r.mpImpl; + return *this; +} + +OutlinerParaObject& OutlinerParaObject::operator=( OutlinerParaObject&& r ) noexcept +{ + mpImpl = std::move(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::IsEffectivelyVertical() const +{ + return mpImpl->mpEditTextObject->IsEffectivelyVertical(); +} + +bool OutlinerParaObject::GetVertical() const +{ + return mpImpl->mpEditTextObject->GetVertical(); +} + +bool OutlinerParaObject::IsTopToBottom() const +{ + return mpImpl->mpEditTextObject->IsTopToBottom(); +} + +void OutlinerParaObject::SetVertical(bool bNew) +{ + const ::o3tl::cow_wrapper< OutlinerParaObjData >* pImpl = &mpImpl; + if ( ( *pImpl )->mpEditTextObject->IsEffectivelyVertical() != 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(std::u16string_view rOldName, + SfxStyleFamily eOldFamily, const OUString& rNewName, SfxStyleFamily eNewFamily) +{ + return mpImpl->mpEditTextObject->ChangeStyleSheets(rOldName, eOldFamily, rNewName, eNewFamily); +} + +void OutlinerParaObject::ChangeStyleSheetName(SfxStyleFamily eFamily, + std::u16string_view 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 +{ + (void)xmlTextWriterStartElement(pWriter, BAD_CAST("OutlinerParaObject")); + (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this); + mpImpl->mpEditTextObject->dumpAsXml(pWriter); + for (ParagraphData const & p : mpImpl->maParagraphDataVector) + Paragraph(p).dumpAsXml(pWriter); + (void)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..066b415e1 --- /dev/null +++ b/editeng/source/outliner/outlundo.hxx @@ -0,0 +1,118 @@ +/* -*- 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 . + */ + +#pragma once + +#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; +}; + +/* 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..d5f426abb --- /dev/null +++ b/editeng/source/outliner/outlvw.cxx @@ -0,0 +1,1503 @@ +/* -*- 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->GetOutlinerMode() != 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->GetOutlinerMode() != OutlinerMode::TextObject ) && + ( pOwner->GetOutlinerMode() != OutlinerMode::TitleObject ) && + ( bSelection || !aSel.nStartPos ) ) + { + Indent( aKeyCode.IsShift() ? -1 : +1 ); + bKeyProcessed = true; + } + else if ( ( pOwner->GetOutlinerMode() == 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->GetOutputDevice().PixelToLogic( rPosPix ); + if( !pEditView->GetOutputArea().Contains( 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->GetOutlinerMode() == OutlinerMode::TextObject ) || pEditView->GetEditEngine()->IsInSelectionMode()) + return pEditView->MouseMove( rMEvt ); + + Point aMousePosWin( pEditView->GetOutputDevice().PixelToLogic( rMEvt.GetPosPixel() ) ); + if( !pEditView->GetOutputArea().Contains( aMousePosWin ) ) + return false; + + PointerStyle aPointer = GetPointer( rMEvt.GetPosPixel() ); + pEditView->GetWindow()->SetPointer( aPointer ); + return pEditView->MouseMove( rMEvt ); +} + + +bool OutlinerView::MouseButtonDown( const MouseEvent& rMEvt ) +{ + if ( ( pOwner->GetOutlinerMode() == OutlinerMode::TextObject ) || pEditView->GetEditEngine()->IsInSelectionMode() ) + return pEditView->MouseButtonDown( rMEvt ); + + Point aMousePosWin( pEditView->GetOutputDevice().PixelToLogic( rMEvt.GetPosPixel() ) ); + if( !pEditView->GetOutputArea().Contains( 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->GetOutlinerMode() == 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->GetOutlinerMode() == OutlinerMode::TextObject ) || pEditView->GetEditEngine()->IsInSelectionMode() ) + return pEditView->MouseButtonUp( rMEvt ); + + Point aMousePosWin( pEditView->GetOutputDevice().PixelToLogic( rMEvt.GetPosPixel() ) ); + if( !pEditView->GetOutputArea().Contains( 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->SetUpdateLayout( 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->SetEditEngineUpdateLayout( 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->SetUpdateLayout( 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->GetOutlinerMode() != 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->GetOutlinerMode() == 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->SetEditEngineUpdateLayout( true ); + pEditView->ShowCursor(); + } + + if( bUndo ) + pOwner->UndoActionEnd(); +} + +void OutlinerView::AdjustHeight( tools::Long nDY ) +{ + pEditView->MoveParagraphs( nDY ); +} + +tools::Rectangle 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->SetUpdateLayout( 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->SetUpdateLayout( 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 ); + + const bool bPrevUpdateLayout = pOwner->pEditEngine->SetUpdateLayout( false ); + sal_Int32 nStart, nParaCount; + nParaCount = pOwner->pEditEngine->GetParagraphCount(); + sal_uInt16 nSize = ImpInitPaste( nStart ); + pEditView->InsertText( rParaObj.GetTextObject() ); + ImpPasted( nStart, nParaCount, nSize); + pEditView->SetEditEngineUpdateLayout( bPrevUpdateLayout ); + + 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 ); + + const bool bPrevUpdateLayout = pOwner->pEditEngine->SetUpdateLayout( false ); + pOwner->bPasting = true; + + if ( bUseSpecial ) + pEditView->PasteSpecial(); + else + pEditView->Paste(); + + if ( pOwner->GetOutlinerMode() == OutlinerMode::OutlineObject ) + { + const sal_Int32 nParaCount = pOwner->pEditEngine->GetParagraphCount(); + + for( sal_Int32 nPara = 0; nPara < nParaCount; nPara++ ) + pOwner->ImplSetLevelDependentStyleSheet( nPara ); + } + + pEditView->SetEditEngineUpdateLayout( bPrevUpdateLayout ); + 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 ); +} + +bool OutlinerView::Command(const CommandEvent& rCEvt) +{ + return 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->SetUpdateLayout( 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( std::move(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->SetUpdateLayout( 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::EnsureNumberingIsOn() +{ + pOwner->UndoActionStart(OLUNDO_DEPTH); + + ESelection aSel(pEditView->GetSelection()); + aSel.Adjust(); + + const bool bUpdate = pOwner->pEditEngine->IsUpdateLayout(); + pOwner->pEditEngine->SetUpdateLayout(false); + + for (sal_Int32 nPara = aSel.nStartPara; nPara <= aSel.nEndPara; nPara++) + { + Paragraph* pPara = pOwner->pParaList->GetParagraph(nPara); + DBG_ASSERT(pPara, "OutlinerView::EnableBullets(), illegal selection?"); + + if (pPara && pOwner->GetDepth(nPara) == -1) + pOwner->SetDepth(pPara, 0); + } + + sal_Int32 nParaCount = pOwner->pParaList->GetParagraphCount(); + pOwner->ImplCheckParagraphs(aSel.nStartPara, nParaCount); + + const sal_Int32 nEndPara = (nParaCount > 0) ? nParaCount-1 : nParaCount; + pOwner->pEditEngine->QuickMarkInvalid(ESelection(aSel.nStartPara, 0, nEndPara, 0)); + + pOwner->pEditEngine->SetUpdateLayout(bUpdate); + + pOwner->UndoActionEnd(); +} + +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->SetUpdateLayout(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 SvxNumBulletItem* pNumBulletItem = rAttrs.GetItemIfSet(EE_PARA_NUMBULLET, false); + if (pNumBulletItem) + { + // Use default value when has not contain bullet item. + ESelection aSelection(nPara, 0); + SfxItemSet aTmpSet(pOwner->pEditEngine->GetAttribs(aSelection)); + pNumBulletItem = aTmpSet.GetItem(EE_PARA_NUMBULLET); + } + + 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())) + { + SvxNumberFormat aNewFmtClone(*pNewFmt); + aNewFmtClone.SetFirstLineOffset(pOldFmt->GetFirstLineOffset()); + aNewFmtClone.SetAbsLSpace(pOldFmt->GetAbsLSpace()); + aNewRule.SetLevel(nLevel, &aNewFmtClone); + } + } + } + } + + aAttrs.Put(SvxNumBulletItem(std::move(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->SetUpdateLayout( 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->SetUpdateLayout( 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->SetUpdateLayout( bUpdate ); + pOwner->UndoActionEnd(); +} + + +void OutlinerView::RemoveAttribsKeepLanguages( bool bRemoveParaAttribs ) +{ + RemoveAttribs( bRemoveParaAttribs, true /*keep language attribs*/ ); +} + +void OutlinerView::RemoveAttribs( bool bRemoveParaAttribs, bool bKeepLanguages ) +{ + bool bUpdate = pOwner->SetUpdateLayout( 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->SetUpdateLayout( 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(weld::Widget* pDialogParent) +{ + pEditView->StartSpeller(pDialogParent); +} + +EESpellState OutlinerView::StartThesaurus(weld::Widget* pDialogParent) +{ + return pEditView->StartThesaurus(pDialogParent); +} + +void OutlinerView::StartTextConversion(weld::Widget* pDialogParent, + 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(pDialogParent, 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( tools::Long nHorzScroll, tools::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 ) +{ + return pEditView->IsWrongSpelledWordAtPos( rPosPixel, /*bMarkIfWrong*/false ); +} + +void OutlinerView::ExecuteSpellPopup(const Point& rPosPixel, const Link<SpellCallbackInfo&,void>& rStartDlg) +{ + pEditView->ExecuteSpellPopup(rPosPixel, rStartDlg); +} + +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 ); + + tools::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->GetOutlinerMode() == 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(); +} + +bool OutlinerView::DeleteSurroundingText(const Selection& rSelection) +{ + return pEditView->DeleteSurroundingText(rSelection); +} + +// ===== 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 ).nLang; + 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..42316fa1f --- /dev/null +++ b/editeng/source/outliner/overflowingtxt.cxx @@ -0,0 +1,227 @@ +/* -*- 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> +#include <utility> + + +std::optional<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::optional<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 > xOverflowingContent) : + mxOverflowingContent(std::move(xOverflowingContent)) +{ + +} + + + +NonOverflowingText::NonOverflowingText(const ESelection &aSel, bool bLastParaInterrupted) + : maContentSel(aSel) + , mbLastParaInterrupted(bLastParaInterrupted) +{ +} + +bool NonOverflowingText::IsLastParaInterrupted() const +{ + return mbLastParaInterrupted; +} + + +std::optional<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::optional<OutlinerParaObject> OverflowingText::JuxtaposeParaObject(Outliner *pOutl, OutlinerParaObject const *pNextPObj) +{ + return TextChainingUtils::JuxtaposeParaObject(mxOverflowingContent, pOutl, pNextPObj); +} + +std::optional<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::optional<OutlinerParaObject> OFlowChainedText::InsertOverflowingText(Outliner *pOutliner, OutlinerParaObject const *pTextToBeMerged) +{ + // Just return the roughly merged paras for now + if (!mpOverflowingTxt) + return std::nullopt; + + 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::optional<OutlinerParaObject> OFlowChainedText::RemoveOverflowingText(Outliner *pOutliner) +{ + if (!mpNonOverflowingTxt) + return std::nullopt; + + 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::optional<OutlinerParaObject> UFlowChainedText::CreateMergedUnderflowParaObject(Outliner *pOutl, OutlinerParaObject const *pNextLinkWholeText) +{ + std::optional<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..5b9f21449 --- /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 ) +: aBulSize( -1, -1) +, nFlags( ParaFlag::NONE ) +, 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 +{ + (void)xmlTextWriterStartElement(pWriter, BAD_CAST("Paragraph")); + (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("nDepth"), "%" SAL_PRIdINT32, static_cast<sal_Int32>(nDepth)); + (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("mnNumberingStartValue"), "%" SAL_PRIdINT32, static_cast<sal_Int32>(mnNumberingStartValue)); + (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("mbParaIsNumberingRestart"), "%" SAL_PRIdINT32, static_cast<sal_Int32>(mbParaIsNumberingRestart)); + (void)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 +{ + (void)xmlTextWriterStartElement(pWriter, BAD_CAST("ParagraphList")); + for (auto const & pParagraph : maEntries) + pParagraph->dumpAsXml(pWriter); + (void)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..47413ff5f --- /dev/null +++ b/editeng/source/outliner/paralist.hxx @@ -0,0 +1,82 @@ +/* -*- 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 . + */ + +#pragma once + +#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; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |