summaryrefslogtreecommitdiffstats
path: root/sw/source/core/doc
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 05:54:39 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 05:54:39 +0000
commit267c6f2ac71f92999e969232431ba04678e7437e (patch)
tree358c9467650e1d0a1d7227a21dac2e3d08b622b2 /sw/source/core/doc
parentInitial commit. (diff)
downloadlibreoffice-267c6f2ac71f92999e969232431ba04678e7437e.tar.xz
libreoffice-267c6f2ac71f92999e969232431ba04678e7437e.zip
Adding upstream version 4:24.2.0.upstream/4%24.2.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'sw/source/core/doc')
-rw-r--r--sw/source/core/doc/CntntIdxStore.cxx470
-rw-r--r--sw/source/core/doc/DocumentChartDataProviderManager.cxx105
-rw-r--r--sw/source/core/doc/DocumentContentOperationsManager.cxx5520
-rw-r--r--sw/source/core/doc/DocumentDeviceManager.cxx370
-rw-r--r--sw/source/core/doc/DocumentDrawModelManager.cxx357
-rw-r--r--sw/source/core/doc/DocumentExternalDataManager.cxx34
-rw-r--r--sw/source/core/doc/DocumentFieldsManager.cxx1775
-rw-r--r--sw/source/core/doc/DocumentLayoutManager.cxx497
-rw-r--r--sw/source/core/doc/DocumentLinksAdministrationManager.cxx508
-rw-r--r--sw/source/core/doc/DocumentListItemsManager.cxx105
-rw-r--r--sw/source/core/doc/DocumentListsManager.cxx214
-rw-r--r--sw/source/core/doc/DocumentOutlineNodesManager.cxx131
-rw-r--r--sw/source/core/doc/DocumentRedlineManager.cxx3940
-rw-r--r--sw/source/core/doc/DocumentSettingManager.cxx1089
-rw-r--r--sw/source/core/doc/DocumentStateManager.cxx118
-rw-r--r--sw/source/core/doc/DocumentStatisticsManager.cxx217
-rw-r--r--sw/source/core/doc/DocumentStylePoolManager.cxx2768
-rw-r--r--sw/source/core/doc/DocumentTimerManager.cxx235
-rw-r--r--sw/source/core/doc/SwDocIdle.cxx62
-rw-r--r--sw/source/core/doc/SwStyleNameMapper.cxx775
-rw-r--r--sw/source/core/doc/acmplwrd.cxx420
-rw-r--r--sw/source/core/doc/dbgoutsw.cxx820
-rw-r--r--sw/source/core/doc/doc.cxx1951
-rw-r--r--sw/source/core/doc/docbasic.cxx233
-rw-r--r--sw/source/core/doc/docbm.cxx2124
-rw-r--r--sw/source/core/doc/docchart.cxx196
-rw-r--r--sw/source/core/doc/doccomp.cxx2691
-rw-r--r--sw/source/core/doc/doccorr.cxx367
-rw-r--r--sw/source/core/doc/docdesc.cxx1044
-rw-r--r--sw/source/core/doc/docdraw.cxx699
-rw-r--r--sw/source/core/doc/docedt.cxx866
-rw-r--r--sw/source/core/doc/docfld.cxx1230
-rw-r--r--sw/source/core/doc/docfly.cxx1165
-rw-r--r--sw/source/core/doc/docfmt.cxx2097
-rw-r--r--sw/source/core/doc/docftn.cxx547
-rw-r--r--sw/source/core/doc/docglbl.cxx515
-rw-r--r--sw/source/core/doc/docglos.cxx219
-rw-r--r--sw/source/core/doc/doclay.cxx1703
-rw-r--r--sw/source/core/doc/docnew.cxx1320
-rw-r--r--sw/source/core/doc/docnum.cxx2665
-rw-r--r--sw/source/core/doc/docredln.cxx2404
-rw-r--r--sw/source/core/doc/docruby.cxx320
-rw-r--r--sw/source/core/doc/docsort.cxx933
-rw-r--r--sw/source/core/doc/docstat.cxx53
-rw-r--r--sw/source/core/doc/doctxm.cxx2135
-rw-r--r--sw/source/core/doc/docxforms.cxx129
-rw-r--r--sw/source/core/doc/extinput.cxx309
-rw-r--r--sw/source/core/doc/fmtcol.cxx723
-rw-r--r--sw/source/core/doc/ftnidx.cxx519
-rw-r--r--sw/source/core/doc/gctable.cxx463
-rw-r--r--sw/source/core/doc/htmltbl.cxx1774
-rw-r--r--sw/source/core/doc/lineinfo.cxx132
-rw-r--r--sw/source/core/doc/list.cxx190
-rw-r--r--sw/source/core/doc/notxtfrm.cxx1502
-rw-r--r--sw/source/core/doc/number.cxx1656
-rw-r--r--sw/source/core/doc/poolfmt.cxx311
-rw-r--r--sw/source/core/doc/rdfhelper.cxx265
-rw-r--r--sw/source/core/doc/sortopt.cxx61
-rw-r--r--sw/source/core/doc/swserv.cxx320
-rw-r--r--sw/source/core/doc/swstylemanager.cxx176
-rw-r--r--sw/source/core/doc/swstylemanager.hxx32
-rw-r--r--sw/source/core/doc/tblafmt.cxx1256
-rw-r--r--sw/source/core/doc/tblcpy.cxx1032
-rw-r--r--sw/source/core/doc/tblrwcl.cxx3380
-rw-r--r--sw/source/core/doc/textboxhelper.cxx1960
-rw-r--r--sw/source/core/doc/visiturl.cxx125
66 files changed, 64322 insertions, 0 deletions
diff --git a/sw/source/core/doc/CntntIdxStore.cxx b/sw/source/core/doc/CntntIdxStore.cxx
new file mode 100644
index 0000000000..6b82ebc20c
--- /dev/null
+++ b/sw/source/core/doc/CntntIdxStore.cxx
@@ -0,0 +1,470 @@
+/* -*- 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 <bookmark.hxx>
+#include <cntfrm.hxx>
+#include <doc.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <docary.hxx>
+#include <editsh.hxx>
+#include <fmtanchr.hxx>
+#include <frmfmt.hxx>
+#include <functional>
+#include <mvsave.hxx>
+#include <node.hxx>
+#include <pam.hxx>
+#include <redline.hxx>
+#include <sal/types.h>
+#include <unocrsr.hxx>
+#include <txtfrm.hxx>
+#include <frameformats.hxx>
+#include <memory>
+
+using namespace ::boost;
+using namespace ::sw::mark;
+
+namespace
+{
+ // #i59534: If a paragraph will be split we have to restore some redline positions
+ // This help function checks a position compared with a node and a content index
+
+ const int BEFORE_NODE = 0; // Position before the given node index
+ const int BEFORE_SAME_NODE = 1; // Same node index but content index before given content index
+ const int SAME_POSITION = 2; // Same node index and samecontent index
+ const int BEHIND_SAME_NODE = 3; // Same node index but content index behind given content index
+ const int BEHIND_NODE = 4; // Position behind the given node index
+
+ int lcl_RelativePosition( const SwPosition& rPos, SwNodeOffset nNode, sal_Int32 nContent )
+ {
+ SwNodeOffset nIndex = rPos.GetNodeIndex();
+ int nReturn = BEFORE_NODE;
+ if( nIndex == nNode )
+ {
+ const sal_Int32 nCntIdx = rPos.GetContentIndex();
+ if( nCntIdx < nContent )
+ nReturn = BEFORE_SAME_NODE;
+ else if( nCntIdx == nContent )
+ nReturn = SAME_POSITION;
+ else
+ nReturn = BEHIND_SAME_NODE;
+ }
+ else if( nIndex > nNode )
+ nReturn = BEHIND_NODE;
+ return nReturn;
+ }
+ struct MarkEntry
+ {
+ tools::Long m_nIdx;
+ bool m_bOther;
+ sal_Int32 m_nContent;
+#if 0
+#include <sal/log.hxx>
+ void Dump()
+ {
+ SAL_INFO("sw.core", "Index: " << m_nIdx << "\tOther: " << m_bOther << "\tContent: " << m_nContent);
+ }
+#endif
+ };
+ struct PaMEntry
+ {
+ SwPaM* m_pPaM;
+ bool m_isMark;
+ sal_Int32 m_nContent;
+ };
+ struct OffsetUpdater
+ {
+ const SwContentNode* m_pNewContentNode;
+ const sal_Int32 m_nOffset;
+ OffsetUpdater(SwContentNode const * pNewContentNode, sal_Int32 nOffset)
+ : m_pNewContentNode(pNewContentNode), m_nOffset(nOffset) {};
+ void operator()(SwPosition& rPos, sal_Int32 nContent) const
+ {
+ rPos.Assign(*m_pNewContentNode, nContent + m_nOffset);
+ };
+ };
+ struct LimitUpdater
+ {
+ const SwContentNode* m_pNewContentNode;
+ const sal_uLong m_nLen;
+ const sal_Int32 m_nCorrLen;
+ LimitUpdater(SwContentNode const * pNewContentNode, sal_uLong nLen, sal_Int32 nCorrLen)
+ : m_pNewContentNode(pNewContentNode), m_nLen(nLen), m_nCorrLen(nCorrLen) {};
+ void operator()(SwPosition& rPos, sal_Int32 nContent) const
+ {
+ if( nContent < m_nCorrLen )
+ {
+ nContent = std::min( nContent, static_cast<sal_Int32>(m_nLen) );
+ }
+ else
+ {
+ nContent -= m_nCorrLen;
+ }
+ rPos.Assign( *m_pNewContentNode, nContent );
+ };
+ };
+ struct ContentIdxStoreImpl : sw::mark::ContentIdxStore
+ {
+ std::vector<MarkEntry> m_aBkmkEntries;
+ std::vector<MarkEntry> m_aRedlineEntries;
+ std::vector<MarkEntry> m_aFlyEntries;
+ std::vector<PaMEntry> m_aUnoCursorEntries;
+ std::vector<PaMEntry> m_aShellCursorEntries;
+ typedef std::function<void (SwPosition& rPos, sal_Int32 nContent)> updater_t;
+ virtual bool Empty() override
+ {
+ return m_aBkmkEntries.empty() && m_aRedlineEntries.empty() && m_aFlyEntries.empty() && m_aUnoCursorEntries.empty() && m_aShellCursorEntries.empty();
+ }
+ virtual void Save(SwDoc& rDoc, SwNodeOffset nNode, sal_Int32 nContent, bool bSaveFlySplit=false) override
+ {
+ SaveBkmks(rDoc, nNode, nContent);
+ SaveRedlines(rDoc, nNode, nContent);
+ SaveFlys(rDoc, nNode, nContent, bSaveFlySplit);
+ SaveUnoCursors(rDoc, nNode, nContent);
+ SaveShellCursors(rDoc, nNode, nContent);
+ }
+ virtual void Restore(SwDoc& rDoc, SwNodeOffset nNode, sal_Int32 nOffset=0, bool bAuto = false, bool bAtStart = false, RestoreMode eMode = RestoreMode::All) override
+ {
+ SwContentNode* pCNd = rDoc.GetNodes()[ nNode ]->GetContentNode();
+ updater_t aUpdater = OffsetUpdater(pCNd, nOffset);
+ if (eMode & RestoreMode::NonFlys)
+ {
+ RestoreBkmks(rDoc, aUpdater);
+ RestoreRedlines(rDoc, aUpdater);
+ RestoreUnoCursors(aUpdater);
+ RestoreShellCursors(aUpdater);
+ }
+ if (eMode & RestoreMode::Flys)
+ {
+ RestoreFlys(rDoc, aUpdater, bAuto, bAtStart);
+ }
+ }
+ virtual void Restore(SwNode& rNd, sal_Int32 nLen, sal_Int32 nCorrLen, RestoreMode eMode = RestoreMode::All) override
+ {
+ SwContentNode* pCNd = rNd.GetContentNode();
+ SwDoc& rDoc = rNd.GetDoc();
+ updater_t aUpdater = LimitUpdater(pCNd, nLen, nCorrLen);
+ if (eMode & RestoreMode::NonFlys)
+ {
+ RestoreBkmks(rDoc, aUpdater);
+ RestoreRedlines(rDoc, aUpdater);
+ RestoreUnoCursors(aUpdater);
+ RestoreShellCursors(aUpdater);
+ }
+ if (eMode & RestoreMode::Flys)
+ {
+ RestoreFlys(rDoc, aUpdater, false, false);
+ }
+ }
+
+ private:
+ void SaveBkmks(SwDoc& rDoc, SwNodeOffset nNode, sal_Int32 nContent);
+ void RestoreBkmks(SwDoc& rDoc, updater_t const & rUpdater);
+ void SaveRedlines(SwDoc& rDoc, SwNodeOffset nNode, sal_Int32 nContent);
+ void RestoreRedlines(SwDoc& rDoc, updater_t const & rUpdater);
+ void SaveFlys(SwDoc& rDoc, SwNodeOffset nNode, sal_Int32 nContent, bool bSaveFlySplit);
+ void RestoreFlys(SwDoc& rDoc, updater_t const & rUpdater, bool bAuto, bool bAtStart);
+ void SaveUnoCursors(SwDoc& rDoc, SwNodeOffset nNode, sal_Int32 nContent);
+ void RestoreUnoCursors(updater_t const & rUpdater);
+ void SaveShellCursors(SwDoc& rDoc, SwNodeOffset nNode, sal_Int32 nContent);
+ void RestoreShellCursors(updater_t const & rUpdater);
+ static const SwPosition& GetRightMarkPos(::sw::mark::IMark const * pMark, bool bOther)
+ { return bOther ? pMark->GetOtherMarkPos() : pMark->GetMarkPos(); };
+ static void SetRightMarkPos(MarkBase* pMark, bool bOther, const SwPosition* const pPos)
+ { bOther ? pMark->SetOtherMarkPos(*pPos) : pMark->SetMarkPos(*pPos); };
+ };
+ void lcl_ChkPaM( std::vector<PaMEntry>& rPaMEntries, const SwNodeOffset nNode, const sal_Int32 nContent, SwPaM& rPaM, const bool bGetPoint, bool bSetMark)
+ {
+ const SwPosition* pPos = &rPaM.GetBound(bGetPoint);
+ if( pPos->GetNodeIndex() == nNode && pPos->GetContentIndex() < nContent )
+ {
+ const PaMEntry aEntry = { &rPaM, bSetMark, pPos->GetContentIndex() };
+ rPaMEntries.push_back(aEntry);
+ }
+ }
+ void lcl_ChkPaMBoth( std::vector<PaMEntry>& rPaMEntries, const SwNodeOffset nNode, const sal_Int32 nContent, SwPaM& rPaM)
+ {
+ lcl_ChkPaM(rPaMEntries, nNode, nContent, rPaM, true, true);
+ lcl_ChkPaM(rPaMEntries, nNode, nContent, rPaM, false, false);
+ }
+ void lcl_ChkUnoCrsrPaMBoth(std::vector<PaMEntry>& rPaMEntries, const SwNodeOffset nNode, const sal_Int32 nContent, SwPaM& rPaM)
+ {
+ lcl_ChkPaM(rPaMEntries, nNode, nContent, rPaM, true, false);
+ lcl_ChkPaM(rPaMEntries, nNode, nContent, rPaM, false, true);
+ }
+
+#if 0
+ static void DumpEntries(std::vector<MarkEntry>* pEntries)
+ {
+ for (MarkEntry& aEntry : *pEntries)
+ aEntry.Dump();
+ }
+#endif
+}
+
+void ContentIdxStoreImpl::SaveBkmks(SwDoc& rDoc, SwNodeOffset nNode, sal_Int32 nContent)
+{
+ IDocumentMarkAccess* const pMarkAccess = rDoc.getIDocumentMarkAccess();
+ const IDocumentMarkAccess::const_iterator_t ppBkmkEnd = pMarkAccess->getAllMarksEnd();
+ for(
+ IDocumentMarkAccess::const_iterator_t ppBkmk = pMarkAccess->getAllMarksBegin();
+ ppBkmk != ppBkmkEnd;
+ ++ppBkmk)
+ {
+ const ::sw::mark::IMark* pBkmk = *ppBkmk;
+ bool bMarkPosEqual = false;
+ if(pBkmk->GetMarkPos().GetNodeIndex() == nNode
+ && pBkmk->GetMarkPos().GetContentIndex() <= nContent)
+ {
+ if(pBkmk->GetMarkPos().GetContentIndex() < nContent)
+ {
+ const MarkEntry aEntry = { static_cast<tools::Long>(ppBkmk - pMarkAccess->getAllMarksBegin()), false, pBkmk->GetMarkPos().GetContentIndex() };
+ m_aBkmkEntries.push_back(aEntry);
+ }
+ else // if a bookmark position is equal nContent, the other position
+ bMarkPosEqual = true; // has to decide if it is added to the array
+ }
+ if(pBkmk->IsExpanded()
+ && pBkmk->GetOtherMarkPos().GetNodeIndex() == nNode
+ && pBkmk->GetOtherMarkPos().GetContentIndex() <= nContent)
+ {
+ if(bMarkPosEqual)
+ { // the other position is before, the (main) position is equal
+ const MarkEntry aEntry = { static_cast<tools::Long>(ppBkmk - pMarkAccess->getAllMarksBegin()), false, pBkmk->GetMarkPos().GetContentIndex() };
+ m_aBkmkEntries.push_back(aEntry);
+ }
+ const MarkEntry aEntry = { static_cast<tools::Long>(ppBkmk - pMarkAccess->getAllMarksBegin()), true, pBkmk->GetOtherMarkPos().GetContentIndex() };
+ m_aBkmkEntries.push_back(aEntry);
+ }
+ }
+}
+
+void ContentIdxStoreImpl::RestoreBkmks(SwDoc& rDoc, updater_t const & rUpdater)
+{
+ IDocumentMarkAccess* const pMarkAccess = rDoc.getIDocumentMarkAccess();
+ for (const MarkEntry& aEntry : m_aBkmkEntries)
+ {
+ if (MarkBase *const pMark = pMarkAccess->getAllMarksBegin().get()[aEntry.m_nIdx])
+ {
+ SwPosition aNewPos(GetRightMarkPos(pMark, aEntry.m_bOther));
+ rUpdater(aNewPos, aEntry.m_nContent);
+ SetRightMarkPos(pMark, aEntry.m_bOther, &aNewPos);
+ }
+ }
+ if (!m_aBkmkEntries.empty())
+ { // tdf#105705 sort bookmarks because SaveBkmks special handling of
+ // "bMarkPosEqual" may destroy sort order
+ pMarkAccess->assureSortedMarkContainers();
+ }
+}
+
+void ContentIdxStoreImpl::SaveRedlines(SwDoc& rDoc, SwNodeOffset nNode, sal_Int32 nContent)
+{
+ SwRedlineTable const & rRedlineTable = rDoc.getIDocumentRedlineAccess().GetRedlineTable();
+ tools::Long nIdx = 0;
+ for (const SwRangeRedline* pRdl : rRedlineTable)
+ {
+ int nPointPos = lcl_RelativePosition( *pRdl->GetPoint(), nNode, nContent );
+ int nMarkPos = pRdl->HasMark() ? lcl_RelativePosition( *pRdl->GetMark(), nNode, nContent ) :
+ nPointPos;
+ // #i59534: We have to store the positions inside the same node before the insert position
+ // and the one at the insert position if the corresponding Point/Mark position is before
+ // the insert position.
+ if( nPointPos == BEFORE_SAME_NODE ||
+ ( nPointPos == SAME_POSITION && nMarkPos < SAME_POSITION ) )
+ {
+ const MarkEntry aEntry = { nIdx, false, pRdl->GetPoint()->GetContentIndex() };
+ m_aRedlineEntries.push_back(aEntry);
+ }
+ if( pRdl->HasMark() && ( nMarkPos == BEFORE_SAME_NODE ||
+ ( nMarkPos == SAME_POSITION && nPointPos < SAME_POSITION ) ) )
+ {
+ const MarkEntry aEntry = { nIdx, true, pRdl->GetMark()->GetContentIndex() };
+ m_aRedlineEntries.push_back(aEntry);
+ }
+ ++nIdx;
+ }
+}
+
+void ContentIdxStoreImpl::RestoreRedlines(SwDoc& rDoc, updater_t const & rUpdater)
+{
+ const SwRedlineTable& rRedlTable = rDoc.getIDocumentRedlineAccess().GetRedlineTable();
+ for (const MarkEntry& aEntry : m_aRedlineEntries)
+ {
+ SwPosition* const pPos = aEntry.m_bOther
+ ? rRedlTable[ aEntry.m_nIdx ]->GetMark()
+ : rRedlTable[ aEntry.m_nIdx ]->GetPoint();
+ rUpdater(*pPos, aEntry.m_nContent);
+ }
+}
+
+void ContentIdxStoreImpl::SaveFlys(SwDoc& rDoc, SwNodeOffset nNode, sal_Int32 nContent, bool bSaveFlySplit)
+{
+ SwContentNode *pNode = rDoc.GetNodes()[nNode]->GetContentNode();
+ if( !pNode )
+ return;
+ SwFrame* pFrame = pNode->getLayoutFrame( rDoc.getIDocumentLayoutAccess().GetCurrentLayout() );
+ if( pFrame )
+ {
+ // sw_redlinehide: this looks like an invalid optimisation if merged,
+ // assuming that flys in deleted redlines should be saved here too.
+ if ((!pFrame->IsTextFrame() || !static_cast<SwTextFrame const*>(pFrame)->GetMergedPara())
+ && !pFrame->GetDrawObjs())
+ return; // if we have a layout and no DrawObjs, we can skip this
+ }
+ MarkEntry aSave = { 0, false, 0 };
+ for (const SwFrameFormat* pFrameFormat : *rDoc.GetSpzFrameFormats())
+ {
+ if ( RES_FLYFRMFMT == pFrameFormat->Which() || RES_DRAWFRMFMT == pFrameFormat->Which() )
+ {
+ const SwFormatAnchor& rAnchor = pFrameFormat->GetAnchor();
+ SwNode const*const pAnchorNode = rAnchor.GetAnchorNode();
+ if ( pAnchorNode && ( nNode == pAnchorNode->GetIndex() ) &&
+ ( RndStdIds::FLY_AT_PARA == rAnchor.GetAnchorId() ||
+ RndStdIds::FLY_AT_CHAR == rAnchor.GetAnchorId() ) )
+ {
+ bool bSkip = false;
+ aSave.m_bOther = false;
+ aSave.m_nContent = rAnchor.GetAnchorContentOffset();
+ if ( RndStdIds::FLY_AT_CHAR == rAnchor.GetAnchorId() )
+ {
+ if( nContent <= aSave.m_nContent )
+ {
+ if( bSaveFlySplit )
+ aSave.m_bOther = true;
+ else
+ bSkip = true;
+ }
+ }
+ if(!bSkip)
+ m_aFlyEntries.push_back(aSave);
+ }
+ }
+ ++aSave.m_nIdx;
+ }
+}
+
+void ContentIdxStoreImpl::RestoreFlys(SwDoc& rDoc, updater_t const & rUpdater, bool bAuto, bool bAtStart)
+{
+ sw::SpzFrameFormats* pSpz = rDoc.GetSpzFrameFormats();
+ for (const MarkEntry& aEntry : m_aFlyEntries)
+ {
+ if(!aEntry.m_bOther)
+ {
+ sw::SpzFrameFormat* pFrameFormat = (*pSpz)[ aEntry.m_nIdx ];
+ const SwFormatAnchor& rFlyAnchor = pFrameFormat->GetAnchor();
+ if( rFlyAnchor.GetContentAnchor() )
+ {
+ if(bAtStart && RndStdIds::FLY_AT_PARA == rFlyAnchor.GetAnchorId())
+ continue;
+ SwFormatAnchor aNew( rFlyAnchor );
+ SwPosition aNewPos( *rFlyAnchor.GetContentAnchor() );
+ rUpdater(aNewPos, aEntry.m_nContent);
+ if ( RndStdIds::FLY_AT_CHAR != rFlyAnchor.GetAnchorId() )
+ {
+ aNewPos.nContent.Assign( nullptr, 0 );
+ }
+ aNew.SetAnchor( &aNewPos );
+ pFrameFormat->SetFormatAttr( aNew );
+ }
+ }
+ else if( bAuto )
+ {
+ SwFrameFormat* pFrameFormat = (*pSpz)[ aEntry.m_nIdx ];
+ const SfxPoolItem* pAnchor = &pFrameFormat->GetAnchor();
+ pFrameFormat->CallSwClientNotify(sw::LegacyModifyHint(pAnchor, pAnchor));
+ }
+ }
+}
+
+void ContentIdxStoreImpl::SaveUnoCursors(SwDoc& rDoc, SwNodeOffset nNode, sal_Int32 nContent)
+{
+ rDoc.cleanupUnoCursorTable();
+ for (const auto& pWeakUnoCursor : rDoc.mvUnoCursorTable)
+ {
+ auto pUnoCursor(pWeakUnoCursor.lock());
+ if(!pUnoCursor)
+ continue;
+ for(SwPaM& rPaM : pUnoCursor->GetRingContainer())
+ {
+ lcl_ChkUnoCrsrPaMBoth(m_aUnoCursorEntries, nNode, nContent, rPaM);
+ }
+ const SwUnoTableCursor* pUnoTableCursor = dynamic_cast<const SwUnoTableCursor*>(pUnoCursor.get());
+ if( pUnoTableCursor )
+ {
+ for(SwPaM& rPaM : const_cast<SwUnoTableCursor*>(pUnoTableCursor)->GetSelRing().GetRingContainer())
+ {
+ lcl_ChkUnoCrsrPaMBoth(m_aUnoCursorEntries, nNode, nContent, rPaM);
+ }
+ }
+ }
+}
+
+void ContentIdxStoreImpl::RestoreUnoCursors(updater_t const & rUpdater)
+{
+ for (const PaMEntry& aEntry : m_aUnoCursorEntries)
+ {
+ rUpdater(aEntry.m_pPaM->GetBound(!aEntry.m_isMark), aEntry.m_nContent);
+ }
+}
+
+void ContentIdxStoreImpl::SaveShellCursors(SwDoc& rDoc, SwNodeOffset nNode, sal_Int32 nContent)
+{
+ SwCursorShell* pShell = rDoc.GetEditShell();
+ if (!pShell)
+ return;
+ for(SwViewShell& rCurShell : pShell->GetRingContainer())
+ {
+ if( auto pCursorShell = dynamic_cast<SwCursorShell *>(&rCurShell) )
+ {
+ SwPaM *_pStackCursor = pCursorShell->GetStackCursor();
+ if( _pStackCursor )
+ for (;;)
+ {
+ lcl_ChkPaMBoth( m_aShellCursorEntries, nNode, nContent, *_pStackCursor);
+ if (!_pStackCursor)
+ break;
+ _pStackCursor = _pStackCursor->GetNext();
+ if (_pStackCursor == pCursorShell->GetStackCursor())
+ break;
+ }
+
+ for(SwPaM& rPaM : pCursorShell->GetCursor_()->GetRingContainer())
+ {
+ lcl_ChkPaMBoth( m_aShellCursorEntries, nNode, nContent, rPaM);
+ }
+ }
+ }
+}
+
+void ContentIdxStoreImpl::RestoreShellCursors(updater_t const & rUpdater)
+{
+ for (const PaMEntry& aEntry : m_aShellCursorEntries)
+ {
+ rUpdater(aEntry.m_pPaM->GetBound(aEntry.m_isMark), aEntry.m_nContent);
+ }
+}
+
+namespace sw::mark {
+ std::shared_ptr<ContentIdxStore> ContentIdxStore::Create()
+ {
+ return std::make_shared<ContentIdxStoreImpl>();
+ }
+}
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/DocumentChartDataProviderManager.cxx b/sw/source/core/doc/DocumentChartDataProviderManager.cxx
new file mode 100644
index 0000000000..e559892abf
--- /dev/null
+++ b/sw/source/core/doc/DocumentChartDataProviderManager.cxx
@@ -0,0 +1,105 @@
+/* -*- 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 <DocumentChartDataProviderManager.hxx>
+
+#include <doc.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <vcl/svapp.hxx>
+#include <swtable.hxx>
+#include <unochart.hxx>
+#include <ndole.hxx>
+#include <com/sun/star/chart2/XChartDocument.hpp>
+#include <com/sun/star/embed/XEmbeddedObject.hpp>
+
+
+using namespace com::sun::star;
+using namespace com::sun::star::uno;
+
+namespace sw {
+
+DocumentChartDataProviderManager::DocumentChartDataProviderManager( SwDoc& i_rSwdoc ) : m_rDoc( i_rSwdoc )
+{
+
+}
+
+SwChartDataProvider * DocumentChartDataProviderManager::GetChartDataProvider( bool bCreate ) const
+{
+ // since there must be only one instance of this object per document
+ // we need a mutex here
+ SolarMutexGuard aGuard;
+
+ if (bCreate && !maChartDataProviderImplRef.is())
+ {
+ maChartDataProviderImplRef = new SwChartDataProvider(m_rDoc);
+ }
+ return maChartDataProviderImplRef.get();
+}
+
+void DocumentChartDataProviderManager::CreateChartInternalDataProviders( const SwTable *pTable )
+{
+ if (!pTable)
+ return;
+
+ OUString aName( pTable->GetFrameFormat()->GetName() );
+ SwOLENode *pONd;
+ SwStartNode *pStNd;
+ SwNodeIndex aIdx( *m_rDoc.GetNodes().GetEndOfAutotext().StartOfSectionNode(), 1 );
+ while (nullptr != (pStNd = aIdx.GetNode().GetStartNode()))
+ {
+ ++aIdx;
+ pONd = aIdx.GetNode().GetOLENode();
+ if( pONd &&
+ aName == pONd->GetChartTableName() /* OLE node is chart? */ &&
+ nullptr != (pONd->getLayoutFrame( m_rDoc.getIDocumentLayoutAccess().GetCurrentLayout() )) /* chart frame is not hidden */ )
+ {
+ uno::Reference < embed::XEmbeddedObject > xIP = pONd->GetOLEObj().GetOleRef();
+ if ( svt::EmbeddedObjectRef::TryRunningState( xIP ) )
+ {
+ uno::Reference< chart2::XChartDocument > xChart( xIP->getComponent(), UNO_QUERY );
+ if (xChart.is())
+ xChart->createInternalDataProvider( true );
+
+ // there may be more than one chart for each table thus we need to continue the loop...
+ }
+ }
+ aIdx.Assign( *pStNd->EndOfSectionNode(), + 1 );
+ }
+}
+
+SwChartLockController_Helper & DocumentChartDataProviderManager::GetChartControllerHelper()
+{
+ if (!mpChartControllerHelper)
+ {
+ mpChartControllerHelper.reset(new SwChartLockController_Helper( & m_rDoc ));
+ }
+ return *mpChartControllerHelper;
+}
+
+DocumentChartDataProviderManager::~DocumentChartDataProviderManager()
+{
+ // clean up chart related structures...
+ // Note: the chart data provider gets already disposed in ~SwDocShell
+ // since all UNO API related functionality requires an existing SwDocShell
+ // this assures that dispose gets called if there is need for it.
+ maChartDataProviderImplRef.clear();
+}
+
+}
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/DocumentContentOperationsManager.cxx b/sw/source/core/doc/DocumentContentOperationsManager.cxx
new file mode 100644
index 0000000000..09e0a1233e
--- /dev/null
+++ b/sw/source/core/doc/DocumentContentOperationsManager.cxx
@@ -0,0 +1,5520 @@
+/* -*- 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 <DocumentContentOperationsManager.hxx>
+#include <DocumentRedlineManager.hxx>
+#include <wrtsh.hxx>
+#include <doc.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <IDocumentMarkAccess.hxx>
+#include <IDocumentState.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <IDocumentStylePoolAccess.hxx>
+#include <IDocumentSettingAccess.hxx>
+#include <UndoManager.hxx>
+#include <docary.hxx>
+#include <textboxhelper.hxx>
+#include <dcontact.hxx>
+#include <grfatr.hxx>
+#include <numrule.hxx>
+#include <charfmt.hxx>
+#include <ndgrf.hxx>
+#include <ndnotxt.hxx>
+#include <ndole.hxx>
+#include <breakit.hxx>
+#include <frmfmt.hxx>
+#include <fmtanchr.hxx>
+#include <fmtcntnt.hxx>
+#include <fmtinfmt.hxx>
+#include <fmtpdsc.hxx>
+#include <fmtcnct.hxx>
+#include <SwStyleNameMapper.hxx>
+#include <redline.hxx>
+#include <txtfrm.hxx>
+#include <rootfrm.hxx>
+#include <frmtool.hxx>
+#include <unocrsr.hxx>
+#include <mvsave.hxx>
+#include <ndtxt.hxx>
+#include <poolfmt.hxx>
+#include <paratr.hxx>
+#include <txatbase.hxx>
+#include <UndoRedline.hxx>
+#include <undobj.hxx>
+#include <UndoBookmark.hxx>
+#include <UndoDelete.hxx>
+#include <UndoSplitMove.hxx>
+#include <UndoOverwrite.hxx>
+#include <UndoInsert.hxx>
+#include <UndoAttribute.hxx>
+#include <rolbck.hxx>
+#include <acorrect.hxx>
+#include <bookmark.hxx>
+#include <ftnidx.hxx>
+#include <txtftn.hxx>
+#include <hints.hxx>
+#include <fmtflcnt.hxx>
+#include <docedt.hxx>
+#include <frameformats.hxx>
+#include <formatflysplit.hxx>
+#include <o3tl/safeint.hxx>
+#include <sal/log.hxx>
+#include <unotools/charclass.hxx>
+#include <unotools/configmgr.hxx>
+#include <unotools/transliterationwrapper.hxx>
+#include <i18nutil/transliteration.hxx>
+#include <sfx2/Metadatable.hxx>
+#include <sot/exchange.hxx>
+#include <svl/stritem.hxx>
+#include <svl/itemiter.hxx>
+#include <svx/svdobj.hxx>
+#include <svx/svdouno.hxx>
+#include <tools/globname.hxx>
+#include <editeng/formatbreakitem.hxx>
+#include <com/sun/star/i18n/Boundary.hpp>
+#include <com/sun/star/i18n/WordType.hpp>
+#include <com/sun/star/i18n/XBreakIterator.hpp>
+#include <com/sun/star/embed/XEmbeddedObject.hpp>
+
+#include <tuple>
+#include <memory>
+
+using namespace ::com::sun::star::i18n;
+
+namespace
+{
+ // Copy method from SwDoc
+ // Prevent copying into Flys that are anchored in the range
+ bool lcl_ChkFlyFly( SwDoc& rDoc, SwNodeOffset nSttNd, SwNodeOffset nEndNd,
+ SwNodeOffset nInsNd )
+ {
+
+ for(sw::SpzFrameFormat* pFormat: *rDoc.GetSpzFrameFormats())
+ {
+ SwFormatAnchor const*const pAnchor = &pFormat->GetAnchor();
+ SwNode const*const pAnchorNode = pAnchor->GetAnchorNode();
+ if (pAnchorNode &&
+ ((RndStdIds::FLY_AS_CHAR == pAnchor->GetAnchorId()) ||
+ (RndStdIds::FLY_AT_CHAR == pAnchor->GetAnchorId()) ||
+ (RndStdIds::FLY_AT_FLY == pAnchor->GetAnchorId()) ||
+ (RndStdIds::FLY_AT_PARA == pAnchor->GetAnchorId())) &&
+ nSttNd <= pAnchorNode->GetIndex() &&
+ pAnchorNode->GetIndex() < nEndNd )
+ {
+ const SwFormatContent& rContent = pFormat->GetContent();
+ SwStartNode* pSNd;
+ if( !rContent.GetContentIdx() ||
+ nullptr == ( pSNd = rContent.GetContentIdx()->GetNode().GetStartNode() ))
+ continue;
+
+ if( pSNd->GetIndex() < nInsNd &&
+ nInsNd < pSNd->EndOfSectionIndex() )
+ // Do not copy !
+ return true;
+
+ if( lcl_ChkFlyFly( rDoc, pSNd->GetIndex(),
+ pSNd->EndOfSectionIndex(), nInsNd ) )
+ // Do not copy !
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ SwNodeIndex InitDelCount(SwPaM const& rSourcePaM, SwNodeOffset & rDelCount)
+ {
+ SwPosition const& rStart(*rSourcePaM.Start());
+ // Special handling for SwDoc::AppendDoc
+ if (rSourcePaM.GetDoc().GetNodes().GetEndOfExtras().GetIndex() + 1
+ == rStart.GetNodeIndex())
+ {
+ rDelCount = SwNodeOffset(1);
+ return SwNodeIndex(rStart.GetNode(), +1);
+ }
+ else
+ {
+ rDelCount = SwNodeOffset(0);
+ return SwNodeIndex(rStart.GetNode());
+ }
+ }
+
+ /*
+ The CopyBookmarks function has to copy bookmarks from the source to the destination nodes
+ array. It is called after a call of the CopyNodes(..) function. But this function does not copy
+ every node (at least at the moment: 2/08/2006 ), section start and end nodes will not be copied
+ if the corresponding end/start node is outside the copied pam.
+ The lcl_NonCopyCount function counts the number of these nodes, given the copied pam and a node
+ index inside the pam.
+ rPam is the original source pam, rLastIdx is the last calculated position, rDelCount the number
+ of "non-copy" nodes between rPam.Start() and rLastIdx.
+ nNewIdx is the new position of interest.
+ */
+ void lcl_NonCopyCount( const SwPaM& rPam, SwNodeIndex& rLastIdx, const SwNodeOffset nNewIdx, SwNodeOffset& rDelCount )
+ {
+ SwNodeOffset nStart = rPam.Start()->GetNodeIndex();
+ SwNodeOffset nEnd = rPam.End()->GetNodeIndex();
+ if( rLastIdx.GetIndex() < nNewIdx ) // Moving forward?
+ {
+ // We never copy the StartOfContent node
+ do // count "non-copy" nodes
+ {
+ SwNode& rNode = rLastIdx.GetNode();
+ if( ( rNode.IsSectionNode() && rNode.EndOfSectionIndex() >= nEnd )
+ || ( rNode.IsEndNode() && rNode.StartOfSectionNode()->GetIndex() < nStart ) )
+ {
+ ++rDelCount;
+ }
+ ++rLastIdx;
+ }
+ while( rLastIdx.GetIndex() < nNewIdx );
+ }
+ else if( rDelCount ) // optimization: if there are no "non-copy" nodes until now,
+ // no move backward needed
+ {
+ while( rLastIdx.GetIndex() > nNewIdx )
+ {
+ SwNode& rNode = rLastIdx.GetNode();
+ if( ( rNode.IsSectionNode() && rNode.EndOfSectionIndex() >= nEnd )
+ || ( rNode.IsEndNode() && rNode.StartOfSectionNode()->GetIndex() < nStart ) )
+ {
+ --rDelCount;
+ }
+ --rLastIdx;
+ }
+ }
+ }
+
+ void lcl_SetCpyPos( const SwPosition& rOrigPos,
+ const SwPosition& rOrigStt,
+ const SwPosition& rCpyStt,
+ SwPosition& rChgPos,
+ SwNodeOffset nDelCount )
+ {
+ SwNodeOffset nNdOff = rOrigPos.GetNodeIndex();
+ nNdOff -= rOrigStt.GetNodeIndex();
+ nNdOff -= nDelCount;
+ sal_Int32 nContentPos = rOrigPos.GetContentIndex();
+
+ // Always adjust <nNode> at to be changed <SwPosition> instance <rChgPos>
+ rChgPos.Assign( nNdOff + rCpyStt.GetNodeIndex() );
+ if (!rChgPos.GetNode().GetContentNode())
+ return;
+ if( !nNdOff )
+ {
+ // just adapt the content index
+ if( nContentPos > rOrigStt.GetContentIndex() )
+ nContentPos -= rOrigStt.GetContentIndex();
+ else
+ nContentPos = 0;
+ nContentPos += rCpyStt.GetContentIndex();
+ }
+ rChgPos.SetContent( nContentPos );
+ }
+
+}
+
+namespace sw
+{
+ // TODO: use SaveBookmark (from DelBookmarks)
+ void CopyBookmarks(const SwPaM& rPam, const SwPosition& rCpyPam, SwCopyFlags flags)
+ {
+ const SwDoc& rSrcDoc = rPam.GetDoc();
+ SwDoc& rDestDoc = rCpyPam.GetDoc();
+ const IDocumentMarkAccess* const pSrcMarkAccess = rSrcDoc.getIDocumentMarkAccess();
+ ::sw::UndoGuard const undoGuard(rDestDoc.GetIDocumentUndoRedo());
+
+ const SwPosition &rStt = *rPam.Start(), &rEnd = *rPam.End();
+ SwPosition const*const pCpyStt = &rCpyPam;
+
+ std::vector< const ::sw::mark::IMark* > vMarksToCopy;
+ for ( IDocumentMarkAccess::const_iterator_t ppMark = pSrcMarkAccess->getAllMarksBegin();
+ ppMark != pSrcMarkAccess->getAllMarksEnd();
+ ++ppMark )
+ {
+ const ::sw::mark::IMark* const pMark = *ppMark;
+
+ const SwPosition& rMarkStart = pMark->GetMarkStart();
+ const SwPosition& rMarkEnd = pMark->GetMarkEnd();
+ // only include marks that are in the range and not touching both start and end
+ // - not for annotation or checkbox marks.
+ bool const isIncludeStart(
+ (rStt.GetContentIndex() == 0 // paragraph start selected?
+ // also: only if inserting at the start - cross reference
+ // marks require index to be 0, and there could be one
+ // on the target node already
+ && rCpyPam.GetContentIndex() == 0)
+ || rMarkStart != rStt);
+ bool const isIncludeEnd(
+ (rEnd.GetNode().IsTextNode() // paragraph end selected?
+ && rEnd.GetContentIndex() == rEnd.GetNode().GetTextNode()->Len())
+ || rMarkEnd != rEnd);
+ const bool bIsNotOnBoundary =
+ pMark->IsExpanded()
+ ? (isIncludeStart || isIncludeEnd) // rMarkStart != rMarkEnd
+ : (isIncludeStart && isIncludeEnd); // rMarkStart == rMarkEnd
+ const IDocumentMarkAccess::MarkType aMarkType = IDocumentMarkAccess::GetType(*pMark);
+ if ( rMarkStart >= rStt && rMarkEnd <= rEnd
+ && ( bIsNotOnBoundary
+ || aMarkType == IDocumentMarkAccess::MarkType::ANNOTATIONMARK
+ || aMarkType == IDocumentMarkAccess::MarkType::TEXT_FIELDMARK
+ || aMarkType == IDocumentMarkAccess::MarkType::CHECKBOX_FIELDMARK
+ || aMarkType == IDocumentMarkAccess::MarkType::DROPDOWN_FIELDMARK
+ || aMarkType == IDocumentMarkAccess::MarkType::DATE_FIELDMARK))
+ {
+ vMarksToCopy.push_back(pMark);
+ }
+ }
+ // We have to count the "non-copied" nodes...
+ SwNodeOffset nDelCount;
+ SwNodeIndex aCorrIdx(InitDelCount(rPam, nDelCount));
+ for(const sw::mark::IMark* const pMark : vMarksToCopy)
+ {
+ SwPaM aTmpPam(*pCpyStt);
+ lcl_NonCopyCount(rPam, aCorrIdx, pMark->GetMarkPos().GetNodeIndex(), nDelCount);
+ lcl_SetCpyPos( pMark->GetMarkPos(), rStt, *pCpyStt, *aTmpPam.GetPoint(), nDelCount);
+ if(pMark->IsExpanded())
+ {
+ aTmpPam.SetMark();
+ lcl_NonCopyCount(rPam, aCorrIdx, pMark->GetOtherMarkPos().GetNodeIndex(), nDelCount);
+ lcl_SetCpyPos(pMark->GetOtherMarkPos(), rStt, *pCpyStt, *aTmpPam.GetMark(), nDelCount);
+ }
+
+ OUString sRequestedName = pMark->GetName();
+ if (flags & SwCopyFlags::IsMoveToFly)
+ {
+ assert(&rSrcDoc == &rDestDoc);
+ // Ensure the name can be given to NewMark, since this is ultimately a move
+ auto pSoonToBeDeletedMark = const_cast<sw::mark::IMark*>(pMark);
+ rDestDoc.getIDocumentMarkAccess()->renameMark(pSoonToBeDeletedMark,
+ sRequestedName + "COPY_IS_MOVE");
+ }
+
+ ::sw::mark::IMark* const pNewMark = rDestDoc.getIDocumentMarkAccess()->makeMark(
+ aTmpPam,
+ sRequestedName,
+ IDocumentMarkAccess::GetType(*pMark),
+ ::sw::mark::InsertMode::CopyText);
+ // Explicitly try to get exactly the same name as in the source
+ // because NavigatorReminders, DdeBookmarks etc. ignore the proposed name
+ if (pNewMark == nullptr)
+ {
+ assert(IDocumentMarkAccess::GetType(*pMark) == IDocumentMarkAccess::MarkType::CROSSREF_NUMITEM_BOOKMARK
+ || IDocumentMarkAccess::GetType(*pMark) == IDocumentMarkAccess::MarkType::CROSSREF_HEADING_BOOKMARK);
+ continue; // can't insert duplicate cross reference mark
+ }
+ rDestDoc.getIDocumentMarkAccess()->renameMark(pNewMark, sRequestedName);
+
+ // copying additional attributes for bookmarks or fieldmarks
+ ::sw::mark::IBookmark* const pNewBookmark =
+ dynamic_cast< ::sw::mark::IBookmark* const >(pNewMark);
+ const ::sw::mark::IBookmark* const pOldBookmark =
+ dynamic_cast< const ::sw::mark::IBookmark* >(pMark);
+ if (pNewBookmark && pOldBookmark)
+ {
+ pNewBookmark->SetKeyCode(pOldBookmark->GetKeyCode());
+ pNewBookmark->SetShortName(pOldBookmark->GetShortName());
+ pNewBookmark->Hide(pOldBookmark->IsHidden());
+ pNewBookmark->SetHideCondition(pOldBookmark->GetHideCondition());
+ }
+ ::sw::mark::IFieldmark* const pNewFieldmark =
+ dynamic_cast< ::sw::mark::IFieldmark* const >(pNewMark);
+ const ::sw::mark::IFieldmark* const pOldFieldmark =
+ dynamic_cast< const ::sw::mark::IFieldmark* >(pMark);
+ if (pNewFieldmark && pOldFieldmark)
+ {
+ pNewFieldmark->SetFieldname(pOldFieldmark->GetFieldname());
+ pNewFieldmark->SetFieldHelptext(pOldFieldmark->GetFieldHelptext());
+ ::sw::mark::IFieldmark::parameter_map_t* pNewParams = pNewFieldmark->GetParameters();
+ const ::sw::mark::IFieldmark::parameter_map_t* pOldParams = pOldFieldmark->GetParameters();
+ for (const auto& rEntry : *pOldParams )
+ {
+ pNewParams->insert( rEntry );
+ }
+ }
+
+ ::sfx2::Metadatable const*const pMetadatable(
+ dynamic_cast< ::sfx2::Metadatable const* >(pMark));
+ ::sfx2::Metadatable *const pNewMetadatable(
+ dynamic_cast< ::sfx2::Metadatable * >(pNewMark));
+ if (pMetadatable && pNewMetadatable)
+ {
+ pNewMetadatable->RegisterAsCopyOf(*pMetadatable);
+ }
+ }
+ }
+} // namespace sw
+
+namespace
+{
+ void lcl_DeleteRedlines( const SwPaM& rPam, SwPaM& rCpyPam )
+ {
+ const SwDoc& rSrcDoc = rPam.GetDoc();
+ const SwRedlineTable& rTable = rSrcDoc.getIDocumentRedlineAccess().GetRedlineTable();
+ if( rTable.empty() )
+ return;
+
+ SwDoc& rDestDoc = rCpyPam.GetDoc();
+ SwPosition* pCpyStt = rCpyPam.Start(), *pCpyEnd = rCpyPam.End();
+ std::unique_ptr<SwPaM> pDelPam;
+ auto [pStt, pEnd] = rPam.StartEnd(); // SwPosition*
+ // We have to count the "non-copied" nodes
+ SwNodeOffset nDelCount;
+ SwNodeIndex aCorrIdx(InitDelCount(rPam, nDelCount));
+
+ SwRedlineTable::size_type n = 0;
+ rSrcDoc.getIDocumentRedlineAccess().GetRedline( *pStt, &n );
+ for( ; n < rTable.size(); ++n )
+ {
+ const SwRangeRedline* pRedl = rTable[ n ];
+ if( RedlineType::Delete == pRedl->GetType() && pRedl->IsVisible() )
+ {
+ auto [pRStt, pREnd] = pRedl->StartEnd(); // SwPosition*
+
+ SwComparePosition eCmpPos = ComparePosition( *pStt, *pEnd, *pRStt, *pREnd );
+ switch( eCmpPos )
+ {
+ case SwComparePosition::CollideEnd:
+ case SwComparePosition::Before:
+ // Pos1 is before Pos2
+ break;
+
+ case SwComparePosition::CollideStart:
+ case SwComparePosition::Behind:
+ // Pos1 is after Pos2
+ n = rTable.size();
+ break;
+
+ default:
+ {
+ pDelPam.reset(new SwPaM( *pCpyStt, pDelPam.release() ));
+ if( *pStt < *pRStt )
+ {
+ lcl_NonCopyCount( rPam, aCorrIdx, pRStt->GetNodeIndex(), nDelCount );
+ lcl_SetCpyPos( *pRStt, *pStt, *pCpyStt,
+ *pDelPam->GetPoint(), nDelCount );
+ }
+ pDelPam->SetMark();
+
+ if( *pEnd < *pREnd )
+ *pDelPam->GetPoint() = *pCpyEnd;
+ else
+ {
+ lcl_NonCopyCount( rPam, aCorrIdx, pREnd->GetNodeIndex(), nDelCount );
+ lcl_SetCpyPos( *pREnd, *pStt, *pCpyStt,
+ *pDelPam->GetPoint(), nDelCount );
+ }
+
+ if (pDelPam->GetNext() != pDelPam.get()
+ && *pDelPam->GetNext()->End() == *pDelPam->Start())
+ {
+ *pDelPam->GetNext()->End() = *pDelPam->End();
+ pDelPam.reset(pDelPam->GetNext());
+ }
+ }
+ }
+ }
+ }
+
+ if( !pDelPam )
+ return;
+
+ RedlineFlags eOld = rDestDoc.getIDocumentRedlineAccess().GetRedlineFlags();
+ rDestDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld | RedlineFlags::Ignore );
+
+ ::sw::UndoGuard const undoGuard(rDestDoc.GetIDocumentUndoRedo());
+
+ // At this point, pDelPam points to the last of maybe several disjoint selections, organized
+ // in reverse order in document (so every GetNext() returns a PaM closer to document start,
+ // until wrap to pDelPam). Removal of the selections must be from last in document to first,
+ // to avoid situations when another PaM in chain points into the node that will be destroyed
+ // (joined to previous) by removal of the currently processed PaM.
+ do {
+ rDestDoc.getIDocumentContentOperations().DeleteAndJoin(*pDelPam);
+ if( !pDelPam->IsMultiSelection() )
+ break;
+ pDelPam.reset(pDelPam->GetNext());
+ } while( true );
+
+ rDestDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
+ }
+
+ void lcl_DeleteRedlines( const SwNodeRange& rRg, SwNodeRange const & rCpyRg )
+ {
+ SwDoc& rSrcDoc = rRg.aStart.GetNode().GetDoc();
+ if( !rSrcDoc.getIDocumentRedlineAccess().GetRedlineTable().empty() )
+ {
+ SwPaM aRgTmp( rRg.aStart, rRg.aEnd );
+ SwPaM aCpyTmp( rCpyRg.aStart, rCpyRg.aEnd );
+ lcl_DeleteRedlines( aRgTmp, aCpyTmp );
+ }
+ }
+
+ void lcl_ChainFormats( SwFlyFrameFormat *pSrc, SwFlyFrameFormat *pDest )
+ {
+ SwFormatChain aSrc( pSrc->GetChain() );
+ if ( !aSrc.GetNext() )
+ {
+ aSrc.SetNext( pDest );
+ pSrc->SetFormatAttr( aSrc );
+ }
+ SwFormatChain aDest( pDest->GetChain() );
+ if ( !aDest.GetPrev() )
+ {
+ aDest.SetPrev( pSrc );
+ pDest->SetFormatAttr( aDest );
+ }
+ }
+
+ // #i86492#
+ bool lcl_ContainsOnlyParagraphsInList( const SwPaM& rPam )
+ {
+ bool bRet = false;
+
+ const SwTextNode* pTextNd = rPam.Start()->GetNode().GetTextNode();
+ const SwTextNode* pEndTextNd = rPam.End()->GetNode().GetTextNode();
+ if ( pTextNd && pTextNd->IsInList() &&
+ pEndTextNd && pEndTextNd->IsInList() )
+ {
+ bRet = true;
+ SwNodeIndex aIdx(rPam.Start()->GetNode());
+
+ do
+ {
+ ++aIdx;
+ pTextNd = aIdx.GetNode().GetTextNode();
+
+ if ( !pTextNd || !pTextNd->IsInList() )
+ {
+ bRet = false;
+ break;
+ }
+ } while (pTextNd != pEndTextNd);
+ }
+
+ return bRet;
+ }
+
+ bool lcl_MarksWholeNode(const SwPaM & rPam)
+ {
+ bool bResult = false;
+ auto [pStt, pEnd] = rPam.StartEnd(); // SwPosition*
+
+ if (nullptr != pStt && nullptr != pEnd)
+ {
+ const SwTextNode* pSttNd = pStt->GetNode().GetTextNode();
+ const SwTextNode* pEndNd = pEnd->GetNode().GetTextNode();
+
+ if (nullptr != pSttNd && nullptr != pEndNd &&
+ pStt->GetContentIndex() == 0 &&
+ pEnd->GetContentIndex() == pEndNd->Len())
+ {
+ bResult = true;
+ }
+ }
+
+ return bResult;
+ }
+}
+
+//local functions originally from sw/source/core/doc/docedt.cxx
+namespace sw
+{
+ void CalcBreaks(std::vector<std::pair<SwNodeOffset, sal_Int32>> & rBreaks,
+ SwPaM const & rPam, bool const isOnlyFieldmarks)
+ {
+ SwNodeOffset const nStartNode(rPam.Start()->GetNodeIndex());
+ SwNodeOffset const nEndNode(rPam.End()->GetNodeIndex());
+ SwNodes const& rNodes(rPam.GetPoint()->GetNodes());
+ IDocumentMarkAccess const& rIDMA(*rPam.GetDoc().getIDocumentMarkAccess());
+
+ std::stack<std::tuple<sw::mark::IFieldmark const*, bool, SwNodeOffset, sal_Int32>> startedFields;
+
+ for (SwNodeOffset n = nStartNode; n <= nEndNode; ++n)
+ {
+ SwNode *const pNode(rNodes[n]);
+ if (pNode->IsTextNode())
+ {
+ SwTextNode & rTextNode(*pNode->GetTextNode());
+ sal_Int32 const nStart(n == nStartNode
+ ? rPam.Start()->GetContentIndex()
+ : 0);
+ sal_Int32 const nEnd(n == nEndNode
+ ? rPam.End()->GetContentIndex()
+ : rTextNode.Len());
+ for (sal_Int32 i = nStart; i < nEnd; ++i)
+ {
+ const sal_Unicode c(rTextNode.GetText()[i]);
+ switch (c)
+ {
+ // note: CH_TXT_ATR_FORMELEMENT does not need handling
+ // not sure how CH_TXT_ATR_INPUTFIELDSTART/END are currently handled
+ case CH_TXTATR_INWORD:
+ case CH_TXTATR_BREAKWORD:
+ {
+ // META hints only have dummy char at the start, not
+ // at the end, so no need to check in nStartNode
+ if (n == nEndNode && !isOnlyFieldmarks)
+ {
+ SwTextAttr const* pAttr(rTextNode.GetTextAttrForCharAt(i));
+ if (pAttr && pAttr->End() && (nEnd < *pAttr->End()))
+ {
+ assert(pAttr->HasDummyChar());
+ rBreaks.emplace_back(n, i);
+ }
+
+ if (!pAttr)
+ {
+ // See if this is an end dummy character for a content control.
+ pAttr = rTextNode.GetTextAttrForEndCharAt(i, RES_TXTATR_CONTENTCONTROL);
+ if (pAttr && (nStart > pAttr->GetStart()))
+ {
+ rBreaks.emplace_back(n, i);
+ }
+ }
+ }
+ break;
+ }
+ case CH_TXT_ATR_FIELDSTART:
+ {
+ auto const pFieldMark(rIDMA.getFieldmarkAt(SwPosition(rTextNode, i)));
+ startedFields.emplace(pFieldMark, false, 0, 0);
+ break;
+ }
+ case CH_TXT_ATR_FIELDSEP:
+ {
+ if (startedFields.empty())
+ {
+ rBreaks.emplace_back(n, i);
+ }
+ else
+ { // no way to find the field via MarkManager...
+ assert(std::get<0>(startedFields.top())->IsCoveringPosition(SwPosition(rTextNode, i)));
+ std::get<1>(startedFields.top()) = true;
+ std::get<2>(startedFields.top()) = n;
+ std::get<3>(startedFields.top()) = i;
+ }
+ break;
+ }
+ case CH_TXT_ATR_FIELDEND:
+ {
+ if (startedFields.empty())
+ {
+ rBreaks.emplace_back(n, i);
+ }
+ else
+ { // fieldmarks must not overlap => stack
+ assert(std::get<0>(startedFields.top()) == rIDMA.getFieldmarkAt(SwPosition(rTextNode, i)));
+ startedFields.pop();
+ }
+ break;
+ }
+ }
+ }
+ }
+ else if (pNode->IsStartNode())
+ {
+ if (pNode->EndOfSectionIndex() <= nEndNode)
+ { // fieldmark cannot overlap node section
+ n = pNode->EndOfSectionIndex();
+ }
+ }
+ else
+ { // EndNode can actually happen with sections :(
+ assert(pNode->IsEndNode() || pNode->IsNoTextNode());
+ }
+ }
+ while (!startedFields.empty())
+ {
+ if (const sw::mark::IFieldmark* pMark = std::get<0>(startedFields.top()))
+ {
+ SwPosition const& rStart(pMark->GetMarkStart());
+ std::pair<SwNodeOffset, sal_Int32> const pos(
+ rStart.GetNodeIndex(), rStart.GetContentIndex());
+ auto it = std::lower_bound(rBreaks.begin(), rBreaks.end(), pos);
+ assert(it == rBreaks.end() || *it != pos);
+ rBreaks.insert(it, pos);
+ }
+ if (std::get<1>(startedFields.top()))
+ {
+ std::pair<SwNodeOffset, sal_Int32> const posSep(
+ std::get<2>(startedFields.top()),
+ std::get<3>(startedFields.top()));
+ auto it = std::lower_bound(rBreaks.begin(), rBreaks.end(), posSep);
+ assert(it == rBreaks.end() || *it != posSep);
+ rBreaks.insert(it, posSep);
+ }
+ startedFields.pop();
+ }
+ }
+}
+
+namespace
+{
+
+ bool lcl_DoWithBreaks(::sw::DocumentContentOperationsManager & rDocumentContentOperations,
+ SwPaM & rPam, SwDeleteFlags const flags,
+ bool (::sw::DocumentContentOperationsManager::*pFunc)(SwPaM&, SwDeleteFlags))
+ {
+ std::vector<std::pair<SwNodeOffset, sal_Int32>> Breaks;
+
+ sw::CalcBreaks(Breaks, rPam);
+
+ if (Breaks.empty())
+ {
+ return (rDocumentContentOperations.*pFunc)(rPam, flags);
+ }
+
+ // Deletion must be split into several parts if the text node
+ // contains a text attribute with end and with dummy character
+ // and the selection does not contain the text attribute completely,
+ // but overlaps its start (left), where the dummy character is.
+
+ SwPosition const & rSelectionEnd( *rPam.End() );
+
+ bool bRet( true );
+ // iterate from end to start, to avoid invalidating the offsets!
+ auto iter( Breaks.rbegin() );
+ SwNodeOffset nOffset(0);
+ SwNodes const& rNodes(rPam.GetPoint()->GetNodes());
+ SwPaM aPam( rSelectionEnd, rSelectionEnd ); // end node!
+ SwPosition & rEnd( *aPam.End() );
+ SwPosition & rStart( *aPam.Start() );
+
+ while (iter != Breaks.rend())
+ {
+ rStart.Assign(*rNodes[iter->first - nOffset]->GetTextNode(), iter->second + 1);
+ if (rStart < rEnd) // check if part is empty
+ {
+ bRet &= (rDocumentContentOperations.*pFunc)(aPam, flags);
+ nOffset = iter->first - rStart.GetNodeIndex(); // deleted fly nodes...
+ }
+ rEnd.Assign(*rNodes[iter->first - nOffset]->GetTextNode(), iter->second);
+ ++iter;
+ }
+
+ rStart = *rPam.Start(); // set to original start
+ if (rStart < rEnd) // check if part is empty
+ {
+ bRet &= (rDocumentContentOperations.*pFunc)(aPam, flags);
+ }
+
+ return bRet;
+ }
+
+ bool lcl_StrLenOverflow( const SwPaM& rPam )
+ {
+ // If we try to merge two paragraphs we have to test if afterwards
+ // the string doesn't exceed the allowed string length
+ if( rPam.GetPoint()->GetNode() != rPam.GetMark()->GetNode() )
+ {
+ auto [pStt, pEnd] = rPam.StartEnd(); // SwPosition*
+ const SwTextNode* pEndNd = pEnd->GetNode().GetTextNode();
+ if( (nullptr != pEndNd) && pStt->GetNode().IsTextNode() )
+ {
+ const sal_uInt64 nSum = pStt->GetContentIndex() +
+ pEndNd->GetText().getLength() - pEnd->GetContentIndex();
+ return nSum > o3tl::make_unsigned(SAL_MAX_INT32);
+ }
+ }
+ return false;
+ }
+
+ struct SaveRedline
+ {
+ SwRangeRedline* pRedl;
+ SwNodeOffset nStt, nEnd;
+ sal_Int32 nSttCnt;
+ sal_Int32 nEndCnt;
+
+ SaveRedline( SwRangeRedline* pR, const SwNodeIndex& rSttIdx )
+ : pRedl(pR)
+ , nEnd(0)
+ , nEndCnt(0)
+ {
+ auto [pStt, pEnd] = pR->StartEnd(); // SwPosition*
+ SwNodeOffset nSttIdx = rSttIdx.GetIndex();
+ nStt = pStt->GetNodeIndex() - nSttIdx;
+ nSttCnt = pStt->GetContentIndex();
+ if( pR->HasMark() )
+ {
+ nEnd = pEnd->GetNodeIndex() - nSttIdx;
+ nEndCnt = pEnd->GetContentIndex();
+ }
+
+ pRedl->GetPoint()->Assign( SwNodeOffset(0) );
+ pRedl->GetMark()->Assign( SwNodeOffset(0) );
+ }
+
+ SaveRedline( SwRangeRedline* pR, const SwPosition& rPos )
+ : pRedl(pR)
+ , nEnd(0)
+ , nEndCnt(0)
+ {
+ auto [pStt, pEnd] = pR->StartEnd(); // SwPosition*
+ SwNodeOffset nSttIdx = rPos.GetNodeIndex();
+ nStt = pStt->GetNodeIndex() - nSttIdx;
+ nSttCnt = pStt->GetContentIndex();
+ if( nStt == SwNodeOffset(0) )
+ nSttCnt = nSttCnt - rPos.GetContentIndex();
+ if( pR->HasMark() )
+ {
+ nEnd = pEnd->GetNodeIndex() - nSttIdx;
+ nEndCnt = pEnd->GetContentIndex();
+ if( nEnd == SwNodeOffset(0) )
+ nEndCnt = nEndCnt - rPos.GetContentIndex();
+ }
+
+ pRedl->GetPoint()->Assign( SwNodeOffset(0) );
+ pRedl->GetMark()->Assign( SwNodeOffset(0) );
+ }
+
+ void SetPos( SwNodeOffset nInsPos )
+ {
+ pRedl->GetPoint()->Assign( nInsPos + nStt, nSttCnt );
+ if( pRedl->HasMark() )
+ {
+ pRedl->GetMark()->Assign( nInsPos + nEnd, nEndCnt );
+ }
+ }
+
+ void SetPos( const SwPosition& aPos )
+ {
+ pRedl->GetPoint()->Assign( aPos.GetNodeIndex() + nStt,
+ nSttCnt + ( nStt == SwNodeOffset(0) ? aPos.GetContentIndex() : 0 ) );
+ if( pRedl->HasMark() )
+ {
+ pRedl->GetMark()->Assign( aPos.GetNodeIndex() + nEnd,
+ nEndCnt + ( nEnd == SwNodeOffset(0) ? aPos.GetContentIndex() : 0 ) );
+ }
+ }
+ };
+
+ typedef std::vector< SaveRedline > SaveRedlines_t;
+
+ void lcl_SaveRedlines(const SwPaM& aPam, SaveRedlines_t& rArr)
+ {
+ SwDoc& rDoc = aPam.GetPointNode().GetDoc();
+
+ auto [pStart, pEnd] = aPam.StartEnd(); // SwPosition*
+
+ // get first relevant redline
+ SwRedlineTable::size_type nCurrentRedline;
+ rDoc.getIDocumentRedlineAccess().GetRedline( *pStart, &nCurrentRedline );
+ if( nCurrentRedline > 0)
+ nCurrentRedline--;
+
+ // redline mode RedlineFlags::Ignore|RedlineFlags::On; save old mode
+ RedlineFlags eOld = rDoc.getIDocumentRedlineAccess().GetRedlineFlags();
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( ( eOld & ~RedlineFlags::Ignore) | RedlineFlags::On );
+
+ // iterate over relevant redlines and decide for each whether it should
+ // be saved, or split + saved
+ SwRedlineTable& rRedlineTable = rDoc.getIDocumentRedlineAccess().GetRedlineTable();
+ for( ; nCurrentRedline < rRedlineTable.size(); nCurrentRedline++ )
+ {
+ SwRangeRedline* pCurrent = rRedlineTable[ nCurrentRedline ];
+ SwComparePosition eCompare =
+ ComparePosition( *pCurrent->Start(), *pCurrent->End(),
+ *pStart, *pEnd);
+
+ // we must save this redline if it overlaps aPam
+ // (we may have to split it, too)
+ if( eCompare == SwComparePosition::OverlapBehind ||
+ eCompare == SwComparePosition::OverlapBefore ||
+ eCompare == SwComparePosition::Outside ||
+ eCompare == SwComparePosition::Inside ||
+ eCompare == SwComparePosition::Equal )
+ {
+ rRedlineTable.Remove( nCurrentRedline-- );
+
+ // split beginning, if necessary
+ if( eCompare == SwComparePosition::OverlapBefore ||
+ eCompare == SwComparePosition::Outside )
+ {
+ SwRangeRedline* pNewRedline = new SwRangeRedline( *pCurrent );
+ *pNewRedline->End() = *pStart;
+ *pCurrent->Start() = *pStart;
+ rDoc.getIDocumentRedlineAccess().AppendRedline( pNewRedline, true );
+ }
+
+ // split end, if necessary
+ if( eCompare == SwComparePosition::OverlapBehind ||
+ eCompare == SwComparePosition::Outside )
+ {
+ SwRangeRedline* pNewRedline = new SwRangeRedline( *pCurrent );
+ *pNewRedline->Start() = *pEnd;
+ *pCurrent->End() = *pEnd;
+ rDoc.getIDocumentRedlineAccess().AppendRedline( pNewRedline, true );
+ }
+
+ // save the current redline
+ rArr.emplace_back( pCurrent, *pStart );
+ }
+ }
+
+ // restore old redline mode
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
+ }
+
+ void lcl_RestoreRedlines(SwDoc& rDoc, const SwPosition& rPos, SaveRedlines_t& rArr)
+ {
+ RedlineFlags eOld = rDoc.getIDocumentRedlineAccess().GetRedlineFlags();
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( ( eOld & ~RedlineFlags::Ignore) | RedlineFlags::On );
+
+ for(SaveRedline & rSvRedLine : rArr)
+ {
+ rSvRedLine.SetPos( rPos );
+ rDoc.getIDocumentRedlineAccess().AppendRedline( rSvRedLine.pRedl, true );
+ }
+
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
+ }
+
+ void lcl_SaveRedlines(const SwNodeRange& rRg, SaveRedlines_t& rArr)
+ {
+ SwDoc& rDoc = rRg.aStart.GetNode().GetDoc();
+ SwRedlineTable::size_type nRedlPos;
+ SwPosition aSrchPos( rRg.aStart );
+ aSrchPos.Adjust(SwNodeOffset(-1));
+ if( rDoc.getIDocumentRedlineAccess().GetRedline( aSrchPos, &nRedlPos ) && nRedlPos )
+ --nRedlPos;
+ else if( nRedlPos >= rDoc.getIDocumentRedlineAccess().GetRedlineTable().size() )
+ return ;
+
+ RedlineFlags eOld = rDoc.getIDocumentRedlineAccess().GetRedlineFlags();
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( ( eOld & ~RedlineFlags::Ignore) | RedlineFlags::On );
+ SwRedlineTable& rRedlTable = rDoc.getIDocumentRedlineAccess().GetRedlineTable();
+
+ do {
+ SwRangeRedline* pTmp = rRedlTable[ nRedlPos ];
+
+ auto [pRStt, pREnd] = pTmp->StartEnd(); // SwPosition*
+
+ if( pRStt->GetNode() < rRg.aStart.GetNode() )
+ {
+ if( pREnd->GetNode() > rRg.aStart.GetNode() && pREnd->GetNode() < rRg.aEnd.GetNode() )
+ {
+ // Create a copy and set the end of the original to the end of the MoveArea.
+ // The copy is moved too.
+ SwRangeRedline* pNewRedl = new SwRangeRedline( *pTmp );
+ SwPosition* pTmpPos = pNewRedl->Start();
+ pTmpPos->Assign(rRg.aStart);
+
+ rArr.emplace_back(pNewRedl, rRg.aStart);
+
+ pTmpPos = pTmp->End();
+ pTmpPos->Assign(rRg.aEnd);
+ }
+ else if( pREnd->GetNode() == rRg.aStart.GetNode() )
+ {
+ SwPosition* pTmpPos = pTmp->End();
+ pTmpPos->Assign(rRg.aEnd);
+ }
+ }
+ else if( pRStt->GetNode() < rRg.aEnd.GetNode() )
+ {
+ rRedlTable.Remove( nRedlPos-- );
+ if( pREnd->GetNode() < rRg.aEnd.GetNode() ||
+ ( pREnd->GetNode() == rRg.aEnd.GetNode() && !pREnd->GetContentIndex()) )
+ {
+ // move everything
+ rArr.emplace_back( pTmp, rRg.aStart );
+ }
+ else
+ {
+ // split
+ SwRangeRedline* pNewRedl = new SwRangeRedline( *pTmp );
+ SwPosition* pTmpPos = pNewRedl->End();
+ pTmpPos->Assign(rRg.aEnd);
+
+ rArr.emplace_back( pNewRedl, rRg.aStart );
+
+ pTmpPos = pTmp->Start();
+ pTmpPos->Assign(rRg.aEnd);
+ rDoc.getIDocumentRedlineAccess().AppendRedline( pTmp, true );
+ }
+ }
+ else
+ break;
+
+ } while( ++nRedlPos < rDoc.getIDocumentRedlineAccess().GetRedlineTable().size() );
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
+ }
+
+ void lcl_RestoreRedlines(SwDoc& rDoc, SwNodeOffset const nInsPos, SaveRedlines_t& rArr)
+ {
+ RedlineFlags eOld = rDoc.getIDocumentRedlineAccess().GetRedlineFlags();
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( ( eOld & ~RedlineFlags::Ignore) | RedlineFlags::On );
+
+ for(SaveRedline & rSvRedLine : rArr)
+ {
+ rSvRedLine.SetPos( nInsPos );
+ IDocumentRedlineAccess::AppendResult const result(
+ rDoc.getIDocumentRedlineAccess().AppendRedline( rSvRedLine.pRedl, true ));
+ if ( IDocumentRedlineAccess::AppendResult::APPENDED == result &&
+ rSvRedLine.pRedl->GetType() == RedlineType::Delete )
+ {
+ UpdateFramesForAddDeleteRedline(rDoc, *rSvRedLine.pRedl);
+ }
+ }
+
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
+ }
+
+ bool lcl_SaveFootnote( const SwNode& rSttNd, const SwNode& rEndNd,
+ const SwNode& rInsPos,
+ SwFootnoteIdxs& rFootnoteArr, SwFootnoteIdxs& rSaveArr,
+ std::optional<sal_Int32> oSttCnt = std::nullopt, std::optional<sal_Int32> oEndCnt = std::nullopt )
+ {
+ bool bUpdateFootnote = false;
+ const SwNodes& rNds = rInsPos.GetNodes();
+ const bool bDelFootnote = rInsPos.GetIndex() < rNds.GetEndOfAutotext().GetIndex() &&
+ rSttNd.GetIndex() >= rNds.GetEndOfAutotext().GetIndex();
+ const bool bSaveFootnote = !bDelFootnote &&
+ rInsPos.GetIndex() >= rNds.GetEndOfExtras().GetIndex();
+ if( !rFootnoteArr.empty() )
+ {
+
+ size_t nPos = 0;
+ rFootnoteArr.SeekEntry( rSttNd, &nPos );
+ SwTextFootnote* pSrch;
+ const SwNode* pFootnoteNd;
+
+ // Delete/save all that come after it
+ while( nPos < rFootnoteArr.size() && ( pFootnoteNd =
+ &( pSrch = rFootnoteArr[ nPos ] )->GetTextNode())->GetIndex()
+ <= rEndNd.GetIndex() )
+ {
+ const sal_Int32 nFootnoteSttIdx = pSrch->GetStart();
+ if( ( oEndCnt && oSttCnt )
+ ? (( &rSttNd == pFootnoteNd &&
+ *oSttCnt > nFootnoteSttIdx) ||
+ ( &rEndNd == pFootnoteNd &&
+ nFootnoteSttIdx >= *oEndCnt ))
+ : ( &rEndNd == pFootnoteNd ))
+ {
+ ++nPos; // continue searching
+ }
+ else
+ {
+ // delete it
+ if( bDelFootnote )
+ {
+ SwTextNode& rTextNd = const_cast<SwTextNode&>(pSrch->GetTextNode());
+ SwContentIndex aIdx( &rTextNd, nFootnoteSttIdx );
+ rTextNd.EraseText( aIdx, 1 );
+ }
+ else
+ {
+ pSrch->DelFrames(nullptr);
+ rFootnoteArr.erase( rFootnoteArr.begin() + nPos );
+ if( bSaveFootnote )
+ rSaveArr.insert( pSrch );
+ }
+ bUpdateFootnote = true;
+ }
+ }
+
+ while( nPos-- && ( pFootnoteNd = &( pSrch = rFootnoteArr[ nPos ] )->
+ GetTextNode())->GetIndex() >= rSttNd.GetIndex() )
+ {
+ const sal_Int32 nFootnoteSttIdx = pSrch->GetStart();
+ if( !oEndCnt || !oSttCnt ||
+ ! (( &rSttNd == pFootnoteNd &&
+ *oSttCnt > nFootnoteSttIdx ) ||
+ ( &rEndNd == pFootnoteNd &&
+ nFootnoteSttIdx >= *oEndCnt )) )
+ {
+ if( bDelFootnote )
+ {
+ // delete it
+ SwTextNode& rTextNd = const_cast<SwTextNode&>(pSrch->GetTextNode());
+ SwContentIndex aIdx( &rTextNd, nFootnoteSttIdx );
+ rTextNd.EraseText( aIdx, 1 );
+ }
+ else
+ {
+ pSrch->DelFrames(nullptr);
+ rFootnoteArr.erase( rFootnoteArr.begin() + nPos );
+ if( bSaveFootnote )
+ rSaveArr.insert( pSrch );
+ }
+ bUpdateFootnote = true;
+ }
+ }
+ }
+ // When moving from redline section into document content section, e.g.
+ // after loading a document with (delete-)redlines, the footnote array
+ // has to be adjusted... (#i70572)
+ if( bSaveFootnote )
+ {
+ SwNodeIndex aIdx( rSttNd );
+ while( aIdx < rEndNd ) // Check the moved section
+ {
+ SwNode* pNode = &aIdx.GetNode();
+ if( pNode->IsTextNode() ) // Looking for text nodes...
+ {
+ SwpHints *pHints = pNode->GetTextNode()->GetpSwpHints();
+ if( pHints && pHints->HasFootnote() ) //...with footnotes
+ {
+ bUpdateFootnote = true; // Heureka
+ const size_t nCount = pHints->Count();
+ for( size_t i = 0; i < nCount; ++i )
+ {
+ SwTextAttr *pAttr = pHints->Get( i );
+ if ( pAttr->Which() == RES_TXTATR_FTN )
+ {
+ rSaveArr.insert( static_cast<SwTextFootnote*>(pAttr) );
+ }
+ }
+ }
+ }
+ ++aIdx;
+ }
+ }
+ return bUpdateFootnote;
+ }
+
+ bool lcl_MayOverwrite( const SwTextNode *pNode, const sal_Int32 nPos )
+ {
+ sal_Unicode const cChr = pNode->GetText()[nPos];
+ switch (cChr)
+ {
+ case CH_TXTATR_BREAKWORD:
+ case CH_TXTATR_INWORD:
+ return !pNode->GetTextAttrForCharAt(nPos);// how could there be none?
+ case CH_TXT_ATR_INPUTFIELDSTART:
+ case CH_TXT_ATR_INPUTFIELDEND:
+ case CH_TXT_ATR_FIELDSTART:
+ case CH_TXT_ATR_FIELDSEP:
+ case CH_TXT_ATR_FIELDEND:
+ case CH_TXT_ATR_FORMELEMENT:
+ return false;
+ default:
+ return true;
+ }
+ }
+
+ void lcl_SkipAttr( const SwTextNode *pNode, SwPosition &rIdx, sal_Int32 &rStart )
+ {
+ if( !lcl_MayOverwrite( pNode, rStart ) )
+ {
+ // skip all special attributes
+ do {
+ rIdx.AdjustContent(+1);
+ rStart = rIdx.GetContentIndex();
+ } while (rStart < pNode->GetText().getLength()
+ && !lcl_MayOverwrite(pNode, rStart) );
+ }
+ }
+
+ bool lcl_GetTokenToParaBreak( OUString& rStr, OUString& rRet, bool bRegExpRplc )
+ {
+ if( bRegExpRplc )
+ {
+ sal_Int32 nPos = 0;
+ static constexpr OUString sPara(u"\\n"_ustr);
+ for (;;)
+ {
+ nPos = rStr.indexOf( sPara, nPos );
+ if (nPos<0)
+ {
+ break;
+ }
+ // Has this been escaped?
+ if( nPos && '\\' == rStr[nPos-1])
+ {
+ ++nPos;
+ if( nPos >= rStr.getLength() )
+ {
+ break;
+ }
+ }
+ else
+ {
+ rRet = rStr.copy( 0, nPos );
+ rStr = rStr.copy( nPos + sPara.getLength() );
+ return true;
+ }
+ }
+ }
+ rRet = rStr;
+ rStr.clear();
+ return false;
+ }
+}
+
+namespace //local functions originally from docfmt.cxx
+{
+
+ bool lcl_ApplyOtherSet(
+ SwContentNode & rNode,
+ SwHistory *const pHistory,
+ SfxItemSet const& rOtherSet,
+ SfxItemSet const& rFirstSet,
+ SfxItemSet const& rPropsSet,
+ SwRootFrame const*const pLayout,
+ SwNodeIndex *const o_pIndex = nullptr)
+ {
+ assert(rOtherSet.Count());
+
+ bool ret(false);
+ SwTextNode *const pTNd = rNode.GetTextNode();
+ sw::MergedPara const* pMerged(nullptr);
+ if (pLayout && pLayout->HasMergedParas() && pTNd)
+ {
+ SwTextFrame const*const pTextFrame(static_cast<SwTextFrame const*>(
+ pTNd->getLayoutFrame(pLayout)));
+ if (pTextFrame)
+ {
+ pMerged = pTextFrame->GetMergedPara();
+ }
+ if (pMerged)
+ {
+ if (rFirstSet.Count())
+ {
+ if (pHistory)
+ {
+ SwRegHistory aRegH(pMerged->pFirstNode, *pMerged->pFirstNode, pHistory);
+ ret = pMerged->pFirstNode->SetAttr(rFirstSet);
+ }
+ else
+ {
+ ret = pMerged->pFirstNode->SetAttr(rFirstSet);
+ }
+ }
+ if (rPropsSet.Count())
+ {
+ if (pHistory)
+ {
+ SwRegHistory aRegH(pMerged->pParaPropsNode, *pMerged->pParaPropsNode, pHistory);
+ ret = pMerged->pParaPropsNode->SetAttr(rPropsSet) || ret;
+ }
+ else
+ {
+ ret = pMerged->pParaPropsNode->SetAttr(rPropsSet) || ret;
+ }
+ }
+ if (o_pIndex)
+ {
+ *o_pIndex = *pMerged->pLastNode; // skip hidden
+ }
+ }
+ }
+
+ // input cursor can't be on hidden node, and iteration skips them
+ assert(!pLayout || !pLayout->HasMergedParas()
+ || rNode.GetRedlineMergeFlag() != SwNode::Merge::Hidden);
+
+ if (!pMerged)
+ {
+ if (pHistory)
+ {
+ SwRegHistory aRegH(&rNode, rNode, pHistory);
+ ret = rNode.SetAttr( rOtherSet );
+ }
+ else
+ {
+ ret = rNode.SetAttr( rOtherSet );
+ }
+ }
+ return ret;
+ }
+
+ #define DELETECHARSETS if ( bDelete ) { delete pCharSet; delete pOtherSet; }
+
+ // set format redline with extra data for lcl_InsAttr()
+ void lcl_SetRedline(
+ SwDoc& rDoc,
+ const SwPaM &rRg)
+ {
+ std::unique_ptr<SwRedlineExtraData_FormatColl> xExtra;
+
+ // check existing redline on the same range, and use its extra data, if it exists
+ SwRedlineTable::size_type nRedlPos = rDoc.getIDocumentRedlineAccess().GetRedlinePos(
+ rRg.Start()->GetNode(), RedlineType::Format );
+ if( SwRedlineTable::npos != nRedlPos )
+ {
+ const SwPosition *pRStt, *pREnd;
+ do {
+ SwRangeRedline* pTmp = rDoc.getIDocumentRedlineAccess().GetRedlineTable()[ nRedlPos ];
+ pRStt = pTmp->Start();
+ pREnd = pTmp->End();
+ SwComparePosition eCompare = ComparePosition( *rRg.Start(), *rRg.End(), *pRStt, *pREnd );
+ if ( eCompare == SwComparePosition::Inside || eCompare == SwComparePosition::Equal )
+ {
+ if (pTmp->GetExtraData())
+ {
+ const SwRedlineExtraData* pExtraData = pTmp->GetExtraData();
+ const SwRedlineExtraData_FormatColl* pFormattingChanges =
+ dynamic_cast<const SwRedlineExtraData_FormatColl*>(pExtraData);
+ // Check if the extra data is of type 'formatting changes'
+ if (pFormattingChanges)
+ {
+ // Get the item set that holds all the changes properties
+ const SfxItemSet *pChangesSet = pFormattingChanges->GetItemSet();
+ xExtra.reset(new SwRedlineExtraData_FormatColl("", USHRT_MAX, pChangesSet));
+ break;
+ }
+ }
+ }
+ } while( pRStt <= rRg.Start() && ++nRedlPos < rDoc.getIDocumentRedlineAccess().GetRedlineTable().size());
+ }
+
+ SwRangeRedline * pRedline = new SwRangeRedline( RedlineType::Format, rRg );
+ auto const result(rDoc.getIDocumentRedlineAccess().AppendRedline( pRedline, true));
+ // store original text attributes to reject formatting change
+ if (IDocumentRedlineAccess::AppendResult::IGNORED == result)
+ return;
+
+ // no existing format redline in the range
+ if (!xExtra)
+ {
+ // Apply the first character's attributes to the ReplaceText
+ SfxItemSetFixed<RES_CHRATR_BEGIN, RES_TXTATR_WITHEND_END - 1,
+ RES_UNKNOWNATR_BEGIN, RES_UNKNOWNATR_END-1> aSet( rDoc.GetAttrPool() );
+ SwTextNode * pNode = rRg.Start()->GetNode().GetTextNode();
+ pNode->GetParaAttr( aSet, rRg.Start()->GetContentIndex() + 1, rRg.End()->GetContentIndex() );
+
+ aSet.ClearItem( RES_TXTATR_REFMARK );
+ aSet.ClearItem( RES_TXTATR_TOXMARK );
+ aSet.ClearItem( RES_TXTATR_CJK_RUBY );
+ aSet.ClearItem( RES_TXTATR_INETFMT );
+ aSet.ClearItem( RES_TXTATR_META );
+ aSet.ClearItem( RES_TXTATR_METAFIELD );
+
+ // After GetParaAttr aSet can contain invalid/dontcare items (true == IsInvalidItem,
+ // DONTCARE == SfxItemState), e.g. RES_TXTATR_CHARFMT and (a copy of) this
+ // SfxItemSet can be passed to MSWordExportBase::OutputItemSet
+ // which doesn't handle invalid/dontcare items so clear them here
+ aSet.ClearInvalidItems();
+
+ xExtra.reset(new SwRedlineExtraData_FormatColl("", USHRT_MAX, &aSet));
+ }
+
+ if (xExtra)
+ {
+ pRedline->SetExtraData(xExtra.get() );
+ }
+ }
+
+ // create format redline(s) for the given range:
+ // to track the original formatting stored in the
+ // hints, create redlines for all parts of the
+ // range partitioned by boundaries of the hints.
+ void lcl_SetRedlines(
+ SwDoc& rDoc,
+ const SwPaM &rRg)
+ {
+ SwNodeIndex aIdx( rRg.Start()->GetNode() );
+ const SwNodeIndex aEndNd( rRg.End()->GetNode() );
+ while( aIdx <= aEndNd )
+ {
+ SwTextNode *pNode = aIdx.GetNode().GetTextNode();
+ if( pNode )
+ {
+ const sal_Int32 nStart = aIdx == rRg.Start()->GetNode()
+ ? rRg.Start()->GetContentIndex()
+ : 0;
+ const sal_Int32 nEnd = aIdx < aEndNd
+ ? pNode->GetText().getLength()
+ : rRg.End()->GetContentIndex();
+
+ if( SwpHints *pHints = pNode->GetpSwpHints() )
+ {
+ const size_t nCount = pHints->Count();
+ sal_Int32 nRedEnd = nStart;
+ for( size_t i = 0; i < nCount; ++i )
+ {
+ SwTextAttr *pAttr = pHints->Get( i );
+
+ if ( pAttr->GetStart() > nEnd )
+ {
+ break; // after the range
+ }
+
+ if ( !pAttr->GetEnd() || *pAttr->GetEnd() < nStart )
+ {
+ continue; // before the range
+ }
+
+ // range part before the hint
+ if ( nRedEnd < pAttr->GetStart() )
+ {
+ SwPaM aPam( *pNode, nRedEnd, *pNode, pAttr->GetStart() );
+ lcl_SetRedline(rDoc, aPam);
+ }
+
+ // range part at the hint
+ sal_Int32 nRedStart = std::max(pAttr->GetStart(), nStart);
+ nRedEnd = std::min(*pAttr->GetEnd(), nEnd);
+ SwPaM aPam2( *pNode, nRedStart, *pNode, nRedEnd );
+ lcl_SetRedline(rDoc, aPam2);
+ }
+
+ // range part after the last hint
+ if ( nRedEnd < nEnd )
+ {
+ SwPaM aPam( *pNode, nRedEnd, *pNode, nEnd );
+ lcl_SetRedline(rDoc, aPam);
+ }
+ }
+ else
+ {
+ SwPaM aPam( *pNode, nStart, *pNode, nEnd );
+ lcl_SetRedline(rDoc, aPam);
+ }
+ }
+ ++aIdx;
+ }
+ }
+
+ /// Insert Hints according to content types;
+ // Is used in SwDoc::Insert(..., SwFormatHint &rHt)
+
+ bool lcl_InsAttr(
+ SwDoc& rDoc,
+ const SwPaM &rRg,
+ const SfxItemSet& rChgSet,
+ const SetAttrMode nFlags,
+ SwUndoAttr *const pUndo,
+ SwRootFrame const*const pLayout,
+ SwTextAttr **ppNewTextAttr)
+ {
+ // Divide the Sets (for selections in Nodes)
+ const SfxItemSet* pCharSet = nullptr;
+ const SfxItemSet* pOtherSet = nullptr;
+ bool bDelete = false;
+ bool bCharAttr = false;
+ bool bOtherAttr = false;
+
+ // Check, if we can work with rChgSet or if we have to create additional SfxItemSets
+ if ( 1 == rChgSet.Count() )
+ {
+ SfxItemIter aIter( rChgSet );
+ const SfxPoolItem* pItem = aIter.GetCurItem();
+ if (pItem && !IsInvalidItem(pItem))
+ {
+ const sal_uInt16 nWhich = pItem->Which();
+
+ if ( isCHRATR(nWhich) ||
+ (RES_TXTATR_CHARFMT == nWhich) ||
+ (RES_TXTATR_INETFMT == nWhich) ||
+ (RES_TXTATR_AUTOFMT == nWhich) ||
+ (RES_TXTATR_UNKNOWN_CONTAINER == nWhich) )
+ {
+ pCharSet = &rChgSet;
+ bCharAttr = true;
+ }
+
+ if ( isPARATR(nWhich)
+ || isPARATR_LIST(nWhich)
+ || isFRMATR(nWhich)
+ || isGRFATR(nWhich)
+ || isUNKNOWNATR(nWhich)
+ || isDrawingLayerAttribute(nWhich) )
+ {
+ pOtherSet = &rChgSet;
+ bOtherAttr = true;
+ }
+ }
+ }
+
+ // Build new itemset if either
+ // - rChgSet.Count() > 1 or
+ // - The attribute in rChgSet does not belong to one of the above categories
+ if ( !bCharAttr && !bOtherAttr )
+ {
+ SfxItemSet* pTmpCharItemSet = new SfxItemSetFixed<
+ RES_CHRATR_BEGIN, RES_CHRATR_END - 1,
+ RES_TXTATR_AUTOFMT, RES_TXTATR_CHARFMT,
+ RES_TXTATR_UNKNOWN_CONTAINER,
+ RES_TXTATR_UNKNOWN_CONTAINER>( rDoc.GetAttrPool() );
+
+ SfxItemSet* pTmpOtherItemSet = new SfxItemSetFixed<
+ RES_PARATR_BEGIN, RES_GRFATR_END - 1,
+ RES_UNKNOWNATR_BEGIN, RES_UNKNOWNATR_END - 1,
+ // FillAttribute support:
+ XATTR_FILL_FIRST, XATTR_FILL_LAST>( rDoc.GetAttrPool() );
+
+ pTmpCharItemSet->Put( rChgSet );
+ pTmpOtherItemSet->Put( rChgSet );
+
+ pCharSet = pTmpCharItemSet;
+ pOtherSet = pTmpOtherItemSet;
+
+ bDelete = true;
+ }
+
+ SwHistory* pHistory = pUndo ? &pUndo->GetHistory() : nullptr;
+ bool bRet = false;
+ const SwPosition *pStt = rRg.Start(), *pEnd = rRg.End();
+ SwContentNode* pNode = pStt->GetNode().GetContentNode();
+
+ if( pNode && pNode->IsTextNode() )
+ {
+ // tdf#127606 at editing, remove different formatting of DOCX-like numbering symbol
+ if (pLayout && pNode->GetTextNode()->getIDocumentSettingAccess()->
+ get(DocumentSettingId::APPLY_PARAGRAPH_MARK_FORMAT_TO_NUMBERING ))
+ {
+ SwContentNode* pEndNode = pEnd->GetNode().GetContentNode();
+ SwContentNode* pCurrentNode = pEndNode;
+ auto nStartIndex = pNode->GetIndex();
+ auto nEndIndex = pEndNode->GetIndex();
+ SwNodeIndex aIdx( pEnd->GetNode() );
+ while ( pCurrentNode != nullptr && nStartIndex <= pCurrentNode->GetIndex() )
+ {
+ if (pCurrentNode->GetSwAttrSet().HasItem(RES_PARATR_LIST_AUTOFMT) &&
+ // remove character formatting only on wholly selected paragraphs
+ (nStartIndex < pCurrentNode->GetIndex() || pStt->GetContentIndex() == 0) &&
+ (pCurrentNode->GetIndex() < nEndIndex || pEnd->GetContentIndex() == pEndNode->Len()))
+ {
+ pCurrentNode->ResetAttr(RES_PARATR_LIST_AUTOFMT);
+ // reset also paragraph marker
+ pCurrentNode->GetTextNode()->RstTextAttr(pCurrentNode->Len(), 1);
+ }
+ pCurrentNode = SwNodes::GoPrevious( &aIdx );
+ }
+ }
+ // #i27615#
+ if (rRg.IsInFrontOfLabel())
+ {
+ SwTextNode * pTextNd = pNode->GetTextNode();
+ if (pLayout)
+ {
+ pTextNd = sw::GetParaPropsNode(*pLayout, *pTextNd);
+ }
+ SwNumRule * pNumRule = pTextNd->GetNumRule();
+
+ if ( !pNumRule )
+ {
+ OSL_FAIL( "<InsAttr(..)> - PaM in front of label, but text node has no numbering rule set. This is a serious defect." );
+ DELETECHARSETS
+ return false;
+ }
+
+ int nLevel = pTextNd->GetActualListLevel();
+
+ if (nLevel < 0)
+ nLevel = 0;
+
+ if (nLevel >= MAXLEVEL)
+ nLevel = MAXLEVEL - 1;
+
+ SwNumFormat aNumFormat = pNumRule->Get(o3tl::narrowing<sal_uInt16>(nLevel));
+ SwCharFormat * pCharFormat =
+ rDoc.FindCharFormatByName(aNumFormat.GetCharFormatName());
+
+ if (pCharFormat)
+ {
+ if (pHistory)
+ pHistory->AddCharFormat(pCharFormat->GetAttrSet(), *pCharFormat);
+
+ if ( pCharSet )
+ pCharFormat->SetFormatAttr(*pCharSet);
+ }
+
+ DELETECHARSETS
+ return true;
+ }
+
+ // Attributes without an end do not have a range
+ if ( !bCharAttr && !bOtherAttr )
+ {
+ SfxItemSetFixed<RES_TXTATR_NOEND_BEGIN, RES_TXTATR_NOEND_END-1>
+ aTextSet( rDoc.GetAttrPool() );
+ aTextSet.Put( rChgSet );
+ if( aTextSet.Count() )
+ {
+ SwRegHistory history( pNode, *pNode, pHistory );
+ bRet = history.InsertItems(
+ aTextSet, pStt->GetContentIndex(), pStt->GetContentIndex(), nFlags, /*ppNewTextAttr*/nullptr ) || bRet;
+
+ if (bRet && (rDoc.getIDocumentRedlineAccess().IsRedlineOn() || (!rDoc.getIDocumentRedlineAccess().IsIgnoreRedline()
+ && !rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty())))
+ {
+ SwPaM aPam( pStt->GetNode(), pStt->GetContentIndex()-1,
+ pStt->GetNode(), pStt->GetContentIndex() );
+
+ if( pUndo )
+ pUndo->SaveRedlineData( aPam, true );
+
+ if( rDoc.getIDocumentRedlineAccess().IsRedlineOn() )
+ rDoc.getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( RedlineType::Insert, aPam ), true);
+ else
+ rDoc.getIDocumentRedlineAccess().SplitRedline( aPam );
+ }
+ }
+ }
+
+ // TextAttributes with an end never expand their range
+ if ( !bCharAttr && !bOtherAttr )
+ {
+ // CharFormat and URL attributes are treated separately!
+ // TEST_TEMP ToDo: AutoFormat!
+ SfxItemSetFixed<
+ RES_TXTATR_REFMARK, RES_TXTATR_METAFIELD,
+ RES_TXTATR_CJK_RUBY, RES_TXTATR_CJK_RUBY,
+ RES_TXTATR_INPUTFIELD, RES_TXTATR_CONTENTCONTROL>
+ aTextSet(rDoc.GetAttrPool());
+
+ aTextSet.Put( rChgSet );
+ if( aTextSet.Count() )
+ {
+ const sal_Int32 nInsCnt = pStt->GetContentIndex();
+ const sal_Int32 nEnd = pStt->GetNode() == pEnd->GetNode()
+ ? pEnd->GetContentIndex()
+ : pNode->Len();
+ SwRegHistory history( pNode, *pNode, pHistory );
+ bRet = history.InsertItems( aTextSet, nInsCnt, nEnd, nFlags, ppNewTextAttr )
+ || bRet;
+
+ if (bRet && (rDoc.getIDocumentRedlineAccess().IsRedlineOn() || (!rDoc.getIDocumentRedlineAccess().IsIgnoreRedline()
+ && !rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty())))
+ {
+ // Was text content inserted? (RefMark/TOXMarks without an end)
+ bool bTextIns = nInsCnt != pStt->GetContentIndex();
+ // Was content inserted or set over the selection?
+ SwPaM aPam( pStt->GetNode(), bTextIns ? nInsCnt + 1 : nEnd,
+ pStt->GetNode(), nInsCnt );
+ if( pUndo )
+ pUndo->SaveRedlineData( aPam, bTextIns );
+
+ if( rDoc.getIDocumentRedlineAccess().IsRedlineOn() )
+ rDoc.getIDocumentRedlineAccess().AppendRedline(
+ new SwRangeRedline(
+ bTextIns ? RedlineType::Insert : RedlineType::Format, aPam ),
+ true);
+ else if( bTextIns )
+ rDoc.getIDocumentRedlineAccess().SplitRedline( aPam );
+ }
+ }
+ }
+ }
+
+ // We always have to set the auto flag for PageDescs that are set at the Node!
+ if( pOtherSet && pOtherSet->Count() )
+ {
+ SwTableNode* pTableNd;
+ const SwFormatPageDesc* pDesc = pOtherSet->GetItemIfSet( RES_PAGEDESC, false );
+ if( pDesc )
+ {
+ if( pNode )
+ {
+ // Set auto flag. Only in the template it's without auto!
+ SwFormatPageDesc aNew( *pDesc );
+
+ // Tables now also know line breaks
+ if( !(nFlags & SetAttrMode::APICALL) &&
+ nullptr != ( pTableNd = pNode->FindTableNode() ) )
+ {
+ SwTableNode* pCurTableNd = pTableNd;
+ while ( nullptr != ( pCurTableNd = pCurTableNd->StartOfSectionNode()->FindTableNode() ) )
+ pTableNd = pCurTableNd;
+
+ // set the table format
+ SwFrameFormat* pFormat = pTableNd->GetTable().GetFrameFormat();
+ SwRegHistory aRegH( pFormat, *pTableNd, pHistory );
+ pFormat->SetFormatAttr( aNew );
+ bRet = true;
+ }
+ else
+ {
+ SwContentNode * pFirstNode(pNode);
+ if (pLayout && pLayout->HasMergedParas())
+ {
+ pFirstNode = sw::GetFirstAndLastNode(*pLayout, pStt->GetNode()).first;
+ }
+ SwRegHistory aRegH( pFirstNode, *pFirstNode, pHistory );
+ bRet = pFirstNode->SetAttr( aNew ) || bRet;
+ }
+ }
+
+ // bOtherAttr = true means that pOtherSet == rChgSet. In this case
+ // we know, that there is only one attribute in pOtherSet. We cannot
+ // perform the following operations, instead we return:
+ if ( bOtherAttr )
+ return bRet;
+
+ const_cast<SfxItemSet*>(pOtherSet)->ClearItem( RES_PAGEDESC );
+ if( !pOtherSet->Count() )
+ {
+ DELETECHARSETS
+ return bRet;
+ }
+ }
+
+ // Tables now also know line breaks
+ const SvxFormatBreakItem* pBreak;
+ if( pNode && !(nFlags & SetAttrMode::APICALL) &&
+ nullptr != (pTableNd = pNode->FindTableNode() ) &&
+ (pBreak = pOtherSet->GetItemIfSet( RES_BREAK, false )) )
+ {
+ SwTableNode* pCurTableNd = pTableNd;
+ while ( nullptr != ( pCurTableNd = pCurTableNd->StartOfSectionNode()->FindTableNode() ) )
+ pTableNd = pCurTableNd;
+
+ // set the table format
+ SwFrameFormat* pFormat = pTableNd->GetTable().GetFrameFormat();
+ SwRegHistory aRegH( pFormat, *pTableNd, pHistory );
+ pFormat->SetFormatAttr( *pBreak );
+ bRet = true;
+
+ // bOtherAttr = true means that pOtherSet == rChgSet. In this case
+ // we know, that there is only one attribute in pOtherSet. We cannot
+ // perform the following operations, instead we return:
+ if ( bOtherAttr )
+ return bRet;
+
+ const_cast<SfxItemSet*>(pOtherSet)->ClearItem( RES_BREAK );
+ if( !pOtherSet->Count() )
+ {
+ DELETECHARSETS
+ return bRet;
+ }
+ }
+
+ {
+ // If we have a PoolNumRule, create it if needed
+ sal_uInt16 nPoolId=0;
+ const SwNumRuleItem* pRule = pOtherSet->GetItemIfSet( RES_PARATR_NUMRULE, false );
+ if( pRule &&
+ !rDoc.FindNumRulePtr( pRule->GetValue() ) &&
+ USHRT_MAX != (nPoolId = SwStyleNameMapper::GetPoolIdFromUIName ( pRule->GetValue(),
+ SwGetPoolIdFromName::NumRule )) )
+ rDoc.getIDocumentStylePoolAccess().GetNumRuleFromPool( nPoolId );
+ }
+ }
+
+ SfxItemSetFixed<RES_PAGEDESC, RES_BREAK> firstSet(rDoc.GetAttrPool());
+ if (pOtherSet && pOtherSet->Count())
+ { // actually only RES_BREAK is possible here...
+ firstSet.Put(*pOtherSet);
+ }
+ SfxItemSetFixed
+ <RES_PARATR_BEGIN, RES_PAGEDESC,
+ RES_BREAK+1, RES_FRMATR_END,
+ XATTR_FILL_FIRST, XATTR_FILL_LAST+1> propsSet(rDoc.GetAttrPool());
+ if (pOtherSet && pOtherSet->Count())
+ {
+ propsSet.Put(*pOtherSet);
+ }
+
+ if( !rRg.HasMark() ) // no range
+ {
+ if( !pNode )
+ {
+ DELETECHARSETS
+ return bRet;
+ }
+
+ if( pNode->IsTextNode() && pCharSet && pCharSet->Count() )
+ {
+ SwTextNode* pTextNd = pNode->GetTextNode();
+ sal_Int32 nMkPos, nPtPos = pStt->GetContentIndex();
+ const OUString& rStr = pTextNd->GetText();
+
+ // Special case: if the Cursor is located within a URL attribute, we take over it's area
+ SwTextAttr const*const pURLAttr(
+ pTextNd->GetTextAttrAt(pStt->GetContentIndex(), RES_TXTATR_INETFMT));
+ if (pURLAttr && !pURLAttr->GetINetFormat().GetValue().isEmpty())
+ {
+ nMkPos = pURLAttr->GetStart();
+ nPtPos = *pURLAttr->End();
+ }
+ else
+ {
+ assert(g_pBreakIt && g_pBreakIt->GetBreakIter().is());
+ Boundary aBndry = g_pBreakIt->GetBreakIter()->getWordBoundary(
+ pTextNd->GetText(), nPtPos,
+ g_pBreakIt->GetLocale( pTextNd->GetLang( nPtPos ) ),
+ WordType::ANY_WORD /*ANYWORD_IGNOREWHITESPACES*/,
+ true);
+
+ if( aBndry.startPos < nPtPos && nPtPos < aBndry.endPos )
+ {
+ nMkPos = aBndry.startPos;
+ nPtPos = aBndry.endPos;
+ }
+ else
+ nPtPos = nMkPos = pStt->GetContentIndex();
+ }
+
+ // Remove the overriding attributes from the SwpHintsArray,
+ // if the selection spans across the whole paragraph.
+ // These attributes are inserted as FormatAttributes and
+ // never override the TextAttributes!
+ if( !(nFlags & SetAttrMode::DONTREPLACE ) &&
+ pTextNd->HasHints() && !nMkPos && nPtPos == rStr.getLength())
+ {
+ if( pHistory )
+ {
+ // Save all attributes for the Undo.
+ SwRegHistory aRHst( *pTextNd, pHistory );
+ pTextNd->GetpSwpHints()->Register( &aRHst );
+ pTextNd->RstTextAttr( 0, nPtPos, 0, pCharSet );
+ if( pTextNd->GetpSwpHints() )
+ pTextNd->GetpSwpHints()->DeRegister();
+ }
+ else
+ pTextNd->RstTextAttr( 0, nPtPos, 0, pCharSet );
+ }
+
+ if( rDoc.getIDocumentRedlineAccess().IsRedlineOn() )
+ {
+ SwPaM aPam( *pNode, nMkPos, *pNode, nPtPos );
+
+ if( pUndo )
+ pUndo->SaveRedlineData( aPam, false );
+
+ lcl_SetRedlines(rDoc, aPam);
+ }
+
+ // the SwRegHistory inserts the attribute into the TextNode!
+ SwRegHistory history( pNode, *pNode, pHistory );
+
+ bRet = history.InsertItems( *pCharSet, nMkPos, nPtPos, nFlags, /*ppNewTextAttr*/nullptr )
+ || bRet;
+
+ }
+ if( pOtherSet && pOtherSet->Count() )
+ {
+ // Need to check for unique item for DrawingLayer items of type NameOrIndex
+ // and evtl. correct that item to ensure unique names for that type. This call may
+ // modify/correct entries inside of the given SfxItemSet
+ SfxItemSet aTempLocalCopy(*pOtherSet);
+
+ rDoc.CheckForUniqueItemForLineFillNameOrIndex(aTempLocalCopy);
+ bRet = lcl_ApplyOtherSet(*pNode, pHistory, aTempLocalCopy, firstSet, propsSet, pLayout) || bRet;
+ }
+
+ DELETECHARSETS
+ return bRet;
+ }
+
+ if( rDoc.getIDocumentRedlineAccess().IsRedlineOn() && pCharSet && pCharSet->Count() )
+ {
+ if( pUndo )
+ pUndo->SaveRedlineData( rRg, false );
+
+ lcl_SetRedlines(rDoc, rRg);
+ }
+
+ /* now if range */
+ sal_uLong nNodes = 0;
+
+ SwNodeIndex aSt( rDoc.GetNodes() );
+ SwNodeIndex aEnd( rDoc.GetNodes() );
+ SwContentIndex aCntEnd( pEnd->GetContentNode(), pEnd->GetContentIndex() );
+
+ if( pNode )
+ {
+ const sal_Int32 nLen = pNode->Len();
+ if( pStt->GetNode() != pEnd->GetNode() )
+ aCntEnd.Assign( pNode, nLen );
+
+ if( pStt->GetContentIndex() != 0 || aCntEnd.GetIndex() != nLen )
+ {
+ // the SwRegHistory inserts the attribute into the TextNode!
+ if( pNode->IsTextNode() && pCharSet && pCharSet->Count() )
+ {
+ SwRegHistory history( pNode, *pNode, pHistory );
+ bRet = history.InsertItems(*pCharSet,
+ pStt->GetContentIndex(), aCntEnd.GetIndex(), nFlags, /*ppNewTextAttr*/nullptr)
+ || bRet;
+ }
+
+ if( pOtherSet && pOtherSet->Count() )
+ {
+ bRet = lcl_ApplyOtherSet(*pNode, pHistory, *pOtherSet, firstSet, propsSet, pLayout) || bRet;
+ }
+
+ // Only selection in a Node.
+ if( pStt->GetNode() == pEnd->GetNode() )
+ {
+ DELETECHARSETS
+ return bRet;
+ }
+ ++nNodes;
+ aSt.Assign( pStt->GetNode(), +1 );
+ }
+ else
+ aSt = pStt->GetNode();
+ aCntEnd.Assign(pEnd->GetContentNode(), pEnd->GetContentIndex()); // aEnd was changed!
+ }
+ else
+ aSt.Assign( pStt->GetNode(), +1 );
+
+ // aSt points to the first full Node now
+
+ /*
+ * The selection spans more than one Node.
+ */
+ if( pStt->GetNode() < pEnd->GetNode() )
+ {
+ pNode = pEnd->GetNode().GetContentNode();
+ if(pNode)
+ {
+ if( aCntEnd.GetIndex() != pNode->Len() )
+ {
+ // the SwRegHistory inserts the attribute into the TextNode!
+ if( pNode->IsTextNode() && pCharSet && pCharSet->Count() )
+ {
+ SwRegHistory history( pNode, *pNode, pHistory );
+ (void)history.InsertItems(*pCharSet,
+ 0, aCntEnd.GetIndex(), nFlags, /*ppNewTextAttr*/nullptr);
+ }
+
+ if( pOtherSet && pOtherSet->Count() )
+ {
+ lcl_ApplyOtherSet(*pNode, pHistory, *pOtherSet, firstSet, propsSet, pLayout);
+ }
+
+ ++nNodes;
+ aEnd = pEnd->GetNode();
+ }
+ else
+ aEnd.Assign( pEnd->GetNode(), +1 );
+ }
+ else
+ aEnd = pEnd->GetNode();
+ }
+ else
+ aEnd.Assign( pEnd->GetNode(), +1 );
+
+ // aEnd points BEHIND the last full node now
+
+ /* Edit the fully selected Nodes. */
+ // Reset all attributes from the set!
+ if( pCharSet && pCharSet->Count() && !( SetAttrMode::DONTREPLACE & nFlags ) )
+ {
+ ::sw::DocumentContentOperationsManager::ParaRstFormat aPara(
+ pStt, pEnd, pHistory, pCharSet, pLayout);
+ rDoc.GetNodes().ForEach( aSt, aEnd, ::sw::DocumentContentOperationsManager::lcl_RstTextAttr, &aPara );
+ }
+
+ bool bCreateSwpHints = pCharSet && (
+ SfxItemState::SET == pCharSet->GetItemState( RES_TXTATR_CHARFMT, false ) ||
+ SfxItemState::SET == pCharSet->GetItemState( RES_TXTATR_INETFMT, false ) );
+
+ for (SwNodeIndex current = aSt; current < aEnd; ++current)
+ {
+ SwTextNode *const pTNd = current.GetNode().GetTextNode();
+ if (!pTNd)
+ continue;
+
+ if (pLayout && pLayout->HasMergedParas()
+ && pTNd->GetRedlineMergeFlag() == SwNode::Merge::Hidden)
+ { // not really sure what to do here, but applying to hidden
+ continue; // nodes doesn't make sense...
+ }
+
+ if( pHistory )
+ {
+ SwRegHistory aRegH( pTNd, *pTNd, pHistory );
+
+ if (pCharSet && pCharSet->Count())
+ {
+ if (SwpHints *pSwpHints = bCreateSwpHints ? &pTNd->GetOrCreateSwpHints()
+ : pTNd->GetpSwpHints())
+ {
+ pSwpHints->Register( &aRegH );
+ }
+
+ pTNd->SetAttr(*pCharSet, 0, pTNd->GetText().getLength(), nFlags);
+
+ // re-fetch as it may be deleted by SetAttr
+ if (SwpHints *pSwpHints = pTNd->GetpSwpHints())
+ pSwpHints->DeRegister();
+ }
+ }
+ else
+ {
+ if (pCharSet && pCharSet->Count())
+ pTNd->SetAttr(*pCharSet, 0, pTNd->GetText().getLength(), nFlags);
+ }
+ ++nNodes;
+ }
+
+ if (pOtherSet && pOtherSet->Count())
+ {
+ for (; aSt < aEnd; ++aSt)
+ {
+ pNode = aSt.GetNode().GetContentNode();
+ if (!pNode)
+ continue;
+
+ lcl_ApplyOtherSet(*pNode, pHistory, *pOtherSet, firstSet, propsSet, pLayout, &aSt);
+ ++nNodes;
+ }
+ }
+
+ DELETECHARSETS
+ return (nNodes != 0) || bRet;
+ }
+}
+
+namespace sw
+{
+
+namespace mark
+{
+ bool IsFieldmarkOverlap(SwPaM const& rPaM)
+ {
+ std::vector<std::pair<SwNodeOffset, sal_Int32>> Breaks;
+ sw::CalcBreaks(Breaks, rPaM);
+ return !Breaks.empty();
+ }
+}
+
+DocumentContentOperationsManager::DocumentContentOperationsManager( SwDoc& i_rSwdoc ) : m_rDoc( i_rSwdoc )
+{
+}
+
+/**
+ * Checks if rStart..rEnd mark a range that makes sense to copy.
+ *
+ * IsMoveToFly means the copy is a move to create a fly
+ * and so existing flys at the edge must not be copied.
+ */
+static bool IsEmptyRange(const SwPosition& rStart, const SwPosition& rEnd,
+ SwCopyFlags const flags)
+{
+ if (rStart == rEnd)
+ { // check if a fly anchored there would be copied - then copy...
+ return !IsDestroyFrameAnchoredAtChar(rStart, rStart, rEnd,
+ (flags & SwCopyFlags::IsMoveToFly)
+ ? DelContentType::WriterfilterHack|DelContentType::AllMask
+ : DelContentType::AllMask);
+ }
+ else
+ {
+ return rEnd < rStart;
+ }
+}
+
+// Copy an area into this document or into another document
+bool DocumentContentOperationsManager::CopyRange(SwPaM& rPam, SwPosition& rPos,
+ SwCopyFlags const flags,
+ sal_uInt32 nMovedID) const
+{
+ const SwPosition *pStt = rPam.Start(), *pEnd = rPam.End();
+
+ SwDoc& rDoc = rPos.GetNode().GetDoc();
+ bool bColumnSel = rDoc.IsClipBoard() && rDoc.IsColumnSelection();
+
+ // Catch if there's no copy to do
+ if (!rPam.HasMark() || (IsEmptyRange(*pStt, *pEnd, flags) && !bColumnSel))
+ return false;
+
+ // Prevent copying into Flys that are anchored in the source range
+ if (&rDoc == &m_rDoc && (flags & SwCopyFlags::CheckPosInFly))
+ {
+ // Correct the Start-/EndNode
+ SwNodeOffset nStt = pStt->GetNodeIndex(),
+ nEnd = pEnd->GetNodeIndex(),
+ nDiff = nEnd - nStt +1;
+ SwNode* pNd = m_rDoc.GetNodes()[ nStt ];
+ if( pNd->IsContentNode() && pStt->GetContentIndex() )
+ {
+ ++nStt;
+ --nDiff;
+ }
+ if( (pNd = m_rDoc.GetNodes()[ nEnd ])->IsContentNode() &&
+ static_cast<SwContentNode*>(pNd)->Len() != pEnd->GetContentIndex() )
+ {
+ --nEnd;
+ --nDiff;
+ }
+ if( nDiff &&
+ lcl_ChkFlyFly( rDoc, nStt, nEnd, rPos.GetNodeIndex() ) )
+ {
+ return false;
+ }
+ }
+
+ SwPaM* pRedlineRange = nullptr;
+ if( rDoc.getIDocumentRedlineAccess().IsRedlineOn() ||
+ (!rDoc.getIDocumentRedlineAccess().IsIgnoreRedline() && !rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty() ) )
+ pRedlineRange = new SwPaM( rPos );
+
+ RedlineFlags eOld = rDoc.getIDocumentRedlineAccess().GetRedlineFlags();
+
+ bool bRet = false;
+
+ if( &rDoc != &m_rDoc )
+ { // ordinary copy
+ bRet = CopyImpl(rPam, rPos, flags & ~SwCopyFlags::CheckPosInFly, pRedlineRange);
+ }
+ else if( ! ( *pStt <= rPos && rPos < *pEnd &&
+ ( pStt->GetNode() != pEnd->GetNode() ||
+ !pStt->GetNode().IsTextNode() )) )
+ {
+ // Copy to a position outside of the area, or copy a single TextNode
+ // Do an ordinary copy
+ bRet = CopyImpl(rPam, rPos, flags & ~SwCopyFlags::CheckPosInFly, pRedlineRange);
+ }
+ else
+ {
+ // Copy the range in itself
+ assert(!"mst: this is assumed to be dead code");
+ }
+
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
+ if( pRedlineRange )
+ {
+ if( rDoc.getIDocumentRedlineAccess().IsRedlineOn() )
+ rDoc.getIDocumentRedlineAccess().AppendRedline(
+ new SwRangeRedline(RedlineType::Insert, *pRedlineRange, nMovedID), true);
+ else
+ rDoc.getIDocumentRedlineAccess().SplitRedline( *pRedlineRange );
+ delete pRedlineRange;
+ }
+
+ return bRet;
+}
+
+static auto GetCorrPosition(SwPaM const& rPam) -> SwPosition
+{
+ // tdf#152710 target position must be on node that survives deletion
+ // so that PaMCorrAbs can invalidate SwUnoCursors properly
+ return rPam.GetPoint()->GetNode().IsContentNode()
+ ? *rPam.GetPoint()
+ : rPam.GetMark()->GetNode().IsContentNode()
+ ? *rPam.GetMark()
+ // this would be the result in SwNodes::RemoveNode()
+ : SwPosition(rPam.End()->GetNode(), SwNodeOffset(+1));
+}
+
+/// Delete a full Section of the NodeArray.
+/// The passed Node is located somewhere in the designated Section.
+void DocumentContentOperationsManager::DeleteSection( SwNode *pNode )
+{
+ assert(pNode && "Didn't pass a Node.");
+
+ SwStartNode* pSttNd = pNode->IsStartNode() ? static_cast<SwStartNode*>(pNode)
+ : pNode->StartOfSectionNode();
+ SwNodeIndex aSttIdx( *pSttNd ), aEndIdx( *pNode->EndOfSectionNode() );
+
+ // delete all Flys, Bookmarks, ...
+ DelFlyInRange( aSttIdx.GetNode(), aEndIdx.GetNode() );
+ m_rDoc.getIDocumentRedlineAccess().DeleteRedline( *pSttNd, true, RedlineType::Any );
+ DelBookmarks(aSttIdx.GetNode(), aEndIdx.GetNode());
+
+ {
+ // move all Cursor/StackCursor/UnoCursor out of the to-be-deleted area
+ SwPaM const range(aSttIdx, aEndIdx);
+ SwPosition const pos(GetCorrPosition(range));
+ ::PaMCorrAbs(range, pos);
+ }
+
+ m_rDoc.GetNodes().DelNodes( aSttIdx, aEndIdx.GetIndex() - aSttIdx.GetIndex() + 1 );
+}
+
+void DocumentContentOperationsManager::DeleteDummyChar(
+ SwPosition const& rPos, sal_Unicode const cDummy)
+{
+ SwPaM aPam(rPos, rPos);
+ aPam.GetPoint()->AdjustContent(+1);
+ assert(aPam.GetText().getLength() == 1 && aPam.GetText()[0] == cDummy);
+ (void) cDummy;
+
+ DeleteRangeImpl(aPam, SwDeleteFlags::Default);
+
+ if (!m_rDoc.getIDocumentRedlineAccess().IsIgnoreRedline()
+ && !m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty())
+ {
+ m_rDoc.getIDocumentRedlineAccess().CompressRedlines();
+ }
+}
+
+void DocumentContentOperationsManager::DeleteRange( SwPaM & rPam )
+{
+ // Seek all redlines that are in that PaM to be deleted..
+ SwRedlineTable::size_type nRedlStart = m_rDoc.getIDocumentRedlineAccess().GetRedlinePos(
+ rPam.Start()->GetNode(), RedlineType::Any);
+ SwRedlineTable::size_type nRedlEnd = m_rDoc.getIDocumentRedlineAccess().GetRedlineEndPos(
+ nRedlStart, rPam.End()->GetNode(), RedlineType::Any);
+
+ lcl_DoWithBreaks(*this, rPam, SwDeleteFlags::Default, &DocumentContentOperationsManager::DeleteRangeImpl);
+
+ // update all redlines was in the Pam that is
+ m_rDoc.getIDocumentRedlineAccess().UpdateRedlineContentNode(nRedlStart, nRedlEnd);
+
+ if (!m_rDoc.getIDocumentRedlineAccess().IsIgnoreRedline()
+ && !m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty())
+ {
+ m_rDoc.getIDocumentRedlineAccess().CompressRedlines();
+ }
+}
+
+bool DocumentContentOperationsManager::DelFullPara( SwPaM& rPam )
+{
+ const SwPosition &rStt = *rPam.Start(), &rEnd = *rPam.End();
+ const SwNode* pNd = &rStt.GetNode();
+ SwNodeOffset nSectDiff = pNd->StartOfSectionNode()->EndOfSectionIndex() -
+ pNd->StartOfSectionIndex();
+ SwNodeOffset nNodeDiff = rEnd.GetNodeIndex() - rStt.GetNodeIndex();
+
+ if ( nSectDiff-SwNodeOffset(2) <= nNodeDiff || m_rDoc.getIDocumentRedlineAccess().IsRedlineOn() ||
+ /* #i9185# Prevent getting the node after the end node (see below) */
+ rEnd.GetNodeIndex() + 1 == m_rDoc.GetNodes().Count() )
+ {
+ return false;
+ }
+
+ {
+ SwPaM temp(rPam, nullptr);
+ if (!temp.HasMark())
+ {
+ temp.SetMark();
+ }
+ if (SwTextNode *const pNode = temp.Start()->GetNode().GetTextNode())
+ { // rPam may not have nContent set but IsFieldmarkOverlap requires it
+ temp.Start()->AssignStartIndex(*pNode);
+ }
+ if (SwTextNode *const pNode = temp.End()->GetNode().GetTextNode())
+ {
+ temp.End()->AssignEndIndex(*pNode);
+ }
+ if (sw::mark::IsFieldmarkOverlap(temp))
+ { // a bit of a problem: we want to completely remove the nodes
+ // but then how can the CH_TXT_ATR survive?
+ return false;
+ }
+ }
+
+ // Move hard page breaks to the following Node.
+ bool bSavePageBreak = false, bSavePageDesc = false;
+
+ /* #i9185# This would lead to a segmentation fault if not caught above. */
+ SwNodeOffset nNextNd = rEnd.GetNodeIndex() + 1;
+ SwTableNode *const pTableNd = m_rDoc.GetNodes()[ nNextNd ]->GetTableNode();
+
+ if( pTableNd && pNd->IsContentNode() )
+ {
+ SwFrameFormat* pTableFormat = pTableNd->GetTable().GetFrameFormat();
+
+ {
+ const SfxPoolItem *pItem;
+ const SfxItemSet* pSet = static_cast<const SwContentNode*>(pNd)->GetpSwAttrSet();
+ if( pSet && SfxItemState::SET == pSet->GetItemState( RES_PAGEDESC,
+ false, &pItem ) )
+ {
+ pTableFormat->SetFormatAttr( *pItem );
+ bSavePageDesc = true;
+ }
+
+ if( pSet && SfxItemState::SET == pSet->GetItemState( RES_BREAK,
+ false, &pItem ) )
+ {
+ pTableFormat->SetFormatAttr( *pItem );
+ bSavePageBreak = true;
+ }
+ }
+ }
+
+ bool const bDoesUndo = m_rDoc.GetIDocumentUndoRedo().DoesUndo();
+ if( bDoesUndo )
+ {
+ if( !rPam.HasMark() )
+ rPam.SetMark();
+ else if( rPam.GetPoint() == &rStt )
+ rPam.Exchange();
+ rPam.GetPoint()->Adjust(SwNodeOffset(1));
+
+ SwContentNode *pTmpNode = rPam.GetPoint()->GetNode().GetContentNode();
+ bool bGoNext = (nullptr == pTmpNode);
+
+ if (rPam.GetMark()->GetContentNode())
+ rPam.GetMark()->SetContent( 0 );
+
+ m_rDoc.GetIDocumentUndoRedo().ClearRedo();
+
+ SwPaM aDelPam( *rPam.GetMark(), *rPam.GetPoint() );
+ {
+ SwPosition aTmpPos( *aDelPam.GetPoint() );
+ if( bGoNext )
+ {
+ m_rDoc.GetNodes().GoNext( &aTmpPos );
+ }
+ ::PaMCorrAbs( aDelPam, aTmpPos );
+ }
+
+ std::unique_ptr<SwUndoDelete> pUndo(new SwUndoDelete(aDelPam, SwDeleteFlags::Default, true));
+
+ *rPam.GetPoint() = *aDelPam.GetPoint();
+ pUndo->SetPgBrkFlags( bSavePageBreak, bSavePageDesc );
+ m_rDoc.GetIDocumentUndoRedo().AppendUndo(std::move(pUndo));
+ rPam.DeleteMark();
+ }
+ else
+ {
+ SwNodeRange aRg( rStt.GetNode(), rEnd.GetNode() );
+ rPam.Normalize(false);
+
+ // Try to move past the End
+ if( !rPam.Move( fnMoveForward, GoInNode ) )
+ {
+ // Fair enough, at the Beginning then
+ rPam.Exchange();
+ if( !rPam.Move( fnMoveBackward, GoInNode ))
+ {
+ SAL_WARN("sw.core", "DelFullPara: no more Nodes");
+ return false;
+ }
+ }
+
+ // must delete all fieldmarks before CorrAbs(), or they'll remain
+ // moved to wrong node without their CH_TXT_ATR_FIELD*
+ // (note: deleteMarks() doesn't help here, in case of partially
+ // selected fieldmarks; let's delete these as re-inserting their chars
+ // elsewhere looks difficult)
+ ::std::set<::sw::mark::IFieldmark*> fieldmarks;
+ for (SwNodeIndex i = aRg.aStart; i <= aRg.aEnd; ++i)
+ {
+ if (SwTextNode *const pTextNode = i.GetNode().GetTextNode())
+ {
+ for (sal_Int32 j = 0; j < pTextNode->GetText().getLength(); ++j)
+ {
+ switch (pTextNode->GetText()[j])
+ {
+ case CH_TXT_ATR_FIELDSTART:
+ case CH_TXT_ATR_FIELDEND:
+ fieldmarks.insert(m_rDoc.getIDocumentMarkAccess()->getFieldmarkAt(SwPosition(*pTextNode, j)));
+ break;
+ case CH_TXT_ATR_FIELDSEP:
+ fieldmarks.insert(m_rDoc.getIDocumentMarkAccess()->getInnerFieldmarkFor(SwPosition(*pTextNode, j)));
+ break;
+ }
+ }
+ }
+ }
+ for (auto const pFieldMark : fieldmarks)
+ {
+ m_rDoc.getIDocumentMarkAccess()->deleteMark(pFieldMark);
+ }
+
+ // move bookmarks, redlines etc.
+ if (aRg.aStart == aRg.aEnd) // only first CorrAbs variant handles this
+ {
+ m_rDoc.CorrAbs( aRg.aStart.GetNode(), *rPam.GetPoint(), 0, true );
+ }
+ else
+ {
+ SwDoc::CorrAbs( aRg.aStart, aRg.aEnd, *rPam.GetPoint(), true );
+ }
+
+ // What's with Flys?
+ {
+ // If there are FlyFrames left, delete these too
+ for( size_t n = 0; n < m_rDoc.GetSpzFrameFormats()->size(); ++n )
+ {
+ sw::SpzFrameFormat* pFly = (*m_rDoc.GetSpzFrameFormats())[n];
+ const SwFormatAnchor* pAnchor = &pFly->GetAnchor();
+ SwNode const*const pAnchorNode = pAnchor->GetAnchorNode();
+ if (pAnchorNode &&
+ ((RndStdIds::FLY_AT_PARA == pAnchor->GetAnchorId()) ||
+ (RndStdIds::FLY_AT_CHAR == pAnchor->GetAnchorId())) &&
+ // note: here use <= not < like in
+ // IsDestroyFrameAnchoredAtChar() because of the increment
+ // of rPam in the bDoesUndo path above!
+ aRg.aStart <= *pAnchorNode && *pAnchorNode <= aRg.aEnd.GetNode() )
+ {
+ m_rDoc.getIDocumentLayoutAccess().DelLayoutFormat( pFly );
+ --n;
+ }
+ }
+ }
+
+ rPam.DeleteMark();
+ m_rDoc.GetNodes().Delete( aRg.aStart, nNodeDiff+1 );
+ }
+
+ if (!m_rDoc.getIDocumentRedlineAccess().IsIgnoreRedline()
+ && !m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty())
+ {
+ m_rDoc.getIDocumentRedlineAccess().CompressRedlines();
+ }
+
+ m_rDoc.getIDocumentState().SetModified();
+
+ return true;
+}
+
+bool DocumentContentOperationsManager::DeleteAndJoin(SwPaM & rPam, SwDeleteFlags const flags)
+{
+ if ( lcl_StrLenOverflow( rPam ) )
+ return false;
+
+ bool const ret = lcl_DoWithBreaks( *this, rPam, flags, (m_rDoc.getIDocumentRedlineAccess().IsRedlineOn())
+ ? &DocumentContentOperationsManager::DeleteAndJoinWithRedlineImpl
+ : &DocumentContentOperationsManager::DeleteAndJoinImpl );
+
+ return ret;
+}
+
+// It seems that this is mostly used by SwDoc internals; the only
+// way to call this from the outside seems to be the special case in
+// SwDoc::CopyRange (but I have not managed to actually hit that case).
+bool DocumentContentOperationsManager::MoveRange( SwPaM& rPaM, SwPosition& rPos, SwMoveFlags eMvFlags )
+{
+ // nothing moved: return
+ const SwPosition *pStt = rPaM.Start(), *pEnd = rPaM.End();
+ if( !rPaM.HasMark() || *pStt >= *pEnd || (*pStt <= rPos && rPos < *pEnd))
+ return false;
+
+ assert(!sw::mark::IsFieldmarkOverlap(rPaM)); // probably an invalid redline was created?
+
+ // Save the paragraph anchored Flys, so that they can be moved.
+ SaveFlyArr aSaveFlyArr;
+ SaveFlyInRange( rPaM, rPos, aSaveFlyArr, bool( SwMoveFlags::ALLFLYS & eMvFlags ) );
+
+ // save redlines (if DOC_MOVEREDLINES is used)
+ SaveRedlines_t aSaveRedl;
+ if( SwMoveFlags::REDLINES & eMvFlags && !m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty() )
+ {
+ lcl_SaveRedlines( rPaM, aSaveRedl );
+
+ // #i17764# unfortunately, code below relies on undos being
+ // in a particular order, and presence of bookmarks
+ // will change this order. Hence, we delete bookmarks
+ // here without undo.
+ ::sw::UndoGuard const undoGuard(m_rDoc.GetIDocumentUndoRedo());
+ DelBookmarks(
+ pStt->GetNode(),
+ pEnd->GetNode(),
+ nullptr,
+ pStt->GetContentIndex(),
+ pEnd->GetContentIndex());
+ }
+
+ bool bUpdateFootnote = false;
+ SwFootnoteIdxs aTmpFntIdx;
+
+ std::unique_ptr<SwUndoMove> pUndoMove;
+ if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ m_rDoc.GetIDocumentUndoRedo().ClearRedo();
+ pUndoMove.reset(new SwUndoMove( rPaM, rPos ));
+ pUndoMove->SetMoveRedlines( eMvFlags == SwMoveFlags::REDLINES );
+ }
+ else
+ {
+ bUpdateFootnote = lcl_SaveFootnote( pStt->GetNode(), pEnd->GetNode(), rPos.GetNode(),
+ m_rDoc.GetFootnoteIdxs(), aTmpFntIdx,
+ pStt->GetContentIndex(), pEnd->GetContentIndex() );
+ }
+
+ bool bSplit = false;
+ SwPaM aSavePam( rPos, rPos );
+
+ // Move the SPoint to the beginning of the range
+ if( rPaM.GetPoint() == pEnd )
+ rPaM.Exchange();
+
+ // If there is a TextNode before and after the Move, create a JoinNext in the EditShell.
+ SwTextNode* pSrcNd = rPaM.GetPoint()->GetNode().GetTextNode();
+ bool bCorrSavePam = pSrcNd && pStt->GetNode() != pEnd->GetNode();
+
+ // If one or more TextNodes are moved, SwNodes::Move will do a SplitNode.
+ // However, this does not update the cursor. So we create a TextNode to keep
+ // updating the indices. After the Move the Node is optionally deleted.
+ SwTextNode * pTNd = rPos.GetNode().GetTextNode();
+ if( pTNd && rPaM.GetPoint()->GetNode() != rPaM.GetMark()->GetNode() &&
+ ( rPos.GetContentIndex() || ( pTNd->Len() && bCorrSavePam )) )
+ {
+ bSplit = true;
+ const sal_Int32 nMkContent = rPaM.GetMark()->GetContentIndex();
+
+ const std::shared_ptr<sw::mark::ContentIdxStore> pContentStore(sw::mark::ContentIdxStore::Create());
+ pContentStore->Save( m_rDoc, rPos.GetNodeIndex(), rPos.GetContentIndex(), true );
+
+ SwTextNode * pOrigNode = pTNd;
+ assert(*aSavePam.GetPoint() == *aSavePam.GetMark() &&
+ *aSavePam.GetPoint() == rPos);
+ assert(aSavePam.GetPoint()->GetContentNode() == pOrigNode);
+ assert(aSavePam.GetPoint()->GetNode() == rPos.GetNode());
+ assert(rPos.GetNodeIndex() == pOrigNode->GetIndex());
+
+ std::function<void (SwTextNode *, sw::mark::RestoreMode, bool)> restoreFunc(
+ [&](SwTextNode *const, sw::mark::RestoreMode const eMode, bool)
+ {
+ if (!pContentStore->Empty())
+ {
+ pContentStore->Restore(m_rDoc, pOrigNode->GetIndex()-SwNodeOffset(1), 0, true, false, eMode);
+ }
+ });
+ pTNd->SplitContentNode(rPos, &restoreFunc);
+
+ //A new node was inserted before the orig pTNd and the content up to
+ //rPos moved into it. The old node is returned with the remainder
+ //of the content in it.
+ //
+ //aSavePam was created with rPos, it continues to point to the
+ //old node, but with the *original* content index into the node.
+ //Seeing as all the orignode content before that index has
+ //been removed, the new index into the original node should now be set
+ //to 0 and the content index of rPos should also be adapted to the
+ //truncated node
+ assert(*aSavePam.GetPoint() == *aSavePam.GetMark() &&
+ *aSavePam.GetPoint() == rPos);
+ assert(aSavePam.GetPoint()->GetContentNode() == pOrigNode);
+ assert(aSavePam.GetPoint()->GetNode() == rPos.GetNode());
+ assert(rPos.GetNodeIndex() == pOrigNode->GetIndex());
+ aSavePam.GetPoint()->SetContent(0);
+ rPos = *aSavePam.GetMark() = *aSavePam.GetPoint();
+
+ // correct the PaM!
+ if( rPos.GetNode() == rPaM.GetMark()->GetNode() )
+ {
+ rPaM.GetMark()->Assign( rPos.GetNodeIndex() - SwNodeOffset(1) );
+ rPaM.GetMark()->SetContent( nMkContent );
+ }
+ }
+
+ // Put back the Pam by one "content"; so that it's always outside of
+ // the manipulated range.
+ // tdf#99692 don't Move() back if that would end up in another node
+ // because moving backward is not necessarily the inverse of forward then.
+ // (but do Move() back if we have split the node)
+ const bool bNullContent = !bSplit && aSavePam.GetPoint()->GetContentIndex() == 0;
+ if( bNullContent )
+ {
+ aSavePam.GetPoint()->Adjust(SwNodeOffset(-1));
+ }
+ else
+ {
+ bool const success(aSavePam.Move(fnMoveBackward, GoInContent));
+ assert(success);
+ (void) success;
+ }
+
+ // Copy all Bookmarks that are within the Move range into an array,
+ // that saves the position as an offset.
+ std::vector< ::sw::mark::SaveBookmark> aSaveBkmks;
+ DelBookmarks(
+ pStt->GetNode(),
+ pEnd->GetNode(),
+ &aSaveBkmks,
+ pStt->GetContentIndex(),
+ pEnd->GetContentIndex());
+
+ // If there is no range anymore due to the above deletions (e.g. the
+ // footnotes got deleted), it's still a valid Move!
+ if( *rPaM.GetPoint() != *rPaM.GetMark() )
+ {
+ // now do the actual move
+ m_rDoc.GetNodes().MoveRange( rPaM, rPos, m_rDoc.GetNodes() );
+
+ // after a MoveRange() the Mark is deleted
+ if ( rPaM.HasMark() ) // => no Move occurred!
+ {
+ return false;
+ }
+ }
+ else
+ rPaM.DeleteMark();
+
+ OSL_ENSURE( *aSavePam.GetMark() == rPos ||
+ ( aSavePam.GetMark()->GetNode().GetContentNode() == nullptr ),
+ "PaM was not moved. Aren't there ContentNodes at the beginning/end?" );
+ *aSavePam.GetMark() = rPos;
+
+ rPaM.SetMark(); // create a Sel. around the new range
+ pTNd = aSavePam.GetPointNode().GetTextNode();
+ assert(!m_rDoc.GetIDocumentUndoRedo().DoesUndo());
+ bool bRemove = true;
+ // Do two Nodes have to be joined at the SavePam?
+ if (bSplit && pTNd)
+ {
+ if (pTNd->CanJoinNext())
+ {
+ // Always join next, because <pTNd> has to stay as it is.
+ // A join previous from its next would more or less delete <pTNd>
+ pTNd->JoinNext();
+ bRemove = false;
+ }
+ }
+ if (bNullContent)
+ {
+ aSavePam.GetPoint()->Adjust(SwNodeOffset(1));
+ }
+ else if (bRemove) // No move forward after joining with next paragraph
+ {
+ aSavePam.Move( fnMoveForward, GoInContent );
+ }
+
+ // Insert the Bookmarks back into the Document.
+ *rPaM.GetMark() = *aSavePam.Start();
+ for(auto& rBkmk : aSaveBkmks)
+ rBkmk.SetInDoc(
+ &m_rDoc,
+ rPaM.GetMark()->GetNode(),
+ rPaM.GetMark()->GetContentIndex());
+ *rPaM.GetPoint() = *aSavePam.End();
+
+ // Move the Flys to the new position.
+ // note: rPos is at the end here; can't really tell flys that used to be
+ // at the start of rPam from flys that used to be at the end of rPam
+ // unfortunately, so some of them are going to end up with wrong anchor...
+ RestFlyInRange( aSaveFlyArr, *rPaM.Start(), &rPos.GetNode() );
+
+ // restore redlines (if DOC_MOVEREDLINES is used)
+ if( !aSaveRedl.empty() )
+ {
+ lcl_RestoreRedlines( m_rDoc, *aSavePam.Start(), aSaveRedl );
+ }
+
+ if( bUpdateFootnote )
+ {
+ if( !aTmpFntIdx.empty() )
+ {
+ m_rDoc.GetFootnoteIdxs().insert( aTmpFntIdx );
+ aTmpFntIdx.clear();
+ }
+
+ m_rDoc.GetFootnoteIdxs().UpdateAllFootnote();
+ }
+
+ m_rDoc.getIDocumentState().SetModified();
+ return true;
+}
+
+bool DocumentContentOperationsManager::MoveNodeRange( SwNodeRange& rRange, SwNode& rDestNd,
+ SwMoveFlags eMvFlags )
+{
+ // Moves all Nodes to the new position.
+ // Bookmarks are moved too (currently without Undo support).
+
+ // If footnotes are being moved to the special section, remove them now.
+
+ // Or else delete the Frames for all footnotes that are being moved
+ // and have it rebuild after the Move (footnotes can change pages).
+ // Additionally we have to correct the FootnoteIdx array's sorting.
+ bool bUpdateFootnote = false;
+ SwFootnoteIdxs aTmpFntIdx;
+
+ std::unique_ptr<SwUndoMove> pUndo;
+ if ((SwMoveFlags::CREATEUNDOOBJ & eMvFlags ) && m_rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ pUndo.reset(new SwUndoMove( m_rDoc, rRange, rDestNd ));
+ }
+ else
+ {
+ bUpdateFootnote = lcl_SaveFootnote( rRange.aStart.GetNode(), rRange.aEnd.GetNode(), rDestNd,
+ m_rDoc.GetFootnoteIdxs(), aTmpFntIdx );
+ }
+
+ SaveRedlines_t aSaveRedl;
+ std::vector<SwRangeRedline*> aSavRedlInsPosArr;
+ if( SwMoveFlags::REDLINES & eMvFlags && !m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty() )
+ {
+ lcl_SaveRedlines( rRange, aSaveRedl );
+
+ // Find all RedLines that end at the InsPos.
+ // These have to be moved back to the "old" position after the Move.
+ SwRedlineTable::size_type nRedlPos = m_rDoc.getIDocumentRedlineAccess().GetRedlinePos( rDestNd, RedlineType::Any );
+ if( SwRedlineTable::npos != nRedlPos )
+ {
+ const SwPosition *pRStt, *pREnd;
+ do {
+ SwRangeRedline* pTmp = m_rDoc.getIDocumentRedlineAccess().GetRedlineTable()[ nRedlPos ];
+ pRStt = pTmp->Start();
+ pREnd = pTmp->End();
+ if( pREnd->GetNode() == rDestNd && pRStt->GetNode() < rDestNd )
+ {
+ aSavRedlInsPosArr.push_back( pTmp );
+ }
+ } while( pRStt->GetNode() < rDestNd && ++nRedlPos < m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().size());
+ }
+ }
+
+ // Copy all Bookmarks that are within the Move range into an array
+ // that stores all references to positions as an offset.
+ // The final mapping happens after the Move.
+ std::vector< ::sw::mark::SaveBookmark> aSaveBkmks;
+ DelBookmarks(rRange.aStart.GetNode(), rRange.aEnd.GetNode(), &aSaveBkmks);
+
+ // Save the paragraph-bound Flys, so that they can be moved.
+ SaveFlyArr aSaveFlyArr;
+ if( !m_rDoc.GetSpzFrameFormats()->empty() )
+ SaveFlyInRange( rRange, aSaveFlyArr );
+
+ // Set it to before the Position, so that it cannot be moved further.
+ SwNodeIndex aIdx( rDestNd, -1 );
+
+ std::optional<SwNodeIndex> oSaveInsPos;
+ if( pUndo )
+ oSaveInsPos.emplace(rRange.aStart, -1 );
+
+ // move the Nodes
+ bool bNoDelFrames = bool(SwMoveFlags::NO_DELFRMS & eMvFlags);
+ if( m_rDoc.GetNodes().MoveNodes( rRange, m_rDoc.GetNodes(), rDestNd, !bNoDelFrames ) )
+ {
+ ++aIdx; // again back to old position
+ if( oSaveInsPos )
+ ++(*oSaveInsPos);
+ }
+ else
+ {
+ aIdx = rRange.aStart;
+ pUndo.reset();
+ }
+
+ // move the Flys to the new position
+ if( !aSaveFlyArr.empty() )
+ {
+ SwPosition const tmp(aIdx);
+ RestFlyInRange(aSaveFlyArr, tmp, nullptr);
+ }
+
+ // Add the Bookmarks back to the Document
+ for(auto& rBkmk : aSaveBkmks)
+ rBkmk.SetInDoc(&m_rDoc, aIdx.GetNode());
+
+ if( !aSavRedlInsPosArr.empty() )
+ {
+ for(SwRangeRedline* pTmp : aSavRedlInsPosArr)
+ {
+ if( m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().Contains( pTmp ) )
+ {
+ SwPosition* pEnd = pTmp->End();
+ pEnd->Assign(aIdx);
+ }
+ }
+ }
+
+ if( !aSaveRedl.empty() )
+ lcl_RestoreRedlines( m_rDoc, aIdx.GetIndex(), aSaveRedl );
+
+ if( pUndo )
+ {
+ pUndo->SetDestRange( aIdx.GetNode(), rDestNd, *oSaveInsPos );
+ m_rDoc.GetIDocumentUndoRedo().AppendUndo(std::move(pUndo));
+ }
+
+ oSaveInsPos.reset();
+
+ if( bUpdateFootnote )
+ {
+ if( !aTmpFntIdx.empty() )
+ {
+ m_rDoc.GetFootnoteIdxs().insert( aTmpFntIdx );
+ aTmpFntIdx.clear();
+ }
+
+ m_rDoc.GetFootnoteIdxs().UpdateAllFootnote();
+ }
+
+ m_rDoc.getIDocumentState().SetModified();
+ return true;
+}
+
+void DocumentContentOperationsManager::MoveAndJoin( SwPaM& rPaM, SwPosition& rPos )
+{
+ SwNodeIndex aIdx( rPaM.Start()->GetNode() );
+ bool bJoinText = aIdx.GetNode().IsTextNode();
+ bool bOneNode = rPaM.GetPoint()->GetNode() == rPaM.GetMark()->GetNode();
+ --aIdx; // in front of the move area!
+
+ bool bRet = MoveRange( rPaM, rPos, SwMoveFlags::DEFAULT );
+ if( !bRet || bOneNode )
+ return;
+
+ if( bJoinText )
+ ++aIdx;
+ SwTextNode * pTextNd = aIdx.GetNode().GetTextNode();
+ SwNodeIndex aNxtIdx( aIdx );
+ if( pTextNd && pTextNd->CanJoinNext( &aNxtIdx ) )
+ {
+ { // Block so SwContentIndex into node is deleted before Join
+ m_rDoc.CorrRel( aNxtIdx.GetNode(),
+ SwPosition( *pTextNd, pTextNd->GetText().getLength() ),
+ 0, true );
+ }
+ pTextNd->JoinNext();
+ }
+}
+
+// Overwrite only uses the point of the PaM, the mark is ignored; characters
+// are replaced from point until the end of the node; at the end of the node,
+// characters are inserted.
+bool DocumentContentOperationsManager::Overwrite( const SwPaM &rRg, const OUString &rStr )
+{
+ assert(rStr.getLength());
+ SwPosition& rPt = *const_cast<SwPosition*>(rRg.GetPoint());
+ if( m_rDoc.GetAutoCorrExceptWord() ) // Add to AutoCorrect
+ {
+ if( 1 == rStr.getLength() )
+ m_rDoc.GetAutoCorrExceptWord()->CheckChar( rPt, rStr[ 0 ] );
+ m_rDoc.DeleteAutoCorrExceptWord();
+ }
+
+ SwTextNode *pNode = rPt.GetNode().GetTextNode();
+ if (!pNode || rStr.getLength() > pNode->GetSpaceLeft()) // worst case: no erase
+ {
+ return false;
+ }
+
+ if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ m_rDoc.GetIDocumentUndoRedo().ClearRedo(); // AppendUndo not always called
+ }
+
+ const size_t nOldAttrCnt = pNode->GetpSwpHints()
+ ? pNode->GetpSwpHints()->Count() : 0;
+ SwDataChanged aTmp( rRg );
+ sal_Int32 const nActualStart(rPt.GetContentIndex());
+ sal_Int32 nStart = 0;
+
+ bool bOldExpFlg = pNode->IsIgnoreDontExpand();
+ pNode->SetIgnoreDontExpand( true );
+
+ for( sal_Int32 nCnt = 0; nCnt < rStr.getLength(); ++nCnt )
+ {
+ // start behind the characters (to fix the attributes!)
+ nStart = rPt.GetContentIndex();
+ if (nStart < pNode->GetText().getLength())
+ {
+ lcl_SkipAttr( pNode, rPt, nStart );
+ }
+ sal_Unicode c = rStr[ nCnt ];
+ if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ bool bMerged(false);
+ if (m_rDoc.GetIDocumentUndoRedo().DoesGroupUndo())
+ {
+ SwUndo *const pUndo = m_rDoc.GetUndoManager().GetLastUndo();
+ SwUndoOverwrite *const pUndoOW(
+ dynamic_cast<SwUndoOverwrite *>(pUndo) );
+ if (pUndoOW)
+ {
+ // if CanGrouping() returns true it's already merged
+ bMerged = pUndoOW->CanGrouping(m_rDoc, rPt, c);
+ }
+ }
+ if (!bMerged)
+ {
+ m_rDoc.GetIDocumentUndoRedo().AppendUndo(
+ std::make_unique<SwUndoOverwrite>(m_rDoc, rPt, c) );
+ }
+ }
+ else
+ {
+ // start behind the characters (to fix the attributes!)
+ if (nStart < pNode->GetText().getLength())
+ rPt.AdjustContent(+1);
+ pNode->InsertText( OUString(c), rPt, SwInsertFlags::EMPTYEXPAND );
+ if( nStart+1 < rPt.GetContentIndex() )
+ {
+ rPt.SetContent(nStart);
+ pNode->EraseText( rPt, 1 );
+ rPt.AdjustContent(+1);
+ }
+ }
+ }
+ pNode->SetIgnoreDontExpand( bOldExpFlg );
+
+ const size_t nNewAttrCnt = pNode->GetpSwpHints()
+ ? pNode->GetpSwpHints()->Count() : 0;
+ if( nOldAttrCnt != nNewAttrCnt )
+ {
+ const SwUpdateAttr aHint(0,0,0);
+ pNode->TriggerNodeUpdate(sw::LegacyModifyHint(&aHint, &aHint));
+ }
+
+ if (!m_rDoc.GetIDocumentUndoRedo().DoesUndo() &&
+ !m_rDoc.getIDocumentRedlineAccess().IsIgnoreRedline() && !m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty())
+ {
+ SwPaM aPam(rPt.GetNode(), nActualStart, rPt.GetNode(), rPt.GetContentIndex());
+ m_rDoc.getIDocumentRedlineAccess().DeleteRedline( aPam, true, RedlineType::Any );
+ }
+ else if( m_rDoc.getIDocumentRedlineAccess().IsRedlineOn() )
+ {
+ // FIXME: this redline is WRONG: there is no DELETE, and the skipped
+ // characters are also included in aPam
+ SwPaM aPam(rPt.GetNode(), nActualStart, rPt.GetNode(), rPt.GetContentIndex());
+ m_rDoc.getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( RedlineType::Insert, aPam ), true);
+ }
+
+ m_rDoc.getIDocumentState().SetModified();
+ return true;
+}
+
+bool DocumentContentOperationsManager::InsertString( const SwPaM &rRg, const OUString &rStr,
+ const SwInsertFlags nInsertMode )
+{
+ // tdf#119019 accept tracked paragraph formatting to do not hide new insertions
+ if( m_rDoc.getIDocumentRedlineAccess().IsRedlineOn() )
+ {
+ RedlineFlags eOld = m_rDoc.getIDocumentRedlineAccess().GetRedlineFlags();
+ m_rDoc.getIDocumentRedlineAccess().AcceptRedlineParagraphFormatting( rRg );
+ if (eOld != m_rDoc.getIDocumentRedlineAccess().GetRedlineFlags())
+ m_rDoc.getIDocumentRedlineAccess().SetRedlineFlags( eOld );
+ }
+
+ // fetching DoesUndo is surprisingly expensive
+ bool bDoesUndo = m_rDoc.GetIDocumentUndoRedo().DoesUndo();
+ if (bDoesUndo)
+ m_rDoc.GetIDocumentUndoRedo().ClearRedo(); // AppendUndo not always called!
+
+ const SwPosition& rPos = *rRg.GetPoint();
+
+ if( m_rDoc.GetAutoCorrExceptWord() ) // add to auto correction
+ {
+ if( 1 == rStr.getLength() && m_rDoc.GetAutoCorrExceptWord()->IsDeleted() )
+ {
+ m_rDoc.GetAutoCorrExceptWord()->CheckChar( rPos, rStr[ 0 ] );
+ }
+ m_rDoc.DeleteAutoCorrExceptWord();
+ }
+
+ SwTextNode *const pNode = rPos.GetNode().GetTextNode();
+ if(!pNode)
+ return false;
+
+ SwDataChanged aTmp( rRg );
+
+ if (!bDoesUndo || !m_rDoc.GetIDocumentUndoRedo().DoesGroupUndo())
+ {
+ OUString const ins(pNode->InsertText(rStr, rPos, nInsertMode));
+ if (bDoesUndo)
+ {
+ m_rDoc.GetIDocumentUndoRedo().AppendUndo(
+ std::make_unique<SwUndoInsert>(rPos.GetNode(),
+ rPos.GetContentIndex(), ins.getLength(), nInsertMode));
+ }
+ }
+ else
+ { // if Undo and grouping is enabled, everything changes!
+ SwUndoInsert * pUndo = nullptr;
+
+ // don't group the start if hints at the start should be expanded
+ if (!(nInsertMode & SwInsertFlags::FORCEHINTEXPAND))
+ {
+ SwUndo *const pLastUndo = m_rDoc.GetUndoManager().GetLastUndo();
+ SwUndoInsert *const pUndoInsert(
+ dynamic_cast<SwUndoInsert *>(pLastUndo) );
+ if (pUndoInsert && pUndoInsert->CanGrouping(rPos))
+ {
+ pUndo = pUndoInsert;
+ }
+ }
+
+ CharClass const& rCC = GetAppCharClass();
+ sal_Int32 nInsPos = rPos.GetContentIndex();
+
+ if (!pUndo)
+ {
+ pUndo = new SwUndoInsert( rPos.GetNode(), nInsPos, 0, nInsertMode,
+ !rCC.isLetterNumeric( rStr, 0 ) );
+ m_rDoc.GetIDocumentUndoRedo().AppendUndo( std::unique_ptr<SwUndo>(pUndo) );
+ }
+
+ OUString const ins(pNode->InsertText(rStr, rPos, nInsertMode));
+
+ for (sal_Int32 i = 0; i < ins.getLength(); ++i)
+ {
+ nInsPos++;
+ // if CanGrouping() returns true, everything has already been done
+ if (!pUndo->CanGrouping(ins[i]))
+ {
+ pUndo = new SwUndoInsert(rPos.GetNode(), nInsPos, 1, nInsertMode,
+ !rCC.isLetterNumeric(ins, i));
+ m_rDoc.GetIDocumentUndoRedo().AppendUndo( std::unique_ptr<SwUndo>(pUndo) );
+ }
+ }
+ }
+
+ // To-Do - add 'SwExtraRedlineTable' also ?
+ if( m_rDoc.getIDocumentRedlineAccess().IsRedlineOn() || (!m_rDoc.getIDocumentRedlineAccess().IsIgnoreRedline() && !m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty() ))
+ {
+ SwPaM aPam( rPos.GetNode(), aTmp.GetContent(),
+ rPos.GetNode(), rPos.GetContentIndex());
+ if( m_rDoc.getIDocumentRedlineAccess().IsRedlineOn() )
+ {
+ m_rDoc.getIDocumentRedlineAccess().AppendRedline(
+ new SwRangeRedline( RedlineType::Insert, aPam ), true);
+ }
+ else
+ {
+ m_rDoc.getIDocumentRedlineAccess().SplitRedline( aPam );
+ }
+ }
+
+ m_rDoc.getIDocumentState().SetModified();
+ return true;
+}
+
+void DocumentContentOperationsManager::SetIME(bool bIME)
+{
+ m_bIME = bIME;
+}
+
+bool DocumentContentOperationsManager::GetIME() const
+{
+ return m_bIME;
+}
+
+void DocumentContentOperationsManager::TransliterateText(
+ const SwPaM& rPaM,
+ utl::TransliterationWrapper& rTrans )
+{
+ std::unique_ptr<SwUndoTransliterate> pUndo;
+ if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
+ pUndo.reset(new SwUndoTransliterate( rPaM, rTrans ));
+
+ auto [pStt, pEnd] = rPaM.StartEnd(); // SwPosition*
+ SwNodeOffset nSttNd = pStt->GetNodeIndex(),
+ nEndNd = pEnd->GetNodeIndex();
+ sal_Int32 nSttCnt = pStt->GetContentIndex();
+ sal_Int32 nEndCnt = pEnd->GetContentIndex();
+
+ SwTextNode* pTNd = pStt->GetNode().GetTextNode();
+ bool bNoSelection = (pStt == pEnd) && pTNd; // no selection?
+ if ( bNoSelection )
+ {
+ /* Check if cursor is inside of a word */
+ assert(g_pBreakIt && g_pBreakIt->GetBreakIter().is());
+ Boundary aBndry = g_pBreakIt->GetBreakIter()->getWordBoundary(
+ pTNd->GetText(), nSttCnt,
+ g_pBreakIt->GetLocale( pTNd->GetLang( nSttCnt ) ),
+ WordType::ANY_WORD /*ANYWORD_IGNOREWHITESPACES*/,
+ true);
+
+ if( aBndry.startPos < nSttCnt && nSttCnt < aBndry.endPos )
+ {
+ /* Cursor is inside of a word */
+ if (rTrans.getType() == TransliterationFlags::SENTENCE_CASE) {
+ /* set current sentence as 'area of effect' */
+ nSttCnt = g_pBreakIt->GetBreakIter()->beginOfSentence(
+ pTNd->GetText(), nSttCnt,
+ g_pBreakIt->GetLocale( pTNd->GetLang( nSttCnt ) ) );
+ nEndCnt = g_pBreakIt->GetBreakIter()->endOfSentence(
+ pTNd->GetText(), nEndCnt,
+ g_pBreakIt->GetLocale( pTNd->GetLang( nEndCnt ) ) );
+ } else {
+ /* Set current word as 'area of effect' */
+ nSttCnt = aBndry.startPos;
+ nEndCnt = aBndry.endPos;
+ }
+ } else {
+ /* Cursor is not inside of a word. Nothing should happen. */
+ /* Except in the case of change tracking, when the cursor is at the end of the change */
+ /* Recognize and reject the previous deleted and inserted words to allow to cycle */
+ IDocumentRedlineAccess& rIDRA = m_rDoc.getIDocumentRedlineAccess();
+ if ( IDocumentRedlineAccess::IsShowChanges( rIDRA.GetRedlineFlags() ) &&
+ pStt->GetContentIndex() > 0 )
+ {
+ SwPosition aPos(*pStt->GetContentNode(), pStt->GetContentIndex() - 1);
+ SwRedlineTable::size_type n = 0;
+
+ const SwRangeRedline* pFnd =
+ rIDRA.GetRedlineTable().FindAtPosition( aPos, n );
+ if ( pFnd && RedlineType::Insert == pFnd->GetType() && n > 0 )
+ {
+ const SwRangeRedline* pFnd2 = rIDRA.GetRedlineTable()[n-1];
+ if ( RedlineType::Delete == pFnd2->GetType() &&
+ m_rDoc.getIDocumentLayoutAccess().GetCurrentViewShell() &&
+ *pFnd2->End() == *pFnd->Start() &&
+ pFnd->GetAuthor() == pFnd2->GetAuthor() )
+ {
+ SwPosition aPos2(*pFnd2->End());
+ rIDRA.RejectRedline(*pFnd, true);
+ rIDRA.RejectRedline(*pFnd2, true);
+ // positionate the text cursor inside the changed word to allow to cycle
+ if ( SwWrtShell *pWrtShell = dynamic_cast<SwWrtShell*>(
+ m_rDoc.getIDocumentLayoutAccess().GetCurrentViewShell()) )
+ {
+ pWrtShell->GetCursor()->GetPoint()->
+ Assign(*aPos2.GetContentNode(), aPos2.GetContentIndex() - 1);
+ }
+ }
+ }
+ }
+ return;
+ }
+ }
+ else
+ {
+ bool bHasTrackedChange = false;
+ IDocumentRedlineAccess& rIDRA = m_rDoc.getIDocumentRedlineAccess();
+ if ( IDocumentRedlineAccess::IsShowChanges( rIDRA.GetRedlineFlags() ) &&
+ pEnd->GetContentIndex() > 0 )
+ {
+ // search all own redlines within the selected area
+ SwRedlineTable::size_type n = SwRedlineTable::npos;
+ const SwRedlineTable& aRedlineTable = rIDRA.GetRedlineTable();
+ for( SwRedlineTable::size_type m = 0; m < aRedlineTable.size(); ++m )
+ {
+ const SwRangeRedline* pRedline = aRedlineTable[ m ];
+
+ if ( *pRedline->Start() > *pEnd )
+ break;
+
+ if ( *pRedline->Start() >= *pStt )
+ n = m;
+ }
+
+ if ( n != SwRedlineTable::npos && n > 0 )
+ {
+ SwWrtShell *pWrtShell = dynamic_cast<SwWrtShell*>(
+ m_rDoc.getIDocumentLayoutAccess().GetCurrentViewShell());
+
+ sal_Int32 nRejectedCharacters = 0;
+ SwRangeRedline* pFnd = rIDRA.GetRedlineTable()[n];
+ SwRangeRedline* pFnd2 = rIDRA.GetRedlineTable()[--n];
+ // loop on all redlines of a case changing, and reject them
+ while ( ( ( RedlineType::Insert == pFnd->GetType() &&
+ RedlineType::Delete == pFnd2->GetType() ) ||
+ ( RedlineType::Delete == pFnd->GetType() &&
+ RedlineType::Insert == pFnd2->GetType() ) ) &&
+ pWrtShell &&
+ // use time stamp to recognize the multiple selections in the text,
+ // not only the changes from the same author within the (sometimes
+ // incomplete) selection
+ ( pFnd2->GetTimeStamp() == pFnd->GetTimeStamp() ||
+ ( pStt->GetContentNode() < pFnd2->Start()->GetContentNode() ||
+ ( pStt->GetContentNode() == pFnd2->Start()->GetContentNode() &&
+ nSttCnt <= pFnd2->Start()->GetContentIndex() ) ) ) &&
+ pFnd->GetAuthor() == pFnd2->GetAuthor() )
+ {
+ bHasTrackedChange = true;
+
+ if ( RedlineType::Insert == pFnd->GetType() )
+ nRejectedCharacters += pFnd->GetText().getLength();
+
+ rIDRA.RejectRedline(*pFnd, true);
+
+ pFnd = pFnd2;
+ if ( n == 0 )
+ break;
+ pFnd2 = rIDRA.GetRedlineTable()[--n];
+ }
+
+ // remove the last item and restore the original selection within the node
+ if ( bHasTrackedChange )
+ {
+ if ( nSttNd == nEndNd )
+ {
+ pWrtShell->GetCursor()->GetPoint()->
+ Assign(*rPaM.Start()->GetContentNode(), nSttCnt);
+ if ( nEndCnt >= nRejectedCharacters )
+ pWrtShell->GetCursor()->GetMark()->
+ Assign(*rPaM.End()->GetContentNode(), nEndCnt - nRejectedCharacters);
+ }
+ rIDRA.RejectRedline(*pFnd, true);
+ }
+ }
+ }
+
+ // TODO handle title case to lowercase
+ if ( bHasTrackedChange )
+ return;
+ }
+
+ bool bUseRedlining = m_rDoc.getIDocumentRedlineAccess().IsRedlineOn();
+ // as a workaround for a known performance problem, switch off redlining
+ // to avoid freezing, if transliteration could result too many redlines
+ if ( bUseRedlining )
+ {
+ const sal_uLong nMaxRedlines = 500;
+ const bool bIsTitleCase = rTrans.getType() == TransliterationFlags::TITLE_CASE;
+ sal_uLong nAffectedNodes = 0;
+ sal_uLong nAffectedChars = nEndCnt;
+ SwNodeIndex aIdx( pStt->GetNode() );
+ for( ; aIdx.GetIndex() <= nEndNd; ++aIdx )
+ {
+ SwTextNode* pAffectedNode = aIdx.GetNode().GetTextNode();
+
+ // don't count not text nodes or empty text nodes
+ if( !pAffectedNode || pAffectedNode->GetText().isEmpty() )
+ continue;
+
+ nAffectedNodes++;
+
+ // count characters of the node (the last - maybe partially
+ // selected - node was counted at initialization of nAffectedChars)
+ if( aIdx.GetIndex() < nEndNd )
+ nAffectedChars += pAffectedNode->GetText().getLength();
+
+ // transliteration creates n redlines for n nodes, except in the
+ // case of title case, where it creates n redlines for n words
+ if( nAffectedNodes > nMaxRedlines ||
+ // estimate word count based on the character count, where
+ // 6 = average English word length is ~5 letters + space
+ ( bIsTitleCase && (nAffectedChars - nSttCnt)/6 > nMaxRedlines ) )
+ {
+ bUseRedlining = false;
+ break;
+ }
+ }
+ }
+
+ if( nSttNd != nEndNd ) // is more than one text node involved?
+ {
+ // iterate over all affected text nodes, the first and the last one
+ // may be incomplete because the selection starts and/or ends there
+
+ SwNodeIndex aIdx( pStt->GetNode() );
+ if( nSttCnt )
+ {
+ ++aIdx;
+ if( pTNd )
+ {
+ pTNd->TransliterateText(
+ rTrans, nSttCnt, pTNd->GetText().getLength(), pUndo.get(), bUseRedlining);
+ }
+ }
+
+ for( ; aIdx.GetIndex() < nEndNd; ++aIdx )
+ {
+ pTNd = aIdx.GetNode().GetTextNode();
+ if (pTNd)
+ {
+ pTNd->TransliterateText(
+ rTrans, 0, pTNd->GetText().getLength(), pUndo.get(), bUseRedlining);
+ }
+ }
+
+ if( nEndCnt && nullptr != ( pTNd = pEnd->GetNode().GetTextNode() ))
+ {
+ pTNd->TransliterateText( rTrans, 0, nEndCnt, pUndo.get(), bUseRedlining );
+ }
+ }
+ else if( pTNd && nSttCnt < nEndCnt )
+ {
+ pTNd->TransliterateText( rTrans, nSttCnt, nEndCnt, pUndo.get(), bUseRedlining );
+ }
+ if( pUndo && pUndo->HasData() )
+ {
+ m_rDoc.GetIDocumentUndoRedo().AppendUndo(std::move(pUndo));
+ }
+
+ // restore selection after tracked changes
+ if ( !bNoSelection && bUseRedlining && nSttNd == nEndNd )
+ {
+ if ( SwWrtShell *pWrtShell = dynamic_cast<SwWrtShell*>(
+ m_rDoc.getIDocumentLayoutAccess().GetCurrentViewShell()) )
+ {
+ *pWrtShell->GetCursor()->GetMark() = *pWrtShell->GetCursor()->End();
+ pWrtShell->GetCursor()->GetPoint()->Assign(*pStt->GetContentNode(), nSttCnt);
+ }
+ }
+
+ m_rDoc.getIDocumentState().SetModified();
+}
+
+SwFlyFrameFormat* DocumentContentOperationsManager::InsertGraphic(
+ const SwPaM &rRg,
+ const OUString& rGrfName,
+ const OUString& rFltName,
+ const Graphic* pGraphic,
+ const SfxItemSet* pFlyAttrSet,
+ const SfxItemSet* pGrfAttrSet,
+ SwFrameFormat* pFrameFormat )
+{
+ if( !pFrameFormat )
+ pFrameFormat = m_rDoc.getIDocumentStylePoolAccess().GetFrameFormatFromPool( RES_POOLFRM_GRAPHIC );
+ SwGrfNode* pSwGrfNode = SwNodes::MakeGrfNode(
+ m_rDoc.GetNodes().GetEndOfAutotext(),
+ rGrfName, rFltName, pGraphic,
+ m_rDoc.GetDfltGrfFormatColl() );
+ SwFlyFrameFormat* pSwFlyFrameFormat = InsNoTextNode( *rRg.GetPoint(), pSwGrfNode,
+ pFlyAttrSet, pGrfAttrSet, pFrameFormat );
+ return pSwFlyFrameFormat;
+}
+
+SwFlyFrameFormat* DocumentContentOperationsManager::InsertEmbObject(
+ const SwPaM &rRg, const svt::EmbeddedObjectRef& xObj,
+ SfxItemSet* pFlyAttrSet)
+{
+ sal_uInt16 nId = RES_POOLFRM_OLE;
+ if (xObj.is())
+ {
+ SvGlobalName aClassName( xObj->getClassID() );
+ if (SotExchange::IsMath(aClassName))
+ {
+ nId = RES_POOLFRM_FORMEL;
+ }
+ }
+
+ SwFrameFormat* pFrameFormat = m_rDoc.getIDocumentStylePoolAccess().GetFrameFormatFromPool( nId );
+
+ return InsNoTextNode( *rRg.GetPoint(), m_rDoc.GetNodes().MakeOLENode(
+ m_rDoc.GetNodes().GetEndOfAutotext(),
+ xObj,
+ m_rDoc.GetDfltGrfFormatColl() ),
+ pFlyAttrSet, nullptr,
+ pFrameFormat );
+}
+
+SwFlyFrameFormat* DocumentContentOperationsManager::InsertOLE(const SwPaM &rRg, const OUString& rObjName,
+ sal_Int64 nAspect,
+ const SfxItemSet* pFlyAttrSet,
+ const SfxItemSet* pGrfAttrSet)
+{
+ SwFrameFormat* pFrameFormat = m_rDoc.getIDocumentStylePoolAccess().GetFrameFormatFromPool( RES_POOLFRM_OLE );
+
+ return InsNoTextNode( *rRg.GetPoint(),
+ m_rDoc.GetNodes().MakeOLENode(
+ m_rDoc.GetNodes().GetEndOfAutotext(),
+ rObjName,
+ nAspect,
+ m_rDoc.GetDfltGrfFormatColl(),
+ nullptr ),
+ pFlyAttrSet, pGrfAttrSet,
+ pFrameFormat );
+}
+
+void DocumentContentOperationsManager::ReRead( SwPaM& rPam, const OUString& rGrfName,
+ const OUString& rFltName, const Graphic* pGraphic )
+{
+ SwGrfNode *pGrfNd;
+ if( !(( !rPam.HasMark()
+ || rPam.GetPoint()->GetNodeIndex() == rPam.GetMark()->GetNodeIndex() )
+ && nullptr != ( pGrfNd = rPam.GetPoint()->GetNode().GetGrfNode() )) )
+ return;
+
+ if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ m_rDoc.GetIDocumentUndoRedo().AppendUndo(std::make_unique<SwUndoReRead>(rPam, *pGrfNd));
+ }
+
+ // Because we don't know if we can mirror the graphic, the mirror attribute is always reset
+ if( MirrorGraph::Dont != pGrfNd->GetSwAttrSet().
+ GetMirrorGrf().GetValue() )
+ pGrfNd->SetAttr( SwMirrorGrf() );
+
+ pGrfNd->ReRead( rGrfName, rFltName, pGraphic );
+ m_rDoc.getIDocumentState().SetModified();
+}
+
+// Insert drawing object, which has to be already inserted in the DrawModel
+SwDrawFrameFormat* DocumentContentOperationsManager::InsertDrawObj(
+ const SwPaM &rRg,
+ SdrObject& rDrawObj,
+ const SfxItemSet& rFlyAttrSet )
+{
+ SwDrawFrameFormat* pFormat = m_rDoc.MakeDrawFrameFormat( OUString(), m_rDoc.GetDfltFrameFormat() );
+
+ const SwFormatAnchor* pAnchor = rFlyAttrSet.GetItemIfSet( RES_ANCHOR, false );
+ pFormat->SetFormatAttr( rFlyAttrSet );
+
+ // Didn't set the Anchor yet?
+ // DrawObjecte must never end up in the Header/Footer!
+ RndStdIds eAnchorId = pAnchor != nullptr ? pAnchor->GetAnchorId() : pFormat->GetAnchor().GetAnchorId();
+ const bool bIsAtContent = (RndStdIds::FLY_AT_PAGE != eAnchorId);
+
+ const SwPosition* pChkPos = nullptr;
+ if ( pAnchor == nullptr )
+ {
+ pChkPos = rRg.GetPoint();
+ }
+ else if ( bIsAtContent )
+ {
+ pChkPos =
+ pAnchor->GetContentAnchor() ? pAnchor->GetContentAnchor() : rRg.GetPoint();
+ }
+
+ // allow drawing objects in header/footer, but control objects aren't allowed in header/footer.
+ if( pChkPos != nullptr
+ && ::CheckControlLayer( &rDrawObj )
+ && m_rDoc.IsInHeaderFooter( pChkPos->GetNode() ) )
+ {
+ // apply at-page anchor format
+ eAnchorId = RndStdIds::FLY_AT_PAGE;
+ pFormat->SetFormatAttr( SwFormatAnchor( eAnchorId ) );
+ }
+ else if( pAnchor == nullptr
+ || ( bIsAtContent
+ && pAnchor->GetAnchorNode() == nullptr ) )
+ {
+ // apply anchor format
+ SwFormatAnchor aAnch( pAnchor != nullptr ? *pAnchor : pFormat->GetAnchor() );
+ eAnchorId = aAnch.GetAnchorId();
+ if ( eAnchorId == RndStdIds::FLY_AT_FLY )
+ {
+ const SwStartNode* pStartNode = rRg.GetPointNode().FindFlyStartNode();
+ assert(pStartNode);
+ SwPosition aPos(*pStartNode);
+ aAnch.SetAnchor( &aPos );
+ }
+ else
+ {
+ aAnch.SetAnchor( rRg.GetPoint() );
+ if ( eAnchorId == RndStdIds::FLY_AT_PAGE )
+ {
+ eAnchorId = dynamic_cast<const SdrUnoObj*>( &rDrawObj) != nullptr ? RndStdIds::FLY_AS_CHAR : RndStdIds::FLY_AT_PARA;
+ aAnch.SetType( eAnchorId );
+ }
+ }
+ pFormat->SetFormatAttr( aAnch );
+ }
+
+ // insert text attribute for as-character anchored drawing object
+ if ( eAnchorId == RndStdIds::FLY_AS_CHAR )
+ {
+ bool bAnchorAtPageAsFallback = true;
+ const SwFormatAnchor& rDrawObjAnchorFormat = pFormat->GetAnchor();
+ if ( rDrawObjAnchorFormat.GetAnchorNode() != nullptr )
+ {
+ SwTextNode* pAnchorTextNode =
+ rDrawObjAnchorFormat.GetAnchorNode()->GetTextNode();
+ if ( pAnchorTextNode != nullptr )
+ {
+ const sal_Int32 nStt = rDrawObjAnchorFormat.GetContentAnchor()->GetContentIndex();
+ SwFormatFlyCnt aFormat( pFormat );
+ pAnchorTextNode->InsertItem( aFormat, nStt, nStt );
+ bAnchorAtPageAsFallback = false;
+ }
+ }
+
+ if ( bAnchorAtPageAsFallback )
+ {
+ OSL_ENSURE( false, "DocumentContentOperationsManager::InsertDrawObj(..) - missing content anchor for as-character anchored drawing object --> anchor at-page" );
+ pFormat->SetFormatAttr( SwFormatAnchor( RndStdIds::FLY_AT_PAGE ) );
+ }
+ }
+
+ SwDrawContact* pContact = new SwDrawContact( pFormat, &rDrawObj );
+
+ // Create Frames if necessary
+ if( m_rDoc.getIDocumentLayoutAccess().GetCurrentViewShell() )
+ {
+ // create layout representation
+ pFormat->MakeFrames();
+ // #i42319# - follow-up of #i35635#
+ // move object to visible layer
+ // #i79391#
+ if ( pContact->GetAnchorFrame() )
+ {
+ pContact->MoveObjToVisibleLayer( &rDrawObj );
+ }
+ }
+
+ if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ m_rDoc.GetIDocumentUndoRedo().AppendUndo( std::make_unique<SwUndoInsLayFormat>(pFormat, SwNodeOffset(0), 0) );
+ }
+
+ m_rDoc.getIDocumentState().SetModified();
+ return pFormat;
+}
+
+bool DocumentContentOperationsManager::SplitNode( const SwPosition &rPos, bool bChkTableStart )
+{
+ SwContentNode *pNode = rPos.GetNode().GetContentNode();
+ if(nullptr == pNode)
+ return false;
+
+ {
+ // BUG 26675: Send DataChanged before deleting, so that we notice which objects are in scope.
+ // After that they can be before/after the position.
+ SwDataChanged aTmp( m_rDoc, rPos );
+ }
+
+ SwUndoSplitNode* pUndo = nullptr;
+ if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ m_rDoc.GetIDocumentUndoRedo().ClearRedo();
+ // insert the Undo object (currently only for TextNode)
+ if( pNode->IsTextNode() )
+ {
+ pUndo = new SwUndoSplitNode( m_rDoc, rPos, bChkTableStart );
+ m_rDoc.GetIDocumentUndoRedo().AppendUndo(std::unique_ptr<SwUndo>(pUndo));
+ }
+ }
+
+ // Update the rsid of the old and the new node unless
+ // the old node is split at the beginning or at the end
+ SwTextNode *pTextNode = rPos.GetNode().GetTextNode();
+ const sal_Int32 nPos = rPos.GetContentIndex();
+ if( pTextNode && nPos && nPos != pTextNode->Len() )
+ {
+ m_rDoc.UpdateParRsid( pTextNode );
+ }
+
+ //JP 28.01.97: Special case for SplitNode at table start:
+ // If it is at the beginning of a Doc/Fly/Footer/... or right at after a table
+ // then insert a paragraph before it.
+ if( bChkTableStart && !rPos.GetContentIndex() && pNode->IsTextNode() )
+ {
+ SwNodeOffset nPrevPos = rPos.GetNodeIndex() - 1;
+ SwTableNode* pTableNd;
+ const SwNode* pNd = m_rDoc.GetNodes()[ nPrevPos ];
+ if( pNd->IsStartNode() &&
+ SwTableBoxStartNode == static_cast<const SwStartNode*>(pNd)->GetStartNodeType() &&
+ nullptr != ( pTableNd = m_rDoc.GetNodes()[ --nPrevPos ]->GetTableNode() ) &&
+ ((( pNd = m_rDoc.GetNodes()[ --nPrevPos ])->IsStartNode() &&
+ SwTableBoxStartNode != static_cast<const SwStartNode*>(pNd)->GetStartNodeType() )
+ || ( pNd->IsEndNode() && pNd->StartOfSectionNode()->IsTableNode() )
+ || pNd->IsContentNode() ))
+ {
+ if( pNd->IsContentNode() )
+ {
+ //JP 30.04.99 Bug 65660:
+ // There are no page breaks outside of the normal body area,
+ // so this is not a valid condition to insert a paragraph.
+ if( nPrevPos < m_rDoc.GetNodes().GetEndOfExtras().GetIndex() )
+ pNd = nullptr;
+ else
+ {
+ // Only if the table has page breaks!
+ const SwFrameFormat* pFrameFormat = pTableNd->GetTable().GetFrameFormat();
+ if( SfxItemState::SET != pFrameFormat->GetItemState(RES_PAGEDESC, false) &&
+ SfxItemState::SET != pFrameFormat->GetItemState( RES_BREAK, false ) )
+ pNd = nullptr;
+ }
+ }
+
+ if( pNd )
+ {
+ SwTextNode* pTextNd = m_rDoc.GetNodes().MakeTextNode(
+ *pTableNd,
+ m_rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_TEXT ));
+ if( pTextNd )
+ {
+ const_cast<SwPosition&>(rPos).Assign( pTableNd->GetIndex() - SwNodeOffset(1) );
+
+ // only add page breaks/styles to the body area
+ if( nPrevPos > m_rDoc.GetNodes().GetEndOfExtras().GetIndex() )
+ {
+ SwFrameFormat* pFrameFormat = pTableNd->GetTable().GetFrameFormat();
+ const SfxPoolItem *pItem;
+ if( SfxItemState::SET == pFrameFormat->GetItemState( RES_PAGEDESC,
+ false, &pItem ) )
+ {
+ pTextNd->SetAttr( *pItem );
+ pFrameFormat->ResetFormatAttr( RES_PAGEDESC );
+ }
+ if( SfxItemState::SET == pFrameFormat->GetItemState( RES_BREAK,
+ false, &pItem ) )
+ {
+ pTextNd->SetAttr( *pItem );
+ pFrameFormat->ResetFormatAttr( RES_BREAK );
+ }
+ }
+
+ if( pUndo )
+ pUndo->SetTableFlag();
+ m_rDoc.getIDocumentState().SetModified();
+ return true;
+ }
+ }
+ }
+ }
+
+ const std::shared_ptr<sw::mark::ContentIdxStore> pContentStore(sw::mark::ContentIdxStore::Create());
+ pContentStore->Save( m_rDoc, rPos.GetNodeIndex(), rPos.GetContentIndex(), true );
+ assert(pNode->IsTextNode());
+ std::function<void (SwTextNode *, sw::mark::RestoreMode, bool bAtStart)> restoreFunc(
+ [&](SwTextNode *const, sw::mark::RestoreMode const eMode, bool const bAtStart)
+ {
+ if (!pContentStore->Empty())
+ { // move all bookmarks, TOXMarks, FlyAtCnt
+ pContentStore->Restore(m_rDoc, rPos.GetNodeIndex()-SwNodeOffset(1), 0, true, bAtStart && (eMode & sw::mark::RestoreMode::Flys), eMode);
+ }
+ if (eMode & sw::mark::RestoreMode::NonFlys)
+ {
+ // To-Do - add 'SwExtraRedlineTable' also ?
+ if (m_rDoc.getIDocumentRedlineAccess().IsRedlineOn() ||
+ (!m_rDoc.getIDocumentRedlineAccess().IsIgnoreRedline() &&
+ !m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty()))
+ {
+ SwPaM aPam( rPos );
+ aPam.SetMark();
+ aPam.Move( fnMoveBackward );
+ if (m_rDoc.getIDocumentRedlineAccess().IsRedlineOn())
+ {
+ m_rDoc.getIDocumentRedlineAccess().AppendRedline(
+ new SwRangeRedline(RedlineType::Insert, aPam), true);
+ }
+ else
+ {
+ m_rDoc.getIDocumentRedlineAccess().SplitRedline(aPam);
+ }
+ }
+ }
+ });
+ pNode->GetTextNode()->SplitContentNode(rPos, &restoreFunc);
+
+ m_rDoc.getIDocumentState().SetModified();
+ return true;
+}
+
+bool DocumentContentOperationsManager::AppendTextNode( SwPosition& rPos )
+{
+ // create new node before EndOfContent
+ SwTextNode * pCurNode = rPos.GetNode().GetTextNode();
+ if( !pCurNode )
+ {
+ // so then one can be created!
+ SwNodeIndex aIdx( rPos.GetNode(), 1 );
+ pCurNode = m_rDoc.GetNodes().MakeTextNode( aIdx.GetNode(),
+ m_rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_STANDARD ));
+ }
+ else
+ pCurNode = pCurNode->AppendNode( rPos )->GetTextNode();
+
+ rPos.Adjust(SwNodeOffset(1));
+
+ if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ m_rDoc.GetIDocumentUndoRedo().AppendUndo( std::make_unique<SwUndoInsert>( rPos.GetNode() ) );
+ }
+
+ // To-Do - add 'SwExtraRedlineTable' also ?
+ if( m_rDoc.getIDocumentRedlineAccess().IsRedlineOn() || (!m_rDoc.getIDocumentRedlineAccess().IsIgnoreRedline() && !m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty() ))
+ {
+ SwPaM aPam( rPos );
+ aPam.SetMark();
+ aPam.Move( fnMoveBackward );
+ if( m_rDoc.getIDocumentRedlineAccess().IsRedlineOn() )
+ m_rDoc.getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( RedlineType::Insert, aPam ), true);
+ else
+ m_rDoc.getIDocumentRedlineAccess().SplitRedline( aPam );
+ }
+
+ m_rDoc.getIDocumentState().SetModified();
+ return true;
+}
+
+bool DocumentContentOperationsManager::ReplaceRange( SwPaM& rPam, const OUString& rStr,
+ const bool bRegExReplace )
+{
+ // unfortunately replace works slightly differently from delete,
+ // so we cannot use lcl_DoWithBreaks here...
+
+ std::vector<std::pair<SwNodeOffset, sal_Int32>> Breaks;
+
+ SwPaM aPam( *rPam.GetMark(), *rPam.GetPoint() );
+ aPam.Normalize(false);
+ if (aPam.GetPoint()->GetNode() != aPam.GetMark()->GetNode())
+ {
+ aPam.Move(fnMoveBackward);
+ }
+ OSL_ENSURE((aPam.GetPoint()->GetNode() == aPam.GetMark()->GetNode()), "invalid pam?");
+
+ sw::CalcBreaks(Breaks, aPam);
+
+ while (!Breaks.empty() // skip over prefix of dummy chars
+ && (aPam.GetMark()->GetNodeIndex() == Breaks.begin()->first)
+ && (aPam.GetMark()->GetContentIndex() == Breaks.begin()->second))
+ {
+ // skip!
+ aPam.GetMark()->AdjustContent(+1); // always in bounds if Breaks valid
+ Breaks.erase(Breaks.begin());
+ }
+ *rPam.Start() = *aPam.GetMark(); // update start of original pam w/ prefix
+
+ if (Breaks.empty())
+ {
+ // park aPam somewhere so it does not point to node that is deleted
+ aPam.DeleteMark();
+ aPam.GetPoint()->Assign(m_rDoc.GetNodes().GetEndOfContent());
+ return ReplaceRangeImpl(rPam, rStr, bRegExReplace); // original pam!
+ }
+
+ // Deletion must be split into several parts if the text node
+ // contains a text attribute with end and with dummy character
+ // and the selection does not contain the text attribute completely,
+ // but overlaps its start (left), where the dummy character is.
+
+ bool bRet( true );
+ // iterate from end to start, to avoid invalidating the offsets!
+ auto iter( Breaks.rbegin() );
+ SwNodeOffset nOffset(0);
+ SwNodes const& rNodes(rPam.GetPoint()->GetNodes());
+ OSL_ENSURE(aPam.GetPoint() == aPam.End(), "wrong!");
+ SwPosition & rEnd( *aPam.End() );
+ SwPosition & rStart( *aPam.Start() );
+
+ // set end of temp pam to original end (undo Move backward above)
+ rEnd = *rPam.End();
+ // after first deletion, rEnd will point into the original text node again!
+
+ while (iter != Breaks.rend())
+ {
+ rStart.Assign(*rNodes[iter->first - nOffset]->GetTextNode(), iter->second + 1);
+ if (rStart < rEnd) // check if part is empty
+ {
+ bRet &= (m_rDoc.getIDocumentRedlineAccess().IsRedlineOn())
+ ? DeleteAndJoinWithRedlineImpl(aPam, SwDeleteFlags::Default)
+ : DeleteAndJoinImpl(aPam, SwDeleteFlags::Default);
+ nOffset = iter->first - rStart.GetNodeIndex(); // deleted fly nodes...
+ }
+ rEnd.Assign(*rNodes[iter->first - nOffset]->GetTextNode(), iter->second);
+ ++iter;
+ }
+
+ rStart = *rPam.Start(); // set to original start
+ assert(rStart < rEnd && "replace part empty!");
+ if (rStart < rEnd) // check if part is empty
+ {
+ bRet &= ReplaceRangeImpl(aPam, rStr, bRegExReplace);
+ }
+
+ rPam = aPam; // update original pam (is this required?)
+
+ return bRet;
+}
+
+bool DocumentContentOperationsManager::InsertPoolItem(
+ const SwPaM &rRg,
+ const SfxPoolItem &rHt,
+ const SetAttrMode nFlags,
+ SwRootFrame const*const pLayout,
+ SwTextAttr **ppNewTextAttr)
+{
+ SwDataChanged aTmp( rRg );
+ std::unique_ptr<SwUndoAttr> pUndoAttr;
+ if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ m_rDoc.GetIDocumentUndoRedo().ClearRedo();
+ pUndoAttr.reset(new SwUndoAttr( rRg, rHt, nFlags ));
+ }
+
+ SfxItemSet aSet( m_rDoc.GetAttrPool(), rHt.Which(), rHt.Which() );
+ aSet.Put( rHt );
+ const bool bRet = lcl_InsAttr(m_rDoc, rRg, aSet, nFlags, pUndoAttr.get(), pLayout, ppNewTextAttr);
+
+ if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ m_rDoc.GetIDocumentUndoRedo().AppendUndo( std::move(pUndoAttr) );
+ }
+
+ if( bRet )
+ {
+ m_rDoc.getIDocumentState().SetModified();
+ }
+ return bRet;
+}
+
+void DocumentContentOperationsManager::InsertItemSet ( const SwPaM &rRg, const SfxItemSet &rSet,
+ const SetAttrMode nFlags, SwRootFrame const*const pLayout)
+{
+ SwDataChanged aTmp( rRg );
+ std::unique_ptr<SwUndoAttr> pUndoAttr;
+ if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ m_rDoc.GetIDocumentUndoRedo().ClearRedo();
+ pUndoAttr.reset(new SwUndoAttr( rRg, rSet, nFlags ));
+ }
+
+ bool bRet = lcl_InsAttr(m_rDoc, rRg, rSet, nFlags, pUndoAttr.get(), pLayout, /*ppNewTextAttr*/nullptr );
+
+ if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ m_rDoc.GetIDocumentUndoRedo().AppendUndo( std::move(pUndoAttr) );
+ }
+
+ if( bRet )
+ m_rDoc.getIDocumentState().SetModified();
+}
+
+void DocumentContentOperationsManager::RemoveLeadingWhiteSpace(const SwPosition & rPos )
+{
+ const SwTextNode* pTNd = rPos.GetNode().GetTextNode();
+ if ( !pTNd )
+ return;
+
+ const OUString& rText = pTNd->GetText();
+ sal_Int32 nIdx = 0;
+ while (nIdx < rText.getLength())
+ {
+ sal_Unicode const cCh = rText[nIdx];
+ if (('\t' != cCh) && (' ' != cCh))
+ {
+ break;
+ }
+ ++nIdx;
+ }
+
+ if ( nIdx > 0 )
+ {
+ SwPaM aPam(rPos);
+ aPam.GetPoint()->SetContent(0);
+ aPam.SetMark();
+ aPam.GetMark()->SetContent(nIdx);
+ DeleteRange( aPam );
+ }
+}
+
+void DocumentContentOperationsManager::RemoveLeadingWhiteSpace(SwPaM& rPaM )
+{
+ for (SwPaM& rSel :rPaM.GetRingContainer())
+ {
+ SwNodeOffset nStt = rSel.Start()->GetNodeIndex();
+ SwNodeOffset nEnd = rSel.End()->GetNodeIndex();
+ for (SwNodeOffset nPos = nStt; nPos<=nEnd; nPos++)
+ RemoveLeadingWhiteSpace(SwPosition(rSel.GetBound().GetNodes(), nPos));
+ }
+}
+
+// Copy method from SwDoc - "copy Flys in Flys"
+/// note: rRg/rInsPos *exclude* a partially selected start text node;
+/// pCopiedPaM *includes* a partially selected start text node
+void DocumentContentOperationsManager::CopyWithFlyInFly(
+ const SwNodeRange& rRg,
+ SwNode& rInsPos,
+ const std::pair<const SwPaM&, const SwPosition&>* pCopiedPaM /*and real insert pos*/,
+ const bool bMakeNewFrames,
+ const bool bDelRedlines,
+ const bool bCopyFlyAtFly,
+ SwCopyFlags const flags) const
+{
+ assert(!pCopiedPaM || pCopiedPaM->first.End()->GetNode() == rRg.aEnd.GetNode());
+ assert(!pCopiedPaM || pCopiedPaM->second.GetNode() <= rInsPos);
+
+ SwDoc& rDest = rInsPos.GetDoc();
+ SwNodeIndex aSavePos( rInsPos );
+
+ if (rRg.aStart != rRg.aEnd)
+ {
+ bool bEndIsEqualEndPos = rInsPos == rRg.aEnd.GetNode();
+ --aSavePos;
+ SaveRedlEndPosForRestore aRedlRest( rInsPos, 0 );
+
+ // insert behind the already copied start node
+ m_rDoc.GetNodes().CopyNodes( rRg, rInsPos, false, true );
+ aRedlRest.Restore();
+
+ if (bEndIsEqualEndPos)
+ {
+ const_cast<SwNodeIndex&>(rRg.aEnd).Assign(aSavePos.GetNode(), +1);
+ }
+ }
+
+ // Also copy all bookmarks
+ // guess this must be done before the DelDummyNodes below as that
+ // deletes nodes so would mess up the index arithmetic
+ // sw_fieldmarkhide: also needs to be done before making frames
+ if (m_rDoc.getIDocumentMarkAccess()->getAllMarksCount())
+ {
+ SwPaM aRgTmp( rRg.aStart, rRg.aEnd );
+ SwPosition targetPos(aSavePos, SwNodeOffset(rRg.aStart != rRg.aEnd ? +1 : 0));
+ if (pCopiedPaM && rRg.aStart != pCopiedPaM->first.Start()->GetNode())
+ {
+ // there is 1 (partially selected, maybe) paragraph before
+ assert(SwNodeIndex(rRg.aStart, -1) == pCopiedPaM->first.Start()->GetNode());
+ // only use the passed in target SwPosition if the source PaM point
+ // is on a different node; if it was the same node then the target
+ // position was likely moved along by the copy operation and now
+ // points to the end of the range!
+ targetPos = pCopiedPaM->second;
+ }
+
+ sw::CopyBookmarks(pCopiedPaM ? pCopiedPaM->first : aRgTmp, targetPos, flags);
+ }
+
+ if (rRg.aStart != rRg.aEnd)
+ {
+ bool isRecreateEndNode(false);
+ if (bMakeNewFrames) // tdf#130685 only after aRedlRest
+ { // recreate from previous node (could be merged now)
+ o3tl::sorted_vector<SwTextFrame*> frames;
+ SwTextNode * pNode = aSavePos.GetNode().GetTextNode();
+ SwTextNode *const pEndNode = rInsPos.GetTextNode();
+ if (pEndNode)
+ {
+ SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(*pEndNode);
+ for (SwTextFrame* pFrame = aIter.First(); pFrame; pFrame = aIter.Next())
+ {
+ if (pFrame->getRootFrame()->HasMergedParas())
+ {
+ frames.insert(pFrame);
+ // tdf#135061 check if end node is merged to a preceding node
+ if (pNode == nullptr && pFrame->GetMergedPara()
+ && pFrame->GetMergedPara()->pFirstNode->GetIndex() < aSavePos.GetIndex())
+ {
+ pNode = pFrame->GetMergedPara()->pFirstNode;
+ }
+ }
+ }
+ }
+ if (pNode != nullptr)
+ {
+ sw::RecreateStartTextFrames(*pNode);
+ if (!frames.empty())
+ { // tdf#132187 check if the end node needs new frames
+ SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(*pEndNode);
+ for (SwTextFrame* pFrame = aIter.First(); pFrame; pFrame = aIter.Next())
+ {
+ if (pFrame->getRootFrame()->HasMergedParas())
+ {
+ auto const it = frames.find(pFrame);
+ if (it != frames.end())
+ {
+ frames.erase(it);
+ }
+ }
+ }
+ if (!frames.empty()) // existing frame was deleted
+ { // all layouts because MakeFrames recreates all layouts
+ pEndNode->DelFrames(nullptr);
+ isRecreateEndNode = true;
+ }
+ }
+ }
+ }
+ bool const isAtStartOfSection(aSavePos.GetNode().IsStartNode());
+ ++aSavePos;
+ if (bMakeNewFrames)
+ {
+ // it's possible that CheckParaRedlineMerge() deleted frames
+ // on rInsPos so have to include it, but it must not be included
+ // if it was the first node in the document so that MakeFrames()
+ // will find the existing (wasn't deleted) frame on it
+ SwNodeIndex const end(rInsPos,
+ SwNodeOffset((!isRecreateEndNode || isAtStartOfSection)
+ ? 0 : +1));
+ ::MakeFrames(&rDest, aSavePos.GetNode(), end.GetNode());
+ }
+ }
+
+#if OSL_DEBUG_LEVEL > 0
+ {
+ //JP 17.06.99: Bug 66973 - check count only if the selection is in
+ // the same section or there's no section, because sections that are
+ // not fully selected are not copied.
+ const SwSectionNode* pSSectNd = rRg.aStart.GetNode().FindSectionNode();
+ SwNodeIndex aTmpI( rRg.aEnd, -1 );
+ const SwSectionNode* pESectNd = aTmpI.GetNode().FindSectionNode();
+ if( pSSectNd == pESectNd &&
+ !rRg.aStart.GetNode().IsSectionNode() &&
+ !aTmpI.GetNode().IsEndNode() )
+ {
+ // If the range starts with a SwStartNode, it isn't copied
+ SwNodeOffset offset( (rRg.aStart.GetNode().GetNodeType() != SwNodeType::Start) ? 1 : 0 );
+ OSL_ENSURE( rInsPos.GetIndex() - aSavePos.GetIndex() ==
+ rRg.aEnd.GetIndex() - rRg.aStart.GetIndex() - 1 + offset,
+ "An insufficient number of nodes were copied!" );
+ }
+ }
+#endif
+
+ {
+ ::sw::UndoGuard const undoGuard(rDest.GetIDocumentUndoRedo());
+ CopyFlyInFlyImpl(rRg, pCopiedPaM ? &pCopiedPaM->first : nullptr,
+ // see comment below regarding use of pCopiedPaM->second
+ (pCopiedPaM && rRg.aStart != pCopiedPaM->first.Start()->GetNode())
+ ? pCopiedPaM->second.GetNode()
+ : aSavePos.GetNode(),
+ bCopyFlyAtFly,
+ flags);
+ }
+
+ SwNodeRange aCpyRange( aSavePos.GetNode(), rInsPos );
+
+ if( bDelRedlines && ( RedlineFlags::DeleteRedlines & rDest.getIDocumentRedlineAccess().GetRedlineFlags() ))
+ lcl_DeleteRedlines( rRg, aCpyRange );
+
+ rDest.GetNodes().DelDummyNodes( aCpyRange );
+}
+
+// note: for the redline Show/Hide this must be in sync with
+// SwRangeRedline::CopyToSection()/DelCopyOfSection()/MoveFromSection()
+void DocumentContentOperationsManager::CopyFlyInFlyImpl(
+ const SwNodeRange& rRg,
+ SwPaM const*const pCopiedPaM,
+ SwNode& rStartIdx,
+ const bool bCopyFlyAtFly,
+ SwCopyFlags const flags) const
+{
+ assert(!pCopiedPaM || pCopiedPaM->End()->GetNode() == rRg.aEnd.GetNode());
+
+ // First collect all Flys, sort them according to their ordering number,
+ // and then only copy them. This maintains the ordering numbers (which are only
+ // managed in the DrawModel).
+ SwDoc& rDest = rStartIdx.GetDoc();
+ std::set< ZSortFly > aSet;
+ const size_t nArrLen = m_rDoc.GetSpzFrameFormats()->size();
+
+ SwTextBoxHelper::SavedLink aOldTextBoxes;
+ SwTextBoxHelper::saveLinks(*m_rDoc.GetSpzFrameFormats(), aOldTextBoxes);
+
+ for ( size_t n = 0; n < nArrLen; ++n )
+ {
+ SwFrameFormat* pFormat = (*m_rDoc.GetSpzFrameFormats())[n];
+ SwFormatAnchor const*const pAnchor = &pFormat->GetAnchor();
+ SwNode const*const pAnchorNode = pAnchor->GetAnchorNode();
+ if ( !pAnchorNode )
+ continue;
+ bool bAdd = false;
+ SwNodeOffset nSkipAfter = pAnchorNode->GetIndex();
+ SwNodeOffset nStart = rRg.aStart.GetIndex();
+ switch ( pAnchor->GetAnchorId() )
+ {
+ case RndStdIds::FLY_AT_FLY:
+ if(bCopyFlyAtFly)
+ ++nSkipAfter;
+ else if(m_rDoc.getIDocumentRedlineAccess().IsRedlineMove())
+ ++nStart;
+ break;
+ case RndStdIds::FLY_AT_PARA:
+ {
+ bAdd = IsSelectFrameAnchoredAtPara(*pAnchor->GetContentAnchor(),
+ pCopiedPaM ? *pCopiedPaM->Start() : SwPosition(rRg.aStart),
+ pCopiedPaM ? *pCopiedPaM->End() : SwPosition(rRg.aEnd),
+ (flags & SwCopyFlags::IsMoveToFly)
+ ? DelContentType::AllMask|DelContentType::WriterfilterHack
+ : DelContentType::AllMask);
+ }
+ break;
+ case RndStdIds::FLY_AT_CHAR:
+ {
+ bAdd = IsDestroyFrameAnchoredAtChar(*pAnchor->GetContentAnchor(),
+ pCopiedPaM ? *pCopiedPaM->Start() : SwPosition(rRg.aStart),
+ pCopiedPaM ? *pCopiedPaM->End() : SwPosition(rRg.aEnd),
+ (flags & SwCopyFlags::IsMoveToFly)
+ ? DelContentType::AllMask|DelContentType::WriterfilterHack
+ : DelContentType::AllMask);
+ }
+ break;
+ default:
+ continue;
+ }
+ if (RndStdIds::FLY_AT_FLY == pAnchor->GetAnchorId())
+ {
+ if (nStart > nSkipAfter)
+ continue;
+ if (*pAnchorNode > rRg.aEnd.GetNode())
+ continue;
+ //frames at the last source node are not always copied:
+ //- if the node is empty and is the last node of the document or a table cell
+ // or a text frame then they have to be copied
+ //- if the content index in this node is > 0 then paragraph and frame bound objects are copied
+ //- to-character bound objects are copied if their index is <= nEndContentIndex
+ if (*pAnchorNode < rRg.aEnd.GetNode())
+ bAdd = true;
+ if (!bAdd && !m_rDoc.getIDocumentRedlineAccess().IsRedlineMove()) // fdo#40599: not for redline move
+ {
+ if (!bAdd)
+ {
+ // technically old code checked nContent of AT_FLY which is pointless
+ bAdd = pCopiedPaM && 0 < pCopiedPaM->End()->GetContentIndex();
+ }
+ }
+ }
+ if( bAdd )
+ {
+ aSet.insert( ZSortFly( pFormat, pAnchor, nArrLen + aSet.size() ));
+ }
+ }
+
+ // Store all copied (and also the newly created) frames in another array.
+ // They are stored as matching the originals, so that we will be later
+ // able to build the chains accordingly.
+ std::vector< SwFrameFormat* > aVecSwFrameFormat;
+ std::set< ZSortFly >::const_iterator it=aSet.begin();
+
+ while (it != aSet.end())
+ {
+ // #i59964#
+ // correct determination of new anchor position
+ SwFormatAnchor aAnchor( *(*it).GetAnchor() );
+ assert( aAnchor.GetContentAnchor() != nullptr );
+ SwPosition newPos = *aAnchor.GetContentAnchor();
+ // for at-paragraph and at-character anchored objects the new anchor
+ // position can *not* be determined by the difference of the current
+ // anchor position to the start of the copied range, because not
+ // complete selected sections in the copied range aren't copied - see
+ // method <SwNodes::CopyNodes(..)>.
+ // Thus, the new anchor position in the destination document is found
+ // by counting the text nodes.
+ if ((aAnchor.GetAnchorId() == RndStdIds::FLY_AT_PARA) ||
+ (aAnchor.GetAnchorId() == RndStdIds::FLY_AT_CHAR) )
+ {
+ // First, determine number of anchor text node in the copied range.
+ // Note: The anchor text node *have* to be inside the copied range.
+ sal_uLong nAnchorTextNdNumInRange( 0 );
+ bool bAnchorTextNdFound( false );
+ // start at the first node for which flys are copied
+ SwNodeIndex aIdx(pCopiedPaM ? pCopiedPaM->Start()->GetNode() : rRg.aStart.GetNode());
+ while ( !bAnchorTextNdFound && aIdx <= rRg.aEnd )
+ {
+ if ( aIdx.GetNode().IsTextNode() )
+ {
+ ++nAnchorTextNdNumInRange;
+ bAnchorTextNdFound = *aAnchor.GetAnchorNode() == aIdx.GetNode();
+ }
+
+ ++aIdx;
+ }
+
+ if ( !bAnchorTextNdFound )
+ {
+ // This case can *not* happen, but to be robust take the first
+ // text node in the destination document.
+ OSL_FAIL( "<SwDoc::_CopyFlyInFly(..)> - anchor text node in copied range not found" );
+ nAnchorTextNdNumInRange = 1;
+ }
+ // Second, search corresponding text node in destination document
+ // by counting forward from start insert position <rStartIdx> the
+ // determined number of text nodes.
+ aIdx = rStartIdx;
+ SwNodeIndex aAnchorNdIdx( rStartIdx );
+ const SwNode& aEndOfContentNd =
+ aIdx.GetNode().GetNodes().GetEndOfContent();
+ while ( nAnchorTextNdNumInRange > 0 &&
+ aIdx.GetNode() != aEndOfContentNd )
+ {
+ if ( aIdx.GetNode().IsTextNode() )
+ {
+ --nAnchorTextNdNumInRange;
+ aAnchorNdIdx = aIdx;
+ }
+
+ ++aIdx;
+ }
+ if ( !aAnchorNdIdx.GetNode().IsTextNode() )
+ {
+ // This case can *not* happen, but to be robust take the first
+ // text node in the destination document.
+ OSL_FAIL( "<SwDoc::_CopyFlyInFly(..)> - found anchor node index isn't a text node" );
+ aAnchorNdIdx = rStartIdx;
+ while ( !aAnchorNdIdx.GetNode().IsTextNode() )
+ {
+ ++aAnchorNdIdx;
+ }
+ }
+ // apply found anchor text node as new anchor position
+ newPos.Assign( aAnchorNdIdx );
+ }
+ else
+ {
+ SwNodeOffset nOffset = newPos.GetNodeIndex() - rRg.aStart.GetIndex();
+ newPos.Assign( rStartIdx, nOffset );
+ }
+ // Set the character bound Flys back at the original character
+ if ((RndStdIds::FLY_AT_CHAR == aAnchor.GetAnchorId()) &&
+ newPos.GetNode().IsTextNode() )
+ {
+ // only if pCopiedPaM: care about partially selected start node
+ sal_Int32 const nContent = pCopiedPaM && pCopiedPaM->Start()->GetNode() == *aAnchor.GetAnchorNode()
+ ? newPos.GetContentIndex() - pCopiedPaM->Start()->GetContentIndex()
+ : newPos.GetContentIndex();
+ newPos.SetContent(nContent);
+ }
+ aAnchor.SetAnchor( &newPos );
+
+ // Check recursion: if copying content inside the same frame, then don't copy the format.
+ if( &rDest == &m_rDoc )
+ {
+ const SwFormatContent& rContent = (*it).GetFormat()->GetContent();
+ const SwStartNode* pSNd;
+ if( rContent.GetContentIdx() &&
+ nullptr != ( pSNd = rContent.GetContentIdx()->GetNode().GetStartNode() ) &&
+ pSNd->GetIndex() < rStartIdx.GetIndex() &&
+ rStartIdx.GetIndex() < pSNd->EndOfSectionIndex() )
+ {
+ it = aSet.erase(it);
+ continue;
+ }
+ }
+
+ // Ignore TextBoxes, they are already handled in
+ // sw::DocumentLayoutManager::CopyLayoutFormat().
+ if (SwTextBoxHelper::isTextBox(it->GetFormat(), RES_FLYFRMFMT))
+ {
+ it = aSet.erase(it);
+ continue;
+ }
+
+ // Copy the format and set the new anchor
+ aVecSwFrameFormat.push_back( rDest.getIDocumentLayoutAccess().CopyLayoutFormat( *(*it).GetFormat(),
+ aAnchor, false, true ) );
+ ++it;
+ }
+
+ // Rebuild as much as possible of all chains that are available in the original,
+ OSL_ENSURE( aSet.size() == aVecSwFrameFormat.size(), "Missing new Flys" );
+ if ( aSet.size() != aVecSwFrameFormat.size() )
+ return;
+
+ size_t n = 0;
+ for (const auto& rFlyN : aSet)
+ {
+ const SwFrameFormat *pFormatN = rFlyN.GetFormat();
+ const SwFormatChain &rChain = pFormatN->GetChain();
+ size_t k = 0;
+ for (const auto& rFlyK : aSet)
+ {
+ const SwFrameFormat *pFormatK = rFlyK.GetFormat();
+ if ( rChain.GetPrev() == pFormatK )
+ {
+ ::lcl_ChainFormats( static_cast< SwFlyFrameFormat* >(aVecSwFrameFormat[k]),
+ static_cast< SwFlyFrameFormat* >(aVecSwFrameFormat[n]) );
+ }
+ else if ( rChain.GetNext() == pFormatK )
+ {
+ ::lcl_ChainFormats( static_cast< SwFlyFrameFormat* >(aVecSwFrameFormat[n]),
+ static_cast< SwFlyFrameFormat* >(aVecSwFrameFormat[k]) );
+ }
+ ++k;
+ }
+ ++n;
+ }
+
+ // Re-create content property of draw formats, knowing how old shapes
+ // were paired with old fly formats (aOldTextBoxes) and that aSet is
+ // parallel with aVecSwFrameFormat.
+ SwTextBoxHelper::restoreLinks(aSet, aVecSwFrameFormat, aOldTextBoxes);
+}
+
+/*
+ * Reset the text's hard formatting
+ */
+/** @params pArgs contains the document's ChrFormatTable
+ * Is need for selections at the beginning/end and with no SSelection.
+ */
+bool DocumentContentOperationsManager::lcl_RstTextAttr( SwNode* pNd, void* pArgs )
+{
+ ParaRstFormat* pPara = static_cast<ParaRstFormat*>(pArgs);
+ if (pPara->pLayout && pPara->pLayout->HasMergedParas()
+ && pNd->GetRedlineMergeFlag() == SwNode::Merge::Hidden)
+ {
+ return true; // skip hidden, since new items aren't applied
+ }
+ SwTextNode * pTextNode = pNd->GetTextNode();
+ if( pTextNode && pTextNode->GetpSwpHints() )
+ {
+ SwContentIndex aSt( pTextNode, 0 );
+ sal_Int32 nEnd = pTextNode->Len();
+
+ if( &pPara->pSttNd->GetNode() == pTextNode &&
+ pPara->pSttNd->GetContentIndex() )
+ aSt = pPara->pSttNd->GetContentIndex();
+
+ if( &pPara->pEndNd->GetNode() == pNd )
+ nEnd = pPara->pEndNd->GetContentIndex();
+
+ if( pPara->pHistory )
+ {
+ // Save all attributes for the Undo.
+ SwRegHistory aRHst( *pTextNode, pPara->pHistory );
+ pTextNode->GetpSwpHints()->Register( &aRHst );
+ pTextNode->RstTextAttr( aSt.GetIndex(), nEnd - aSt.GetIndex(), pPara->nWhich,
+ pPara->pDelSet, pPara->bInclRefToxMark, pPara->bExactRange );
+ if( pTextNode->GetpSwpHints() )
+ pTextNode->GetpSwpHints()->DeRegister();
+ }
+ else
+ pTextNode->RstTextAttr( aSt.GetIndex(), nEnd - aSt.GetIndex(), pPara->nWhich,
+ pPara->pDelSet, pPara->bInclRefToxMark, pPara->bExactRange );
+ }
+ return true;
+}
+
+DocumentContentOperationsManager::~DocumentContentOperationsManager()
+{
+}
+//Private methods
+
+bool DocumentContentOperationsManager::DeleteAndJoinWithRedlineImpl(SwPaM & rPam, SwDeleteFlags const flags)
+{
+ assert(m_rDoc.getIDocumentRedlineAccess().IsRedlineOn());
+
+ RedlineFlags eOld = m_rDoc.getIDocumentRedlineAccess().GetRedlineFlags();
+
+ if (*rPam.GetPoint() == *rPam.GetMark())
+ {
+ return false; // do not add empty redlines
+ }
+
+ std::vector<std::unique_ptr<SwRangeRedline>> redlines;
+ {
+ auto pRedline(std::make_unique<SwRangeRedline>(RedlineType::Delete, rPam));
+ if (pRedline->HasValidRange())
+ {
+ redlines.push_back(std::move(pRedline));
+ }
+ else // sigh ... why is such a selection even possible...
+ { // split it up so we get one SwUndoRedlineDelete per inserted RL
+ redlines = GetAllValidRanges(std::move(pRedline));
+ }
+ }
+
+ if (redlines.empty())
+ {
+ return false;
+ }
+
+ // tdf#54819 current redlining needs also modification of paragraph style and
+ // attributes added to the same grouped Undo
+ if (m_rDoc.GetIDocumentUndoRedo().DoesGroupUndo())
+ m_rDoc.GetIDocumentUndoRedo().StartUndo(SwUndoId::EMPTY, nullptr);
+
+ auto & rDMA(*m_rDoc.getIDocumentMarkAccess());
+ std::vector<std::unique_ptr<SwUndo>> MarkUndos;
+ for (auto iter = rDMA.getAnnotationMarksBegin();
+ iter != rDMA.getAnnotationMarksEnd(); )
+ {
+ // tdf#111524 remove annotation marks that have their field
+ // characters deleted
+ SwPosition const& rEndPos((**iter).GetMarkEnd());
+ if (*rPam.Start() < rEndPos && rEndPos <= *rPam.End())
+ {
+ if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ MarkUndos.emplace_back(std::make_unique<SwUndoDeleteBookmark>(**iter));
+ }
+ // iter is into annotation mark vector so must be dereferenced!
+ rDMA.deleteMark(&**iter);
+ // this invalidates iter, have to start over...
+ iter = rDMA.getAnnotationMarksBegin();
+ }
+ else
+ { // marks are sorted by start
+ if (*rPam.End() < (**iter).GetMarkStart())
+ {
+ break;
+ }
+ ++iter;
+ }
+ }
+
+ // tdf#119019 accept tracked paragraph formatting to do not hide new deletions
+ if (*rPam.GetPoint() != *rPam.GetMark())
+ m_rDoc.getIDocumentRedlineAccess().AcceptRedlineParagraphFormatting(rPam);
+
+ std::vector<std::unique_ptr<SwUndoRedlineDelete>> undos;
+ if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ // this should no longer happen in calls from the UI but maybe via API
+ // (randomTest and testTdf54819 triggers it)
+ SAL_WARN_IF((eOld & RedlineFlags::ShowMask) != RedlineFlags::ShowMask,
+ "sw.core", "redlines will be moved in DeleteAndJoin");
+ m_rDoc.getIDocumentRedlineAccess().SetRedlineFlags(
+ RedlineFlags::On | RedlineFlags::ShowInsert | RedlineFlags::ShowDelete);
+
+ for (std::unique_ptr<SwRangeRedline> & pRedline : redlines)
+ {
+ assert(pRedline->HasValidRange());
+ undos.emplace_back(std::make_unique<SwUndoRedlineDelete>(
+ *pRedline, SwUndoId::DELETE, flags));
+ }
+ const SwRewriter aRewriter = undos.front()->GetRewriter();
+ // can only group a single undo action
+ if (MarkUndos.empty() && undos.size() == 1
+ && m_rDoc.GetIDocumentUndoRedo().DoesGroupUndo())
+ {
+ SwUndo * const pLastUndo( m_rDoc.GetUndoManager().GetLastUndo() );
+ SwUndoRedlineDelete *const pUndoRedlineDel(dynamic_cast<SwUndoRedlineDelete*>(pLastUndo));
+ bool const bMerged = pUndoRedlineDel
+ && pUndoRedlineDel->CanGrouping(*undos.front());
+ if (!bMerged)
+ {
+ m_rDoc.GetIDocumentUndoRedo().AppendUndo(std::move(undos.front()));
+ }
+ undos.clear(); // prevent unmatched EndUndo
+ }
+ else
+ {
+ m_rDoc.GetIDocumentUndoRedo().StartUndo(SwUndoId::DELETE, &aRewriter);
+ for (auto& it : MarkUndos)
+ {
+ m_rDoc.GetIDocumentUndoRedo().AppendUndo(std::move(it));
+ }
+ for (auto & it : undos)
+ {
+ m_rDoc.GetIDocumentUndoRedo().AppendUndo(std::move(it));
+ }
+ }
+ }
+
+ for (std::unique_ptr<SwRangeRedline> & pRedline : redlines)
+ {
+ // note: 1. the pRedline can still be merged & deleted
+ // 2. the impl. can even DeleteAndJoin the range => no plain PaM
+ std::shared_ptr<SwUnoCursor> const pCursor(m_rDoc.CreateUnoCursor(*pRedline->GetMark()));
+ pCursor->SetMark();
+ *pCursor->GetPoint() = *pRedline->GetPoint();
+ m_rDoc.getIDocumentRedlineAccess().AppendRedline(pRedline.release(), true);
+ // sw_redlinehide: 2 reasons why this is needed:
+ // 1. it's the first redline in node => RedlineDelText was sent but ignored
+ // 2. redline spans multiple nodes => must merge text frames
+ sw::UpdateFramesForAddDeleteRedline(m_rDoc, *pCursor);
+ }
+ m_rDoc.getIDocumentState().SetModified();
+
+ if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ if (!undos.empty())
+ {
+ m_rDoc.GetIDocumentUndoRedo().EndUndo(SwUndoId::EMPTY, nullptr);
+ }
+ m_rDoc.getIDocumentRedlineAccess().SetRedlineFlags( eOld );
+ }
+
+ if (m_rDoc.GetIDocumentUndoRedo().DoesGroupUndo())
+ m_rDoc.GetIDocumentUndoRedo().EndUndo(SwUndoId::EMPTY, nullptr);
+
+ return true;
+}
+
+bool DocumentContentOperationsManager::DeleteAndJoinImpl(SwPaM & rPam, SwDeleteFlags const flags)
+{
+ bool bJoinText, bJoinPrev;
+ ::sw_GetJoinFlags( rPam, bJoinText, bJoinPrev );
+
+ bool const bSuccess( DeleteRangeImpl(rPam, flags) );
+ if (!bSuccess)
+ return false;
+
+ if( bJoinText )
+ {
+ ::sw_JoinText( rPam, bJoinPrev );
+ }
+
+ if (!m_rDoc.getIDocumentRedlineAccess().IsIgnoreRedline()
+ && !m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty())
+ {
+ m_rDoc.getIDocumentRedlineAccess().CompressRedlines();
+ }
+
+ return true;
+}
+
+bool DocumentContentOperationsManager::DeleteRangeImpl(SwPaM & rPam, SwDeleteFlags const flags)
+{
+ // Move all cursors out of the deleted range, but first copy the
+ // passed PaM, because it could be a cursor that would be moved!
+ SwPaM aDelPam( *rPam.GetMark(), *rPam.GetPoint() );
+ {
+ SwPosition const pos(GetCorrPosition(aDelPam));
+ ::PaMCorrAbs(aDelPam, pos);
+ }
+
+ bool const bSuccess( DeleteRangeImplImpl(aDelPam, flags) );
+ if (bSuccess)
+ { // now copy position from temp copy to given PaM
+ *rPam.GetPoint() = *aDelPam.GetPoint();
+ }
+
+ return bSuccess;
+}
+
+bool DocumentContentOperationsManager::DeleteRangeImplImpl(SwPaM & rPam, SwDeleteFlags const flags)
+{
+ auto [pStt, pEnd] = rPam.StartEnd(); // SwPosition*
+
+ if (!rPam.HasMark()
+ || (*pStt == *pEnd && !IsFlySelectedByCursor(m_rDoc, *pStt, *pEnd)))
+ {
+ return false;
+ }
+
+ if( m_rDoc.GetAutoCorrExceptWord() )
+ {
+ // if necessary the saved Word for the exception
+ if( m_rDoc.GetAutoCorrExceptWord()->IsDeleted() || pStt->GetNode() != pEnd->GetNode() ||
+ pStt->GetContentIndex() + 1 != pEnd->GetContentIndex() ||
+ !m_rDoc.GetAutoCorrExceptWord()->CheckDelChar( *pStt ))
+ { m_rDoc.DeleteAutoCorrExceptWord(); }
+ }
+
+ {
+ // Delete all empty TextHints at the Mark's position
+ SwTextNode* pTextNd = rPam.GetMark()->GetNode().GetTextNode();
+ SwpHints* pHts;
+ if( pTextNd && nullptr != ( pHts = pTextNd->GetpSwpHints()) && pHts->Count() )
+ {
+ const sal_Int32 nMkCntPos = rPam.GetMark()->GetContentIndex();
+ for( size_t n = pHts->Count(); n; )
+ {
+ const SwTextAttr* pAttr = pHts->Get( --n );
+ if( nMkCntPos > pAttr->GetStart() )
+ break;
+
+ const sal_Int32 *pEndIdx;
+ if( nMkCntPos == pAttr->GetStart() &&
+ nullptr != (pEndIdx = pAttr->End()) &&
+ *pEndIdx == pAttr->GetStart() )
+ pTextNd->DestroyAttr( pHts->Cut( n ) );
+ }
+ }
+ }
+
+ {
+ // Send DataChanged before deletion, so that we still know
+ // which objects are in the range.
+ // Afterwards they could be before/after the Position.
+ SwDataChanged aTmp( rPam );
+ }
+
+ if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ m_rDoc.GetIDocumentUndoRedo().ClearRedo();
+ bool bMerged(false);
+ if (m_rDoc.GetIDocumentUndoRedo().DoesGroupUndo())
+ {
+ SwUndo *const pLastUndo( m_rDoc.GetUndoManager().GetLastUndo() );
+ SwUndoDelete *const pUndoDelete(
+ dynamic_cast<SwUndoDelete *>(pLastUndo) );
+ if (pUndoDelete)
+ {
+ bMerged = pUndoDelete->CanGrouping(m_rDoc, rPam);
+ // if CanGrouping() returns true it's already merged
+ }
+ }
+ if (!bMerged)
+ {
+ m_rDoc.GetIDocumentUndoRedo().AppendUndo(std::make_unique<SwUndoDelete>(rPam, flags));
+ }
+
+ m_rDoc.getIDocumentState().SetModified();
+
+ return true;
+ }
+
+ if( !m_rDoc.getIDocumentRedlineAccess().IsIgnoreRedline() && !m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty() )
+ m_rDoc.getIDocumentRedlineAccess().DeleteRedline( rPam, true, RedlineType::Any );
+
+ // Delete and move all "Flys at the paragraph", which are within the Selection
+ if (!(flags & SwDeleteFlags::ArtificialSelection))
+ {
+ DelFlyInRange(rPam.GetMark()->GetNode(), rPam.GetPoint()->GetNode(),
+ rPam.GetMark()->GetContentIndex(), rPam.GetPoint()->GetContentIndex());
+ }
+ DelBookmarks(
+ pStt->GetNode(),
+ pEnd->GetNode(),
+ nullptr,
+ pStt->GetContentIndex(),
+ pEnd->GetContentIndex(),
+ bool(flags & SwDeleteFlags::ArtificialSelection));
+
+ SwNodeIndex aSttIdx( pStt->GetNode() );
+ SwContentNode * pCNd = aSttIdx.GetNode().GetContentNode();
+
+ do { // middle checked loop!
+ if( pCNd )
+ {
+ SwTextNode * pStartTextNode( pCNd->GetTextNode() );
+ if ( pStartTextNode )
+ {
+ // now move the Content to the new Node
+ bool bOneNd = pStt->GetNode() == pEnd->GetNode();
+ const sal_Int32 nLen = ( bOneNd ? pEnd->GetContentIndex()
+ : pCNd->Len() )
+ - pStt->GetContentIndex();
+
+ // Don't call again, if already empty
+ if( nLen )
+ {
+ pStartTextNode->EraseText( *pStt, nLen );
+
+ if( !pStartTextNode->Len() )
+ {
+ // METADATA: remove reference if empty (consider node deleted)
+ pStartTextNode->RemoveMetadataReference();
+ }
+ }
+
+ if( bOneNd ) // that's it
+ break;
+
+ ++aSttIdx;
+ }
+ else
+ {
+ // So that there are no indices left registered when deleted,
+ // we remove a SwPaM from the Content here.
+ pStt->nContent.Assign( nullptr, 0 );
+ }
+ }
+
+ pCNd = pEnd->GetNode().GetContentNode();
+ if( pCNd )
+ {
+ SwTextNode * pEndTextNode( pCNd->GetTextNode() );
+ if( pEndTextNode )
+ {
+ // if already empty, don't call again
+ if( pEnd->GetContentIndex() )
+ {
+ SwContentIndex aIdx( pCNd, 0 );
+ pEndTextNode->EraseText( aIdx, pEnd->GetContentIndex() );
+
+ if( !pEndTextNode->Len() )
+ {
+ // METADATA: remove reference if empty (consider node deleted)
+ pEndTextNode->RemoveMetadataReference();
+ }
+ }
+ }
+ else
+ {
+ // So that there are no indices left registered when deleted,
+ // we remove a SwPaM from the Content here.
+ pEnd->nContent.Assign( nullptr, 0 );
+ }
+ }
+
+ // if the end is not a content node, delete it as well
+ SwNodeOffset nEnd = pEnd->GetNodeIndex();
+ if( pCNd == nullptr )
+ nEnd++;
+
+ if( aSttIdx != nEnd )
+ {
+ // tdf#134436 delete section nodes like SwUndoDelete::SwUndoDelete
+ SwNode *pTmpNd;
+ while (pEnd == rPam.GetPoint()
+ && nEnd + SwNodeOffset(2) < m_rDoc.GetNodes().Count()
+ && (pTmpNd = m_rDoc.GetNodes()[nEnd + 1])->IsEndNode()
+ && pTmpNd->StartOfSectionNode()->IsSectionNode()
+ && aSttIdx <= pTmpNd->StartOfSectionNode()->GetIndex())
+ {
+ SwNodeRange range(*pTmpNd->StartOfSectionNode(), *pTmpNd);
+ m_rDoc.GetNodes().SectionUp(&range);
+ --nEnd; // account for deleted start node
+ }
+
+ // delete the Nodes from the NodesArray
+ m_rDoc.GetNodes().Delete( aSttIdx, nEnd - aSttIdx.GetIndex() );
+ }
+
+ // If the Node that contained the Cursor has been deleted,
+ // the Content has to be assigned to the current Content.
+ if (pStt->GetNode().GetContentNode())
+ pStt->SetContent( pStt->GetContentIndex() );
+
+ // If we deleted across Node boundaries we have to correct the PaM,
+ // because they are in different Nodes now.
+ // Also, the Selection is revoked.
+ *pEnd = *pStt;
+ rPam.DeleteMark();
+
+ } while( false );
+
+ m_rDoc.getIDocumentState().SetModified();
+
+ return true;
+}
+
+// It's possible to call Replace with a PaM that spans 2 paragraphs:
+// search with regex for "$", then replace _all_
+bool DocumentContentOperationsManager::ReplaceRangeImpl( SwPaM& rPam, const OUString& rStr,
+ const bool bRegExReplace )
+{
+ if (!rPam.HasMark())
+ return false;
+
+ bool bJoinText, bJoinPrev;
+ ::sw_GetJoinFlags( rPam, bJoinText, bJoinPrev );
+
+ {
+ // Create a copy of the Cursor in order to move all Pams from
+ // the other views out of the deletion range.
+ // Except for itself!
+ SwPaM aDelPam( *rPam.GetMark(), *rPam.GetPoint() );
+ ::PaMCorrAbs( aDelPam, *aDelPam.End() );
+
+ auto [pStt, pEnd] = aDelPam.StartEnd(); // SwPosition*
+ bool bOneNode = pStt->GetNode() == pEnd->GetNode();
+
+ // Own Undo?
+ OUString sRepl( rStr );
+ SwTextNode* pTextNd = pStt->GetNode().GetTextNode();
+ sal_Int32 nStt = pStt->GetContentIndex();
+ sal_Int32 nEnd;
+
+ SwDataChanged aTmp( aDelPam );
+
+ if( m_rDoc.getIDocumentRedlineAccess().IsRedlineOn() )
+ {
+ RedlineFlags eOld = m_rDoc.getIDocumentRedlineAccess().GetRedlineFlags();
+ if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ // this should no longer happen in calls from the UI but maybe via API
+ SAL_WARN_IF((eOld & RedlineFlags::ShowMask) != RedlineFlags::ShowMask,
+ "sw.core", "redlines will be moved in ReplaceRange");
+
+ m_rDoc.GetIDocumentUndoRedo().StartUndo(SwUndoId::EMPTY, nullptr);
+
+ // If any Redline will change (split!) the node
+ const ::sw::mark::IMark* pBkmk =
+ m_rDoc.getIDocumentMarkAccess()->makeMark( aDelPam,
+ OUString(), IDocumentMarkAccess::MarkType::UNO_BOOKMARK,
+ ::sw::mark::InsertMode::New);
+
+ m_rDoc.getIDocumentRedlineAccess().SetRedlineFlags(
+ RedlineFlags::On | RedlineFlags::ShowInsert | RedlineFlags::ShowDelete );
+
+ *aDelPam.GetPoint() = pBkmk->GetMarkPos();
+ if(pBkmk->IsExpanded())
+ *aDelPam.GetMark() = pBkmk->GetOtherMarkPos();
+ m_rDoc.getIDocumentMarkAccess()->deleteMark(pBkmk);
+ pStt = aDelPam.Start();
+ pTextNd = pStt->GetNode().GetTextNode();
+ nStt = pStt->GetContentIndex();
+ }
+
+ if( !sRepl.isEmpty() )
+ {
+ // Apply the first character's attributes to the ReplaceText
+ SfxItemSetFixed
+ <RES_CHRATR_BEGIN, RES_TXTATR_WITHEND_END - 1,
+ RES_UNKNOWNATR_BEGIN, RES_UNKNOWNATR_END-1> aSet( m_rDoc.GetAttrPool() );
+ pTextNd->GetParaAttr( aSet, nStt+1, nStt+1 );
+
+ aSet.ClearItem( RES_TXTATR_REFMARK );
+ aSet.ClearItem( RES_TXTATR_TOXMARK );
+ aSet.ClearItem( RES_TXTATR_CJK_RUBY );
+ aSet.ClearItem( RES_TXTATR_INETFMT );
+ aSet.ClearItem( RES_TXTATR_META );
+ aSet.ClearItem( RES_TXTATR_METAFIELD );
+
+ if( aDelPam.GetPoint() != aDelPam.End() )
+ aDelPam.Exchange();
+
+ // Remember the End
+ SwNodeIndex aPtNd( aDelPam.GetPoint()->GetNode(), -1 );
+ const sal_Int32 nPtCnt = aDelPam.GetPoint()->GetContentIndex();
+
+ bool bFirst = true;
+ OUString sIns;
+ while ( lcl_GetTokenToParaBreak( sRepl, sIns, bRegExReplace ) )
+ {
+ InsertString( aDelPam, sIns );
+ if( bFirst )
+ {
+ SwNodeIndex aMkNd( aDelPam.GetMark()->GetNode(), -1 );
+ const sal_Int32 nMkCnt = aDelPam.GetMark()->GetContentIndex();
+
+ SplitNode( *aDelPam.GetPoint(), false );
+
+ ++aMkNd;
+ aDelPam.GetMark()->Assign( aMkNd, nMkCnt );
+ bFirst = false;
+ }
+ else
+ SplitNode( *aDelPam.GetPoint(), false );
+ }
+ if( !sIns.isEmpty() )
+ {
+ InsertString( aDelPam, sIns );
+ }
+
+ SwPaM aTmpRange( *aDelPam.GetPoint() );
+ aTmpRange.SetMark();
+
+ ++aPtNd;
+ aDelPam.GetPoint()->Assign(aPtNd, nPtCnt);
+ *aTmpRange.GetMark() = *aDelPam.GetPoint();
+
+ m_rDoc.RstTextAttrs( aTmpRange );
+ InsertItemSet( aTmpRange, aSet );
+ }
+
+ // tdf#139982: Appending the redline may immediately delete flys
+ // anchored in the previous text if it's inside an insert redline.
+ // Also flys will be deleted if the redline is accepted. Move them
+ // to the position between the previous text and the new text,
+ // there the chance of surviving both accept and reject is best.
+ SaveFlyArr flys;
+ SaveFlyInRange(aDelPam, *aDelPam.End(), flys, false);
+
+ if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ m_rDoc.GetIDocumentUndoRedo().AppendUndo(
+ std::make_unique<SwUndoRedlineDelete>( aDelPam, SwUndoId::REPLACE ));
+ }
+ // add redline similar to DeleteAndJoinWithRedlineImpl()
+ std::shared_ptr<SwUnoCursor> const pCursor(m_rDoc.CreateUnoCursor(*aDelPam.GetMark()));
+ pCursor->SetMark();
+ *pCursor->GetPoint() = *aDelPam.GetPoint();
+ m_rDoc.getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( RedlineType::Delete, aDelPam ), true);
+ RestFlyInRange(flys, *aDelPam.End(), &aDelPam.End()->GetNode(), true);
+ sw::UpdateFramesForAddDeleteRedline(m_rDoc, *pCursor);
+
+ *rPam.GetMark() = *aDelPam.GetMark();
+ if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ *aDelPam.GetPoint() = *rPam.GetPoint();
+ m_rDoc.GetIDocumentUndoRedo().EndUndo(SwUndoId::EMPTY, nullptr);
+
+ // If any Redline will change (split!) the node
+ const ::sw::mark::IMark* pBkmk =
+ m_rDoc.getIDocumentMarkAccess()->makeMark( aDelPam,
+ OUString(), IDocumentMarkAccess::MarkType::UNO_BOOKMARK,
+ ::sw::mark::InsertMode::New);
+
+ aDelPam.GetPoint()->Assign( SwNodeOffset(0) );
+ aDelPam.GetMark()->Assign( SwNodeOffset(0) );
+ rPam.GetPoint()->Assign( SwNodeOffset(0) );
+ *rPam.GetMark() = *rPam.GetPoint();
+ m_rDoc.getIDocumentRedlineAccess().SetRedlineFlags( eOld );
+
+ *rPam.GetPoint() = pBkmk->GetMarkPos();
+ *rPam.GetMark() = pBkmk->IsExpanded() ? pBkmk->GetOtherMarkPos() : pBkmk->GetMarkPos();
+
+ m_rDoc.getIDocumentMarkAccess()->deleteMark(pBkmk);
+ }
+ bJoinText = false;
+ }
+ else
+ {
+ assert((pStt->GetNode() == pEnd->GetNode() ||
+ ( pStt->GetNodeIndex() + 1 == pEnd->GetNodeIndex() &&
+ !pEnd->GetContentIndex() )) &&
+ "invalid range: Point and Mark on different nodes" );
+
+ if( !m_rDoc.getIDocumentRedlineAccess().IsIgnoreRedline() && !m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty() )
+ m_rDoc.getIDocumentRedlineAccess().DeleteRedline( aDelPam, true, RedlineType::Any );
+
+ SwUndoReplace* pUndoRpl = nullptr;
+ bool const bDoesUndo = m_rDoc.GetIDocumentUndoRedo().DoesUndo();
+ if (bDoesUndo)
+ {
+ pUndoRpl = new SwUndoReplace(aDelPam, sRepl, bRegExReplace);
+ m_rDoc.GetIDocumentUndoRedo().AppendUndo(std::unique_ptr<SwUndo>(pUndoRpl));
+ }
+ ::sw::UndoGuard const undoGuard(m_rDoc.GetIDocumentUndoRedo());
+
+ if( aDelPam.GetPoint() != pStt )
+ aDelPam.Exchange();
+
+ SwNodeIndex aPtNd( pStt->GetNode(), -1 );
+ const sal_Int32 nPtCnt = pStt->GetContentIndex();
+
+ // Set the values again, if Frames or footnotes on the Text have been removed.
+ nStt = nPtCnt;
+ nEnd = bOneNode ? pEnd->GetContentIndex()
+ : pTextNd->GetText().getLength();
+
+ bool bFirst = true;
+ OUString sIns;
+ while ( lcl_GetTokenToParaBreak( sRepl, sIns, bRegExReplace ) )
+ {
+ if (!bFirst || nStt == pTextNd->GetText().getLength())
+ {
+ InsertString( aDelPam, sIns );
+ }
+ else if( nStt < nEnd || !sIns.isEmpty() )
+ {
+ pTextNd->ReplaceText( *pStt, nEnd - nStt, sIns );
+ }
+ SplitNode( *pStt, false);
+ bFirst = false;
+ }
+
+ if( bFirst || !sIns.isEmpty() )
+ {
+ if (!bFirst || nStt == pTextNd->GetText().getLength())
+ {
+ InsertString( aDelPam, sIns );
+ }
+ else if( nStt < nEnd || !sIns.isEmpty() )
+ {
+ pTextNd->ReplaceText( *pStt, nEnd - nStt, sIns );
+ }
+ }
+
+ *rPam.GetPoint() = *aDelPam.GetMark();
+ ++aPtNd;
+ rPam.GetMark()->Assign( aPtNd, nPtCnt );
+
+ if (bJoinText)
+ {
+ assert(rPam.GetPoint() == rPam.End());
+ // move so that SetEnd remembers position after sw_JoinText
+ rPam.Move(fnMoveBackward);
+ }
+ else if (aDelPam.GetPoint() == pStt) // backward selection?
+ {
+ assert(*rPam.GetMark() <= *rPam.GetPoint());
+ rPam.Exchange(); // swap so that rPam is backwards
+ }
+
+ if( pUndoRpl )
+ {
+ pUndoRpl->SetEnd(rPam);
+ }
+ }
+ }
+
+ bool bRet(true);
+ if (bJoinText)
+ {
+ bRet = ::sw_JoinText(rPam, bJoinPrev);
+ }
+
+ m_rDoc.getIDocumentState().SetModified();
+ return bRet;
+}
+
+SwFlyFrameFormat* DocumentContentOperationsManager::InsNoTextNode( const SwPosition& rPos, SwNoTextNode* pNode,
+ const SfxItemSet* pFlyAttrSet,
+ const SfxItemSet* pGrfAttrSet,
+ SwFrameFormat* pFrameFormat)
+{
+ SwFlyFrameFormat *pFormat = nullptr;
+ if( pNode )
+ {
+ pFormat = m_rDoc.MakeFlySection_( rPos, *pNode, RndStdIds::FLY_AT_PARA,
+ pFlyAttrSet, pFrameFormat );
+ if( pGrfAttrSet )
+ pNode->SetAttr( *pGrfAttrSet );
+ }
+ return pFormat;
+}
+
+#define NUMRULE_STATE \
+ std::shared_ptr<SwNumRuleItem> aNumRuleItemHolderIfSet; \
+ std::shared_ptr<SfxStringItem> aListIdItemHolderIfSet; \
+
+#define PUSH_NUMRULE_STATE \
+ lcl_PushNumruleState( aNumRuleItemHolderIfSet, aListIdItemHolderIfSet, pDestTextNd );
+
+#define POP_NUMRULE_STATE \
+ lcl_PopNumruleState( aNumRuleItemHolderIfSet, aListIdItemHolderIfSet, pDestTextNd, rPam );
+
+static void lcl_PushNumruleState(
+ std::shared_ptr<SwNumRuleItem>& aNumRuleItemHolderIfSet,
+ std::shared_ptr<SfxStringItem>& aListIdItemHolderIfSet,
+ const SwTextNode *pDestTextNd )
+{
+ // Safe numrule item at destination.
+ // #i86492# - Safe also <ListId> item of destination.
+ const SfxItemSet * pAttrSet = pDestTextNd->GetpSwAttrSet();
+ if (pAttrSet == nullptr)
+ return;
+
+ if (const SwNumRuleItem* pItem = pAttrSet->GetItemIfSet(RES_PARATR_NUMRULE, false))
+ {
+ aNumRuleItemHolderIfSet.reset(pItem->Clone());
+ }
+
+ if (const SfxStringItem* pItem = pAttrSet->GetItemIfSet(RES_PARATR_LIST_ID, false))
+ {
+ aListIdItemHolderIfSet.reset(pItem->Clone());
+ }
+}
+
+static void lcl_PopNumruleState(
+ const std::shared_ptr<SwNumRuleItem>& aNumRuleItemHolderIfSet,
+ const std::shared_ptr<SfxStringItem>& aListIdItemHolderIfSet,
+ SwTextNode *pDestTextNd, const SwPaM& rPam )
+{
+ /* If only a part of one paragraph is copied
+ restore the numrule at the destination. */
+ // #i86492# - restore also <ListId> item
+ if ( lcl_MarksWholeNode(rPam) )
+ return;
+
+ if (aNumRuleItemHolderIfSet)
+ {
+ pDestTextNd->SetAttr(*aNumRuleItemHolderIfSet);
+ }
+ else
+ {
+ pDestTextNd->ResetAttr(RES_PARATR_NUMRULE);
+ }
+
+ if (aListIdItemHolderIfSet)
+ {
+ pDestTextNd->SetAttr(*aListIdItemHolderIfSet);
+ }
+ else
+ {
+ pDestTextNd->ResetAttr(RES_PARATR_LIST_ID);
+ }
+}
+
+bool DocumentContentOperationsManager::CopyImpl(SwPaM& rPam, SwPosition& rPos,
+ SwCopyFlags const flags,
+ SwPaM *const pCopyRange) const
+{
+ std::vector<std::pair<SwNodeOffset, sal_Int32>> Breaks;
+
+ sw::CalcBreaks(Breaks, rPam, true);
+
+ if (Breaks.empty())
+ {
+ return CopyImplImpl(rPam, rPos, flags, pCopyRange);
+ }
+
+ SwPosition const & rSelectionEnd( *rPam.End() );
+
+ bool bRet(true);
+ bool bFirst(true);
+ // iterate from end to start, ... don't think it's necessary here?
+ auto iter( Breaks.rbegin() );
+ SwNodeOffset nOffset(0);
+ SwNodes const& rNodes(rPam.GetPoint()->GetNodes());
+ SwPaM aPam( rSelectionEnd, rSelectionEnd ); // end node!
+ SwPosition & rEnd( *aPam.End() );
+ SwPosition & rStart( *aPam.Start() );
+ SwPaM copyRange(rPos, rPos);
+
+ while (iter != Breaks.rend())
+ {
+ rStart.Assign(*rNodes[iter->first - nOffset]->GetTextNode(), iter->second + 1);
+ if (rStart < rEnd) // check if part is empty
+ {
+ // pass in copyRange member as rPos; should work ...
+ bRet &= CopyImplImpl(aPam, *copyRange.Start(), flags & ~SwCopyFlags::IsMoveToFly, &copyRange);
+ nOffset = iter->first - rStart.GetNodeIndex(); // fly nodes...
+ if (pCopyRange)
+ {
+ if (bFirst)
+ {
+ pCopyRange->SetMark();
+ *pCopyRange->GetMark() = *copyRange.End();
+ }
+ *pCopyRange->GetPoint() = *copyRange.Start();
+ }
+ bFirst = false;
+ }
+ rEnd.Assign(*rNodes[iter->first - nOffset]->GetTextNode(), iter->second);
+ ++iter;
+ }
+
+ rStart = *rPam.Start(); // set to original start
+ if (rStart < rEnd) // check if part is empty
+ {
+ bRet &= CopyImplImpl(aPam, *copyRange.Start(), flags & ~SwCopyFlags::IsMoveToFly, &copyRange);
+ if (pCopyRange)
+ {
+ if (bFirst)
+ {
+ pCopyRange->SetMark();
+ *pCopyRange->GetMark() = *copyRange.End();
+ }
+ *pCopyRange->GetPoint() = *copyRange.Start();
+ }
+ }
+
+ return bRet;
+}
+
+bool DocumentContentOperationsManager::CopyImplImpl(SwPaM& rPam, SwPosition& rPos,
+ SwCopyFlags const flags,
+ SwPaM *const pCpyRange) const
+{
+ SwDoc& rDoc = rPos.GetNode().GetDoc();
+ const bool bColumnSel = rDoc.IsClipBoard() && rDoc.IsColumnSelection();
+
+ auto [pStt, pEnd] = rPam.StartEnd(); // SwPosition*
+
+ // Catch when there's no copy to do.
+ if (!rPam.HasMark() || (IsEmptyRange(*pStt, *pEnd, flags) && !bColumnSel) ||
+ //JP 29.6.2001: 88963 - don't copy if inspos is in region of start to end
+ //JP 15.11.2001: don't test inclusive the end, ever exclusive
+ ( &rDoc == &m_rDoc && *pStt <= rPos && rPos < *pEnd ))
+ {
+ return false;
+ }
+
+ const bool bEndEqualIns = &rDoc == &m_rDoc && rPos == *pEnd;
+
+ // If Undo is enabled, create the UndoCopy object
+ SwUndoCpyDoc* pUndo = nullptr;
+ // lcl_DeleteRedlines may delete the start or end node of the cursor when
+ // removing the redlines so use cursor that is corrected by PaMCorrAbs
+ std::shared_ptr<SwUnoCursor> const pCopyPam(rDoc.CreateUnoCursor(rPos));
+
+ SwTableNumFormatMerge aTNFM( m_rDoc, rDoc );
+ std::optional<std::vector<SwFrameFormat*>> pFlys;
+ std::vector<SwFrameFormat*> const* pFlysAtInsPos;
+
+ if (rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ pUndo = new SwUndoCpyDoc(*pCopyPam);
+ pFlysAtInsPos = pUndo->GetFlysAnchoredAt();
+ }
+ else
+ {
+ pFlys = sw::GetFlysAnchoredAt(rDoc, rPos.GetNodeIndex());
+ pFlysAtInsPos = pFlys ? &*pFlys : nullptr;
+ }
+
+ RedlineFlags eOld = rDoc.getIDocumentRedlineAccess().GetRedlineFlags();
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern(eOld | RedlineFlags::Ignore);
+
+ // Move the PaM one node back from the insert position, so that
+ // the position doesn't get moved
+ pCopyPam->SetMark();
+ bool bCanMoveBack = pCopyPam->Move(fnMoveBackward, GoInContent);
+ // If the position was shifted from more than one node, an end node has been skipped
+ bool bAfterTable = false;
+ if ((rPos.GetNodeIndex() - pCopyPam->GetPoint()->GetNodeIndex()) > SwNodeOffset(1))
+ {
+ // First go back to the original place
+ *(pCopyPam->GetPoint()) = rPos;
+
+ bCanMoveBack = false;
+ bAfterTable = true;
+ }
+ if( !bCanMoveBack )
+ {
+ pCopyPam->GetPoint()->Adjust(SwNodeOffset(-1));
+ assert(pCopyPam->GetPoint()->GetContentIndex() == 0);
+ }
+
+ SwNodeRange aRg( pStt->GetNode(), pEnd->GetNode() );
+ SwNodeIndex aInsPos( rPos.GetNode() );
+ const bool bOneNode = pStt->GetNode() == pEnd->GetNode();
+ SwTextNode* pSttTextNd = pStt->GetNode().GetTextNode();
+ SwTextNode* pEndTextNd = pEnd->GetNode().GetTextNode();
+ SwTextNode* pDestTextNd = aInsPos.GetNode().GetTextNode();
+ bool bCopyCollFormat = !rDoc.IsInsOnlyTextGlossary() &&
+ ( (pDestTextNd && !pDestTextNd->GetText().getLength()) ||
+ ( !bOneNode && !rPos.GetContentIndex() ) );
+ bool bCopyBookmarks = true;
+ bool bCopyPageSource = false;
+ SwNodeOffset nDeleteTextNodes(0);
+
+ // #i104585# copy outline num rule to clipboard (for ASCII filter)
+ if (rDoc.IsClipBoard() && m_rDoc.GetOutlineNumRule())
+ {
+ rDoc.SetOutlineNumRule(*m_rDoc.GetOutlineNumRule());
+ }
+
+ // #i86492#
+ // Correct the search for a previous list:
+ // First search for non-outline numbering list. Then search for non-outline
+ // bullet list.
+ // Keep also the <ListId> value for possible propagation.
+ OUString aListIdToPropagate;
+ const SwNumRule* pNumRuleToPropagate =
+ rDoc.SearchNumRule( rPos, false, true, false, 0, aListIdToPropagate, nullptr, true );
+ if ( !pNumRuleToPropagate )
+ {
+ pNumRuleToPropagate =
+ rDoc.SearchNumRule( rPos, false, false, false, 0, aListIdToPropagate, nullptr, true );
+ }
+ // #i86492#
+ // Do not propagate previous found list, if
+ // - destination is an empty paragraph which is not in a list and
+ // - source contains at least one paragraph which is not in a list
+ // or
+ // - source is a table
+ if ( pNumRuleToPropagate &&
+ ((pDestTextNd && !pDestTextNd->GetText().getLength() &&
+ !pDestTextNd->IsInList() &&
+ !lcl_ContainsOnlyParagraphsInList(rPam)) ||
+ rPam.GetBound().nNode.GetNode().GetNodeType() == SwNodeType::Table) )
+ {
+ pNumRuleToPropagate = nullptr;
+ }
+
+ // This do/while block is only there so that we can break out of it!
+ do {
+ if( pSttTextNd )
+ {
+ ++nDeleteTextNodes; // must be joined in Undo
+ // Don't copy the beginning completely?
+ if( !bCopyCollFormat || bColumnSel || pStt->GetContentIndex() )
+ {
+ SwContentIndex aDestIdx( rPos.GetContentNode(), rPos.GetContentIndex() );
+ bool bCopyOk = false;
+ if( !pDestTextNd )
+ {
+ if( pStt->GetContentIndex() || bOneNode )
+ pDestTextNd = rDoc.GetNodes().MakeTextNode( aInsPos.GetNode(),
+ rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool(RES_POOLCOLL_STANDARD));
+ else
+ {
+ pDestTextNd = pSttTextNd->MakeCopy(rDoc, aInsPos.GetNode(), true)->GetTextNode();
+ bCopyOk = true;
+ }
+ aDestIdx.Assign( pDestTextNd, 0 );
+ bCopyCollFormat = true;
+ }
+ else if( !bOneNode || bColumnSel )
+ {
+ const sal_Int32 nContentEnd = pEnd->GetContentIndex();
+ {
+ ::sw::UndoGuard const ug(rDoc.GetIDocumentUndoRedo());
+ rDoc.getIDocumentContentOperations().SplitNode( rPos, false );
+ }
+
+ if (bCanMoveBack && rPos == *pCopyPam->GetPoint())
+ {
+ // after the SplitNode, span the CpyPam correctly again
+ pCopyPam->Move( fnMoveBackward, GoInContent );
+ pCopyPam->Move( fnMoveBackward, GoInContent );
+ }
+
+ pDestTextNd = rDoc.GetNodes()[ aInsPos.GetIndex()-SwNodeOffset(1) ]->GetTextNode();
+ aDestIdx.Assign(
+ pDestTextNd, pDestTextNd->GetText().getLength());
+
+ // Correct the area again
+ if( bEndEqualIns )
+ {
+ bool bChg = pEnd != rPam.GetPoint();
+ if( bChg )
+ rPam.Exchange();
+ rPam.Move( fnMoveBackward, GoInContent );
+ if( bChg )
+ rPam.Exchange();
+ }
+ else if( rPos == *pEnd )
+ {
+ // The end was also moved
+ pEnd->Adjust(SwNodeOffset(-1));
+ pEnd->SetContent( nContentEnd );
+ }
+ // tdf#63022 always reset pEndTextNd after SplitNode
+ aRg.aEnd = pEnd->GetNode();
+ pEndTextNd = pEnd->GetNode().GetTextNode();
+ }
+
+ NUMRULE_STATE
+ if( bCopyCollFormat && bOneNode )
+ {
+ PUSH_NUMRULE_STATE
+ }
+
+ if( !bCopyOk )
+ {
+ const sal_Int32 nCpyLen = ( bOneNode
+ ? pEnd->GetContentIndex()
+ : pSttTextNd->GetText().getLength())
+ - pStt->GetContentIndex();
+ pSttTextNd->CopyText( pDestTextNd, aDestIdx, *pStt, nCpyLen );
+ if( bEndEqualIns )
+ pEnd->AdjustContent( -nCpyLen );
+ }
+
+ ++aRg.aStart;
+
+ if( bOneNode )
+ {
+ if (bCopyCollFormat)
+ {
+ // tdf#138897 no Undo for applying style, SwUndoInserts does it
+ pSttTextNd->CopyCollFormat(*pDestTextNd, false);
+ POP_NUMRULE_STATE
+ }
+
+ // Copy at-char flys in rPam.
+ // Update to new (start) node for flys.
+ // tdf#126626 prevent duplicate Undos.
+ ::sw::UndoGuard const ug(rDoc.GetIDocumentUndoRedo());
+ CopyFlyInFlyImpl(aRg, &rPam, *pDestTextNd, false);
+
+ break;
+ }
+ }
+ }
+ else if( pDestTextNd )
+ {
+ // Problems with insertion of table selections into "normal" text solved.
+ // We have to set the correct PaM for Undo, if this PaM starts in a textnode,
+ // the undo operation will try to merge this node after removing the table.
+ // If we didn't split a textnode, the PaM should start at the inserted table node
+ if( rPos.GetContentIndex() == pDestTextNd->Len() )
+ { // Insertion at the last position of a textnode (empty or not)
+ ++aInsPos; // The table will be inserted behind the text node
+ }
+ else if( rPos.GetContentIndex() )
+ { // Insertion in the middle of a text node, it has to be split
+ // (and joined from undo)
+ ++nDeleteTextNodes;
+
+ const sal_Int32 nContentEnd = pEnd->GetContentIndex();
+ {
+ ::sw::UndoGuard const ug(rDoc.GetIDocumentUndoRedo());
+ rDoc.getIDocumentContentOperations().SplitNode( rPos, false );
+ }
+
+ if (bCanMoveBack && rPos == *pCopyPam->GetPoint())
+ {
+ // after the SplitNode, span the CpyPam correctly again
+ pCopyPam->Move( fnMoveBackward, GoInContent );
+ pCopyPam->Move( fnMoveBackward, GoInContent );
+ }
+
+ // Correct the area again
+ if( bEndEqualIns )
+ --aRg.aEnd;
+ // The end would also be moved
+ else if( rPos == *pEnd )
+ {
+ rPos.Adjust(SwNodeOffset(-1));
+ rPos.SetContent( nContentEnd );
+ --aRg.aEnd;
+ }
+ }
+ else if( bCanMoveBack )
+ { // Insertion at the first position of a text node. It will not be split, the table
+ // will be inserted before the text node.
+ // See below, before the SetInsertRange function of the undo object will be called,
+ // the CpyPam would be moved to the next content position. This has to be avoided
+ // We want to be moved to the table node itself thus we have to set bCanMoveBack
+ // and to manipulate pCopyPam.
+ bCanMoveBack = false;
+ pCopyPam->GetPoint()->Adjust(SwNodeOffset(-1));
+ }
+ }
+
+ pDestTextNd = aInsPos.GetNode().GetTextNode();
+ if (pEndTextNd)
+ {
+ SwContentIndex aDestIdx( aInsPos.GetNode().GetContentNode(), rPos.GetContentIndex() );
+ if( !pDestTextNd )
+ {
+ pDestTextNd = rDoc.GetNodes().MakeTextNode( aInsPos.GetNode(),
+ rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool(RES_POOLCOLL_STANDARD));
+ aDestIdx.Assign( pDestTextNd, 0 );
+ --aInsPos;
+
+ // if we have to insert an extra text node
+ // at the destination, this node will be our new destination
+ // (text) node, and thus we increment nDeleteTextNodes. This
+ // will ensure that this node will be deleted during Undo.
+ ++nDeleteTextNodes; // must be deleted
+ }
+
+ const bool bEmptyDestNd = pDestTextNd->GetText().isEmpty();
+
+ NUMRULE_STATE
+ if( bCopyCollFormat && ( bOneNode || bEmptyDestNd ))
+ {
+ PUSH_NUMRULE_STATE
+ }
+
+ pEndTextNd->CopyText( pDestTextNd, aDestIdx, SwContentIndex( pEndTextNd ),
+ pEnd->GetContentIndex() );
+
+ // Also copy all format templates
+ if( bCopyCollFormat && ( bOneNode || bEmptyDestNd ))
+ {
+ // tdf#138897 no Undo for applying style, SwUndoInserts does it
+ pEndTextNd->CopyCollFormat(*pDestTextNd, false);
+ if ( bOneNode )
+ {
+ POP_NUMRULE_STATE
+ }
+ }
+ }
+
+ SfxItemSet aBrkSet( rDoc.GetAttrPool(), aBreakSetRange );
+ if ((flags & SwCopyFlags::CopyAll) || aRg.aStart != aRg.aEnd)
+ {
+ if (pSttTextNd && bCopyCollFormat && pDestTextNd->HasSwAttrSet())
+ {
+ aBrkSet.Put( *pDestTextNd->GetpSwAttrSet() );
+ if( SfxItemState::SET == aBrkSet.GetItemState( RES_BREAK, false ) )
+ pDestTextNd->ResetAttr( RES_BREAK );
+ if( SfxItemState::SET == aBrkSet.GetItemState( RES_PAGEDESC, false ) )
+ pDestTextNd->ResetAttr( RES_PAGEDESC );
+ }
+ }
+
+ {
+ SwPosition startPos(pCopyPam->GetPoint()->GetNode(), SwNodeOffset(+1));
+ if (bCanMoveBack)
+ { // pCopyPam is actually 1 before the copy range so move it fwd
+ SwPaM temp(*pCopyPam->GetPoint());
+ temp.Move(fnMoveForward, GoInContent);
+ startPos = *temp.GetPoint();
+ }
+ assert(startPos.GetNode().IsContentNode());
+ std::pair<SwPaM const&, SwPosition const&> tmp(rPam, startPos);
+ if( aInsPos == pEnd->GetNode() )
+ {
+ SwNodeIndex aSaveIdx( aInsPos, -1 );
+ assert(pStt->GetNode() != pEnd->GetNode());
+ pEnd->SetContent(0); // TODO why this?
+ CopyWithFlyInFly(aRg, aInsPos.GetNode(), &tmp, /*bMakeNewFrames*/true, false, /*bCopyFlyAtFly=*/false, flags);
+ ++aSaveIdx;
+ pEnd->Assign(aSaveIdx);
+ }
+ else
+ CopyWithFlyInFly(aRg, aInsPos.GetNode(), &tmp, /*bMakeNewFrames*/true, false, /*bCopyFlyAtFly=*/false, flags);
+
+ bCopyBookmarks = false;
+ }
+
+ // at-char anchors post SplitNode are on index 0 of 2nd node and will
+ // remain there - move them back to the start (end would also work?)
+ // ... also for at-para anchors; here start is preferable because
+ // it's consistent with SplitNode from SwUndoInserts::RedoImpl()
+ if (pFlysAtInsPos)
+ {
+ // init *again* - because CopyWithFlyInFly moved startPos
+ SwPosition startPos(pCopyPam->GetPoint()->GetNode(), SwNodeOffset(+1));
+ if (bCanMoveBack)
+ { // pCopyPam is actually 1 before the copy range so move it fwd
+ SwPaM temp(*pCopyPam->GetPoint());
+ temp.Move(fnMoveForward, GoInContent);
+ startPos = *temp.GetPoint();
+ }
+ assert(startPos.GetNode().IsContentNode());
+ SwPosition startPosAtPara(startPos);
+ startPosAtPara.nContent.Assign(nullptr, 0);
+
+ for (SwFrameFormat * pFly : *pFlysAtInsPos)
+ {
+ SwFormatAnchor const*const pAnchor = &pFly->GetAnchor();
+ if (pAnchor->GetAnchorId() == RndStdIds::FLY_AT_CHAR)
+ {
+ SwFormatAnchor anchor(*pAnchor);
+ anchor.SetAnchor( &startPos );
+ pFly->SetFormatAttr(anchor);
+ }
+ else if (pAnchor->GetAnchorId() == RndStdIds::FLY_AT_PARA)
+ {
+ SwFormatAnchor anchor(*pAnchor);
+ anchor.SetAnchor( &startPosAtPara );
+
+ bool bSplitFly = false;
+ if (pFly->GetFlySplit().GetValue())
+ {
+ SwIterator<SwFrame, SwModify> aIter(*pFly);
+ bSplitFly = aIter.First() && aIter.Next();
+ }
+ if (bSplitFly)
+ {
+ // This fly format has multiple frames, and we change the anchor. Remove the
+ // old frames, which were based on the old anchor position.
+ pFly->DelFrames();
+ }
+
+ pFly->SetFormatAttr(anchor);
+
+ if (bSplitFly)
+ {
+ // Re-create the frames now that the new anchor is set.
+ pFly->MakeFrames();
+ }
+ }
+ }
+ }
+
+ if ((flags & SwCopyFlags::CopyAll) || aRg.aStart != aRg.aEnd)
+ {
+ // Put the breaks back into the first node
+ if( aBrkSet.Count() && nullptr != ( pDestTextNd = rDoc.GetNodes()[
+ pCopyPam->GetPoint()->GetNodeIndex()+1 ]->GetTextNode()))
+ {
+ pDestTextNd->SetAttr( aBrkSet );
+ bCopyPageSource = true;
+ }
+ }
+ } while( false );
+
+
+ // it is not possible to make this test when copy from the clipBoard to document
+ // in this case the PageNum not exist anymore
+ // tdf#39400 and tdf#97526
+ // when copy from document to ClipBoard, and it is from the first page
+ // and not the source has the page break
+ if (rDoc.IsClipBoard() && (rPam.GetPageNum(pStt == rPam.GetPoint()) == 1) && !bCopyPageSource)
+ {
+ if (pDestTextNd)
+ {
+ pDestTextNd->ResetAttr(RES_BREAK); // remove the page-break
+ pDestTextNd->ResetAttr(RES_PAGEDESC);
+ }
+ }
+
+
+ // Adjust position (in case it was moved / in another node)
+ rPos.nContent.Assign( rPos.GetNode().GetContentNode(),
+ rPos.GetContentIndex() );
+
+ if( rPos.GetNode() != aInsPos.GetNode() )
+ {
+ if (aInsPos < rPos.GetNode())
+ { // tdf#134250 decremented in (pEndTextNd && !pDestTextNd) above
+ pCopyPam->GetMark()->AssignEndIndex(*aInsPos.GetNode().GetContentNode());
+ }
+ else // incremented in (!pSttTextNd && pDestTextNd) above
+ {
+ pCopyPam->GetMark()->Assign(aInsPos);
+ }
+ rPos = *pCopyPam->GetMark();
+ }
+ else
+ *pCopyPam->GetMark() = rPos;
+
+ if ( !bAfterTable )
+ pCopyPam->Move( fnMoveForward, bCanMoveBack ? GoInContent : GoInNode );
+ else
+ {
+ // Reset the offset to 0 as it was before the insertion
+ pCopyPam->GetPoint()->Adjust(SwNodeOffset(+1));
+
+ // If the next node is a start node, then step back: the start node
+ // has been copied and needs to be in the selection for the undo
+ if (pCopyPam->GetPoint()->GetNode().IsStartNode())
+ pCopyPam->GetPoint()->Adjust(SwNodeOffset(-1));
+
+ }
+ pCopyPam->Exchange();
+
+ // Also copy all bookmarks
+ if( bCopyBookmarks && m_rDoc.getIDocumentMarkAccess()->getAllMarksCount() )
+ {
+ sw::CopyBookmarks(rPam, *pCopyPam->Start());
+ }
+
+ if( RedlineFlags::DeleteRedlines & eOld )
+ {
+ assert(*pCopyPam->GetPoint() == rPos);
+ // the Node rPos points to may be deleted so unregister ...
+ rPos.nContent.Assign(nullptr, 0);
+ lcl_DeleteRedlines(rPam, *pCopyPam);
+ rPos = *pCopyPam->GetPoint(); // ... and restore.
+ }
+
+ // If Undo is enabled, store the inserted area
+ if (rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ // append it after styles have been copied when copying nodes
+ rDoc.GetIDocumentUndoRedo().AppendUndo( std::unique_ptr<SwUndo>(pUndo) );
+ pUndo->SetInsertRange(*pCopyPam, true, nDeleteTextNodes);
+ }
+
+ if( pCpyRange )
+ {
+ pCpyRange->SetMark();
+ *pCpyRange->GetPoint() = *pCopyPam->GetPoint();
+ *pCpyRange->GetMark() = *pCopyPam->GetMark();
+ }
+
+ if ( pNumRuleToPropagate != nullptr )
+ {
+ // #i86492# - use <SwDoc::SetNumRule(..)>, because it also handles the <ListId>
+ // Don't reset indent attributes, that would mean loss of direct
+ // formatting.
+ rDoc.SetNumRule( *pCopyPam, *pNumRuleToPropagate, false, nullptr,
+ aListIdToPropagate, true, /*bResetIndentAttrs=*/false );
+ }
+
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
+ rDoc.getIDocumentState().SetModified();
+
+ return true;
+}
+
+
+}
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/DocumentDeviceManager.cxx b/sw/source/core/doc/DocumentDeviceManager.cxx
new file mode 100644
index 0000000000..b90474082c
--- /dev/null
+++ b/sw/source/core/doc/DocumentDeviceManager.cxx
@@ -0,0 +1,370 @@
+/* -*- 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 <DocumentDeviceManager.hxx>
+
+#include <memory>
+#include <utility>
+
+#include <doc.hxx>
+#include <DocumentSettingManager.hxx>
+#include <IDocumentDrawModelAccess.hxx>
+#include <IDocumentState.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <osl/diagnose.h>
+#include <sfx2/printer.hxx>
+#include <vcl/virdev.hxx>
+#include <vcl/outdev.hxx>
+#include <vcl/jobset.hxx>
+#include <printdata.hxx>
+#include <vcl/mapmod.hxx>
+#include <svl/itemset.hxx>
+#include <cfgitems.hxx>
+#include <cmdid.h>
+#include <drawdoc.hxx>
+#include <wdocsh.hxx>
+#include <prtopt.hxx>
+#include <viewsh.hxx>
+#include <rootfrm.hxx>
+#include <viewopt.hxx>
+#include <swwait.hxx>
+#include <fntcache.hxx>
+
+class SwDocShell;
+class SwWait;
+
+namespace sw {
+
+DocumentDeviceManager::DocumentDeviceManager( SwDoc& i_rSwdoc ) : m_rDoc( i_rSwdoc ), mpPrt(nullptr), mpVirDev(nullptr) {}
+
+SfxPrinter* DocumentDeviceManager::getPrinter(/*[in]*/ bool bCreate ) const
+{
+ SfxPrinter* pRet = nullptr;
+ if ( !bCreate || mpPrt )
+ pRet = mpPrt;
+ else
+ pRet = &CreatePrinter_();
+
+ return pRet;
+}
+
+void DocumentDeviceManager::setPrinter(/*[in]*/ SfxPrinter *pP,/*[in]*/ bool bDeleteOld,/*[in]*/ bool bCallPrtDataChanged )
+{
+ assert ( !pP || !pP->isDisposed() );
+ if ( pP != mpPrt )
+ {
+ if ( bDeleteOld )
+ mpPrt.disposeAndClear();
+ mpPrt = pP;
+
+ // our printer should always use TWIP. Don't rely on this being set in SwViewShell::InitPrt, there
+ // are situations where this isn't called. #i108712#
+ if ( mpPrt )
+ {
+ MapMode aMapMode( mpPrt->GetMapMode() );
+ aMapMode.SetMapUnit( MapUnit::MapTwip );
+ mpPrt->SetMapMode( aMapMode );
+ }
+
+ if ( m_rDoc.getIDocumentDrawModelAccess().GetDrawModel() && !m_rDoc.GetDocumentSettingManager().get( DocumentSettingId::USE_VIRTUAL_DEVICE ) )
+ m_rDoc.getIDocumentDrawModelAccess().GetDrawModel()->SetRefDevice( mpPrt );
+ }
+
+ if ( bCallPrtDataChanged &&
+ // #i41075# Do not call PrtDataChanged() if we do not
+ // use the printer for formatting:
+ !m_rDoc.GetDocumentSettingManager().get(DocumentSettingId::USE_VIRTUAL_DEVICE) )
+ PrtDataChanged();
+}
+
+VirtualDevice* DocumentDeviceManager::getVirtualDevice(/*[in]*/ bool bCreate ) const
+{
+ VirtualDevice* pRet = nullptr;
+ if ( !bCreate || mpVirDev )
+ pRet = mpVirDev;
+ else
+ pRet = &CreateVirtualDevice_();
+
+ assert ( !pRet || !pRet->isDisposed() );
+
+ return pRet;
+}
+
+void DocumentDeviceManager::setVirtualDevice(/*[in]*/ VirtualDevice* pVd )
+{
+ assert ( !pVd->isDisposed() );
+
+ if ( mpVirDev.get() != pVd )
+ {
+ mpVirDev.disposeAndClear();
+ mpVirDev = pVd;
+
+ if ( m_rDoc.getIDocumentDrawModelAccess().GetDrawModel() && m_rDoc.GetDocumentSettingManager().get( DocumentSettingId::USE_VIRTUAL_DEVICE ) )
+ m_rDoc.getIDocumentDrawModelAccess().GetDrawModel()->SetRefDevice( mpVirDev );
+ }
+}
+
+OutputDevice* DocumentDeviceManager::getReferenceDevice(/*[in]*/ bool bCreate ) const
+{
+ OutputDevice* pRet = nullptr;
+ if ( !m_rDoc.GetDocumentSettingManager().get(DocumentSettingId::USE_VIRTUAL_DEVICE) )
+ {
+ pRet = getPrinter( bCreate );
+
+ if ( bCreate && !mpPrt->IsValid() )
+ {
+ pRet = getVirtualDevice( true );
+ }
+ }
+ else
+ {
+ pRet = getVirtualDevice( bCreate );
+ }
+
+ assert ( !pRet || !pRet->isDisposed() );
+
+ return pRet;
+}
+
+void DocumentDeviceManager::setReferenceDeviceType(/*[in]*/ bool bNewVirtual, /*[in]*/ bool bNewHiRes )
+{
+ if ( m_rDoc.GetDocumentSettingManager().get(DocumentSettingId::USE_VIRTUAL_DEVICE) == bNewVirtual &&
+ m_rDoc.GetDocumentSettingManager().get(DocumentSettingId::USE_HIRES_VIRTUAL_DEVICE) == bNewHiRes )
+ return;
+
+ if ( bNewVirtual )
+ {
+ VirtualDevice* pMyVirDev = getVirtualDevice( true );
+ if ( !bNewHiRes )
+ pMyVirDev->SetReferenceDevice( VirtualDevice::RefDevMode::Dpi600 );
+ else
+ pMyVirDev->SetReferenceDevice( VirtualDevice::RefDevMode::MSO1 );
+
+ if( m_rDoc.getIDocumentDrawModelAccess().GetDrawModel() )
+ m_rDoc.getIDocumentDrawModelAccess().GetDrawModel()->SetRefDevice( pMyVirDev );
+ }
+ else
+ {
+ // #i41075#
+ // We have to take care that a printer exists before calling
+ // PrtDataChanged() in order to prevent that PrtDataChanged()
+ // triggers this funny situation:
+ // getReferenceDevice()->getPrinter()->CreatePrinter_()
+ // ->setPrinter()-> PrtDataChanged()
+ SfxPrinter* pPrinter = getPrinter( true );
+ if( m_rDoc.getIDocumentDrawModelAccess().GetDrawModel() )
+ m_rDoc.getIDocumentDrawModelAccess().GetDrawModel()->SetRefDevice( pPrinter );
+ }
+
+ m_rDoc.GetDocumentSettingManager().set(DocumentSettingId::USE_VIRTUAL_DEVICE, bNewVirtual );
+ m_rDoc.GetDocumentSettingManager().set(DocumentSettingId::USE_HIRES_VIRTUAL_DEVICE, bNewHiRes );
+ PrtDataChanged();
+ m_rDoc.getIDocumentState().SetModified();
+}
+
+const JobSetup* DocumentDeviceManager::getJobsetup() const
+{
+ return mpPrt ? &mpPrt->GetJobSetup() : nullptr;
+}
+
+void DocumentDeviceManager::setJobsetup(/*[in]*/ const JobSetup &rJobSetup )
+{
+ bool bCheckPageDescs = !mpPrt;
+ bool bDataChanged = false;
+
+ if ( mpPrt )
+ {
+ if ( mpPrt->GetName() == rJobSetup.GetPrinterName() )
+ {
+ if ( mpPrt->GetJobSetup() != rJobSetup )
+ {
+ mpPrt->SetJobSetup( rJobSetup );
+ bDataChanged = true;
+ }
+ }
+ else
+ mpPrt.disposeAndClear();
+ }
+
+ if( !mpPrt )
+ {
+ //The ItemSet is deleted by Sfx!
+ auto pSet = std::make_unique<SfxItemSetFixed<
+ SID_PRINTER_NOTFOUND_WARN, SID_PRINTER_NOTFOUND_WARN,
+ SID_PRINTER_CHANGESTODOC, SID_PRINTER_CHANGESTODOC,
+ SID_HTML_MODE, SID_HTML_MODE,
+ FN_PARAM_ADDPRINTER, FN_PARAM_ADDPRINTER>>(m_rDoc.GetAttrPool());
+ VclPtr<SfxPrinter> p = VclPtr<SfxPrinter>::Create( std::move(pSet), rJobSetup );
+ if ( bCheckPageDescs )
+ setPrinter( p, true, true );
+ else
+ {
+ mpPrt = p;
+ bDataChanged = true;
+ }
+ }
+ if ( bDataChanged && !m_rDoc.GetDocumentSettingManager().get(DocumentSettingId::USE_VIRTUAL_DEVICE) )
+ PrtDataChanged();
+}
+
+const SwPrintData & DocumentDeviceManager::getPrintData() const
+{
+ if(!mpPrtData)
+ {
+ DocumentDeviceManager * pThis = const_cast< DocumentDeviceManager * >(this);
+ pThis->mpPrtData.reset(new SwPrintData);
+
+ // SwPrintData should be initialized from the configuration,
+ // the respective config item is implemented by SwPrintOptions which
+ // is also derived from SwPrintData
+ const SwDocShell *pDocSh = m_rDoc.GetDocShell();
+ OSL_ENSURE( pDocSh, "pDocSh is 0, can't determine if this is a WebDoc or not" );
+ bool bWeb = dynamic_cast< const SwWebDocShell * >(pDocSh) != nullptr;
+ SwPrintOptions aPrintOptions( bWeb );
+ *pThis->mpPrtData = aPrintOptions;
+ }
+ assert(mpPrtData && "this will always be set by now");
+ return *mpPrtData;
+}
+
+void DocumentDeviceManager::setPrintData(/*[in]*/ const SwPrintData& rPrtData )
+{
+ if(!mpPrtData)
+ mpPrtData.reset(new SwPrintData);
+ *mpPrtData = rPrtData;
+}
+
+DocumentDeviceManager::~DocumentDeviceManager()
+{
+ mpPrtData.reset();
+ mpVirDev.disposeAndClear();
+ mpPrt.disposeAndClear();
+}
+
+VirtualDevice& DocumentDeviceManager::CreateVirtualDevice_() const
+{
+#ifdef IOS
+ VclPtr<VirtualDevice> pNewVir = VclPtr<VirtualDevice>::Create(DeviceFormat::GRAYSCALE);
+#else
+ VclPtr<VirtualDevice> pNewVir = VclPtr<VirtualDevice>::Create(DeviceFormat::WITHOUT_ALPHA);
+#endif
+
+ pNewVir->SetReferenceDevice( VirtualDevice::RefDevMode::MSO1 );
+
+ // #i60945# External leading compatibility for unix systems.
+ if ( m_rDoc.GetDocumentSettingManager().get(DocumentSettingId::UNIX_FORCE_ZERO_EXT_LEADING ) )
+ pNewVir->Compat_ZeroExtleadBug();
+
+ MapMode aMapMode( pNewVir->GetMapMode() );
+ aMapMode.SetMapUnit( MapUnit::MapTwip );
+ pNewVir->SetMapMode( aMapMode );
+
+ const_cast<DocumentDeviceManager*>(this)->setVirtualDevice( pNewVir );
+ return *mpVirDev;
+}
+
+SfxPrinter& DocumentDeviceManager::CreatePrinter_() const
+{
+ OSL_ENSURE( ! mpPrt, "Do not call CreatePrinter_(), call getPrinter() instead" );
+
+ // We create a default SfxPrinter.
+ // The ItemSet is deleted by Sfx!
+ auto pSet = std::make_unique<SfxItemSetFixed<
+ SID_PRINTER_NOTFOUND_WARN, SID_PRINTER_NOTFOUND_WARN,
+ SID_PRINTER_CHANGESTODOC, SID_PRINTER_CHANGESTODOC,
+ SID_HTML_MODE, SID_HTML_MODE,
+ FN_PARAM_ADDPRINTER, FN_PARAM_ADDPRINTER>>(m_rDoc.GetAttrPool());
+
+ VclPtr<SfxPrinter> pNewPrt = VclPtr<SfxPrinter>::Create( std::move(pSet) );
+
+ // assign PrintData to newly created printer
+ const SwPrintData& rPrtData = getPrintData();
+ SwAddPrinterItem aAddPrinterItem(rPrtData);
+ SfxItemSet aOptions(pNewPrt->GetOptions());
+ aOptions.Put(aAddPrinterItem);
+ pNewPrt->SetOptions(aOptions);
+
+ const_cast<DocumentDeviceManager*>(this)->setPrinter( pNewPrt, true, true );
+ return *mpPrt;
+}
+
+void DocumentDeviceManager::PrtDataChanged()
+{
+// If you change this, also modify InJobSetup in Sw3io if appropriate.
+
+ // #i41075#
+ OSL_ENSURE( m_rDoc.getIDocumentSettingAccess().get(DocumentSettingId::USE_VIRTUAL_DEVICE) ||
+ nullptr != getPrinter( false ), "PrtDataChanged will be called recursively!" );
+ SwRootFrame* pTmpRoot = m_rDoc.getIDocumentLayoutAccess().GetCurrentLayout();
+ std::optional<SwWait> oWait;
+ bool bEndAction = false;
+
+ if( m_rDoc.GetDocShell() )
+ m_rDoc.GetDocShell()->UpdateFontList();
+
+ bool bDraw = true;
+ if ( pTmpRoot )
+ {
+ SwViewShell *pSh = m_rDoc.getIDocumentLayoutAccess().GetCurrentViewShell();
+ if( pSh &&
+ (!pSh->GetViewOptions()->getBrowseMode() ||
+ pSh->GetViewOptions()->IsPrtFormat()) )
+ {
+ if ( m_rDoc.GetDocShell() )
+ oWait.emplace( *m_rDoc.GetDocShell(), true );
+
+ pTmpRoot->StartAllAction();
+ bEndAction = true;
+
+ bDraw = false;
+ if( m_rDoc.getIDocumentDrawModelAccess().GetDrawModel() )
+ {
+ m_rDoc.getIDocumentDrawModelAccess().GetDrawModel()->SetAddExtLeading( m_rDoc.GetDocumentSettingManager().get(DocumentSettingId::ADD_EXT_LEADING) );
+ m_rDoc.getIDocumentDrawModelAccess().GetDrawModel()->SetRefDevice( getReferenceDevice( false ) );
+ }
+
+ pFntCache->Flush();
+
+ for(SwRootFrame* aLayout : m_rDoc.GetAllLayouts())
+ aLayout->InvalidateAllContent(SwInvalidateFlags::Size);
+
+ for(SwViewShell& rShell : pSh->GetRingContainer())
+ rShell.InitPrt(getPrinter(false));
+ }
+ }
+ if ( bDraw && m_rDoc.getIDocumentDrawModelAccess().GetDrawModel() )
+ {
+ const bool bTmpAddExtLeading = m_rDoc.GetDocumentSettingManager().get(DocumentSettingId::ADD_EXT_LEADING);
+ if ( bTmpAddExtLeading != m_rDoc.getIDocumentDrawModelAccess().GetDrawModel()->IsAddExtLeading() )
+ m_rDoc.getIDocumentDrawModelAccess().GetDrawModel()->SetAddExtLeading( bTmpAddExtLeading );
+
+ OutputDevice* pOutDev = getReferenceDevice( false );
+ if ( pOutDev != m_rDoc.getIDocumentDrawModelAccess().GetDrawModel()->GetRefDevice() )
+ m_rDoc.getIDocumentDrawModelAccess().GetDrawModel()->SetRefDevice( pOutDev );
+ }
+
+ m_rDoc.PrtOLENotify( true );
+
+ if ( bEndAction )
+ pTmpRoot->EndAllAction();
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/DocumentDrawModelManager.cxx b/sw/source/core/doc/DocumentDrawModelManager.cxx
new file mode 100644
index 0000000000..7f3098ef66
--- /dev/null
+++ b/sw/source/core/doc/DocumentDrawModelManager.cxx
@@ -0,0 +1,357 @@
+/* -*- 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 <DocumentDrawModelManager.hxx>
+
+#include <doc.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <IDocumentSettingAccess.hxx>
+#include <IDocumentDeviceAccess.hxx>
+#include <IDocumentLinksAdministration.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <docsh.hxx>
+#include <wrtsh.hxx>
+#include <swtypes.hxx>
+#include <svl/hint.hxx>
+#include <viewsh.hxx>
+#include <view.hxx>
+#include <drawdoc.hxx>
+#include <rootfrm.hxx>
+#include <fmtanchr.hxx>
+#include <editeng/eeitem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <svx/svdlayer.hxx>
+#include <svx/svdoutl.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdpagv.hxx>
+#include <svx/svdotext.hxx>
+#include <svx/svdview.hxx>
+#include <svl/srchitem.hxx>
+#include <unotools/configmgr.hxx>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+
+class SdrOutliner;
+
+namespace sw
+{
+
+DocumentDrawModelManager::DocumentDrawModelManager(SwDoc& i_rSwdoc)
+ : m_rDoc(i_rSwdoc)
+ , mnHeaven(0)
+ , mnHell(0)
+ , mnControls(0)
+ , mnInvisibleHeaven(0)
+ , mnInvisibleHell(0)
+ , mnInvisibleControls(0)
+{
+}
+
+// Is also called by the Sw3 Reader, if there was an error when reading the
+// drawing layer. If it is called by the Sw3 Reader the layer is rebuilt
+// from scratch.
+void DocumentDrawModelManager::InitDrawModel()
+{
+ // !! Attention: there is similar code in the Sw3 Reader (sw3imp.cxx) that
+ // also has to be maintained!!
+ if ( mpDrawModel )
+ ReleaseDrawModel();
+
+ // set FontHeight pool defaults without changing static SdrEngineDefaults
+ m_rDoc.GetAttrPool().SetPoolDefaultItem(SvxFontHeightItem( 240, 100, EE_CHAR_FONTHEIGHT ));
+
+ SAL_INFO( "sw.doc", "before create DrawDocument" );
+ // The document owns the SwDrawModel. We always have two layers and one page.
+ mpDrawModel.reset(new SwDrawModel(m_rDoc));
+
+ mpDrawModel->EnableUndo( m_rDoc.GetIDocumentUndoRedo().DoesUndo() );
+
+ OUString sLayerNm;
+ sLayerNm = "Hell";
+ mnHell = mpDrawModel->GetLayerAdmin().NewLayer( sLayerNm )->GetID();
+
+ sLayerNm = "Heaven";
+ mnHeaven = mpDrawModel->GetLayerAdmin().NewLayer( sLayerNm )->GetID();
+
+ sLayerNm = "Controls";
+ mnControls = mpDrawModel->GetLayerAdmin().NewLayer( sLayerNm )->GetID();
+ mpDrawModel->GetLayerAdmin().SetControlLayerName(sLayerNm);
+
+ // add invisible layers corresponding to the visible ones.
+ {
+ sLayerNm = "InvisibleHell";
+ mnInvisibleHell = mpDrawModel->GetLayerAdmin().NewLayer( sLayerNm )->GetID();
+
+ sLayerNm = "InvisibleHeaven";
+ mnInvisibleHeaven = mpDrawModel->GetLayerAdmin().NewLayer( sLayerNm )->GetID();
+
+ sLayerNm = "InvisibleControls";
+ mnInvisibleControls = mpDrawModel->GetLayerAdmin().NewLayer( sLayerNm )->GetID();
+ }
+
+ rtl::Reference<SdrPage> pMasterPage = mpDrawModel->AllocPage( false );
+ mpDrawModel->InsertPage( pMasterPage.get() );
+ SAL_INFO( "sw.doc", "after create DrawDocument" );
+ SdrOutliner& rOutliner = mpDrawModel->GetDrawOutliner();
+ if (!utl::ConfigManager::IsFuzzing())
+ {
+ SAL_INFO( "sw.doc", "before create Spellchecker/Hyphenator" );
+ css::uno::Reference< css::linguistic2::XSpellChecker1 > xSpell = ::GetSpellChecker();
+ rOutliner.SetSpeller( xSpell );
+ css::uno::Reference< css::linguistic2::XHyphenator > xHyphenator( ::GetHyphenator() );
+ rOutliner.SetHyphenator( xHyphenator );
+ SAL_INFO( "sw.doc", "after create Spellchecker/Hyphenator" );
+ }
+ m_rDoc.SetCalcFieldValueHdl(&rOutliner);
+ m_rDoc.SetCalcFieldValueHdl(&mpDrawModel->GetHitTestOutliner());
+
+ // Set the LinkManager in the model so that linked graphics can be inserted.
+ // The WinWord import needs it too.
+ mpDrawModel->SetLinkManager( & m_rDoc.getIDocumentLinksAdministration().GetLinkManager() );
+ mpDrawModel->SetAddExtLeading( m_rDoc.getIDocumentSettingAccess().get(DocumentSettingId::ADD_EXT_LEADING) );
+
+ OutputDevice* pRefDev = m_rDoc.getIDocumentDeviceAccess().getReferenceDevice( false );
+ if ( pRefDev )
+ mpDrawModel->SetRefDevice( pRefDev );
+
+ mpDrawModel->SetNotifyUndoActionHdl( std::bind( &SwDoc::AddDrawUndo, &m_rDoc, std::placeholders::_1 ));
+ SwViewShell* const pSh = m_rDoc.getIDocumentLayoutAccess().GetCurrentViewShell();
+ if ( !pSh )
+ return;
+
+ for(const SwViewShell& rViewSh : pSh->GetRingContainer())
+ {
+ SwRootFrame* pRoot = rViewSh.GetLayout();
+ if( pRoot && !pRoot->GetDrawPage() )
+ {
+ // Disable "multiple layout" for the moment:
+ // use pMasterPage instead of a new created SdrPage
+ // mpDrawModel->AllocPage( FALSE );
+ // mpDrawModel->InsertPage( pDrawPage );
+ SdrPage* pDrawPage = pMasterPage.get();
+ pRoot->SetDrawPage( pDrawPage );
+ pDrawPage->SetSize( pRoot->getFrameArea().SSize() );
+ }
+ }
+}
+
+
+void DocumentDrawModelManager::ReleaseDrawModel()
+{
+ // !! Also maintain the code in the sw3io for inserting documents!!
+ mpDrawModel.reset();
+}
+
+
+const SwDrawModel* DocumentDrawModelManager::GetDrawModel() const
+{
+ return mpDrawModel.get();
+}
+
+SwDrawModel* DocumentDrawModelManager::GetDrawModel()
+{
+ return mpDrawModel.get();
+}
+
+SwDrawModel* DocumentDrawModelManager::MakeDrawModel_()
+{
+ OSL_ENSURE( !mpDrawModel, "MakeDrawModel_: Why?" );
+ InitDrawModel();
+ SwViewShell* const pSh = m_rDoc.getIDocumentLayoutAccess().GetCurrentViewShell();
+ if ( pSh )
+ {
+ for(SwViewShell& rViewSh : pSh->GetRingContainer())
+ rViewSh.MakeDrawView();
+
+ // Broadcast, so that the FormShell can be connected to the DrawView
+ if( m_rDoc.GetDocShell() )
+ {
+ SfxHint aHint( SfxHintId::SwDrawViewsCreated );
+ m_rDoc.GetDocShell()->Broadcast( aHint );
+ }
+ }
+ return mpDrawModel.get();
+}
+
+SwDrawModel* DocumentDrawModelManager::GetOrCreateDrawModel()
+{
+ return GetDrawModel() ? GetDrawModel() : MakeDrawModel_();
+}
+
+SdrLayerID DocumentDrawModelManager::GetHeavenId() const
+{
+ return mnHeaven;
+}
+
+SdrLayerID DocumentDrawModelManager::GetHellId() const
+{
+ return mnHell;
+}
+
+SdrLayerID DocumentDrawModelManager::GetControlsId() const
+{
+ return mnControls;
+}
+
+SdrLayerID DocumentDrawModelManager::GetInvisibleHeavenId() const
+{
+ return mnInvisibleHeaven;
+}
+
+SdrLayerID DocumentDrawModelManager::GetInvisibleHellId() const
+{
+ return mnInvisibleHell;
+}
+
+SdrLayerID DocumentDrawModelManager::GetInvisibleControlsId() const
+{
+ return mnInvisibleControls;
+}
+
+void DocumentDrawModelManager::NotifyInvisibleLayers( SdrPageView& _rSdrPageView )
+{
+ OUString sLayerNm;
+ sLayerNm = "InvisibleHell";
+ _rSdrPageView.SetLayerVisible( sLayerNm, false );
+
+ sLayerNm = "InvisibleHeaven";
+ _rSdrPageView.SetLayerVisible( sLayerNm, false );
+
+ sLayerNm = "InvisibleControls";
+ _rSdrPageView.SetLayerVisible( sLayerNm, false );
+}
+
+bool DocumentDrawModelManager::IsVisibleLayerId( SdrLayerID _nLayerId ) const
+{
+ bool bRetVal;
+
+ if ( _nLayerId == GetHeavenId() ||
+ _nLayerId == GetHellId() ||
+ _nLayerId == GetControlsId() )
+ {
+ bRetVal = true;
+ }
+ else if ( _nLayerId == GetInvisibleHeavenId() ||
+ _nLayerId == GetInvisibleHellId() ||
+ _nLayerId == GetInvisibleControlsId() )
+ {
+ bRetVal = false;
+ }
+ else
+ {
+ OSL_FAIL( "<SwDoc::IsVisibleLayerId(..)> - unknown layer ID." );
+ bRetVal = false;
+ }
+
+ return bRetVal;
+}
+
+SdrLayerID DocumentDrawModelManager::GetInvisibleLayerIdByVisibleOne( SdrLayerID _nVisibleLayerId )
+{
+ SdrLayerID nInvisibleLayerId;
+
+ if ( _nVisibleLayerId == GetHeavenId() )
+ {
+ nInvisibleLayerId = GetInvisibleHeavenId();
+ }
+ else if ( _nVisibleLayerId == GetHellId() )
+ {
+ nInvisibleLayerId = GetInvisibleHellId();
+ }
+ else if ( _nVisibleLayerId == GetControlsId() )
+ {
+ nInvisibleLayerId = GetInvisibleControlsId();
+ }
+ else if ( _nVisibleLayerId == GetInvisibleHeavenId() ||
+ _nVisibleLayerId == GetInvisibleHellId() ||
+ _nVisibleLayerId == GetInvisibleControlsId() )
+ {
+ OSL_FAIL( "<SwDoc::GetInvisibleLayerIdByVisibleOne(..)> - given layer ID already an invisible one." );
+ nInvisibleLayerId = _nVisibleLayerId;
+ }
+ else
+ {
+ OSL_FAIL( "<SwDoc::GetInvisibleLayerIdByVisibleOne(..)> - given layer ID is unknown." );
+ nInvisibleLayerId = _nVisibleLayerId;
+ }
+
+ return nInvisibleLayerId;
+}
+
+bool DocumentDrawModelManager::Search(const SwPaM& rPaM, const SvxSearchItem& rSearchItem)
+{
+ SwPosFlyFrames aFrames = m_rDoc.GetAllFlyFormats(&rPaM, /*bDrawAlso=*/true);
+
+ for (const SwPosFlyFrame& rPosFlyFrame : aFrames)
+ {
+ // Filter for at-paragraph anchored draw frames.
+ const SwFrameFormat& rFrameFormat = rPosFlyFrame.GetFormat();
+ const SwFormatAnchor& rAnchor = rFrameFormat.GetAnchor();
+ if (rAnchor.GetAnchorId() != RndStdIds::FLY_AT_PARA || rFrameFormat.Which() != RES_DRAWFRMFMT)
+ continue;
+
+ // Does the shape have matching text?
+ SdrOutliner& rOutliner = GetDrawModel()->GetDrawOutliner();
+ SdrObject* pObject = const_cast<SdrObject*>(rFrameFormat.FindSdrObject());
+ SdrTextObj* pTextObj = DynCastSdrTextObj(pObject);
+ if (!pTextObj)
+ continue;
+ const OutlinerParaObject* pParaObj = pTextObj->GetOutlinerParaObject();
+ if (!pParaObj)
+ continue;
+ rOutliner.SetText(*pParaObj);
+ SwDocShell* pDocShell = m_rDoc.GetDocShell();
+ if (!pDocShell)
+ return false;
+ SwWrtShell* pWrtShell = pDocShell->GetWrtShell();
+ if (!pWrtShell)
+ return false;
+ if (!rOutliner.HasText(rSearchItem))
+ continue;
+
+ // If so, then select highlight the search result.
+ pWrtShell->SelectObj(Point(), 0, pObject);
+ SwView* pView = pDocShell->GetView();
+ if (!pView)
+ return false;
+ if (!pView->EnterShapeDrawTextMode(pObject))
+ continue;
+ SdrView* pSdrView = pWrtShell->GetDrawView();
+ if (!pSdrView)
+ return false;
+ OutlinerView* pOutlinerView = pSdrView->GetTextEditOutlinerView();
+ if (!rSearchItem.GetBackward())
+ pOutlinerView->SetSelection(ESelection(0, 0, 0, 0));
+ else
+ pOutlinerView->SetSelection(ESelection(EE_PARA_MAX_COUNT, EE_TEXTPOS_MAX_COUNT, EE_PARA_MAX_COUNT, EE_TEXTPOS_MAX_COUNT));
+ pOutlinerView->StartSearchAndReplace(rSearchItem);
+ return true;
+ }
+
+ return false;
+}
+
+void DocumentDrawModelManager::DrawNotifyUndoHdl()
+{
+ mpDrawModel->SetNotifyUndoActionHdl( nullptr );
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/DocumentExternalDataManager.cxx b/sw/source/core/doc/DocumentExternalDataManager.cxx
new file mode 100644
index 0000000000..3e751a3168
--- /dev/null
+++ b/sw/source/core/doc/DocumentExternalDataManager.cxx
@@ -0,0 +1,34 @@
+/* -*- 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 <DocumentExternalDataManager.hxx>
+
+namespace sw
+{
+
+void DocumentExternalDataManager::setExternalData(::sw::tExternalDataType eType, ::sw::tExternalDataPointer pPayload)
+{
+ m_externalData[eType] = pPayload;
+}
+
+::sw::tExternalDataPointer DocumentExternalDataManager::getExternalData(::sw::tExternalDataType eType)
+{
+ return m_externalData[eType];
+}
+
+}
diff --git a/sw/source/core/doc/DocumentFieldsManager.cxx b/sw/source/core/doc/DocumentFieldsManager.cxx
new file mode 100644
index 0000000000..66f65438c0
--- /dev/null
+++ b/sw/source/core/doc/DocumentFieldsManager.cxx
@@ -0,0 +1,1775 @@
+/* -*- 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 <DocumentFieldsManager.hxx>
+#include <config_features.h>
+#include <config_fuzzers.h>
+#include <doc.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <IDocumentState.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <redline.hxx>
+#include <rootfrm.hxx>
+#include <dbmgr.hxx>
+#include <chpfld.hxx>
+#include <dbfld.hxx>
+#include <reffld.hxx>
+#include <flddropdown.hxx>
+#include <strings.hrc>
+#include <SwUndoField.hxx>
+#include <flddat.hxx>
+#include <cntfrm.hxx>
+#include <node2lay.hxx>
+#include <section.hxx>
+#include <docufld.hxx>
+#include <calbck.hxx>
+#include <cellatr.hxx>
+#include <swtable.hxx>
+#include <frmfmt.hxx>
+#include <fmtfld.hxx>
+#include <ndtxt.hxx>
+#include <txtfld.hxx>
+#include <docfld.hxx>
+#include <hints.hxx>
+#include <docary.hxx>
+#include <fldbas.hxx>
+#include <expfld.hxx>
+#include <ddefld.hxx>
+#include <authfld.hxx>
+#include <usrfld.hxx>
+#include <ndindex.hxx>
+#include <pam.hxx>
+#include <o3tl/deleter.hxx>
+#include <osl/diagnose.h>
+#include <unotools/transliterationwrapper.hxx>
+#include <comphelper/scopeguard.hxx>
+#include <com/sun/star/uno/Any.hxx>
+
+using namespace ::com::sun::star::uno;
+
+namespace sw
+{
+ bool IsFieldDeletedInModel(IDocumentRedlineAccess const& rIDRA,
+ SwTextField const& rTextField)
+ {
+ SwRedlineTable::size_type tmp;
+ SwPosition const pos(rTextField.GetTextNode(),
+ rTextField.GetStart());
+ SwRangeRedline const*const pRedline(rIDRA.GetRedline(pos, &tmp));
+ return (pRedline
+ && pRedline->GetType() == RedlineType::Delete
+ && *pRedline->GetPoint() != *pRedline->GetMark());
+ }
+}
+
+namespace
+{
+#if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS
+
+ OUString lcl_GetDBVarName( SwDoc& rDoc, SwDBNameInfField& rDBField )
+ {
+ SwDBData aDBData( rDBField.GetDBData( &rDoc ));
+ OUString sDBNumNm;
+ SwDBData aDocData = rDoc.GetDBData();
+
+ if( aDBData != aDocData )
+ {
+ sDBNumNm = aDBData.sDataSource + OUStringChar(DB_DELIM)
+ + aDBData.sCommand + OUStringChar(DB_DELIM);
+ }
+ sDBNumNm += SwFieldType::GetTypeStr(SwFieldTypesEnum::DatabaseSetNumber);
+
+ return sDBNumNm;
+ }
+
+#endif
+
+ bool IsFieldDeleted(IDocumentRedlineAccess const& rIDRA,
+ SwRootFrame const& rLayout, SwTextField const& rTextField)
+ {
+ SwTextNode const& rNode(rTextField.GetTextNode());
+ bool const isInBody(
+ rNode.GetNodes().GetEndOfExtras().GetIndex() < rNode.GetIndex());
+ if (!isInBody && nullptr == rNode.getLayoutFrame(&rLayout))
+ { // see SwDocUpdateField::GetBodyNode() - fields in hidden sections
+ // don't have layout frames but must be updated, so use the same
+ // check as there, but do it again because GetBodyNode() checks
+ // for *any* layout...
+ return true;
+ }
+ return sw::IsFieldDeletedInModel(rIDRA, rTextField);
+ }
+
+ void lcl_CalcField( SwDoc& rDoc, SwCalc& rCalc, const SetGetExpField& rSGEField,
+ SwDBManager* pMgr, SwRootFrame const*const pLayout)
+ {
+ const SwTextField* pTextField = rSGEField.GetTextField();
+ if( !pTextField )
+ return ;
+
+ if (pLayout && pLayout->IsHideRedlines()
+ && IsFieldDeleted(rDoc.getIDocumentRedlineAccess(), *pLayout, *pTextField))
+ {
+ return;
+ }
+
+ const SwField* pField = pTextField->GetFormatField().GetField();
+ const SwFieldIds nFieldWhich = pField->GetTyp()->Which();
+
+ if( SwFieldIds::SetExp == nFieldWhich )
+ {
+ SwSbxValue aValue;
+ if( nsSwGetSetExpType::GSE_EXPR & pField->GetSubType() )
+ aValue.PutDouble( static_cast<const SwSetExpField*>(pField)->GetValue(pLayout) );
+ else
+ // Extension to calculate with Strings
+ aValue.PutString( static_cast<const SwSetExpField*>(pField)->GetExpStr(pLayout) );
+
+ // set the new value in Calculator
+ rCalc.VarChange( pField->GetTyp()->GetName(), aValue );
+ }
+ else if( pMgr )
+ {
+ #if !HAVE_FEATURE_DBCONNECTIVITY || ENABLE_FUZZERS
+ (void) rDoc;
+ #else
+ switch( nFieldWhich )
+ {
+ case SwFieldIds::DbNumSet:
+ {
+ SwDBNumSetField* pDBField = const_cast<SwDBNumSetField*>(static_cast<const SwDBNumSetField*>(pField));
+
+ SwDBData aDBData(pDBField->GetDBData(&rDoc));
+
+ if( pDBField->IsCondValid() &&
+ pMgr->OpenDataSource( aDBData.sDataSource, aDBData.sCommand ))
+ rCalc.VarChange( lcl_GetDBVarName( rDoc, *pDBField),
+ pDBField->GetFormat() );
+ }
+ break;
+ case SwFieldIds::DbNextSet:
+ {
+ SwDBNextSetField* pDBField = const_cast<SwDBNextSetField*>(static_cast<const SwDBNextSetField*>(pField));
+ SwDBData aDBData(pDBField->GetDBData(&rDoc));
+ if( !pDBField->IsCondValid() ||
+ !pMgr->OpenDataSource( aDBData.sDataSource, aDBData.sCommand ))
+ break;
+
+ OUString sDBNumNm(lcl_GetDBVarName( rDoc, *pDBField));
+ SwCalcExp* pExp = rCalc.VarLook( sDBNumNm );
+ if( pExp )
+ rCalc.VarChange( sDBNumNm, pExp->nValue.GetLong() + 1 );
+ }
+ break;
+
+ default: break;
+ }
+ #endif
+ }
+ }
+}
+
+namespace sw
+{
+
+DocumentFieldsManager::DocumentFieldsManager( SwDoc& i_rSwdoc ) : m_rDoc( i_rSwdoc ),
+ mbNewFieldLst(true),
+ mpUpdateFields(new SwDocUpdateField(m_rDoc)),
+ mpFieldTypes( new SwFieldTypes ),
+ mnLockExpField( 0 )
+{
+}
+
+const SwFieldTypes* DocumentFieldsManager::GetFieldTypes() const
+{
+ return mpFieldTypes.get();
+}
+
+/** Insert field types
+ *
+ * @param rFieldTyp ???
+ * @return Always returns a pointer to the type, if it's new or already added.
+ */
+SwFieldType* DocumentFieldsManager::InsertFieldType(const SwFieldType &rFieldTyp)
+{
+ const SwFieldTypes::size_type nSize = mpFieldTypes->size();
+ const SwFieldIds nFieldWhich = rFieldTyp.Which();
+
+ SwFieldTypes::size_type i = INIT_FLDTYPES;
+
+ switch( nFieldWhich )
+ {
+ case SwFieldIds::SetExp:
+ //JP 29.01.96: SequenceFields start at INIT_FLDTYPES - 3!!
+ // Or we get doubble number circles!!
+ //MIB 14.03.95: From now on also the SW3-Reader relies on this, when
+ //constructing string pools and when reading SetExp fields
+ if( nsSwGetSetExpType::GSE_SEQ & static_cast<const SwSetExpFieldType&>(rFieldTyp).GetType() )
+ i -= INIT_SEQ_FLDTYPES;
+ [[fallthrough]];
+ case SwFieldIds::Database:
+ case SwFieldIds::User:
+ case SwFieldIds::Dde:
+ {
+ const ::utl::TransliterationWrapper& rSCmp = GetAppCmpStrIgnore();
+ OUString sFieldNm( rFieldTyp.GetName() );
+ for( ; i < nSize; ++i )
+ if( nFieldWhich == (*mpFieldTypes)[i]->Which() &&
+ rSCmp.isEqual( sFieldNm, (*mpFieldTypes)[i]->GetName() ))
+ return (*mpFieldTypes)[i].get();
+ }
+ break;
+
+ case SwFieldIds::TableOfAuthorities:
+ for( ; i < nSize; ++i )
+ if( nFieldWhich == (*mpFieldTypes)[i]->Which() )
+ return (*mpFieldTypes)[i].get();
+ break;
+
+ default:
+ for( i = 0; i < nSize; ++i )
+ if( nFieldWhich == (*mpFieldTypes)[i]->Which() )
+ return (*mpFieldTypes)[i].get();
+ }
+
+ std::unique_ptr<SwFieldType> pNew = rFieldTyp.Copy();
+ switch( nFieldWhich )
+ {
+ case SwFieldIds::Dde:
+ static_cast<SwDDEFieldType*>(pNew.get())->SetDoc( &m_rDoc );
+ break;
+
+ case SwFieldIds::Database:
+ case SwFieldIds::Table:
+ case SwFieldIds::DateTime:
+ case SwFieldIds::GetExp:
+ static_cast<SwValueFieldType*>(pNew.get())->SetDoc( &m_rDoc );
+ break;
+
+ case SwFieldIds::User:
+ case SwFieldIds::SetExp:
+ static_cast<SwValueFieldType*>(pNew.get())->SetDoc( &m_rDoc );
+ // JP 29.07.96: Optionally prepare FieldList for Calculator:
+ mpUpdateFields->InsertFieldType( *pNew );
+ break;
+ case SwFieldIds::TableOfAuthorities :
+ static_cast<SwAuthorityFieldType*>(pNew.get())->SetDoc( &m_rDoc );
+ break;
+ default: break;
+ }
+
+ mpFieldTypes->insert( mpFieldTypes->begin() + nSize, std::move(pNew) );
+ m_rDoc.getIDocumentState().SetModified();
+
+ return (*mpFieldTypes)[ nSize ].get();
+}
+
+/// @returns the field type of the Doc
+SwFieldType *DocumentFieldsManager::GetSysFieldType( const SwFieldIds eWhich ) const
+{
+ for( SwFieldTypes::size_type i = 0; i < INIT_FLDTYPES; ++i )
+ if( eWhich == (*mpFieldTypes)[i]->Which() )
+ return (*mpFieldTypes)[i].get();
+ return nullptr;
+}
+
+/// Find first type with ResId and name
+SwFieldType* DocumentFieldsManager::GetFieldType(
+ SwFieldIds nResId,
+ const OUString& rName,
+ bool bDbFieldMatching // used in some UNO calls for SwFieldIds::Database to use different string matching code #i51815#
+ ) const
+{
+ const SwFieldTypes::size_type nSize = mpFieldTypes->size();
+ SwFieldTypes::size_type i {0};
+ const ::utl::TransliterationWrapper& rSCmp = GetAppCmpStrIgnore();
+
+ switch( nResId )
+ {
+ case SwFieldIds::SetExp:
+ //JP 29.01.96: SequenceFields start at INIT_FLDTYPES - 3!!
+ // Or we get doubble number circles!!
+ //MIB 14.03.95: From now on also the SW3-Reader relies on this, when
+ //constructing string pools and when reading SetExp fields
+ i = INIT_FLDTYPES - INIT_SEQ_FLDTYPES;
+ break;
+
+ case SwFieldIds::Database:
+ case SwFieldIds::User:
+ case SwFieldIds::Dde:
+ case SwFieldIds::TableOfAuthorities:
+ i = INIT_FLDTYPES;
+ break;
+ default: break;
+ }
+
+ SwFieldType* pRet = nullptr;
+ for( ; i < nSize; ++i )
+ {
+ SwFieldType* pFieldType = (*mpFieldTypes)[i].get();
+
+ if (nResId == pFieldType->Which())
+ {
+ OUString aFieldName( pFieldType->GetName() );
+ if (bDbFieldMatching && nResId == SwFieldIds::Database) // #i51815#
+ aFieldName = aFieldName.replace(DB_DELIM, '.');
+
+ if (rSCmp.isEqual( rName, aFieldName ))
+ {
+ pRet = pFieldType;
+ break;
+ }
+ }
+ }
+ return pRet;
+}
+
+/// Remove field type
+void DocumentFieldsManager::RemoveFieldType(size_t nField)
+{
+ OSL_ENSURE( INIT_FLDTYPES <= nField, "don't remove InitFields" );
+ /*
+ * Dependent fields present -> ErrRaise
+ */
+ if(nField >= mpFieldTypes->size())
+ return;
+
+ SwFieldType* pTmp = (*mpFieldTypes)[nField].get();
+
+ // JP 29.07.96: Optionally prepare FieldList for Calculator
+ SwFieldIds nWhich = pTmp->Which();
+ switch( nWhich )
+ {
+ case SwFieldIds::SetExp:
+ case SwFieldIds::User:
+ mpUpdateFields->RemoveFieldType( *pTmp );
+ [[fallthrough]];
+ case SwFieldIds::Dde:
+ if( pTmp->HasWriterListeners() && !m_rDoc.IsUsed( *pTmp ) )
+ {
+ if( SwFieldIds::SetExp == nWhich )
+ static_cast<SwSetExpFieldType*>(pTmp)->SetDeleted( true );
+ else if( SwFieldIds::User == nWhich )
+ static_cast<SwUserFieldType*>(pTmp)->SetDeleted( true );
+ else
+ static_cast<SwDDEFieldType*>(pTmp)->SetDeleted( true );
+ nWhich = SwFieldIds::Database;
+ }
+ break;
+ default: break;
+ }
+
+ if( nWhich != SwFieldIds::Database )
+ {
+ OSL_ENSURE( !pTmp->HasWriterListeners(), "Dependent fields present!" );
+ }
+ else
+ {
+ // coverity[leaked_storage] - at this point DB fields are ref-counted and delete themselves
+ (*mpFieldTypes)[nField].release();
+ }
+
+ mpFieldTypes->erase( mpFieldTypes->begin() + nField );
+ m_rDoc.getIDocumentState().SetModified();
+}
+
+// All have to be re-evaluated.
+void DocumentFieldsManager::UpdateFields(bool bCloseDB)
+{
+ // Tell all types to update their fields
+ for(auto const& pFieldType: *mpFieldTypes)
+ pFieldType->UpdateFields();
+
+ if(!IsExpFieldsLocked())
+ UpdateExpFields(nullptr, false); // update expression fields
+
+ // Tables
+ UpdateTableFields(nullptr);
+
+ // References
+ UpdateRefFields();
+ if(bCloseDB)
+ {
+#if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS
+ m_rDoc.GetDBManager()->CloseAll();
+#endif
+ }
+ // Only evaluate on full update
+ m_rDoc.getIDocumentState().SetModified();
+}
+
+void DocumentFieldsManager::InsDeletedFieldType( SwFieldType& rFieldTyp )
+{
+ // The FieldType was marked as deleted and removed from the array.
+ // One has to look this up again, now.
+ // - If it's not present, it can be re-inserted.
+ // - If the same type is found, the deleted one has to be renamed.
+
+ const SwFieldTypes::size_type nSize = mpFieldTypes->size();
+ const SwFieldIds nFieldWhich = rFieldTyp.Which();
+
+ OSL_ENSURE( SwFieldIds::SetExp == nFieldWhich ||
+ SwFieldIds::User == nFieldWhich ||
+ SwFieldIds::Dde == nFieldWhich, "Wrong FieldType" );
+
+ const ::utl::TransliterationWrapper& rSCmp = GetAppCmpStrIgnore();
+ const OUString& rFieldNm = rFieldTyp.GetName();
+
+ for( SwFieldTypes::size_type i = INIT_FLDTYPES; i < nSize; ++i )
+ {
+ SwFieldType* pFnd = (*mpFieldTypes)[i].get();
+ if( nFieldWhich == pFnd->Which() &&
+ rSCmp.isEqual( rFieldNm, pFnd->GetName() ) )
+ {
+ // find new name
+ SwFieldTypes::size_type nNum = 1;
+ do {
+ OUString sSrch = rFieldNm + OUString::number( nNum );
+ for( i = INIT_FLDTYPES; i < nSize; ++i )
+ {
+ pFnd = (*mpFieldTypes)[i].get();
+ if( nFieldWhich == pFnd->Which() &&
+ rSCmp.isEqual( sSrch, pFnd->GetName() ) )
+ break;
+ }
+ if( i >= nSize ) // not found
+ {
+ const_cast<OUString&>(rFieldNm) = sSrch;
+ break; // exit while loop
+ }
+ ++nNum;
+ } while( true );
+ break;
+ }
+ }
+
+ // not found, so insert, and updated deleted flag
+ mpFieldTypes->insert( mpFieldTypes->begin() + nSize, std::unique_ptr<SwFieldType>(&rFieldTyp) );
+ switch( nFieldWhich )
+ {
+ case SwFieldIds::SetExp:
+ static_cast<SwSetExpFieldType&>(rFieldTyp).SetDeleted( false );
+ break;
+ case SwFieldIds::User:
+ static_cast<SwUserFieldType&>(rFieldTyp).SetDeleted( false );
+ break;
+ case SwFieldIds::Dde:
+ static_cast<SwDDEFieldType&>(rFieldTyp).SetDeleted( false );
+ break;
+ default: break;
+ }
+}
+
+void DocumentFieldsManager::PutValueToField(const SwPosition & rPos,
+ const Any& rVal, sal_uInt16 nWhich)
+{
+ Any aOldVal;
+ SwField * pField = GetFieldAtPos(rPos);
+
+ if (m_rDoc.GetIDocumentUndoRedo().DoesUndo() &&
+ pField->QueryValue(aOldVal, nWhich))
+ {
+ m_rDoc.GetIDocumentUndoRedo().AppendUndo(
+ std::make_unique<SwUndoFieldFromAPI>(rPos, aOldVal, rVal, nWhich));
+ }
+
+ pField->PutValue(rVal, nWhich);
+}
+
+bool DocumentFieldsManager::UpdateField(SwTextField* pDstTextField, SwField& rSrcField, bool bUpdateFields)
+{
+ //static const sw::RefmarkFieldUpdate aRefMarkHint;
+ OSL_ENSURE(pDstTextField, "no field to update!");
+
+ bool bTableSelBreak = false;
+
+ SwFormatField * pDstFormatField = const_cast<SwFormatField*>(&pDstTextField->GetFormatField());
+ SwField * pDstField = pDstFormatField->GetField();
+ SwFieldIds nFieldWhich = rSrcField.GetTyp()->Which();
+ SwNodeIndex aTableNdIdx(pDstTextField->GetTextNode());
+
+ if (pDstField->GetTyp()->Which() ==
+ rSrcField.GetTyp()->Which())
+ {
+ if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ SwPosition aPosition( pDstTextField->GetTextNode(), pDstTextField->GetStart() );
+ m_rDoc.GetIDocumentUndoRedo().AppendUndo(std::make_unique<SwUndoFieldFromDoc>(aPosition, *pDstField, rSrcField, bUpdateFields));
+ }
+
+ pDstFormatField->SetField(rSrcField.CopyField());
+ SwField* pNewField = pDstFormatField->GetField();
+
+ switch( nFieldWhich )
+ {
+ case SwFieldIds::SetExp:
+ case SwFieldIds::GetExp:
+ case SwFieldIds::HiddenText:
+ case SwFieldIds::HiddenPara:
+ UpdateExpFields( pDstTextField, true );
+ break;
+
+ case SwFieldIds::Table:
+ {
+ const SwTableNode* pTableNd =
+ SwDoc::IsIdxInTable(aTableNdIdx);
+ if( pTableNd )
+ {
+ if (bUpdateFields)
+ UpdateTableFields(&pTableNd->GetTable());
+ else
+ pNewField->GetTyp()->CallSwClientNotify(sw::LegacyModifyHint(nullptr, nullptr));
+
+ if (! bUpdateFields)
+ bTableSelBreak = true;
+ }
+ }
+ break;
+
+ case SwFieldIds::Macro:
+ if( bUpdateFields && pDstTextField->GetpTextNode() )
+ pDstTextField->GetpTextNode()->TriggerNodeUpdate(sw::LegacyModifyHint(nullptr, pDstFormatField));
+ break;
+
+ case SwFieldIds::DatabaseName:
+ case SwFieldIds::DbNextSet:
+ case SwFieldIds::DbNumSet:
+ case SwFieldIds::DbSetNumber:
+ m_rDoc.ChgDBData(static_cast<SwDBNameInfField*>( pNewField)->GetRealDBData());
+ pNewField->GetTyp()->UpdateFields();
+
+ break;
+
+ case SwFieldIds::Database:
+#if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS
+ {
+ // JP 10.02.96: call ChgValue, so that the style change sets the
+ // ContentString correctly
+ SwDBField* pDBField = static_cast<SwDBField*>(pNewField);
+ if (pDBField->IsInitialized())
+ pDBField->ChgValue( pDBField->GetValue(), true );
+
+ pDBField->ClearInitialized();
+ pDBField->InitContent();
+ }
+#endif
+ [[fallthrough]];
+
+ default:
+ pDstFormatField->ForceUpdateTextNode();
+ }
+
+ // The fields we can calculate here are being triggered for an update
+ // here explicitly.
+ if( nFieldWhich == SwFieldIds::User )
+ UpdateUsrFields();
+ }
+
+ return bTableSelBreak;
+}
+
+/// Update reference and table fields
+void DocumentFieldsManager::UpdateRefFields()
+{
+ for(auto const& pFieldType: *mpFieldTypes)
+ if(SwFieldIds::GetRef == pFieldType->Which())
+ static_cast<SwGetRefFieldType*>(pFieldType.get())->UpdateGetReferences();
+}
+
+void DocumentFieldsManager::UpdateTableFields(const SwTable* pTable)
+{
+ auto pFieldType = GetFieldType( SwFieldIds::Table, OUString(), false );
+ if(pFieldType)
+ {
+ std::vector<SwFormatField*> vFields;
+ pFieldType->GatherFields(vFields);
+ for(auto pFormatField : vFields)
+ {
+ if(!pFormatField->GetTextField()->GetTextNode().FindTableNode())
+ continue;
+ SwTableField* pField = static_cast<SwTableField*>(pFormatField->GetField());
+ // re-set the value flag
+ // JP 17.06.96: internal representation of all formulas
+ // (reference to other table!!!)
+ if(pTable && nsSwExtendedSubType::SUB_CMD & pField->GetSubType())
+ pField->PtrToBoxNm(pTable);
+ else
+ // reset the value flag for all
+ pField->ChgValid(false);
+ }
+ }
+ // process all table box formulas
+ for (const SfxPoolItem* pItem : m_rDoc.GetAttrPool().GetItemSurrogates(RES_BOXATR_FORMULA))
+ {
+ auto pBoxFormula = const_cast<SwTableBoxFormula*>(pItem->DynamicWhichCast(RES_BOXATR_FORMULA));
+ if(pBoxFormula && pBoxFormula->GetDefinedIn())
+ pBoxFormula->ChangeState();
+ }
+
+ SwRootFrame const* pLayout(nullptr);
+ for (SwRootFrame const*const pLay : m_rDoc.GetAllLayouts())
+ {
+ assert(!pLayout || pLay->IsHideRedlines() == pLayout->IsHideRedlines()); // TODO
+ pLayout = pLay;
+ }
+
+ std::optional<SwCalc> oCalc;
+
+ if( pFieldType )
+ {
+ std::vector<SwFormatField*> vFields;
+ pFieldType->GatherFields(vFields);
+ for(SwFormatField* pFormatField: vFields)
+ {
+ // start calculation at the end
+ // new fields are inserted at the beginning of the modify chain
+ // that gives faster calculation on import
+ // mba: do we really need this "optimization"? Is it still valid?
+ SwTableField *const pField(static_cast<SwTableField*>(pFormatField->GetField()));
+ if (nsSwExtendedSubType::SUB_CMD & pField->GetSubType())
+ continue;
+
+ // needs to be recalculated
+ if( !pField->IsValid() )
+ {
+ // table where this field is located
+ const SwTextNode& rTextNd = pFormatField->GetTextField()->GetTextNode();
+ const SwTableNode* pTableNd = rTextNd.FindTableNode();
+ if( !pTableNd )
+ continue;
+
+ // if this field is not in the to-be-updated table, skip it
+ if(pTable && &pTableNd->GetTable() != pTable)
+ continue;
+
+ if( !oCalc )
+ oCalc.emplace( m_rDoc );
+
+ // get the values of all SetExpression fields that are valid
+ // until the table
+ SwFrame* pFrame = nullptr;
+ if( pTableNd->GetIndex() < m_rDoc.GetNodes().GetEndOfExtras().GetIndex() )
+ {
+ // is in the special section, that's expensive!
+ Point aPt; // return the first frame of the layout - Tab.Headline!!
+ std::pair<Point, bool> const tmp(aPt, true);
+ pFrame = rTextNd.getLayoutFrame(pLayout, nullptr, &tmp);
+ if( pFrame )
+ {
+ SwPosition aPos( *pTableNd );
+ if( GetBodyTextNode( m_rDoc, aPos, *pFrame ) )
+ {
+ FieldsToCalc( *oCalc, SetGetExpField(
+ aPos.GetNode(), pFormatField->GetTextField(),
+ aPos.GetContentIndex(), pFrame->GetPhyPageNum()),
+ pLayout);
+ }
+ else
+ pFrame = nullptr;
+ }
+ }
+ if( !pFrame )
+ {
+ // create index to determine the TextNode
+ SwFrame const*const pFrame2 = ::sw::FindNeighbourFrameForNode(rTextNd);
+ FieldsToCalc( *oCalc,
+ SetGetExpField(rTextNd, pFormatField->GetTextField(),
+ std::nullopt,
+ pFrame2 ? pFrame2->GetPhyPageNum() : 0),
+ pLayout);
+ }
+
+ SwTableCalcPara aPara(*oCalc, pTableNd->GetTable(), pLayout);
+ pField->CalcField( aPara );
+ if( aPara.IsStackOverflow() )
+ {
+ bool const bResult = aPara.CalcWithStackOverflow();
+ if (bResult)
+ {
+ pField->CalcField( aPara );
+ }
+ OSL_ENSURE(bResult,
+ "the chained formula could no be calculated");
+ }
+ oCalc->SetCalcError( SwCalcError::NONE );
+ }
+ pFormatField->ForceUpdateTextNode();
+ }
+ }
+
+ // calculate the formula at the boxes
+ for (const SfxPoolItem* pItem : m_rDoc.GetAttrPool().GetItemSurrogates(RES_BOXATR_FORMULA))
+ {
+ auto pFormula = const_cast<SwTableBoxFormula*>(pItem->DynamicWhichCast(RES_BOXATR_FORMULA));
+ if(!pFormula || !pFormula->GetDefinedIn() || pFormula->IsValid())
+ continue;
+ SwTableBox* pBox = pFormula->GetTableBox();
+ if(!pBox || !pBox->GetSttNd() || !pBox->GetSttNd()->GetNodes().IsDocNodes())
+ continue;
+ const SwTableNode* pTableNd = pBox->GetSttNd()->FindTableNode();
+ if(pTable && &pTableNd->GetTable() != pTable)
+ continue;
+ double nValue;
+ if( !oCalc )
+ oCalc.emplace( m_rDoc );
+
+ // get the values of all SetExpression fields that are valid
+ // until the table
+ SwFrame* pFrame = nullptr;
+ if( pTableNd->GetIndex() < m_rDoc.GetNodes().GetEndOfExtras().GetIndex() )
+ {
+ // is in the special section, that's expensive!
+ SwNodeIndex aCNdIdx( *pTableNd, +2 );
+ SwContentNode* pCNd = aCNdIdx.GetNode().GetContentNode();
+ if( !pCNd )
+ pCNd = m_rDoc.GetNodes().GoNext( &aCNdIdx );
+
+ if (pCNd)
+ {
+ Point aPt; // return the first frame of the layout - Tab.Headline!!
+ std::pair<Point, bool> const tmp(aPt, true);
+ pFrame = pCNd->getLayoutFrame(pLayout, nullptr, &tmp);
+ if( pFrame )
+ {
+ SwPosition aPos( *pCNd );
+ if( GetBodyTextNode( m_rDoc, aPos, *pFrame ) )
+ {
+ FieldsToCalc(*oCalc, SetGetExpField(aPos.GetNode(),
+ nullptr, std::nullopt, pFrame->GetPhyPageNum()),
+ pLayout);
+ }
+ else
+ pFrame = nullptr;
+ }
+ }
+ }
+ if( !pFrame )
+ {
+ // create index to determine the TextNode
+ SwFrame const*const pFrame2 = ::sw::FindNeighbourFrameForNode(*pTableNd);
+ FieldsToCalc(*oCalc, SetGetExpField(*pTableNd, nullptr, std::nullopt,
+ pFrame2 ? pFrame2->GetPhyPageNum() : 0),
+ pLayout);
+ }
+
+ SwTableCalcPara aPara(*oCalc, pTableNd->GetTable(), pLayout);
+ pFormula->Calc( aPara, nValue );
+
+ if( aPara.IsStackOverflow() )
+ {
+ bool const bResult = aPara.CalcWithStackOverflow();
+ if (bResult)
+ {
+ pFormula->Calc( aPara, nValue );
+ }
+ OSL_ENSURE(bResult,
+ "the chained formula could no be calculated");
+ }
+
+ SwFrameFormat* pFormat = pBox->ClaimFrameFormat();
+ SfxItemSetFixed<RES_BOXATR_BEGIN,RES_BOXATR_END-1> aTmp( m_rDoc.GetAttrPool() );
+
+ if( oCalc->IsCalcError() )
+ nValue = DBL_MAX;
+ aTmp.Put( SwTableBoxValue( nValue ));
+ if( SfxItemState::SET != pFormat->GetItemState( RES_BOXATR_FORMAT ))
+ aTmp.Put( SwTableBoxNumFormat( 0 ));
+ pFormat->SetFormatAttr( aTmp );
+
+ oCalc->SetCalcError( SwCalcError::NONE );
+ }
+}
+
+void DocumentFieldsManager::UpdateExpFields( SwTextField* pUpdateField, bool bUpdRefFields )
+{
+ if( IsExpFieldsLocked() || m_rDoc.IsInReading() )
+ return;
+
+ bool bOldInUpdateFields = mpUpdateFields->IsInUpdateFields();
+ mpUpdateFields->SetInUpdateFields( true );
+
+ mpUpdateFields->MakeFieldList( m_rDoc, true, GETFLD_ALL );
+ mbNewFieldLst = false;
+
+ if (mpUpdateFields->GetSortList()->empty())
+ {
+ if( bUpdRefFields )
+ UpdateRefFields();
+
+ mpUpdateFields->SetInUpdateFields( bOldInUpdateFields );
+ mpUpdateFields->SetFieldsDirty( false );
+ return ;
+ }
+
+ SwRootFrame const* pLayout(nullptr);
+ SwRootFrame const* pLayoutRLHidden(nullptr);
+ for (SwRootFrame const*const pLay : m_rDoc.GetAllLayouts())
+ {
+ if (pLay->IsHideRedlines())
+ {
+ pLayoutRLHidden = pLay;
+ }
+ else
+ {
+ pLayout = pLay;
+ }
+ }
+ if (pLayout || !pLayoutRLHidden) // always calc *something*...
+ {
+ UpdateExpFieldsImpl(pUpdateField, pLayout);
+ }
+ if (pLayoutRLHidden)
+ {
+ UpdateExpFieldsImpl(pUpdateField, pLayoutRLHidden);
+ }
+
+ // update reference fields
+ if( bUpdRefFields )
+ UpdateRefFields();
+
+ mpUpdateFields->SetInUpdateFields( bOldInUpdateFields );
+ mpUpdateFields->SetFieldsDirty( false );
+}
+
+void DocumentFieldsManager::UpdateExpFieldsImpl(
+ SwTextField * pUpdateField, SwRootFrame const*const pLayout)
+{
+ SwFieldIds nWhich;
+
+ // Hash table for all string replacements is filled on-the-fly.
+ std::unordered_map<OUString, OUString> aHashStrTable;
+
+ {
+ const SwFieldType* pFieldType;
+ // process separately:
+ for( auto n = mpFieldTypes->size(); n; )
+ {
+ pFieldType = (*mpFieldTypes)[ --n ].get();
+ switch( pFieldType->Which() )
+ {
+ case SwFieldIds::User:
+ {
+ // Entry present?
+ const OUString& rNm = pFieldType->GetName();
+ OUString sExpand(const_cast<SwUserFieldType*>(static_cast<const SwUserFieldType*>(pFieldType))->Expand(nsSwGetSetExpType::GSE_STRING, 0, LANGUAGE_SYSTEM));
+ auto pFnd = aHashStrTable.find( rNm );
+ if( pFnd != aHashStrTable.end() )
+ // modify entry in the hash table
+ pFnd->second = sExpand;
+ else
+ // insert the new entry
+ aHashStrTable.insert( { rNm, sExpand } );
+ }
+ break;
+ default: break;
+ }
+ }
+ }
+
+ // The array is filled with all fields; start calculation.
+ SwCalc aCalc( m_rDoc );
+
+#if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS
+ OUString sDBNumNm( SwFieldType::GetTypeStr( SwFieldTypesEnum::DatabaseSetNumber ) );
+
+ // already set the current record number
+ SwDBManager* pMgr = m_rDoc.GetDBManager();
+ pMgr->CloseAll( false );
+
+ SvtSysLocale aSysLocale;
+ const LocaleDataWrapper* pLclData = &aSysLocale.GetLocaleData();
+ const LanguageType nLang = pLclData->getLanguageTag().getLanguageType();
+ bool bCanFill = pMgr->FillCalcWithMergeData( m_rDoc.GetNumberFormatter(), nLang, aCalc );
+#endif
+
+ // Make sure we don't hide all content, which would lead to a crash. First, count how many visible sections we have.
+ int nShownSections = 0;
+ SwNodeOffset nContentStart = m_rDoc.GetNodes().GetEndOfContent().StartOfSectionIndex() + 1;
+ SwNodeOffset nContentEnd = m_rDoc.GetNodes().GetEndOfContent().GetIndex();
+ SwSectionFormats& rSectFormats = m_rDoc.GetSections();
+ for( SwSectionFormats::size_type n = 0; n<rSectFormats.size(); ++n )
+ {
+ SwSectionFormat& rSectFormat = *rSectFormats[ n ];
+ SwSectionNode* pSectionNode = rSectFormat.GetSectionNode();
+ SwSection* pSect = rSectFormat.GetSection();
+
+ // Usually some of the content is not in a section: count that as a virtual section, so that all real sections can be hidden.
+ // Only look for section gaps at the lowest level, ignoring sub-sections.
+ if ( pSectionNode && !rSectFormat.GetParent() )
+ {
+ SwNodeIndex aNextIdx( *pSectionNode->EndOfSectionNode(), 1 );
+ if ( n == 0 && pSectionNode->GetIndex() != nContentStart )
+ nShownSections++; //document does not start with a section
+ if ( n == rSectFormats.size() - 1 )
+ {
+ if ( aNextIdx.GetIndex() != nContentEnd )
+ nShownSections++; //document does not end in a section
+ }
+ else if ( !aNextIdx.GetNode().IsSectionNode() )
+ nShownSections++; //section is not immediately followed by another section
+ }
+
+ // count only visible sections
+ if ( pSect && !pSect->CalcHiddenFlag())
+ nShownSections++;
+ }
+
+ IDocumentRedlineAccess const& rIDRA(m_rDoc.getIDocumentRedlineAccess());
+ std::unordered_map<SwSetExpFieldType const*, SwTextNode const*> SetExpOutlineNodeMap;
+
+ for (std::unique_ptr<SetGetExpField> const& it : *mpUpdateFields->GetSortList())
+ {
+ SwSection* pSect = const_cast<SwSection*>(it->GetSection());
+ if( pSect )
+ {
+ SwSbxValue aValue = aCalc.Calculate(
+ pSect->GetCondition() );
+ if(!aValue.IsVoidValue())
+ {
+ // Do we want to hide this one?
+ bool bHide = aValue.GetBool();
+ if (bHide && !pSect->IsCondHidden())
+ {
+ // This section will be hidden, but it wasn't before
+ if (nShownSections == 1)
+ {
+ // This would be the last section, so set its condition to false, and avoid hiding it.
+ pSect->SetCondition("0");
+ bHide = false;
+ }
+ nShownSections--;
+ }
+ pSect->SetCondHidden( bHide );
+ }
+ continue;
+ }
+ ::sw::mark::IBookmark *const pBookmark(
+ const_cast<::sw::mark::IBookmark *>(it->GetBookmark()));
+ if (pBookmark)
+ {
+ SwSbxValue const aValue(aCalc.Calculate(pBookmark->GetHideCondition()));
+ if (!aValue.IsVoidValue())
+ {
+ pBookmark->Hide(aValue.GetBool());
+ }
+ continue;
+ }
+
+ SwTextField* pTextField = const_cast<SwTextField*>(it->GetTextField());
+ if( !pTextField )
+ {
+ OSL_ENSURE( false, "what's wrong now'" );
+ continue;
+ }
+
+ if (pLayout && pLayout->IsHideRedlines()
+ && IsFieldDeleted(rIDRA, *pLayout, *pTextField))
+ {
+ continue;
+ }
+
+ SwFormatField* pFormatField = const_cast<SwFormatField*>(&pTextField->GetFormatField());
+ const SwField* pField = pFormatField->GetField();
+
+ nWhich = pField->GetTyp()->Which();
+ switch( nWhich )
+ {
+ case SwFieldIds::HiddenText:
+ {
+ SwHiddenTextField* pHField = const_cast<SwHiddenTextField*>(static_cast<const SwHiddenTextField*>(pField));
+ SwSbxValue aValue = aCalc.Calculate( pHField->GetPar1() );
+ bool bValue = !aValue.GetBool();
+ if(!aValue.IsVoidValue())
+ {
+ pHField->SetValue( bValue );
+ // evaluate field
+ pHField->Evaluate(m_rDoc);
+ }
+ }
+ break;
+ case SwFieldIds::HiddenPara:
+ {
+ SwHiddenParaField* pHPField = const_cast<SwHiddenParaField*>(static_cast<const SwHiddenParaField*>(pField));
+ SwSbxValue aValue = aCalc.Calculate( pHPField->GetPar1() );
+ bool bValue = aValue.GetBool();
+ if(!aValue.IsVoidValue())
+ pHPField->SetHidden( bValue );
+ }
+ break;
+ case SwFieldIds::DbSetNumber:
+#if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS
+ {
+ const_cast<SwDBSetNumberField*>(static_cast<const SwDBSetNumberField*>(pField))->Evaluate(m_rDoc);
+ aCalc.VarChange( sDBNumNm, static_cast<const SwDBSetNumberField*>(pField)->GetSetNumber());
+ pField->ExpandField(m_rDoc.IsClipBoard(), nullptr);
+ }
+#endif
+ break;
+ case SwFieldIds::DbNextSet:
+ case SwFieldIds::DbNumSet:
+#if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS
+ {
+ UpdateDBNumFields( *const_cast<SwDBNameInfField*>(static_cast<const SwDBNameInfField*>(pField)), aCalc );
+ if( bCanFill )
+ bCanFill = pMgr->FillCalcWithMergeData( m_rDoc.GetNumberFormatter(), nLang, aCalc );
+ }
+#endif
+ break;
+ case SwFieldIds::Database:
+ {
+#if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS
+ // evaluate field
+ const_cast<SwDBField*>(static_cast<const SwDBField*>(pField))->Evaluate();
+
+ SwDBData aTmpDBData(static_cast<const SwDBField*>(pField)->GetDBData());
+
+ if( pMgr->IsDataSourceOpen(aTmpDBData.sDataSource, aTmpDBData.sCommand, false))
+ aCalc.VarChange( sDBNumNm, pMgr->GetSelectedRecordId(aTmpDBData.sDataSource, aTmpDBData.sCommand, aTmpDBData.nCommandType));
+
+ const OUString& rName = pField->GetTyp()->GetName();
+
+ // Add entry to hash table
+ // Entry present?
+ auto pFnd = aHashStrTable.find( rName );
+ OUString const value(pField->ExpandField(m_rDoc.IsClipBoard(), nullptr));
+ if( pFnd != aHashStrTable.end() )
+ {
+ // Modify entry in the hash table
+ pFnd->second = value;
+ }
+ else
+ {
+ // insert new entry
+ aHashStrTable.insert( { rName, value } );
+ }
+#endif
+ }
+ break;
+ case SwFieldIds::GetExp:
+ case SwFieldIds::SetExp:
+ {
+ if( nsSwGetSetExpType::GSE_STRING & pField->GetSubType() ) // replace String
+ {
+ if( SwFieldIds::GetExp == nWhich )
+ {
+ SwGetExpField* pGField = const_cast<SwGetExpField*>(static_cast<const SwGetExpField*>(pField));
+
+ if( (!pUpdateField || pUpdateField == pTextField )
+ && pGField->IsInBodyText() )
+ {
+ OUString aNew = LookString( aHashStrTable, pGField->GetFormula() );
+ pGField->ChgExpStr( aNew, pLayout );
+ }
+ }
+ else
+ {
+ SwSetExpField* pSField = const_cast<SwSetExpField*>(static_cast<const SwSetExpField*>(pField));
+ // is the "formula" a field?
+ OUString aNew = LookString( aHashStrTable, pSField->GetFormula() );
+
+ if( aNew.isEmpty() ) // nothing found then the formula is the new value
+ aNew = pSField->GetFormula();
+
+ // only update one field
+ if( !pUpdateField || pUpdateField == pTextField )
+ pSField->ChgExpStr( aNew, pLayout );
+
+ // lookup the field's name
+ aNew = static_cast<SwSetExpFieldType*>(pSField->GetTyp())->GetSetRefName();
+ // Entry present?
+ auto pFnd = aHashStrTable.find( aNew );
+ if( pFnd != aHashStrTable.end() )
+ // Modify entry in the hash table
+ pFnd->second = pSField->GetExpStr(pLayout);
+ else
+ // insert new entry
+ pFnd = aHashStrTable.insert( { aNew, pSField->GetExpStr(pLayout) } ).first;
+
+ // Extension for calculation with Strings
+ SwSbxValue aValue;
+ aValue.PutString( pFnd->second );
+ aCalc.VarChange( aNew, aValue );
+ }
+ }
+ else // recalculate formula
+ {
+ if( SwFieldIds::GetExp == nWhich )
+ {
+ SwGetExpField* pGField = const_cast<SwGetExpField*>(static_cast<const SwGetExpField*>(pField));
+
+ if( (!pUpdateField || pUpdateField == pTextField )
+ && pGField->IsInBodyText() )
+ {
+ SwSbxValue aValue = aCalc.Calculate(
+ pGField->GetFormula());
+ if(!aValue.IsVoidValue())
+ pGField->SetValue(aValue.GetDouble(), pLayout);
+ }
+ }
+ else
+ {
+ SwSetExpField* pSField = const_cast<SwSetExpField*>(static_cast<const SwSetExpField*>(pField));
+ SwSetExpFieldType* pSFieldTyp = static_cast<SwSetExpFieldType*>(pField->GetTyp());
+ OUString aNew = pSFieldTyp->GetName();
+
+ SwNode* pSeqNd = nullptr;
+
+ if( pSField->IsSequenceField() )
+ {
+ const sal_uInt8 nLvl = pSFieldTyp->GetOutlineLvl();
+ if( MAXLEVEL > nLvl )
+ {
+ // test if the Number needs to be updated
+ pSeqNd = m_rDoc.GetNodes()[ it->GetNode() ];
+
+ const SwTextNode* pOutlNd = pSeqNd->
+ FindOutlineNodeOfLevel(nLvl, pLayout);
+ auto const iter(SetExpOutlineNodeMap.find(pSFieldTyp));
+ if (iter == SetExpOutlineNodeMap.end()
+ || iter->second != pOutlNd)
+ {
+ SetExpOutlineNodeMap[pSFieldTyp] = pOutlNd;
+ aCalc.VarChange( aNew, 0 );
+ }
+ }
+ }
+
+ aNew += "=" + pSField->GetFormula();
+
+ SwSbxValue aValue = aCalc.Calculate( aNew );
+ if (!aCalc.IsCalcError())
+ {
+ double nErg = aValue.GetDouble();
+ // only update one field
+ if( !aValue.IsVoidValue() && (!pUpdateField || pUpdateField == pTextField) )
+ {
+ pSField->SetValue(nErg, pLayout);
+
+ if( pSeqNd )
+ pSFieldTyp->SetChapter(*pSField, *pSeqNd, pLayout);
+ }
+ }
+ }
+ }
+ }
+ break;
+ default: break;
+ } // switch
+
+ {
+ // avoid calling ReplaceText() for input fields, it is pointless
+ // here and moves the cursor if it's inside the field ...
+ SwTextInputField *const pInputField(
+ pUpdateField == pTextField // ... except once, when the dialog
+ ? nullptr // is used to change content via UpdateOneField()
+ : dynamic_cast<SwTextInputField *>(pTextField));
+ if (pInputField)
+ {
+ bool const tmp = pInputField->LockNotifyContentChange();
+ (void) tmp;
+ assert(tmp && "should not be locked here?");
+ }
+ ::comphelper::ScopeGuard g([pInputField]()
+ {
+ if (pInputField)
+ {
+ pInputField->UnlockNotifyContentChange();
+ }
+ });
+ pFormatField->ForceUpdateTextNode();
+ }
+
+ if (pUpdateField == pTextField) // if only this one is updated
+ {
+ if( SwFieldIds::GetExp == nWhich || // only GetField or
+ SwFieldIds::HiddenText == nWhich || // HiddenText?
+ SwFieldIds::HiddenPara == nWhich) // HiddenParaField?
+ break; // quit
+ pUpdateField = nullptr; // update all from here on
+ }
+ }
+
+#if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS
+ pMgr->CloseAll(false);
+#endif
+}
+
+/// Insert field type that was marked as deleted
+void DocumentFieldsManager::UpdateUsrFields()
+{
+ SwCalc* pCalc = nullptr;
+ for( SwFieldTypes::size_type i = INIT_FLDTYPES; i < mpFieldTypes->size(); ++i )
+ {
+ const SwFieldType* pFieldType = (*mpFieldTypes)[i].get();
+ if( SwFieldIds::User == pFieldType->Which() )
+ {
+ if( !pCalc )
+ pCalc = new SwCalc( m_rDoc );
+ const_cast<SwUserFieldType*>(static_cast<const SwUserFieldType*>(pFieldType))->GetValue( *pCalc );
+ }
+ }
+
+ if( pCalc )
+ {
+ delete pCalc;
+ m_rDoc.getIDocumentState().SetModified();
+ }
+}
+
+sal_Int32 DocumentFieldsManager::GetRecordsPerDocument() const
+{
+ sal_Int32 nRecords = 1;
+
+ mpUpdateFields->MakeFieldList( m_rDoc, true, GETFLD_ALL );
+ if (mpUpdateFields->GetSortList()->empty())
+ return nRecords;
+
+ for (std::unique_ptr<SetGetExpField> const& it : *mpUpdateFields->GetSortList())
+ {
+ const SwTextField *pTextField = it->GetTextField();
+ if( !pTextField )
+ continue;
+
+ const SwFormatField &pFormatField = pTextField->GetFormatField();
+ const SwField* pField = pFormatField.GetField();
+
+ switch( pField->GetTyp()->Which() )
+ {
+ case SwFieldIds::DbNextSet:
+ case SwFieldIds::DbNumSet:
+ nRecords++;
+ break;
+ default:
+ break;
+ }
+ }
+
+ return nRecords;
+}
+
+void DocumentFieldsManager::UpdatePageFields(const SwTwips nDocPos)
+{
+ for(SwFieldTypes::size_type i = 0; i < INIT_FLDTYPES; ++i)
+ {
+ SwFieldType* pFieldType = (*mpFieldTypes)[i].get();
+ switch(pFieldType->Which())
+ {
+ case SwFieldIds::PageNumber:
+ case SwFieldIds::Chapter:
+ case SwFieldIds::GetExp:
+ case SwFieldIds::RefPageGet:
+ pFieldType->UpdateDocPos(nDocPos);
+ break;
+ case SwFieldIds::DocStat:
+ pFieldType->CallSwClientNotify(sw::LegacyModifyHint(nullptr, nullptr));
+ break;
+ case SwFieldIds::GetRef:
+ static_cast<SwGetRefFieldType*>(pFieldType)->UpdateStyleReferences();
+ // Style references can vary across different pages (e.g. in header/footer)
+ // so they must be updated when page fields are
+ break;
+ default: break;
+ }
+ }
+ SetNewFieldLst(true);
+}
+
+void DocumentFieldsManager::LockExpFields()
+{
+ ++mnLockExpField;
+}
+
+void DocumentFieldsManager::UnlockExpFields()
+{
+ assert(mnLockExpField != 0);
+ if( mnLockExpField )
+ --mnLockExpField;
+}
+
+bool DocumentFieldsManager::IsExpFieldsLocked() const
+{
+ return 0 != mnLockExpField;
+}
+
+SwDocUpdateField& DocumentFieldsManager::GetUpdateFields() const
+{
+ return *mpUpdateFields;
+}
+
+bool DocumentFieldsManager::SetFieldsDirty( bool b, const SwNode* pChk, SwNodeOffset nLen )
+{
+ // See if the supplied nodes actually contain fields.
+ // If they don't, the flag doesn't need to be changed.
+ bool bFieldsFnd = false;
+ if( b && pChk && !GetUpdateFields().IsFieldsDirty() && !m_rDoc.IsInDtor()
+ // ?? what's up with Undo, this is also wanted there!
+ /*&& &pChk->GetNodes() == &GetNodes()*/ )
+ {
+ b = false;
+ if( !nLen )
+ ++nLen;
+ SwNodeOffset nStt = pChk->GetIndex();
+ const SwNodes& rNds = pChk->GetNodes();
+ while( nLen-- )
+ {
+ const SwTextNode* pTNd = rNds[ nStt++ ]->GetTextNode();
+ if( pTNd )
+ {
+ if( pTNd->GetAttrOutlineLevel() != 0 )
+ // update chapter fields
+ b = true;
+ else if( pTNd->GetpSwpHints() && pTNd->GetSwpHints().Count() )
+ {
+ const size_t nEnd = pTNd->GetSwpHints().Count();
+ for( size_t n = 0 ; n < nEnd; ++n )
+ {
+ const SwTextAttr* pAttr = pTNd->GetSwpHints().Get(n);
+ if ( pAttr->Which() == RES_TXTATR_FIELD
+ || pAttr->Which() == RES_TXTATR_INPUTFIELD)
+ {
+ b = true;
+ break;
+ }
+ }
+ }
+
+ if( b )
+ break;
+ }
+ }
+ bFieldsFnd = b;
+ }
+ GetUpdateFields().SetFieldsDirty( b );
+ return bFieldsFnd;
+}
+
+void DocumentFieldsManager::SetFixFields( const DateTime* pNewDateTime )
+{
+ bool bIsModified = m_rDoc.getIDocumentState().IsModified();
+
+ sal_Int32 nDate;
+ sal_Int64 nTime;
+ if( pNewDateTime )
+ {
+ nDate = pNewDateTime->GetDate();
+ nTime = pNewDateTime->GetTime();
+ }
+ else
+ {
+ DateTime aDateTime( DateTime::SYSTEM );
+ nDate = aDateTime.GetDate();
+ nTime = aDateTime.GetTime();
+ }
+
+ SwFieldIds const aTypes[] {
+ /*0*/ SwFieldIds::DocInfo,
+ /*1*/ SwFieldIds::Author,
+ /*2*/ SwFieldIds::ExtUser,
+ /*3*/ SwFieldIds::Filename,
+ /*4*/ SwFieldIds::DateTime }; // MUST be at the end!
+
+ for(SwFieldIds aType : aTypes)
+ {
+ std::vector<SwFormatField*> vFields;
+ GetSysFieldType(aType)->GatherFields(vFields);
+ for(auto pFormatField: vFields)
+ {
+ if (pFormatField->GetTextField())
+ {
+ bool bChgd = false;
+ switch( aType )
+ {
+ case SwFieldIds::DocInfo:
+ if( static_cast<SwDocInfoField*>(pFormatField->GetField())->IsFixed() )
+ {
+ bChgd = true;
+ SwDocInfoField* pDocInfField = static_cast<SwDocInfoField*>(pFormatField->GetField());
+ pDocInfField->SetExpansion( static_cast<SwDocInfoFieldType*>(
+ pDocInfField->GetTyp())->Expand(
+ pDocInfField->GetSubType(),
+ pDocInfField->GetFormat(),
+ pDocInfField->GetLanguage(),
+ pDocInfField->GetName() ) );
+ }
+ break;
+
+ case SwFieldIds::Author:
+ if( static_cast<SwAuthorField*>(pFormatField->GetField())->IsFixed() )
+ {
+ bChgd = true;
+ SwAuthorField* pAuthorField = static_cast<SwAuthorField*>(pFormatField->GetField());
+ pAuthorField->SetExpansion( SwAuthorFieldType::Expand( pAuthorField->GetFormat() ) );
+ }
+ break;
+
+ case SwFieldIds::ExtUser:
+ if( static_cast<SwExtUserField*>(pFormatField->GetField())->IsFixed() )
+ {
+ bChgd = true;
+ SwExtUserField* pExtUserField = static_cast<SwExtUserField*>(pFormatField->GetField());
+ pExtUserField->SetExpansion( SwExtUserFieldType::Expand(pExtUserField->GetSubType()) );
+ }
+ break;
+
+ case SwFieldIds::DateTime:
+ if( static_cast<SwDateTimeField*>(pFormatField->GetField())->IsFixed() )
+ {
+ bChgd = true;
+ static_cast<SwDateTimeField*>(pFormatField->GetField())->SetDateTime(
+ DateTime(Date(nDate), tools::Time(nTime)) );
+ }
+ break;
+
+ case SwFieldIds::Filename:
+ if( static_cast<SwFileNameField*>(pFormatField->GetField())->IsFixed() )
+ {
+ bChgd = true;
+ SwFileNameField* pFileNameField =
+ static_cast<SwFileNameField*>(pFormatField->GetField());
+ pFileNameField->SetExpansion( static_cast<SwFileNameFieldType*>(
+ pFileNameField->GetTyp())->Expand(
+ pFileNameField->GetFormat() ) );
+ }
+ break;
+ default: break;
+ }
+
+ // Trigger formatting
+ if( bChgd )
+ pFormatField->ForceUpdateTextNode();
+ }
+ }
+ }
+
+ if( !bIsModified )
+ m_rDoc.getIDocumentState().ResetModified();
+}
+
+void DocumentFieldsManager::FieldsToCalc(SwCalc& rCalc,
+ const SetGetExpField& rToThisField, SwRootFrame const*const pLayout)
+{
+ // create the sorted list of all SetFields
+ mpUpdateFields->MakeFieldList( m_rDoc, mbNewFieldLst, GETFLD_CALC );
+ mbNewFieldLst = false;
+
+#if !HAVE_FEATURE_DBCONNECTIVITY || ENABLE_FUZZERS
+ SwDBManager* pMgr = NULL;
+#else
+ SwDBManager* pMgr = m_rDoc.GetDBManager();
+ pMgr->CloseAll(false);
+#endif
+
+ if (!mpUpdateFields->GetSortList()->empty())
+ {
+ SetGetExpFields::const_iterator const itLast =
+ mpUpdateFields->GetSortList()->upper_bound(
+ &rToThisField);
+ for (auto it = mpUpdateFields->GetSortList()->begin(); it != itLast; ++it)
+ {
+ lcl_CalcField(m_rDoc, rCalc, **it, pMgr, pLayout);
+ }
+ }
+#if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS
+ pMgr->CloseAll(false);
+#endif
+}
+
+void DocumentFieldsManager::FieldsToCalc(SwCalc& rCalc,
+ SwNodeOffset const nLastNd, sal_Int32 const nLastCnt)
+{
+ // create the sorted list of all SetFields
+ mpUpdateFields->MakeFieldList( m_rDoc, mbNewFieldLst, GETFLD_CALC );
+ mbNewFieldLst = false;
+
+#if !HAVE_FEATURE_DBCONNECTIVITY || ENABLE_FUZZERS
+ SwDBManager* pMgr = NULL;
+#else
+ SwDBManager* pMgr = m_rDoc.GetDBManager();
+ pMgr->CloseAll(false);
+#endif
+
+ SwRootFrame const* pLayout(nullptr);
+ SwRootFrame const* pLayoutRLHidden(nullptr);
+ for (SwRootFrame const*const pLay : m_rDoc.GetAllLayouts())
+ {
+ if (pLay->IsHideRedlines())
+ {
+ pLayoutRLHidden = pLay;
+ }
+ else
+ {
+ pLayout = pLay;
+ }
+ }
+
+ // note this is not duplicate of the other FieldsToCalc because there is
+ // (currently) no SetGetExpField that compares only a position
+ for(auto it = mpUpdateFields->GetSortList()->begin();
+ it != mpUpdateFields->GetSortList()->end() &&
+ ( (*it)->GetNode() < nLastNd ||
+ ( (*it)->GetNode() == nLastNd && (*it)->GetContent() <= nLastCnt )
+ );
+ ++it )
+ {
+ if (pLayout || !pLayoutRLHidden) // always calc *something*...
+ {
+ lcl_CalcField( m_rDoc, rCalc, **it, pMgr, pLayout );
+ }
+ if (pLayoutRLHidden)
+ {
+ lcl_CalcField( m_rDoc, rCalc, **it, pMgr, pLayoutRLHidden );
+ }
+ }
+
+#if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS
+ pMgr->CloseAll(false);
+#endif
+}
+
+void DocumentFieldsManager::FieldsToExpand( std::unordered_map<OUString, OUString> & rHashTable,
+ const SetGetExpField& rToThisField, SwRootFrame const& rLayout)
+{
+ // create the sorted list of all SetFields
+ mpUpdateFields->MakeFieldList( m_rDoc, mbNewFieldLst, GETFLD_EXPAND );
+ mbNewFieldLst = false;
+
+ IDocumentRedlineAccess const& rIDRA(m_rDoc.getIDocumentRedlineAccess());
+
+ SetGetExpFields::const_iterator const itLast =
+ mpUpdateFields->GetSortList()->upper_bound(&rToThisField);
+
+ for (auto it = mpUpdateFields->GetSortList()->begin(); it != itLast; ++it)
+ {
+ const SwTextField* pTextField = (*it)->GetTextField();
+ if( !pTextField )
+ continue;
+
+ if (rLayout.IsHideRedlines()
+ && IsFieldDeleted(rIDRA, rLayout, *pTextField))
+ {
+ continue;
+ }
+
+ const SwField* pField = pTextField->GetFormatField().GetField();
+ switch( pField->GetTyp()->Which() )
+ {
+ case SwFieldIds::SetExp:
+ if( nsSwGetSetExpType::GSE_STRING & pField->GetSubType() )
+ {
+ // set the new value in the hash table
+ // is the formula a field?
+ SwSetExpField* pSField = const_cast<SwSetExpField*>(static_cast<const SwSetExpField*>(pField));
+ OUString aNew = LookString( rHashTable, pSField->GetFormula() );
+
+ if( aNew.isEmpty() ) // nothing found, then the formula is
+ aNew = pSField->GetFormula(); // the new value
+
+ // #i3141# - update expression of field as in method
+ // <SwDoc::UpdateExpFields(..)> for string/text fields
+ pSField->ChgExpStr(aNew, &rLayout);
+
+ // look up the field's name
+ aNew = static_cast<SwSetExpFieldType*>(pSField->GetTyp())->GetSetRefName();
+ // Entry present?
+ auto pFnd = rHashTable.find( aNew );
+ if( pFnd != rHashTable.end() )
+ // modify entry in the hash table
+ pFnd->second = pSField->GetExpStr(&rLayout);
+ else
+ // insert the new entry
+ rHashTable.insert( { aNew, pSField->GetExpStr(&rLayout) } );
+ }
+ break;
+ case SwFieldIds::Database:
+ {
+ const OUString& rName = pField->GetTyp()->GetName();
+
+ // Insert entry in the hash table
+ // Entry present?
+ auto pFnd = rHashTable.find( rName );
+ OUString const value(pField->ExpandField(m_rDoc.IsClipBoard(), nullptr));
+ if( pFnd != rHashTable.end() )
+ // modify entry in the hash table
+ pFnd->second = value;
+ else
+ // insert the new entry
+ rHashTable.insert( { rName, value } );
+ }
+ break;
+ default: break;
+ }
+ }
+}
+
+
+bool DocumentFieldsManager::IsNewFieldLst() const
+{
+ return mbNewFieldLst;
+}
+
+void DocumentFieldsManager::SetNewFieldLst(bool bFlag)
+{
+ mbNewFieldLst = bFlag;
+}
+
+void DocumentFieldsManager::InsDelFieldInFieldLst( bool bIns, const SwTextField& rField )
+{
+ if (!mbNewFieldLst && !m_rDoc.IsInDtor())
+ mpUpdateFields->InsDelFieldInFieldLst( bIns, rField );
+}
+
+SwField * DocumentFieldsManager::GetFieldAtPos(const SwPosition & rPos)
+{
+ SwTextField * const pAttr = GetTextFieldAtPos(rPos);
+
+ return pAttr ? const_cast<SwField *>( pAttr->GetFormatField().GetField() ) : nullptr;
+}
+
+SwTextField * DocumentFieldsManager::GetTextFieldAtPos(const SwPosition & rPos)
+{
+ SwTextNode * const pNode = rPos.GetNode().GetTextNode();
+
+ return (pNode != nullptr)
+ ? pNode->GetFieldTextAttrAt(rPos.GetContentIndex(), ::sw::GetTextAttrMode::Default)
+ : nullptr;
+}
+
+/// @note For simplicity assume that all field types have updatable contents so
+/// optimization currently only available when no fields exist.
+bool DocumentFieldsManager::containsUpdatableFields()
+{
+ std::vector<SwFormatField*> vFields;
+ for (auto const& pFieldType: *mpFieldTypes)
+ {
+ pFieldType->GatherFields(vFields);
+ if(vFields.size()>0)
+ return true;
+ }
+ return false;
+}
+
+/// Remove all unreferenced field types of a document
+void DocumentFieldsManager::GCFieldTypes()
+{
+ for( auto n = mpFieldTypes->size(); n > INIT_FLDTYPES; )
+ if( !(*mpFieldTypes)[ --n ]->HasWriterListeners() )
+ RemoveFieldType( n );
+}
+
+void DocumentFieldsManager::InitFieldTypes() // is being called by the CTOR
+{
+ // Field types
+ mpFieldTypes->emplace_back( new SwDateTimeFieldType(&m_rDoc) );
+ mpFieldTypes->emplace_back( new SwChapterFieldType );
+ mpFieldTypes->emplace_back( new SwPageNumberFieldType );
+ mpFieldTypes->emplace_back( new SwAuthorFieldType );
+ mpFieldTypes->emplace_back( new SwFileNameFieldType(m_rDoc) );
+ mpFieldTypes->emplace_back( new SwDBNameFieldType(&m_rDoc) );
+ mpFieldTypes->emplace_back( new SwGetExpFieldType(&m_rDoc) );
+ mpFieldTypes->emplace_back( new SwGetRefFieldType(m_rDoc) );
+ mpFieldTypes->emplace_back( new SwHiddenTextFieldType );
+ mpFieldTypes->emplace_back( new SwPostItFieldType(m_rDoc) );
+ mpFieldTypes->emplace_back( new SwDocStatFieldType(m_rDoc) );
+ mpFieldTypes->emplace_back( new SwDocInfoFieldType(&m_rDoc) );
+ mpFieldTypes->emplace_back( new SwInputFieldType( &m_rDoc ) );
+ mpFieldTypes->emplace_back( new SwTableFieldType( &m_rDoc ) );
+ mpFieldTypes->emplace_back( new SwMacroFieldType(m_rDoc) );
+ mpFieldTypes->emplace_back( new SwHiddenParaFieldType );
+ mpFieldTypes->emplace_back( new SwDBNextSetFieldType );
+ mpFieldTypes->emplace_back( new SwDBNumSetFieldType );
+ mpFieldTypes->emplace_back( new SwDBSetNumberFieldType );
+ mpFieldTypes->emplace_back( new SwTemplNameFieldType(m_rDoc) );
+ mpFieldTypes->emplace_back( new SwTemplNameFieldType(m_rDoc) );
+ mpFieldTypes->emplace_back( new SwExtUserFieldType );
+ mpFieldTypes->emplace_back( new SwRefPageSetFieldType );
+ mpFieldTypes->emplace_back( new SwRefPageGetFieldType(m_rDoc) );
+ mpFieldTypes->emplace_back( new SwJumpEditFieldType(m_rDoc) );
+ mpFieldTypes->emplace_back( new SwScriptFieldType(m_rDoc) );
+ mpFieldTypes->emplace_back( new SwCombinedCharFieldType );
+ mpFieldTypes->emplace_back( new SwDropDownFieldType );
+
+ // Types have to be at the end!
+ // We expect this in the InsertFieldType!
+ // MIB 14.04.95: In Sw3StringPool::Setup (sw3imp.cxx) and
+ // lcl_sw3io_InSetExpField (sw3field.cxx) now also
+ mpFieldTypes->emplace_back( new SwSetExpFieldType(&m_rDoc,
+ SwResId(STR_POOLCOLL_LABEL_ABB), nsSwGetSetExpType::GSE_SEQ) );
+ mpFieldTypes->emplace_back( new SwSetExpFieldType(&m_rDoc,
+ SwResId(STR_POOLCOLL_LABEL_TABLE), nsSwGetSetExpType::GSE_SEQ) );
+ mpFieldTypes->emplace_back( new SwSetExpFieldType(&m_rDoc,
+ SwResId(STR_POOLCOLL_LABEL_FRAME), nsSwGetSetExpType::GSE_SEQ) );
+ mpFieldTypes->emplace_back( new SwSetExpFieldType(&m_rDoc,
+ SwResId(STR_POOLCOLL_LABEL_DRAWING), nsSwGetSetExpType::GSE_SEQ) );
+ mpFieldTypes->emplace_back( new SwSetExpFieldType(&m_rDoc,
+ SwResId(STR_POOLCOLL_LABEL_FIGURE), nsSwGetSetExpType::GSE_SEQ) );
+
+ assert( mpFieldTypes->size() == INIT_FLDTYPES );
+}
+
+void DocumentFieldsManager::ClearFieldTypes()
+{
+ mpFieldTypes->erase( mpFieldTypes->begin() + INIT_FLDTYPES, mpFieldTypes->end() );
+}
+
+void DocumentFieldsManager::UpdateDBNumFields( SwDBNameInfField& rDBField, SwCalc& rCalc )
+{
+#if !HAVE_FEATURE_DBCONNECTIVITY || ENABLE_FUZZERS
+ (void) rDBField;
+ (void) rCalc;
+#else
+ SwDBManager* pMgr = m_rDoc.GetDBManager();
+
+ SwFieldIds nFieldType = rDBField.Which();
+
+ bool bPar1 = rCalc.Calculate( rDBField.GetPar1() ).GetBool();
+
+ if( SwFieldIds::DbNextSet == nFieldType )
+ static_cast<SwDBNextSetField&>(rDBField).SetCondValid( bPar1 );
+ else
+ static_cast<SwDBNumSetField&>(rDBField).SetCondValid( bPar1 );
+
+ if( !rDBField.GetRealDBData().sDataSource.isEmpty() )
+ {
+ // Edit a certain database
+ if( SwFieldIds::DbNextSet == nFieldType )
+ static_cast<SwDBNextSetField&>(rDBField).Evaluate(m_rDoc);
+ else
+ static_cast<SwDBNumSetField&>(rDBField).Evaluate(m_rDoc);
+
+ SwDBData aTmpDBData( rDBField.GetDBData(&m_rDoc) );
+
+ if( pMgr->OpenDataSource( aTmpDBData.sDataSource, aTmpDBData.sCommand ))
+ rCalc.VarChange( lcl_GetDBVarName( m_rDoc, rDBField),
+ pMgr->GetSelectedRecordId(aTmpDBData.sDataSource, aTmpDBData.sCommand, aTmpDBData.nCommandType) );
+ }
+ else
+ {
+ OSL_FAIL("TODO: what should happen with unnamed DBFields?");
+ }
+#endif
+}
+
+DocumentFieldsManager::~DocumentFieldsManager()
+{
+ mpUpdateFields.reset();
+ mpFieldTypes.reset();
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/DocumentLayoutManager.cxx b/sw/source/core/doc/DocumentLayoutManager.cxx
new file mode 100644
index 0000000000..6481f104c7
--- /dev/null
+++ b/sw/source/core/doc/DocumentLayoutManager.cxx
@@ -0,0 +1,497 @@
+/* -*- 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 <DocumentLayoutManager.hxx>
+#include <doc.hxx>
+#include <IDocumentState.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <DocumentContentOperationsManager.hxx>
+#include <IDocumentStylePoolAccess.hxx>
+#include <undobj.hxx>
+#include <viewsh.hxx>
+#include <layouter.hxx>
+#include <poolfmt.hxx>
+#include <frmfmt.hxx>
+#include <fmtcntnt.hxx>
+#include <fmtcnct.hxx>
+#include <ndole.hxx>
+#include <fmtanchr.hxx>
+#include <txtflcnt.hxx>
+#include <fmtflcnt.hxx>
+#include <ndtxt.hxx>
+#include <unoframe.hxx>
+#include <textboxhelper.hxx>
+#include <ndindex.hxx>
+#include <pam.hxx>
+#include <frameformats.hxx>
+#include <com/sun/star/embed/EmbedStates.hpp>
+#include <svx/svdobj.hxx>
+#include <svx/svdpage.hxx>
+#include <osl/diagnose.h>
+
+using namespace ::com::sun::star;
+
+namespace sw
+{
+
+DocumentLayoutManager::DocumentLayoutManager( SwDoc& i_rSwdoc ) :
+ m_rDoc( i_rSwdoc ),
+ mpCurrentView( nullptr )
+{
+}
+
+const SwViewShell *DocumentLayoutManager::GetCurrentViewShell() const
+{
+ return mpCurrentView;
+}
+
+SwViewShell *DocumentLayoutManager::GetCurrentViewShell()
+{
+ return mpCurrentView;
+}
+
+void DocumentLayoutManager::SetCurrentViewShell( SwViewShell* pNew )
+{
+ mpCurrentView = pNew;
+}
+
+// It must be able to communicate to a SwViewShell. This is going to be removed later.
+const SwRootFrame *DocumentLayoutManager::GetCurrentLayout() const
+{
+ if(GetCurrentViewShell())
+ return GetCurrentViewShell()->GetLayout();
+ return nullptr;
+}
+
+SwRootFrame *DocumentLayoutManager::GetCurrentLayout()
+{
+ if(GetCurrentViewShell())
+ return GetCurrentViewShell()->GetLayout();
+ return nullptr;
+}
+
+bool DocumentLayoutManager::HasLayout() const
+{
+ // if there is a view, there is always a layout
+ return (mpCurrentView != nullptr);
+}
+
+SwLayouter* DocumentLayoutManager::GetLayouter()
+{
+ return mpLayouter.get();
+}
+
+const SwLayouter* DocumentLayoutManager::GetLayouter() const
+{
+ return mpLayouter.get();
+}
+
+void DocumentLayoutManager::SetLayouter( SwLayouter* pNew )
+{
+ mpLayouter.reset( pNew );
+}
+
+/** Create a new format whose settings fit to the Request by default.
+
+ The format is put into the respective format array.
+ If there already is a fitting format, it is returned instead. */
+SwFrameFormat *DocumentLayoutManager::MakeLayoutFormat( RndStdIds eRequest, const SfxItemSet* pSet )
+{
+ SwFrameFormat *pFormat = nullptr;
+ const bool bMod = m_rDoc.getIDocumentState().IsModified();
+ bool bHeader = false;
+
+ switch ( eRequest )
+ {
+ case RndStdIds::HEADER:
+ case RndStdIds::HEADERL:
+ case RndStdIds::HEADERR:
+ {
+ bHeader = true;
+ [[fallthrough]];
+ }
+ case RndStdIds::FOOTER:
+ {
+ pFormat = new SwFrameFormat( m_rDoc.GetAttrPool(),
+ (bHeader ? "Right header" : "Right footer"),
+ m_rDoc.GetDfltFrameFormat() );
+
+ const SwNode& rEndOfAutotext( m_rDoc.GetNodes().GetEndOfAutotext() );
+ SwStartNode* pSttNd =
+ m_rDoc.GetNodes().MakeTextSection
+ ( rEndOfAutotext,
+ bHeader ? SwHeaderStartNode : SwFooterStartNode,
+ m_rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool(o3tl::narrowing<sal_uInt16>( bHeader
+ ? ( eRequest == RndStdIds::HEADERL
+ ? RES_POOLCOLL_HEADERL
+ : eRequest == RndStdIds::HEADERR
+ ? RES_POOLCOLL_HEADERR
+ : RES_POOLCOLL_HEADER )
+ : RES_POOLCOLL_FOOTER
+ ) ) );
+ pFormat->SetFormatAttr( SwFormatContent( pSttNd ));
+
+ if( pSet ) // Set a few more attributes
+ pFormat->SetFormatAttr( *pSet );
+
+ // Why set it back? Doc has changed, or not?
+ // In any case, wrong for the FlyFrames!
+ if ( !bMod )
+ m_rDoc.getIDocumentState().ResetModified();
+ }
+ break;
+
+ case RndStdIds::DRAW_OBJECT:
+ {
+ pFormat = m_rDoc.MakeDrawFrameFormat( OUString(), m_rDoc.GetDfltFrameFormat() );
+ if( pSet ) // Set a few more attributes
+ pFormat->SetFormatAttr( *pSet );
+
+ if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ m_rDoc.GetIDocumentUndoRedo().AppendUndo(
+ std::make_unique<SwUndoInsLayFormat>(pFormat, SwNodeOffset(0), 0));
+ }
+ }
+ break;
+
+#if OSL_DEBUG_LEVEL > 0
+ case RndStdIds::FLY_AT_PAGE:
+ case RndStdIds::FLY_AT_CHAR:
+ case RndStdIds::FLY_AT_FLY:
+ case RndStdIds::FLY_AT_PARA:
+ case RndStdIds::FLY_AS_CHAR:
+ OSL_FAIL( "use new interface instead: SwDoc::MakeFlySection!" );
+ break;
+#endif
+
+ default:
+ OSL_ENSURE( false,
+ "LayoutFormat was requested with an invalid Request." );
+
+ }
+ return pFormat;
+}
+
+/// Deletes the denoted format and its content.
+void DocumentLayoutManager::DelLayoutFormat( SwFrameFormat *pFormat )
+{
+ // A chain of frames needs to be merged, if necessary,
+ // so that the Frame's contents are adjusted accordingly before we destroy the Frames.
+ const SwFormatChain &rChain = pFormat->GetChain();
+ if ( rChain.GetPrev() )
+ {
+ SwFormatChain aChain( rChain.GetPrev()->GetChain() );
+ aChain.SetNext( rChain.GetNext() );
+ m_rDoc.SetAttr( aChain, *rChain.GetPrev() );
+ }
+ if ( rChain.GetNext() )
+ {
+ SwFormatChain aChain( rChain.GetNext()->GetChain() );
+ aChain.SetPrev( rChain.GetPrev() );
+ m_rDoc.SetAttr( aChain, *rChain.GetNext() );
+ }
+
+ const SwNodeIndex* pCntIdx = nullptr;
+ // The draw format doesn't own its content, it just has a pointer to it.
+ if (pFormat->Which() != RES_DRAWFRMFMT)
+ pCntIdx = pFormat->GetContent().GetContentIdx();
+ if (pCntIdx && !m_rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ // Disconnect if it's an OLE object
+ SwOLENode* pOLENd = m_rDoc.GetNodes()[ pCntIdx->GetIndex()+1 ]->GetOLENode();
+ if( pOLENd && pOLENd->GetOLEObj().IsOleRef() )
+ {
+ try
+ {
+ pOLENd->GetOLEObj().GetOleRef()->changeState( embed::EmbedStates::LOADED );
+ }
+ catch ( uno::Exception& )
+ {
+ }
+ }
+ }
+
+ // Destroy Frames
+ pFormat->DelFrames();
+
+ // Only FlyFrames are undoable at first
+ const sal_uInt16 nWh = pFormat->Which();
+ if (m_rDoc.GetIDocumentUndoRedo().DoesUndo() &&
+ (RES_FLYFRMFMT == nWh || RES_DRAWFRMFMT == nWh))
+ {
+ m_rDoc.GetIDocumentUndoRedo().AppendUndo( std::make_unique<SwUndoDelLayFormat>( pFormat ));
+ }
+ else
+ {
+ // #i32089# - delete at-frame anchored objects
+ if ( nWh == RES_FLYFRMFMT )
+ {
+ // determine frame formats of at-frame anchored objects
+ const SwNodeIndex* pContentIdx = nullptr;
+ if (pFormat->Which() != RES_DRAWFRMFMT)
+ pContentIdx = pFormat->GetContent().GetContentIdx();
+ if (pContentIdx)
+ {
+ sw::SpzFrameFormats* pSpzs = pFormat->GetDoc()->GetSpzFrameFormats();
+ if ( pSpzs )
+ {
+ std::vector<SwFrameFormat*> aToDeleteFrameFormats;
+ const SwNodeOffset nNodeIdxOfFlyFormat( pContentIdx->GetIndex() );
+
+ for(sw::SpzFrameFormat* pSpz: *pSpzs)
+ {
+ const SwFormatAnchor &rAnch = pSpz->GetAnchor();
+ if ( rAnch.GetAnchorId() == RndStdIds::FLY_AT_FLY &&
+ rAnch.GetAnchorNode()->GetIndex() == nNodeIdxOfFlyFormat )
+ {
+ aToDeleteFrameFormats.push_back(pSpz);
+ }
+ }
+
+ // delete found frame formats
+ while ( !aToDeleteFrameFormats.empty() )
+ {
+ SwFrameFormat* pTmpFormat = aToDeleteFrameFormats.back();
+ pFormat->GetDoc()->getIDocumentLayoutAccess().DelLayoutFormat( pTmpFormat );
+
+ aToDeleteFrameFormats.pop_back();
+ }
+ }
+ }
+ }
+
+ // Delete content
+ if( pCntIdx )
+ {
+ SwNode *pNode = &pCntIdx->GetNode();
+ const_cast<SwFormatContent&>(pFormat->GetFormatAttr( RES_CNTNT )).SetNewContentIdx( nullptr );
+ m_rDoc.getIDocumentContentOperations().DeleteSection( pNode );
+ }
+
+ // Delete the character for FlyFrames anchored as char (if necessary)
+ const SwFormatAnchor& rAnchor = pFormat->GetAnchor();
+ if ((RndStdIds::FLY_AS_CHAR == rAnchor.GetAnchorId()) && rAnchor.GetAnchorNode())
+ {
+ SwTextNode *pTextNd = rAnchor.GetAnchorNode()->GetTextNode();
+
+ // attribute is still in text node, delete it
+ if ( pTextNd )
+ {
+ SwTextFlyCnt* const pAttr = static_cast<SwTextFlyCnt*>(
+ pTextNd->GetTextAttrForCharAt( rAnchor.GetAnchorContentOffset(),
+ RES_TXTATR_FLYCNT ));
+ if ( pAttr && (pAttr->GetFlyCnt().GetFrameFormat() == pFormat) )
+ {
+ // don't delete, set pointer to 0
+ const_cast<SwFormatFlyCnt&>(pAttr->GetFlyCnt()).SetFlyFormat();
+ pTextNd->EraseText( *rAnchor.GetContentAnchor(), 1 );
+ }
+ }
+ }
+
+ m_rDoc.DelFrameFormat( pFormat );
+ }
+ m_rDoc.getIDocumentState().SetModified();
+}
+
+/** Copies the stated format (pSrc) to pDest and returns pDest.
+
+ If there's no pDest, it is created.
+ If the source format is located in another document, also copy correctly
+ in this case.
+ The Anchor attribute's position is always set to 0! */
+SwFrameFormat *DocumentLayoutManager::CopyLayoutFormat(
+ const SwFrameFormat& rSource,
+ const SwFormatAnchor& rNewAnchor,
+ bool bSetTextFlyAtt,
+ bool bMakeFrames )
+{
+ const bool bFly = RES_FLYFRMFMT == rSource.Which();
+ const bool bDraw = RES_DRAWFRMFMT == rSource.Which();
+ OSL_ENSURE( bFly || bDraw, "this method only works for fly or draw" );
+
+ SwDoc* pSrcDoc = const_cast<SwDoc*>(rSource.GetDoc());
+
+ // May we copy this object?
+ // We may, unless it's 1) it's a control (and therefore a draw)
+ // 2) anchored in a header/footer
+ // 3) anchored (to paragraph?)
+ bool bMayNotCopy = false;
+ const SwNode* pCAnchor = rNewAnchor.GetAnchorNode();
+ bool bInHeaderFooter = pCAnchor && m_rDoc.IsInHeaderFooter(*pCAnchor);
+ if(bDraw)
+ {
+ bool bCheckControlLayer = false;
+ rSource.CallSwClientNotify(sw::CheckDrawFrameFormatLayerHint(&bCheckControlLayer));
+ bMayNotCopy =
+ bCheckControlLayer &&
+ ((RndStdIds::FLY_AT_PARA == rNewAnchor.GetAnchorId()) || (RndStdIds::FLY_AT_FLY == rNewAnchor.GetAnchorId()) || (RndStdIds::FLY_AT_CHAR == rNewAnchor.GetAnchorId())) &&
+ bInHeaderFooter;
+ }
+
+ // just return if we can't copy this
+ if( bMayNotCopy )
+ return nullptr;
+
+ SwFrameFormat* pDest = m_rDoc.GetDfltFrameFormat();
+ if( rSource.GetRegisteredIn() != pSrcDoc->GetDfltFrameFormat() )
+ pDest = m_rDoc.CopyFrameFormat( *static_cast<const SwFrameFormat*>(rSource.GetRegisteredIn()) );
+ if( bFly )
+ {
+ // #i11176#
+ // To do a correct cloning concerning the ZOrder for all objects
+ // it is necessary to actually create a draw object for fly frames, too.
+ // These are then added to the DrawingLayer (which needs to exist).
+ // Together with correct sorting of all drawinglayer based objects
+ // before cloning ZOrder transfer works correctly then.
+ SwFlyFrameFormat *pFormat = m_rDoc.MakeFlyFrameFormat( rSource.GetName(), pDest );
+ pDest = pFormat;
+
+ SwXFrame::GetOrCreateSdrObject(*pFormat);
+ }
+ else
+ pDest = m_rDoc.MakeDrawFrameFormat( OUString(), pDest );
+
+ // Copy all other or new attributes
+ pDest->CopyAttrs( rSource );
+
+ // Do not copy chains
+ pDest->ResetFormatAttr( RES_CHAIN );
+
+ if( bFly )
+ {
+ // Duplicate the content.
+ const SwNode& rCSttNd = rSource.GetContent().GetContentIdx()->GetNode();
+ SwNodeRange aRg( rCSttNd, SwNodeOffset(1), *rCSttNd.EndOfSectionNode() );
+
+ SwStartNode* pSttNd = SwNodes::MakeEmptySection( m_rDoc.GetNodes().GetEndOfAutotext(), SwFlyStartNode );
+
+ // Set the Anchor/ContentIndex first.
+ // Within the copying part, we can access the values (DrawFormat in Headers and Footers)
+ SwNodeIndex aIdx( *pSttNd );
+ SwFormatContent aAttr( rSource.GetContent() );
+ aAttr.SetNewContentIdx( &aIdx );
+ pDest->SetFormatAttr( aAttr );
+ pDest->SetFormatAttr( rNewAnchor );
+
+ if( !m_rDoc.IsCopyIsMove() || &m_rDoc != pSrcDoc )
+ {
+ if( (m_rDoc.IsInReading() && !bInHeaderFooter) || m_rDoc.IsInMailMerge() )
+ pDest->SetFormatName( OUString() );
+ else
+ {
+ // Test first if the name is already taken, if so generate a new one.
+ SwNodeType nNdTyp = aRg.aStart.GetNode().GetNodeType();
+
+ OUString sOld( pDest->GetName() );
+ pDest->SetFormatName( OUString() );
+ if( m_rDoc.FindFlyByName( sOld, nNdTyp ) ) // found one
+ switch( nNdTyp )
+ {
+ case SwNodeType::Grf: sOld = m_rDoc.GetUniqueGrfName(sOld); break;
+ case SwNodeType::Ole: sOld = m_rDoc.GetUniqueOLEName(); break;
+ default: sOld = m_rDoc.GetUniqueFrameName(); break;
+ }
+
+ pDest->SetFormatName( sOld );
+ }
+ }
+
+ if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ m_rDoc.GetIDocumentUndoRedo().AppendUndo(std::make_unique<SwUndoInsLayFormat>(pDest,SwNodeOffset(0),0));
+ }
+
+ // Make sure that FlyFrames in FlyFrames are copied
+ aIdx = *pSttNd->EndOfSectionNode();
+
+ //fdo#36631 disable (scoped) any undo operations associated with the
+ //contact object itself. They should be managed by SwUndoInsLayFormat.
+ const ::sw::DrawUndoGuard drawUndoGuard(m_rDoc.GetIDocumentUndoRedo());
+
+ pSrcDoc->GetDocumentContentOperationsManager().CopyWithFlyInFly(aRg, aIdx.GetNode(), nullptr, false, true, true);
+ }
+ else
+ {
+ OSL_ENSURE( RES_DRAWFRMFMT == rSource.Which(), "Neither Fly nor Draw." );
+ // #i52780# - Note: moving object to visible layer not needed.
+ rSource.CallSwClientNotify(sw::DrawFormatLayoutCopyHint(static_cast<SwDrawFrameFormat&>(*pDest), m_rDoc));
+
+ if(pDest->GetAnchor() == rNewAnchor)
+ {
+ // Do *not* connect to layout, if a <MakeFrames> will not be called.
+ if(bMakeFrames)
+ pDest->CallSwClientNotify(sw::DrawFrameFormatHint(sw::DrawFrameFormatHintId::MAKE_FRAMES));
+
+ }
+ else
+ pDest->SetFormatAttr( rNewAnchor );
+
+ if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ m_rDoc.GetIDocumentUndoRedo().AppendUndo(std::make_unique<SwUndoInsLayFormat>(pDest,SwNodeOffset(0),0));
+ }
+ }
+
+ if (bSetTextFlyAtt && (RndStdIds::FLY_AS_CHAR == rNewAnchor.GetAnchorId()))
+ {
+ SwNode* pAnchorNode = rNewAnchor.GetAnchorNode();
+ SwFormatFlyCnt aFormat( pDest );
+ assert(pAnchorNode->GetTextNode() && "sw.core: text node expected");
+ if (SwTextNode *pTextNd = pAnchorNode->GetTextNode())
+ pTextNd->InsertItem( aFormat, rNewAnchor.GetAnchorContentOffset(), 0 );
+ }
+
+ if( bMakeFrames )
+ pDest->MakeFrames();
+
+ if (pDest->GetName().isEmpty())
+ {
+ // Format name should have unique name. Let's use object name as a fallback
+ SdrObject *pObj = pDest->FindSdrObject();
+ if (pObj)
+ pDest->SetFormatName(pObj->GetName());
+ }
+
+ // If the draw format has a TextBox, then copy its fly format as well.
+ if (const auto& pTextBoxes = rSource.GetOtherTextBoxFormats())
+ pTextBoxes->Clone(&m_rDoc, rNewAnchor, pDest, bSetTextFlyAtt, bMakeFrames);
+
+ return pDest;
+}
+
+//Load document from fdo#42534 under valgrind, drag the scrollbar down so full
+//document layout is triggered. Close document before layout has completed, and
+//SwAnchoredObject objects deleted by the deletion of layout remain referenced
+//by the SwLayouter
+void DocumentLayoutManager::ClearSwLayouterEntries()
+{
+ SwLayouter::ClearMovedFwdFrames( m_rDoc );
+ SwLayouter::ClearObjsTmpConsiderWrapInfluence( m_rDoc );
+ // #i65250#
+ SwLayouter::ClearMoveBwdLayoutInfo( m_rDoc );
+}
+
+DocumentLayoutManager::~DocumentLayoutManager()
+{
+}
+
+}
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
+
diff --git a/sw/source/core/doc/DocumentLinksAdministrationManager.cxx b/sw/source/core/doc/DocumentLinksAdministrationManager.cxx
new file mode 100644
index 0000000000..2d491021c3
--- /dev/null
+++ b/sw/source/core/doc/DocumentLinksAdministrationManager.cxx
@@ -0,0 +1,508 @@
+/* -*- 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 <DocumentLinksAdministrationManager.hxx>
+
+#include <doc.hxx>
+#include <DocumentSettingManager.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <IDocumentState.hxx>
+#include <IDocumentMarkAccess.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/linkmgr.hxx>
+#include <sfx2/docfile.hxx>
+#include <dialoghelp.hxx>
+#include <linkenum.hxx>
+#include <com/sun/star/document/UpdateDocMode.hpp>
+#include <swtypes.hxx>
+#include <docsh.hxx>
+#include <bookmark.hxx>
+#include <swserv.hxx>
+#include <swbaslnk.hxx>
+#include <section.hxx>
+#include <docary.hxx>
+#include <frmfmt.hxx>
+#include <fmtcntnt.hxx>
+#include <swtable.hxx>
+#include <ndtxt.hxx>
+#include <frameformats.hxx>
+#include <tools/urlobj.hxx>
+#include <unotools/charclass.hxx>
+#include <unotools/securityoptions.hxx>
+#include <utility>
+
+using namespace ::com::sun::star;
+
+//Helper functions for this file
+namespace
+{
+ ::sfx2::SvBaseLink* lcl_FindNextRemovableLink( const ::sfx2::SvBaseLinks& rLinks )
+ {
+ for (const auto& rLinkIter : rLinks)
+ {
+ ::sfx2::SvBaseLink& rLnk = *rLinkIter;
+ if ((sfx2::SvBaseLinkObjectType::ClientGraphic == rLnk.GetObjType() || sfx2::SvBaseLinkObjectType::ClientFile == rLnk.GetObjType())
+ && dynamic_cast<const SwBaseLink*>(&rLnk) != nullptr)
+ {
+ tools::SvRef<sfx2::SvBaseLink> xLink(&rLnk);
+
+ OUString sFName;
+ sfx2::LinkManager::GetDisplayNames( xLink.get(), nullptr, &sFName );
+
+ INetURLObject aURL( sFName );
+ if( INetProtocol::File == aURL.GetProtocol() ||
+ INetProtocol::Cid == aURL.GetProtocol() )
+ return &rLnk;
+ }
+ }
+ return nullptr;
+ }
+
+
+ ::sw::mark::DdeBookmark* lcl_FindDdeBookmark( const IDocumentMarkAccess& rMarkAccess, const OUString& rName, const bool bCaseSensitive )
+ {
+ //Iterating over all bookmarks, checking DdeBookmarks
+ const OUString sNameLc = bCaseSensitive ? rName : GetAppCharClass().lowercase(rName);
+ for(IDocumentMarkAccess::const_iterator_t ppMark = rMarkAccess.getAllMarksBegin();
+ ppMark != rMarkAccess.getAllMarksEnd();
+ ++ppMark)
+ {
+ if (::sw::mark::DdeBookmark* const pBkmk = dynamic_cast< ::sw::mark::DdeBookmark*>(*ppMark))
+ {
+ if (
+ (bCaseSensitive && (pBkmk->GetName() == sNameLc)) ||
+ (!bCaseSensitive && GetAppCharClass().lowercase(pBkmk->GetName()) == sNameLc)
+ )
+ {
+ return pBkmk;
+ }
+ }
+ }
+ return nullptr;
+ }
+
+
+ SwSectionNode* lcl_FindSection(const SwDoc& rDoc, const OUString& rItem, bool bCaseSensitive)
+ {
+ const OUString sCompare = bCaseSensitive ? rItem : GetAppCharClass().lowercase(rItem);
+ for (const SwSectionFormat* pSectFormat : rDoc.GetSections())
+ {
+ SwSection* pSect = pSectFormat->GetSection();
+ if (pSect)
+ {
+ OUString sNm(bCaseSensitive ? pSect->GetSectionName()
+ : GetAppCharClass().lowercase(pSect->GetSectionName()));
+ if (sNm == sCompare)
+ {
+ // found, so get the data
+ const SwNodeIndex* pIdx = pSectFormat->GetContent().GetContentIdx();
+ if (pIdx && &pSectFormat->GetDoc()->GetNodes() == &pIdx->GetNodes())
+ {
+ // a table in the normal NodesArr
+ return pIdx->GetNode().GetSectionNode();
+ }
+ // If the name is already correct, but not the rest then we don't have them.
+ // The names are always unique.
+ }
+ }
+ }
+ return nullptr;
+ }
+
+ SwTableNode* lcl_FindTable(const SwDoc& rDoc, const OUString& rItem)
+ {
+ const OUString& aItem = GetAppCharClass().lowercase(rItem);
+ for (const SwFrameFormat* pTableFormat : *rDoc.GetTableFrameFormats())
+ {
+ OUString sNm(GetAppCharClass().lowercase(pTableFormat->GetName()));
+ if (sNm == aItem)
+ {
+ SwTable* pTmpTable = SwTable::FindTable(pTableFormat);
+ if (pTmpTable)
+ {
+ SwTableBox* pFBox = pTmpTable->GetTabSortBoxes()[0];
+ if (pFBox && pFBox->GetSttNd()
+ && &pTableFormat->GetDoc()->GetNodes() == &pFBox->GetSttNd()->GetNodes())
+ {
+ // a table in the normal NodesArr
+ return const_cast<SwTableNode*>(pFBox->GetSttNd()->FindTableNode());
+ }
+ }
+ // If the name is already correct, but not the rest then we don't have them.
+ // The names are always unique.
+ }
+ }
+ return nullptr;
+ }
+
+}
+
+
+namespace sw
+{
+
+DocumentLinksAdministrationManager::DocumentLinksAdministrationManager( SwDoc& i_rSwdoc )
+ : mbVisibleLinks(true)
+ , mbLinksUpdated( false ) //#i38810#
+ , m_pLinkMgr( new sfx2::LinkManager(nullptr) )
+ , m_rDoc( i_rSwdoc )
+{
+}
+
+bool DocumentLinksAdministrationManager::IsVisibleLinks() const
+{
+ return mbVisibleLinks;
+}
+
+void DocumentLinksAdministrationManager::SetVisibleLinks(bool bFlag)
+{
+ mbVisibleLinks = bFlag;
+}
+
+sfx2::LinkManager& DocumentLinksAdministrationManager::GetLinkManager()
+{
+ return *m_pLinkMgr;
+}
+
+const sfx2::LinkManager& DocumentLinksAdministrationManager::GetLinkManager() const
+{
+ return *m_pLinkMgr;
+}
+
+// #i42634# Moved common code of SwReader::Read() and SwDocShell::UpdateLinks()
+// to new SwDoc::UpdateLinks():
+void DocumentLinksAdministrationManager::UpdateLinks()
+{
+ if (!m_rDoc.GetDocShell())
+ return;
+ SfxObjectCreateMode eMode = m_rDoc.GetDocShell()->GetCreateMode();
+ if (eMode == SfxObjectCreateMode::INTERNAL)
+ return;
+ if (eMode == SfxObjectCreateMode::ORGANIZER)
+ return;
+ if (m_rDoc.GetDocShell()->IsPreview())
+ return;
+ if (GetLinkManager().GetLinks().empty())
+ return;
+ sal_uInt16 nLinkMode = m_rDoc.GetDocumentSettingManager().getLinkUpdateMode(true);
+ sal_uInt16 nUpdateDocMode = m_rDoc.GetDocShell()->GetUpdateDocMode();
+ if (nLinkMode == NEVER && nUpdateDocMode != document::UpdateDocMode::FULL_UPDATE)
+ return;
+
+ bool bAskUpdate = nLinkMode == MANUAL;
+ bool bUpdate = true;
+ switch(nUpdateDocMode)
+ {
+ case document::UpdateDocMode::NO_UPDATE: bUpdate = false;break;
+ case document::UpdateDocMode::QUIET_UPDATE:bAskUpdate = false; break;
+ case document::UpdateDocMode::FULL_UPDATE: bAskUpdate = true; break;
+ }
+ if (nLinkMode == AUTOMATIC && !bAskUpdate)
+ {
+ SfxMedium * medium = m_rDoc.GetDocShell()->GetMedium();
+ if (!SvtSecurityOptions::isTrustedLocationUriForUpdatingLinks(
+ medium == nullptr ? OUString() : medium->GetName()))
+ {
+ bAskUpdate = true;
+ }
+ }
+ comphelper::EmbeddedObjectContainer& rEmbeddedObjectContainer = m_rDoc.GetDocShell()->getEmbeddedObjectContainer();
+ if (bUpdate)
+ {
+ rEmbeddedObjectContainer.setUserAllowsLinkUpdate(true);
+
+ weld::Window* pDlgParent = GetFrameWeld(m_rDoc.GetDocShell());
+ GetLinkManager().UpdateAllLinks(bAskUpdate, false, pDlgParent);
+ }
+ else
+ {
+ rEmbeddedObjectContainer.setUserAllowsLinkUpdate(false);
+ }
+}
+
+bool DocumentLinksAdministrationManager::GetData( const OUString& rItem, const OUString& rMimeType,
+ uno::Any & rValue ) const
+{
+ // search for bookmarks and sections case sensitive at first. If nothing is found then try again case insensitive
+ bool bCaseSensitive = true;
+ while( true )
+ {
+ ::sw::mark::DdeBookmark* const pBkmk = lcl_FindDdeBookmark(*m_rDoc.getIDocumentMarkAccess(), rItem, bCaseSensitive);
+ if(pBkmk)
+ return SwServerObject(*pBkmk).GetData(rValue, rMimeType);
+
+ // Do we already have the Item?
+ if (SwSectionNode* pSectNd = lcl_FindSection(m_rDoc, rItem, bCaseSensitive))
+ {
+ // found, so get the data
+ return SwServerObject(*pSectNd).GetData( rValue, rMimeType );
+ }
+ if( !bCaseSensitive )
+ break;
+ bCaseSensitive = false;
+ }
+
+ if (SwTableNode* pTableNd = lcl_FindTable(m_rDoc, rItem))
+ {
+ return SwServerObject(*pTableNd).GetData( rValue, rMimeType );
+ }
+
+ return false;
+}
+
+// TODO/FIXME: do something with the found items? For now, it's just an expensive no-op.
+void DocumentLinksAdministrationManager::SetData( const OUString& rItem )
+{
+ // search for bookmarks and sections case sensitive at first. If nothing is found then try again case insensitive
+ bool bCaseSensitive = true;
+ while( true )
+ {
+ ::sw::mark::DdeBookmark* const pBkmk = lcl_FindDdeBookmark(*m_rDoc.getIDocumentMarkAccess(), rItem, bCaseSensitive);
+ if(pBkmk)
+ {
+ return;
+ }
+
+ // Do we already have the Item?
+ if (lcl_FindSection(m_rDoc, rItem, bCaseSensitive))
+ {
+ // found, so get the data
+ return;
+ }
+ if( !bCaseSensitive )
+ break;
+ bCaseSensitive = false;
+ }
+
+ (void)lcl_FindTable(m_rDoc, rItem);
+}
+
+::sfx2::SvLinkSource* DocumentLinksAdministrationManager::CreateLinkSource(const OUString& rItem)
+{
+ // search for bookmarks and sections case sensitive at first. If nothing is found then try again case insensitive
+ bool bCaseSensitive = true;
+ while( true )
+ {
+ // bookmarks
+ ::sw::mark::DdeBookmark* const pBkmk = lcl_FindDdeBookmark(*m_rDoc.getIDocumentMarkAccess(), rItem, bCaseSensitive);
+ if(pBkmk && pBkmk->IsExpanded())
+ {
+ SwServerObject* pObj = pBkmk->GetRefObject();
+ if( !pObj )
+ {
+ // mark found, but no link yet -> create hotlink
+ pObj = new SwServerObject(*pBkmk);
+ pBkmk->SetRefObject(pObj);
+ GetLinkManager().InsertServer(pObj);
+ }
+ return pObj;
+ }
+
+ // sections
+ if (SwSectionNode* pSectNd = lcl_FindSection(m_rDoc, rItem, bCaseSensitive))
+ {
+ SwServerObject* pObj = pSectNd->GetSection().GetObject();
+ if( !pObj )
+ {
+ // section found, but no link yet -> create hotlink
+ pObj = new SwServerObject(*pSectNd);
+ pSectNd->GetSection().SetRefObject( pObj );
+ GetLinkManager().InsertServer(pObj);
+ }
+ return pObj;
+ }
+ if( !bCaseSensitive )
+ break;
+ bCaseSensitive = false;
+ }
+
+ // tables
+ if (SwTableNode* pTableNd = lcl_FindTable(m_rDoc, rItem))
+ {
+ SwServerObject* pObj = pTableNd->GetTable().GetObject();
+ if( !pObj )
+ {
+ // table found, but no link yet -> create hotlink
+ pObj = new SwServerObject(*pTableNd);
+ pTableNd->GetTable().SetRefObject(pObj);
+ GetLinkManager().InsertServer(pObj);
+ }
+ return pObj;
+ }
+ return nullptr;
+}
+
+/// embedded all local links (Areas/Graphics)
+bool DocumentLinksAdministrationManager::EmbedAllLinks()
+{
+ bool bRet = false;
+ sfx2::LinkManager& rLnkMgr = GetLinkManager();
+ const ::sfx2::SvBaseLinks& rLinks = rLnkMgr.GetLinks();
+ if( !rLinks.empty() )
+ {
+ ::sw::UndoGuard const undoGuard(m_rDoc.GetIDocumentUndoRedo());
+
+ ::sfx2::SvBaseLink* pLnk = nullptr;
+ while( nullptr != (pLnk = lcl_FindNextRemovableLink( rLinks ) ) )
+ {
+ tools::SvRef<sfx2::SvBaseLink> xLink = pLnk;
+ // Tell the link that it's being destroyed!
+ xLink->Closed();
+
+ // if one forgot to remove itself
+ if( xLink.is() )
+ rLnkMgr.Remove( xLink.get() );
+
+ bRet = true;
+ }
+
+ m_rDoc.GetIDocumentUndoRedo().DelAllUndoObj();
+ m_rDoc.getIDocumentState().SetModified();
+ }
+ return bRet;
+}
+
+void DocumentLinksAdministrationManager::SetLinksUpdated(const bool bNewLinksUpdated)
+{
+ mbLinksUpdated = bNewLinksUpdated;
+}
+
+bool DocumentLinksAdministrationManager::LinksUpdated() const
+{
+ return mbLinksUpdated;
+}
+
+DocumentLinksAdministrationManager::~DocumentLinksAdministrationManager()
+{
+}
+
+bool DocumentLinksAdministrationManager::SelectServerObj( std::u16string_view rStr, SwPaM*& rpPam, std::optional<SwNodeRange>& roRange ) const
+{
+ // Do we actually have the Item?
+ rpPam = nullptr;
+ roRange.reset();
+
+ OUString sItem( INetURLObject::decode( rStr,
+ INetURLObject::DecodeMechanism::WithCharset ));
+
+ sal_Int32 nPos = sItem.indexOf( cMarkSeparator );
+
+ // Extension for sections: not only link bookmarks/sections
+ // but also frames (text!), tables, outlines:
+ if( -1 != nPos )
+ {
+ OUString sName( sItem.copy( 0, nPos ) );
+ std::u16string_view sCmp( sItem.subView( nPos + 1 ));
+
+ if( sCmp == u"table" )
+ {
+ if (SwTableNode* pTableNd = lcl_FindTable(m_rDoc, sName))
+ {
+ roRange.emplace( *pTableNd, SwNodeOffset(0),
+ *pTableNd->EndOfSectionNode(), SwNodeOffset(1) );
+ }
+ return roRange.has_value();
+ }
+ else if( sCmp == u"frame" )
+ {
+ const SwFlyFrameFormat* pFlyFormat = m_rDoc.FindFlyByName( sName );
+ if( pFlyFormat )
+ {
+ SwNodeIndex* pIdx = const_cast<SwNodeIndex*>(pFlyFormat->GetContent().GetContentIdx());
+ if( pIdx )
+ {
+ SwNode* pNd = &pIdx->GetNode();
+ if( !pNd->IsNoTextNode() )
+ {
+ roRange.emplace( *pNd, SwNodeOffset(1), *pNd->EndOfSectionNode() );
+ }
+ }
+ }
+ return roRange.has_value();
+ }
+ else if( sCmp == u"region" )
+ {
+ sItem = sName; // Is being dealt with further down!
+ }
+ else if( sCmp == u"outline" )
+ {
+ SwPosition aPos( m_rDoc.GetNodes() );
+ if (m_rDoc.GotoOutline(aPos, sName, nullptr))
+ {
+ SwNode* pNd = &aPos.GetNode();
+ const int nLvl = pNd->GetTextNode()->GetAttrOutlineLevel()-1;
+
+ const SwOutlineNodes& rOutlNds = m_rDoc.GetNodes().GetOutLineNds();
+ SwOutlineNodes::size_type nTmpPos;
+ (void)rOutlNds.Seek_Entry( pNd, &nTmpPos );
+ roRange.emplace( aPos.GetNode(), SwNodeOffset(0), aPos.GetNode() );
+
+ // look for the section's end, now
+ for( ++nTmpPos;
+ nTmpPos < rOutlNds.size() &&
+ nLvl < rOutlNds[ nTmpPos ]->GetTextNode()->
+ GetAttrOutlineLevel()-1;
+ ++nTmpPos )
+ ; // there is no block
+
+ if( nTmpPos < rOutlNds.size() )
+ roRange->aEnd = *rOutlNds[ nTmpPos ];
+ else
+ roRange->aEnd = m_rDoc.GetNodes().GetEndOfContent();
+ }
+ return roRange.has_value();
+ }
+ }
+
+ // search for bookmarks and sections case sensitive at first. If nothing is found then try again case insensitive
+ bool bCaseSensitive = true;
+ while( true )
+ {
+ ::sw::mark::DdeBookmark* const pBkmk = lcl_FindDdeBookmark(*m_rDoc.getIDocumentMarkAccess(), sItem, bCaseSensitive);
+ if(pBkmk)
+ {
+ if(pBkmk->IsExpanded())
+ rpPam = new SwPaM(
+ pBkmk->GetMarkPos(),
+ pBkmk->GetOtherMarkPos());
+ return static_cast<bool>(rpPam);
+ }
+
+ if( !m_rDoc.GetSections().empty() )
+ {
+ if (SwSectionNode* pSectNd = lcl_FindSection(m_rDoc, sItem, bCaseSensitive))
+ {
+ roRange.emplace( *pSectNd, SwNodeOffset(1),
+ *pSectNd->EndOfSectionNode() );
+ return true;
+
+ }
+ }
+ if( !bCaseSensitive )
+ break;
+ bCaseSensitive = false;
+ }
+ return false;
+}
+
+
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/DocumentListItemsManager.cxx b/sw/source/core/doc/DocumentListItemsManager.cxx
new file mode 100644
index 0000000000..2a8f0691d3
--- /dev/null
+++ b/sw/source/core/doc/DocumentListItemsManager.cxx
@@ -0,0 +1,105 @@
+/* -*- 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 <DocumentListItemsManager.hxx>
+
+#include <SwNodeNum.hxx>
+#include <txtfrm.hxx>
+#include <ndtxt.hxx>
+#include <osl/diagnose.h>
+
+namespace sw
+{
+
+DocumentListItemsManager::DocumentListItemsManager() : mpListItemsList( new tImplSortedNodeNumList ) // #i83479#
+{
+}
+
+bool DocumentListItemsManager::lessThanNodeNum::operator()( const SwNodeNum* pNodeNumOne,
+ const SwNodeNum* pNodeNumTwo ) const
+{
+ return pNodeNumOne->LessThan( *pNodeNumTwo );
+}
+
+void DocumentListItemsManager::addListItem( const SwNodeNum& rNodeNum )
+{
+ if ( mpListItemsList == nullptr )
+ {
+ return;
+ }
+
+ const bool bAlreadyInserted(
+ mpListItemsList->insert( &rNodeNum ).second );
+ OSL_ENSURE( bAlreadyInserted,
+ "<DocumentListItemsManager::addListItem(..)> - <SwNodeNum> instance already registered as numbered item!" );
+}
+
+void DocumentListItemsManager::removeListItem( const SwNodeNum& rNodeNum )
+{
+ if ( mpListItemsList == nullptr )
+ {
+ return;
+ }
+
+ const tImplSortedNodeNumList::size_type nDeleted = mpListItemsList->erase( &rNodeNum );
+ if ( nDeleted > 1 )
+ {
+ OSL_FAIL( "<DocumentListItemsManager::removeListItem(..)> - <SwNodeNum> was registered more than once as numbered item!" );
+ }
+}
+
+OUString DocumentListItemsManager::getListItemText(const SwNodeNum& rNodeNum,
+ SwRootFrame const& rLayout) const
+{
+ SwTextNode const*const pNode(rNodeNum.GetTextNode());
+ assert(pNode);
+ return sw::GetExpandTextMerged(&rLayout, *pNode, true, true, ExpandMode::ExpandFootnote);
+}
+
+bool DocumentListItemsManager::isNumberedInLayout(
+ SwNodeNum const& rNodeNum, // note: this is the non-hidden Num ...
+ SwRootFrame const& rLayout) const
+{
+ return sw::IsParaPropsNode(rLayout, *rNodeNum.GetTextNode());
+}
+
+void DocumentListItemsManager::getNumItems( tSortedNodeNumList& orNodeNumList ) const
+{
+ orNodeNumList.clear();
+ orNodeNumList.reserve( mpListItemsList->size() );
+
+ for ( const SwNodeNum* pNodeNum : *mpListItemsList )
+ {
+ if ( pNodeNum->IsCounted() &&
+ pNodeNum->GetTextNode() && pNodeNum->GetTextNode()->HasNumber() )
+ {
+ orNodeNumList.push_back( pNodeNum );
+ }
+ }
+}
+
+DocumentListItemsManager::~DocumentListItemsManager()
+{
+}
+
+
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/DocumentListsManager.cxx b/sw/source/core/doc/DocumentListsManager.cxx
new file mode 100644
index 0000000000..16cb540710
--- /dev/null
+++ b/sw/source/core/doc/DocumentListsManager.cxx
@@ -0,0 +1,214 @@
+/* -*- 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 <DocumentListsManager.hxx>
+#include <doc.hxx>
+#include <list.hxx>
+#include <numrule.hxx>
+
+#include <comphelper/random.hxx>
+#include <osl/diagnose.h>
+
+
+namespace sw
+{
+
+DocumentListsManager::DocumentListsManager( SwDoc& i_rSwdoc ) : m_rDoc( i_rSwdoc )
+{
+}
+
+SwList* DocumentListsManager::createList( const OUString& rListId,
+ const OUString& sDefaultListStyleName )
+{
+ OUString sListId = rListId;
+ if ( sListId.isEmpty() )
+ {
+ sListId = CreateUniqueListId();
+ }
+
+ if ( getListByName( sListId ) )
+ {
+ OSL_FAIL( "<DocumentListsManager::createList(..)> - provided list id already used. Serious defect." );
+ return nullptr;
+ }
+
+ SwNumRule* pDefaultNumRuleForNewList = m_rDoc.FindNumRulePtr( sDefaultListStyleName );
+ if ( !pDefaultNumRuleForNewList )
+ {
+ OSL_FAIL( "<DocumentListsManager::createList(..)> - for provided default list style name no list style is found. Serious defect." );
+ return nullptr;
+ }
+
+ SwList* pNewList = new SwList( sListId, *pDefaultNumRuleForNewList, m_rDoc.GetNodes() );
+ maLists[sListId].reset(pNewList);
+
+ return pNewList;
+}
+
+SwList* DocumentListsManager::getListByName( const OUString& sListId ) const
+{
+ SwList* pList = nullptr;
+
+ auto aListIter = maLists.find( sListId );
+ if ( aListIter != maLists.end() )
+ {
+ pList = (*aListIter).second.get();
+ }
+
+ return pList;
+}
+
+void DocumentListsManager::createListForListStyle( const OUString& sListStyleName )
+{
+ if ( sListStyleName.isEmpty() )
+ {
+ OSL_FAIL( "<DocumentListsManager::createListForListStyle(..)> - no list style name provided. Serious defect." );
+ return;
+ }
+
+ if ( getListForListStyle( sListStyleName ) )
+ {
+ OSL_FAIL( "<DocumentListsManager::createListForListStyle(..)> - a list for the provided list style name already exists. Serious defect." );
+ return;
+ }
+
+ SwNumRule* pNumRule = m_rDoc.FindNumRulePtr( sListStyleName );
+ if ( !pNumRule )
+ {
+ OSL_FAIL( "<DocumentListsManager::createListForListStyle(..)> - for provided list style name no list style is found. Serious defect." );
+ return;
+ }
+
+ OUString sListId( pNumRule->GetDefaultListId() ); // can be empty String
+ if ( getListByName( sListId ) )
+ {
+ sListId.clear();
+ }
+ SwList* pNewList = createList( sListId, sListStyleName );
+ maListStyleLists[sListStyleName] = pNewList;
+ pNumRule->SetDefaultListId( pNewList->GetListId() );
+}
+
+SwList* DocumentListsManager::getListForListStyle( const OUString& sListStyleName ) const
+{
+ SwList* pList = nullptr;
+
+ std::unordered_map< OUString, SwList* >::const_iterator
+ aListIter = maListStyleLists.find( sListStyleName );
+ if ( aListIter != maListStyleLists.end() )
+ {
+ pList = (*aListIter).second;
+ }
+
+ return pList;
+}
+
+void DocumentListsManager::deleteListForListStyle( const OUString& sListStyleName )
+{
+ OUString sListId;
+ {
+ SwList* pList = getListForListStyle( sListStyleName );
+ OSL_ENSURE( pList,
+ "<DocumentListsManager::deleteListForListStyle(..)> - misusage of method: no list found for given list style name" );
+ if ( pList )
+ {
+ sListId = pList->GetListId();
+ }
+ }
+ if ( !sListId.isEmpty() )
+ {
+ maListStyleLists.erase( sListStyleName );
+ maLists.erase( sListId );
+ }
+}
+
+void DocumentListsManager::deleteListsByDefaultListStyle( const OUString& rListStyleName )
+{
+ auto aListIter = maLists.begin();
+ while ( aListIter != maLists.end() )
+ {
+ if ( (*aListIter).second->GetDefaultListStyleName() == rListStyleName )
+ {
+ aListIter = maLists.erase(aListIter);
+ }
+ else
+ ++aListIter;
+ }
+}
+
+void DocumentListsManager::trackChangeOfListStyleName( const OUString& sListStyleName,
+ const OUString& sNewListStyleName )
+{
+ SwList* pList = getListForListStyle( sListStyleName );
+ OSL_ENSURE( pList,
+ "<DocumentListsManager::changeOfListStyleName(..)> - misusage of method: no list found for given list style name" );
+
+ if ( pList != nullptr )
+ {
+ maListStyleLists.erase( sListStyleName );
+ maListStyleLists[sNewListStyleName] = pList;
+ }
+ for (auto & it : maLists) // tdf#91131 update these references too
+ {
+ if (it.second->GetDefaultListStyleName() == sListStyleName)
+ {
+ it.second->SetDefaultListStyleName(sNewListStyleName);
+ }
+ }
+}
+
+
+DocumentListsManager::~DocumentListsManager()
+{
+}
+
+
+OUString DocumentListsManager::MakeListIdUnique( const OUString& aSuggestedUniqueListId )
+{
+ tools::Long nHitCount = 0;
+ OUString aTmpStr = aSuggestedUniqueListId;
+ while ( getListByName( aTmpStr ) )
+ {
+ ++nHitCount;
+ aTmpStr = aSuggestedUniqueListId + OUString::number( nHitCount );
+ }
+
+ return aTmpStr;
+}
+
+OUString DocumentListsManager::CreateUniqueListId()
+{
+ static bool bHack = (getenv("LIBO_ONEWAY_STABLE_ODF_EXPORT") != nullptr);
+ if (bHack)
+ {
+ static sal_Int64 nIdCounter = SAL_CONST_INT64(7000000000);
+ return MakeListIdUnique( OUString( "list" + OUString::number(nIdCounter++) ) );
+ }
+ else
+ {
+ // #i92478#
+ unsigned int const n(comphelper::rng::uniform_uint_distribution(0,
+ std::numeric_limits<unsigned int>::max()));
+ OUString const aNewListId = "list" + OUString::number(n);
+ return MakeListIdUnique( aNewListId );
+ }
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/DocumentOutlineNodesManager.cxx b/sw/source/core/doc/DocumentOutlineNodesManager.cxx
new file mode 100644
index 0000000000..8d02377276
--- /dev/null
+++ b/sw/source/core/doc/DocumentOutlineNodesManager.cxx
@@ -0,0 +1,131 @@
+/* -*- 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 <DocumentOutlineNodesManager.hxx>
+#include <doc.hxx>
+#include <ndtxt.hxx>
+#include <txtfrm.hxx>
+#include <rootfrm.hxx>
+#include <modeltoviewhelper.hxx>
+#include <rtl/ustrbuf.hxx>
+
+namespace sw
+{
+
+DocumentOutlineNodesManager::DocumentOutlineNodesManager( SwDoc& i_rSwdoc ) : m_rDoc( i_rSwdoc )
+{
+}
+
+IDocumentOutlineNodes::tSortedOutlineNodeList::size_type DocumentOutlineNodesManager::getOutlineNodesCount() const
+{
+ return m_rDoc.GetNodes().GetOutLineNds().size();
+}
+
+int DocumentOutlineNodesManager::getOutlineLevel( const tSortedOutlineNodeList::size_type nIdx ) const
+{
+ return m_rDoc.GetNodes().GetOutLineNds()[ nIdx ]->
+ GetTextNode()->GetAttrOutlineLevel()-1;
+}
+
+OUString GetExpandTextMerged(SwRootFrame const*const pLayout,
+ SwTextNode const& rNode, bool const bWithNumber,
+ bool const bWithSpacesForLevel, ExpandMode const i_mode)
+{
+ if (pLayout && pLayout->HasMergedParas())
+ {
+ SwTextFrame const*const pFrame(static_cast<SwTextFrame*>(rNode.getLayoutFrame(pLayout)));
+ if (pFrame)
+ {
+ sw::MergedPara const*const pMerged = pFrame->GetMergedPara();
+ if (pMerged)
+ {
+ if (&rNode != pMerged->pParaPropsNode)
+ {
+ return OUString();
+ }
+ else
+ {
+ ExpandMode const mode(ExpandMode::HideDeletions | i_mode);
+ OUStringBuffer ret(rNode.GetExpandText(pLayout, 0, -1,
+ bWithNumber, bWithNumber, bWithSpacesForLevel, mode));
+ for (SwNodeOffset i = rNode.GetIndex() + 1;
+ i <= pMerged->pLastNode->GetIndex(); ++i)
+ {
+ SwNode *const pTmp(rNode.GetNodes()[i]);
+ if (pTmp->GetRedlineMergeFlag() == SwNode::Merge::NonFirst)
+ {
+ ret.append(pTmp->GetTextNode()->GetExpandText(
+ pLayout, 0, -1, false, false, false, mode));
+ }
+ }
+ return ret.makeStringAndClear();
+ }
+ }
+ }
+ }
+ return rNode.GetExpandText(pLayout, 0, -1, bWithNumber,
+ bWithNumber, bWithSpacesForLevel, i_mode);
+}
+
+OUString DocumentOutlineNodesManager::getOutlineText(
+ const tSortedOutlineNodeList::size_type nIdx,
+ SwRootFrame const*const pLayout,
+ const bool bWithNumber,
+ const bool bWithSpacesForLevel,
+ const bool bWithFootnote ) const
+{
+ SwTextNode const*const pNode(m_rDoc.GetNodes().GetOutLineNds()[ nIdx ]->GetTextNode());
+ return GetExpandTextMerged(pLayout, *pNode,
+ bWithNumber, bWithSpacesForLevel,
+ (bWithFootnote ? ExpandMode::ExpandFootnote : ExpandMode(0)));
+}
+
+SwTextNode* DocumentOutlineNodesManager::getOutlineNode( const tSortedOutlineNodeList::size_type nIdx ) const
+{
+ return m_rDoc.GetNodes().GetOutLineNds()[ nIdx ]->GetTextNode();
+}
+
+bool DocumentOutlineNodesManager::isOutlineInLayout(
+ const tSortedOutlineNodeList::size_type nIdx,
+ SwRootFrame const& rLayout) const
+{
+ auto const pNode(m_rDoc.GetNodes().GetOutLineNds()[ nIdx ]->GetTextNode());
+ return sw::IsParaPropsNode(rLayout, *pNode);
+}
+
+void DocumentOutlineNodesManager::getOutlineNodes( IDocumentOutlineNodes::tSortedOutlineNodeList& orOutlineNodeList ) const
+{
+ orOutlineNodeList.clear();
+ orOutlineNodeList.reserve( getOutlineNodesCount() );
+
+ const tSortedOutlineNodeList::size_type nOutlCount = getOutlineNodesCount();
+ for ( tSortedOutlineNodeList::size_type i = 0; i < nOutlCount; ++i )
+ {
+ orOutlineNodeList.push_back(
+ m_rDoc.GetNodes().GetOutLineNds()[i]->GetTextNode() );
+ }
+}
+
+DocumentOutlineNodesManager::~DocumentOutlineNodesManager()
+{
+}
+
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/DocumentRedlineManager.cxx b/sw/source/core/doc/DocumentRedlineManager.cxx
new file mode 100644
index 0000000000..8d52c814e8
--- /dev/null
+++ b/sw/source/core/doc/DocumentRedlineManager.cxx
@@ -0,0 +1,3940 @@
+/* -*- 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 <DocumentRedlineManager.hxx>
+#include <frmfmt.hxx>
+#include <rootfrm.hxx>
+#include <txtfrm.hxx>
+#include <doc.hxx>
+#include <docsh.hxx>
+#include <wrtsh.hxx>
+#include <fmtfld.hxx>
+#include <frmtool.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <IDocumentFieldsAccess.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <IDocumentState.hxx>
+#include <redline.hxx>
+#include <UndoRedline.hxx>
+#include <docary.hxx>
+#include <ndtxt.hxx>
+#include <unocrsr.hxx>
+#include <ftnidx.hxx>
+#include <authfld.hxx>
+#include <strings.hrc>
+#include <swmodule.hxx>
+#include <osl/diagnose.h>
+#include <editeng/prntitem.hxx>
+
+using namespace com::sun::star;
+
+#ifdef DBG_UTIL
+
+ #define ERROR_PREFIX "redline table corrupted: "
+
+ namespace
+ {
+ // helper function for lcl_CheckRedline
+ // 1. make sure that pPos->nContent points into pPos->nNode
+ // 2. check that position is valid and doesn't point after text
+ void lcl_CheckPosition( const SwPosition* pPos )
+ {
+ assert(dynamic_cast<SwContentIndexReg*>(&pPos->GetNode())
+ == pPos->GetContentNode());
+
+ SwTextNode* pTextNode = pPos->GetNode().GetTextNode();
+ if( pTextNode == nullptr )
+ {
+ assert(pPos->GetContentIndex() == 0);
+ }
+ else
+ {
+ assert(pPos->GetContentIndex() >= 0 && pPos->GetContentIndex() <= pTextNode->Len());
+ }
+ }
+
+ void lcl_CheckPam( const SwPaM* pPam )
+ {
+ assert(pPam);
+ lcl_CheckPosition( pPam->GetPoint() );
+ lcl_CheckPosition( pPam->GetMark() );
+ }
+
+ // check validity of the redline table. Checks redline bounds, and make
+ // sure the redlines are sorted and non-overlapping.
+ void lcl_CheckRedline( const IDocumentRedlineAccess& redlineAccess )
+ {
+ const SwRedlineTable& rTable = redlineAccess.GetRedlineTable();
+
+ // verify valid redline positions
+ for(SwRangeRedline* i : rTable)
+ lcl_CheckPam( i );
+
+ for(SwRangeRedline* j : rTable)
+ {
+ // check for empty redlines
+ // note: these can destroy sorting in SwTextNode::Update()
+ // if there's another one without mark on the same pos.
+ OSL_ENSURE( ( *(j->GetPoint()) != *(j->GetMark()) ) ||
+ ( j->GetContentIdx() != nullptr ),
+ ERROR_PREFIX "empty redline" );
+ }
+
+ // verify proper redline sorting
+ for( size_t n = 1; n < rTable.size(); ++n )
+ {
+ const SwRangeRedline* pPrev = rTable[ n-1 ];
+ const SwRangeRedline* pCurrent = rTable[ n ];
+
+ // check redline sorting
+ SAL_WARN_IF( *pPrev->Start() > *pCurrent->Start(), "sw",
+ ERROR_PREFIX "not sorted correctly" );
+
+ // check for overlapping redlines
+ SAL_WARN_IF( *pPrev->End() > *pCurrent->Start(), "sw",
+ ERROR_PREFIX "overlapping redlines" );
+ }
+
+ assert(std::is_sorted(rTable.begin(), rTable.end(), CompareSwRedlineTable()));
+ }
+ }
+
+ #define CHECK_REDLINE( pDoc ) lcl_CheckRedline( pDoc );
+
+#else
+
+ #define CHECK_REDLINE( pDoc )
+
+#endif
+
+namespace sw {
+
+static void UpdateFieldsForRedline(IDocumentFieldsAccess & rIDFA)
+{
+ auto const pAuthType(static_cast<SwAuthorityFieldType*>(rIDFA.GetFieldType(
+ SwFieldIds::TableOfAuthorities, OUString(), false)));
+ if (pAuthType) // created on demand...
+ {
+ pAuthType->DelSequenceArray();
+ }
+ rIDFA.GetFieldType(SwFieldIds::RefPageGet, OUString(), false)->UpdateFields();
+ rIDFA.GetSysFieldType(SwFieldIds::Chapter)->UpdateFields();
+ rIDFA.UpdateExpFields(nullptr, false);
+ rIDFA.UpdateRefFields();
+}
+
+void UpdateFramesForAddDeleteRedline(SwDoc & rDoc, SwPaM const& rPam)
+{
+ if (rDoc.IsClipBoard())
+ {
+ return;
+ }
+ // no need to call UpdateFootnoteNums for FTNNUM_PAGE:
+ // the AppendFootnote/RemoveFootnote will do it by itself!
+ rDoc.GetFootnoteIdxs().UpdateFootnote(rPam.Start()->GetNode());
+ SwPosition currentStart(*rPam.Start());
+ SwTextNode * pStartNode(rPam.Start()->GetNode().GetTextNode());
+ while (!pStartNode)
+ {
+ // note: branch only taken for redlines, not fieldmarks
+ SwStartNode *const pTableOrSectionNode(
+ currentStart.GetNode().IsTableNode()
+ ? static_cast<SwStartNode*>(currentStart.GetNode().GetTableNode())
+ : static_cast<SwStartNode*>(currentStart.GetNode().GetSectionNode()));
+ if ( !pTableOrSectionNode )
+ {
+ SAL_WARN("sw.core", "UpdateFramesForAddDeleteRedline:: known pathology (or ChangesInRedline mode)");
+ return;
+ }
+ for (SwNodeOffset j = pTableOrSectionNode->GetIndex(); j <= pTableOrSectionNode->EndOfSectionIndex(); ++j)
+ {
+ pTableOrSectionNode->GetNodes()[j]->SetRedlineMergeFlag(SwNode::Merge::Hidden);
+ }
+ for (SwRootFrame const*const pLayout : rDoc.GetAllLayouts())
+ {
+ if (pLayout->HasMergedParas())
+ {
+ if (pTableOrSectionNode->IsTableNode())
+ {
+ static_cast<SwTableNode*>(pTableOrSectionNode)->DelFrames(pLayout);
+ }
+ else
+ {
+ static_cast<SwSectionNode*>(pTableOrSectionNode)->DelFrames(pLayout);
+ }
+ }
+ }
+ currentStart.Assign( pTableOrSectionNode->EndOfSectionIndex() + 1 );
+ pStartNode = currentStart.GetNode().GetTextNode();
+ }
+ if (currentStart < *rPam.End())
+ {
+ SwTextNode * pNode(pStartNode);
+ do
+ {
+ // deleted text node: remove it from "hidden" list
+ // to update numbering in Show Changes mode
+ SwPosition aPos( *pNode, pNode->Len() );
+ if ( pNode->GetNumRule() && aPos < *rPam.End() )
+ pNode->RemoveFromListRLHidden();
+
+ std::vector<SwTextFrame*> frames;
+ SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(*pNode);
+ for (SwTextFrame * pFrame = aIter.First(); pFrame; pFrame = aIter.Next())
+ {
+ if (pFrame->getRootFrame()->HasMergedParas())
+ {
+ frames.push_back(pFrame);
+ }
+ // set anchored objects as deleted
+ pFrame->SetDrawObjsAsDeleted(true);
+ }
+ if (frames.empty())
+ {
+ auto const& layouts(rDoc.GetAllLayouts());
+ assert(std::none_of(layouts.begin(), layouts.end(),
+ [](SwRootFrame const*const pLayout) { return pLayout->IsHideRedlines(); }));
+ (void) layouts;
+ break;
+ }
+ auto eMode(sw::FrameMode::Existing);
+ SwTextNode * pLast(pNode);
+ for (SwTextFrame * pFrame : frames)
+ {
+ SwTextNode & rFirstNode(pFrame->GetMergedPara()
+ ? *pFrame->GetMergedPara()->pFirstNode
+ : *pNode);
+ assert(pNode == pStartNode
+ ? rFirstNode.GetIndex() <= pNode->GetIndex()
+ : &rFirstNode == pNode);
+ // clear old one first to avoid DelFrames confusing updates & asserts...
+ pFrame->SetMergedPara(nullptr);
+ pFrame->SetMergedPara(sw::CheckParaRedlineMerge(
+ *pFrame, rFirstNode, eMode));
+ eMode = sw::FrameMode::New; // Existing is not idempotent!
+ // the first node of the new redline is not necessarily the first
+ // node of the merged frame, there could be another redline nearby
+ sw::AddRemoveFlysAnchoredToFrameStartingAtNode(*pFrame, *pNode, nullptr);
+ // if redline is split across table and table cell is empty, there's no redline in the cell and so no merged para
+ if (pFrame->GetMergedPara())
+ {
+ pLast = const_cast<SwTextNode*>(pFrame->GetMergedPara()->pLastNode);
+ }
+ }
+ SwNodeIndex tmp(*pLast);
+ // skip over hidden sections!
+ pNode = static_cast<SwTextNode*>(pLast->GetNodes().GoNextSection(&tmp, /*bSkipHidden=*/true, /*bSkipProtect=*/false));
+ }
+ while (pNode && pNode->GetIndex() <= rPam.End()->GetNodeIndex());
+ }
+ // fields last - SwGetRefField::UpdateField requires up-to-date frames
+ UpdateFieldsForRedline(rDoc.getIDocumentFieldsAccess()); // after footnotes
+
+ // update SwPostItMgr / notes in the margin
+ rDoc.GetDocShell()->Broadcast(
+ SwFormatFieldHint(nullptr, SwFormatFieldHintWhich::REMOVED) );
+}
+
+void UpdateFramesForRemoveDeleteRedline(SwDoc & rDoc, SwPaM const& rPam)
+{
+ // tdf#147006 fieldmark command may be empty => do not call AppendAllObjs()
+ if (rDoc.IsClipBoard() || *rPam.GetPoint() == *rPam.GetMark())
+ {
+ return;
+ }
+ bool isAppendObjsCalled(false);
+ rDoc.GetFootnoteIdxs().UpdateFootnote(rPam.Start()->GetNode());
+ SwPosition currentStart(*rPam.Start());
+ SwTextNode * pStartNode(rPam.Start()->GetNode().GetTextNode());
+ while (!pStartNode)
+ {
+ // note: branch only taken for redlines, not fieldmarks
+ SwStartNode *const pTableOrSectionNode(
+ currentStart.GetNode().IsTableNode()
+ ? static_cast<SwStartNode*>(currentStart.GetNode().GetTableNode())
+ : static_cast<SwStartNode*>(currentStart.GetNode().GetSectionNode()));
+ assert(pTableOrSectionNode); // known pathology
+ for (SwNodeOffset j = pTableOrSectionNode->GetIndex(); j <= pTableOrSectionNode->EndOfSectionIndex(); ++j)
+ {
+ pTableOrSectionNode->GetNodes()[j]->SetRedlineMergeFlag(SwNode::Merge::None);
+ }
+ if (rDoc.getIDocumentLayoutAccess().GetCurrentLayout()->HasMergedParas())
+ {
+ // note: this will also create frames for all currently hidden flys
+ // because it calls AppendAllObjs
+ ::MakeFrames(&rDoc, currentStart.GetNode(), *pTableOrSectionNode->EndOfSectionNode());
+ isAppendObjsCalled = true;
+ }
+ currentStart.Assign( pTableOrSectionNode->EndOfSectionIndex() + 1 );
+ pStartNode = currentStart.GetNode().GetTextNode();
+ }
+ if (currentStart < *rPam.End())
+ {
+ SwTextNode * pNode(pStartNode);
+ do
+ {
+ // undeleted text node: add it to the "hidden" list
+ // to update numbering in Show Changes mode
+ SwPosition aPos( *pNode, pNode->Len() );
+ if ( pNode->GetNumRule() && aPos < *rPam.End() )
+ pNode->AddToListRLHidden();
+
+ std::vector<SwTextFrame*> frames;
+ SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(*pNode);
+ for (SwTextFrame * pFrame = aIter.First(); pFrame; pFrame = aIter.Next())
+ {
+ if (pFrame->getRootFrame()->HasMergedParas())
+ {
+ frames.push_back(pFrame);
+ }
+ // set anchored objects as not deleted
+ pFrame->SetDrawObjsAsDeleted(false);
+ }
+ if (frames.empty())
+ {
+ // in SwUndoSaveSection::SaveSection(), DelFrames() preceded this call
+ if (!pNode->FindTableBoxStartNode() && !pNode->FindFlyStartNode())
+ {
+ auto const& layouts(rDoc.GetAllLayouts());
+ assert(std::none_of(layouts.begin(), layouts.end(),
+ [](SwRootFrame const*const pLayout) { return pLayout->IsHideRedlines(); }));
+ (void) layouts;
+ }
+ isAppendObjsCalled = true; // skip that!
+ break;
+ }
+
+ // no nodes can be unmerged by this - skip MakeFrames() etc.
+ if (rPam.GetPoint()->GetNode() == rPam.GetMark()->GetNode())
+ {
+ break; // continue with AppendAllObjs()
+ }
+
+ // first, call CheckParaRedlineMerge on the first paragraph,
+ // to init flag on new merge range (if any) + 1st node post the merge
+ auto eMode(sw::FrameMode::Existing);
+ SwTextNode * pLast(pNode);
+ for (SwTextFrame * pFrame : frames)
+ {
+ if (auto const pMergedPara = pFrame->GetMergedPara())
+ {
+ pLast = const_cast<SwTextNode*>(pMergedPara->pLastNode);
+ assert(pNode == pStartNode
+ ? pMergedPara->pFirstNode->GetIndex() <= pNode->GetIndex()
+ : pMergedPara->pFirstNode == pNode);
+ // clear old one first to avoid DelFrames confusing updates & asserts...
+ SwTextNode & rFirstNode(*pMergedPara->pFirstNode);
+ pFrame->SetMergedPara(nullptr);
+ pFrame->SetMergedPara(sw::CheckParaRedlineMerge(
+ *pFrame, rFirstNode, eMode));
+ eMode = sw::FrameMode::New; // Existing is not idempotent!
+ // update pNode so MakeFrames starts on 2nd node
+ pNode = &rFirstNode;
+ }
+ }
+ if (pLast != pNode)
+ {
+ // now start node until end of merge + 1 has proper flags; MakeFrames
+ // should pick up from the next node in need of frames by checking flags
+ SwNodeIndex const start(*pNode, +1);
+ SwNodeIndex const end(*pLast, +1); // end is exclusive
+ // note: this will also create frames for all currently hidden flys
+ // both on first and non-first nodes because it calls AppendAllObjs
+ ::MakeFrames(&rDoc, start.GetNode(), end.GetNode());
+ isAppendObjsCalled = true;
+ // re-use this to move flys that are now on the wrong frame, with end
+ // of redline as "second" node; the nodes between start and end should
+ // be complete with MakeFrames already
+ sw::MoveMergedFlysAndFootnotes(frames, *pNode, *pLast, false);
+ }
+ SwNodeIndex tmp(*pLast);
+ // skip over hidden sections!
+ pNode = static_cast<SwTextNode*>(pLast->GetNodes().GoNextSection(&tmp, /*bSkipHidden=*/true, /*bSkipProtect=*/false));
+ }
+ while (pNode && pNode->GetIndex() <= rPam.End()->GetNodeIndex());
+ }
+
+ if (!isAppendObjsCalled)
+ { // recreate flys in the one node the hard way...
+ for (auto const& pLayout : rDoc.GetAllLayouts())
+ {
+ if (pLayout->HasMergedParas())
+ {
+ AppendAllObjs(rDoc.GetSpzFrameFormats(), pLayout);
+ break;
+ }
+ }
+ }
+ // fields last - SwGetRefField::UpdateField requires up-to-date frames
+ UpdateFieldsForRedline(rDoc.getIDocumentFieldsAccess()); // after footnotes
+
+ // update SwPostItMgr / notes in the margin
+ rDoc.GetDocShell()->Broadcast(
+ SwFormatFieldHint(nullptr, SwFormatFieldHintWhich::INSERTED) );
+}
+
+} // namespace sw
+
+namespace
+{
+ bool IsPrevPos( const SwPosition & rPos1, const SwPosition & rPos2 )
+ {
+ const SwContentNode* pCNd;
+ if( 0 != rPos2.GetContentIndex() )
+ return false;
+ if( rPos2.GetNodeIndex() - 1 != rPos1.GetNodeIndex() )
+ return false;
+ pCNd = rPos1.GetNode().GetContentNode();
+ return pCNd && rPos1.GetContentIndex() == pCNd->Len();
+ }
+
+ // copy style or return with SwRedlineExtra_FormatColl with reject data of the upcoming copy
+ SwRedlineExtraData_FormatColl* lcl_CopyStyle( const SwPosition & rFrom, const SwPosition & rTo, bool bCopy = true )
+ {
+ SwTextNode* pToNode = rTo.GetNode().GetTextNode();
+ SwTextNode* pFromNode = rFrom.GetNode().GetTextNode();
+ if (pToNode != nullptr && pFromNode != nullptr && pToNode != pFromNode)
+ {
+ const SwPaM aPam(*pToNode);
+ SwDoc& rDoc = aPam.GetDoc();
+ // using Undo, copy paragraph style
+ SwTextFormatColl* pFromColl = pFromNode->GetTextColl();
+ SwTextFormatColl* pToColl = pToNode->GetTextColl();
+ if (bCopy && pFromColl != pToColl)
+ rDoc.SetTextFormatColl(aPam, pFromColl);
+
+ // using Undo, remove direct paragraph formatting of the "To" paragraph,
+ // and apply here direct paragraph formatting of the "From" paragraph
+ SfxItemSetFixed<
+ RES_PARATR_BEGIN, RES_PARATR_END - 3, // skip RSID and GRABBAG
+ RES_PARATR_LIST_BEGIN, RES_UL_SPACE, // skip PAGEDESC and BREAK
+ RES_CNTNT, RES_FRMATR_END - 1>
+ aTmp(rDoc.GetAttrPool());
+ SfxItemSet aTmp2(aTmp);
+
+ pToNode->GetParaAttr(aTmp, 0, 0);
+ pFromNode->GetParaAttr(aTmp2, 0, 0);
+
+ bool bSameSet = aTmp == aTmp2;
+
+ if (!bSameSet)
+ {
+ for( sal_uInt16 nItem = 0; nItem < aTmp.TotalCount(); ++nItem)
+ {
+ sal_uInt16 nWhich = aTmp.GetWhichByOffset(nItem);
+ if( SfxItemState::SET == aTmp.GetItemState( nWhich, false ) &&
+ SfxItemState::SET != aTmp2.GetItemState( nWhich, false ) )
+ aTmp2.Put( aTmp.GetPool()->GetDefaultItem(nWhich), nWhich );
+ }
+ }
+
+ if (bCopy && !bSameSet)
+ rDoc.getIDocumentContentOperations().InsertItemSet(aPam, aTmp2);
+ else if (!bCopy && (!bSameSet || pFromColl != pToColl))
+ return new SwRedlineExtraData_FormatColl( pFromColl->GetName(), USHRT_MAX, &aTmp2 );
+ }
+ return nullptr;
+ }
+
+ // delete the empty tracked table row (i.e. if it's last tracked deletion was accepted)
+ void lcl_DeleteTrackedTableRow ( const SwPosition* pPos )
+ {
+ const SwTableBox* pBox = pPos->GetNode().GetTableBox();
+ if ( !pBox )
+ return;
+
+ // tracked column deletion
+
+ const SvxPrintItem *pHasBoxTextChangesOnlyProp =
+ pBox->GetFrameFormat()->GetAttrSet().GetItem<SvxPrintItem>(RES_PRINT);
+ // empty table cell with property "HasTextChangesOnly" = false
+ if ( pHasBoxTextChangesOnlyProp && !pHasBoxTextChangesOnlyProp->GetValue() )
+ {
+ SwCursor aCursor( *pPos, nullptr );
+ if ( pBox->IsEmpty() )
+ {
+ // tdf#155747 remove table cursor
+ pPos->GetDoc().GetDocShell()->GetWrtShell()->EnterStdMode();
+ // TODO check the other cells of the column
+ // before removing the column
+ pPos->GetDoc().DeleteCol( aCursor );
+ return;
+ }
+ else
+ {
+ SvxPrintItem aHasTextChangesOnly(RES_PRINT, false);
+ pPos->GetDoc().SetBoxAttr( aCursor, aHasTextChangesOnly );
+ }
+ }
+
+ // tracked row deletion
+
+ const SwTableLine* pLine = pBox->GetUpper();
+ const SvxPrintItem *pHasTextChangesOnlyProp =
+ pLine->GetFrameFormat()->GetAttrSet().GetItem<SvxPrintItem>(RES_PRINT);
+ // empty table row with property "HasTextChangesOnly" = false
+ if ( pHasTextChangesOnlyProp && !pHasTextChangesOnlyProp->GetValue() )
+ {
+ if ( pLine->IsEmpty() )
+ {
+ SwCursor aCursor( *pPos, nullptr );
+ pPos->GetDoc().DeleteRow( aCursor );
+ }
+ else
+ {
+ // update property "HasTextChangesOnly"
+ SwRedlineTable::size_type nPos = 0;
+ (void)pLine->UpdateTextChangesOnly(nPos);
+ }
+ }
+ }
+
+ // at rejection of a deletion in a table, remove the tracking of the table row
+ // (also at accepting the last redline insertion of a tracked table row insertion)
+ void lcl_RemoveTrackingOfTableRow( const SwPosition* pPos, bool bRejectDeletion )
+ {
+ const SwTableBox* pBox = pPos->GetNode().GetTableBox();
+ if ( !pBox )
+ return;
+
+ // tracked column deletion
+
+ const SvxPrintItem *pHasBoxTextChangesOnlyProp =
+ pBox->GetFrameFormat()->GetAttrSet().GetItem<SvxPrintItem>(RES_PRINT);
+ // table cell property "HasTextChangesOnly" is set and its value is false
+ if ( pHasBoxTextChangesOnlyProp && !pHasBoxTextChangesOnlyProp->GetValue() )
+ {
+ SvxPrintItem aUnsetTracking(RES_PRINT, true);
+ SwCursor aCursor( *pPos, nullptr );
+ pPos->GetDoc().SetBoxAttr( aCursor, aUnsetTracking );
+ }
+
+ // tracked row deletion
+
+ const SwTableLine* pLine = pBox->GetUpper();
+ const SvxPrintItem *pHasTextChangesOnlyProp =
+ pLine->GetFrameFormat()->GetAttrSet().GetItem<SvxPrintItem>(RES_PRINT);
+ // table row property "HasTextChangesOnly" is set and its value is false
+ if ( pHasTextChangesOnlyProp && !pHasTextChangesOnlyProp->GetValue() )
+ {
+ bool bNoMoreInsertion = false;
+ if ( !bRejectDeletion )
+ {
+ SwRedlineTable::size_type nPos = 0;
+ SwRedlineTable::size_type nInsert = pLine->UpdateTextChangesOnly(nPos, /*bUpdateProperty=*/false);
+
+ if ( SwRedlineTable::npos == nInsert )
+ bNoMoreInsertion = true;
+ }
+ if ( bRejectDeletion || bNoMoreInsertion )
+ {
+ SvxPrintItem aUnsetTracking(RES_PRINT, true);
+ SwCursor aCursor( *pPos, nullptr );
+ pPos->GetDoc().SetRowNotTracked( aCursor, aUnsetTracking );
+ }
+ }
+ }
+
+ bool lcl_AcceptRedline( SwRedlineTable& rArr, SwRedlineTable::size_type& rPos,
+ bool bCallDelete,
+ const SwPosition* pSttRng = nullptr,
+ const SwPosition* pEndRng = nullptr )
+ {
+ bool bRet = true;
+ SwRangeRedline* pRedl = rArr[ rPos ];
+ SwPosition *pRStt = nullptr, *pREnd = nullptr;
+ SwComparePosition eCmp = SwComparePosition::Outside;
+ if( pSttRng && pEndRng )
+ {
+ pRStt = pRedl->Start();
+ pREnd = pRedl->End();
+ eCmp = ComparePosition( *pSttRng, *pEndRng, *pRStt, *pREnd );
+ }
+
+ pRedl->InvalidateRange(SwRangeRedline::Invalidation::Remove);
+
+ switch( pRedl->GetType() )
+ {
+ case RedlineType::Insert:
+ case RedlineType::Format:
+ {
+ bool bCheck = false, bReplace = false;
+ switch( eCmp )
+ {
+ case SwComparePosition::Inside:
+ if( *pSttRng == *pRStt )
+ pRedl->SetStart( *pEndRng, pRStt );
+ else
+ {
+ if( *pEndRng != *pREnd )
+ {
+ // split up
+ SwRangeRedline* pNew = new SwRangeRedline( *pRedl );
+ pNew->SetStart( *pEndRng );
+ rArr.Insert( pNew ); ++rPos;
+ }
+ pRedl->SetEnd( *pSttRng, pREnd );
+ bCheck = true;
+ }
+ break;
+
+ case SwComparePosition::OverlapBefore:
+ pRedl->SetStart( *pEndRng, pRStt );
+ bReplace = true;
+ break;
+
+ case SwComparePosition::OverlapBehind:
+ pRedl->SetEnd( *pSttRng, pREnd );
+ bCheck = true;
+ break;
+
+ case SwComparePosition::Outside:
+ case SwComparePosition::Equal:
+ {
+ bool bInsert = RedlineType::Insert == pRedl->GetType();
+ SwPosition aPos(pRedl->Start()->GetNode());
+ rArr.DeleteAndDestroy( rPos-- );
+
+ // remove tracking of the table row, if needed
+ if ( bInsert )
+ lcl_RemoveTrackingOfTableRow( &aPos, /*bRejectDelete=*/false );
+ }
+ break;
+
+ default:
+ bRet = false;
+ }
+
+ if( bReplace || ( bCheck && !pRedl->HasValidRange() ))
+ {
+ // re-insert
+ rArr.Remove( pRedl );
+ rArr.Insert( pRedl );
+ }
+ }
+ break;
+ case RedlineType::Delete:
+ {
+ SwDoc& rDoc = pRedl->GetDoc();
+ const SwPosition *pDelStt = nullptr, *pDelEnd = nullptr;
+ bool bDelRedl = false;
+ switch( eCmp )
+ {
+ case SwComparePosition::Inside:
+ if( bCallDelete )
+ {
+ pDelStt = pSttRng;
+ pDelEnd = pEndRng;
+ }
+ break;
+
+ case SwComparePosition::OverlapBefore:
+ if( bCallDelete )
+ {
+ pDelStt = pRStt;
+ pDelEnd = pEndRng;
+ }
+ break;
+ case SwComparePosition::OverlapBehind:
+ if( bCallDelete )
+ {
+ pDelStt = pREnd;
+ pDelEnd = pSttRng;
+ }
+ break;
+
+ case SwComparePosition::Outside:
+ case SwComparePosition::Equal:
+ {
+ rArr.Remove( rPos-- );
+ bDelRedl = true;
+ if( bCallDelete )
+ {
+ pDelStt = pRedl->Start();
+ pDelEnd = pRedl->End();
+ }
+ }
+ break;
+ default:
+ bRet = false;
+ }
+
+ if( pDelStt && pDelEnd )
+ {
+ SwPaM aPam( *pDelStt, *pDelEnd );
+ SwContentNode* pCSttNd = pDelStt->GetNode().GetContentNode();
+ SwContentNode* pCEndNd = pDelEnd->GetNode().GetContentNode();
+ pRStt = pRedl->Start();
+ pREnd = pRedl->End();
+
+ // keep style of the empty paragraph after deletion of wholly paragraphs
+ if( pCSttNd && pCEndNd && pRStt && pREnd && pRStt->GetContentIndex() == 0 )
+ lcl_CopyStyle(*pREnd, *pRStt);
+
+ if( bDelRedl )
+ delete pRedl;
+
+ RedlineFlags eOld = rDoc.getIDocumentRedlineAccess().GetRedlineFlags();
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld & ~RedlineFlags(RedlineFlags::On | RedlineFlags::Ignore));
+
+ if( pCSttNd && pCEndNd )
+ {
+ rDoc.getIDocumentContentOperations().DeleteAndJoin( aPam );
+ lcl_DeleteTrackedTableRow( aPam.End() );
+ }
+ else if (pCSttNd && !pCEndNd)
+ {
+ aPam.GetBound().nContent.Assign( nullptr, 0 );
+ aPam.GetBound( false ).nContent.Assign( nullptr, 0 );
+ rDoc.getIDocumentContentOperations().DelFullPara( aPam );
+ }
+ else
+ {
+ rDoc.getIDocumentContentOperations().DeleteRange(aPam);
+ }
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
+ }
+ else if( bDelRedl )
+ delete pRedl;
+ }
+ break;
+
+ case RedlineType::FmtColl:
+ rArr.DeleteAndDestroy( rPos-- );
+ break;
+
+ case RedlineType::ParagraphFormat:
+ rArr.DeleteAndDestroy( rPos-- );
+ break;
+
+ default:
+ bRet = false;
+ }
+ return bRet;
+ }
+
+ bool lcl_RejectRedline( SwRedlineTable& rArr, SwRedlineTable::size_type& rPos,
+ bool bCallDelete,
+ const SwPosition* pSttRng = nullptr,
+ const SwPosition* pEndRng = nullptr )
+ {
+ bool bRet = true;
+ SwRangeRedline* pRedl = rArr[ rPos ];
+ SwDoc& rDoc = pRedl->GetDoc();
+ SwPosition *pRStt = nullptr, *pREnd = nullptr;
+ SwComparePosition eCmp = SwComparePosition::Outside;
+ if( pSttRng && pEndRng )
+ {
+ pRStt = pRedl->Start();
+ pREnd = pRedl->End();
+ eCmp = ComparePosition( *pSttRng, *pEndRng, *pRStt, *pREnd );
+ }
+
+ pRedl->InvalidateRange(SwRangeRedline::Invalidation::Remove);
+
+ switch( pRedl->GetType() )
+ {
+ case RedlineType::Insert:
+ {
+ const SwPosition *pDelStt = nullptr, *pDelEnd = nullptr;
+ bool bDelRedl = false;
+ switch( eCmp )
+ {
+ case SwComparePosition::Inside:
+ if( bCallDelete )
+ {
+ pDelStt = pSttRng;
+ pDelEnd = pEndRng;
+ }
+ break;
+
+ case SwComparePosition::OverlapBefore:
+ if( bCallDelete )
+ {
+ pDelStt = pRStt;
+ pDelEnd = pEndRng;
+ }
+ break;
+ case SwComparePosition::OverlapBehind:
+ if( bCallDelete )
+ {
+ pDelStt = pREnd;
+ pDelEnd = pSttRng;
+ }
+ break;
+ case SwComparePosition::Outside:
+ case SwComparePosition::Equal:
+ {
+ // delete the range again
+ rArr.Remove( rPos-- );
+ bDelRedl = true;
+ if( bCallDelete )
+ {
+ pDelStt = pRedl->Start();
+ pDelEnd = pRedl->End();
+ }
+ }
+ break;
+
+ default:
+ bRet = false;
+ }
+ if( pDelStt && pDelEnd )
+ {
+ SwPaM aPam( *pDelStt, *pDelEnd );
+
+ SwContentNode* pCSttNd = pDelStt->GetNode().GetContentNode();
+ SwContentNode* pCEndNd = pDelEnd->GetNode().GetContentNode();
+
+ if( bDelRedl )
+ delete pRedl;
+
+ RedlineFlags eOld = rDoc.getIDocumentRedlineAccess().GetRedlineFlags();
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld & ~RedlineFlags(RedlineFlags::On | RedlineFlags::Ignore));
+
+ if( pCSttNd && pCEndNd )
+ {
+ rDoc.getIDocumentContentOperations().DeleteAndJoin( aPam );
+ lcl_DeleteTrackedTableRow( aPam.End() );
+ }
+ else if (pCSttNd && !pCEndNd)
+ {
+ aPam.GetBound().nContent.Assign( nullptr, 0 );
+ aPam.GetBound( false ).nContent.Assign( nullptr, 0 );
+ if (aPam.End()->GetNode().IsStartNode())
+ { // end node will be deleted too! see nNodeDiff+1
+ aPam.End()->Adjust(SwNodeOffset(-1));
+ }
+ assert(!aPam.End()->GetNode().IsStartNode());
+ rDoc.getIDocumentContentOperations().DelFullPara( aPam );
+ }
+ else
+ {
+ rDoc.getIDocumentContentOperations().DeleteRange(aPam);
+ }
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
+ }
+ else if( bDelRedl )
+ delete pRedl;
+ }
+ break;
+ case RedlineType::Delete:
+ {
+ SwRangeRedline* pNew = nullptr;
+ bool bCheck = false, bReplace = false;
+ SwPaM const updatePaM(pSttRng ? *pSttRng : *pRedl->Start(),
+ pEndRng ? *pEndRng : *pRedl->End());
+
+ if( pRedl->GetExtraData() )
+ pRedl->GetExtraData()->Reject( *pRedl );
+
+ // remove tracking of the table row, if needed
+ lcl_RemoveTrackingOfTableRow( updatePaM.End(), /*bRejectDelete=*/true );
+
+ switch( eCmp )
+ {
+ case SwComparePosition::Inside:
+ {
+ if( 1 < pRedl->GetStackCount() )
+ {
+ pNew = new SwRangeRedline( *pRedl );
+ pNew->PopData();
+ }
+ if( *pSttRng == *pRStt )
+ {
+ pRedl->SetStart( *pEndRng, pRStt );
+ bReplace = true;
+ if( pNew )
+ pNew->SetEnd( *pEndRng );
+ }
+ else
+ {
+ if( *pEndRng != *pREnd )
+ {
+ // split up
+ SwRangeRedline* pCpy = new SwRangeRedline( *pRedl );
+ pCpy->SetStart( *pEndRng );
+ rArr.Insert( pCpy ); ++rPos;
+ if( pNew )
+ pNew->SetEnd( *pEndRng );
+ }
+
+ pRedl->SetEnd( *pSttRng, pREnd );
+ bCheck = true;
+ if( pNew )
+ pNew->SetStart( *pSttRng );
+ }
+ }
+ break;
+
+ case SwComparePosition::OverlapBefore:
+ if( 1 < pRedl->GetStackCount() )
+ {
+ pNew = new SwRangeRedline( *pRedl );
+ pNew->PopData();
+ }
+ pRedl->SetStart( *pEndRng, pRStt );
+ bReplace = true;
+ if( pNew )
+ pNew->SetEnd( *pEndRng );
+ break;
+
+ case SwComparePosition::OverlapBehind:
+ if( 1 < pRedl->GetStackCount() )
+ {
+ pNew = new SwRangeRedline( *pRedl );
+ pNew->PopData();
+ }
+ pRedl->SetEnd( *pSttRng, pREnd );
+ bCheck = true;
+ if( pNew )
+ pNew->SetStart( *pSttRng );
+ break;
+
+ case SwComparePosition::Outside:
+ case SwComparePosition::Equal:
+ if( !pRedl->PopData() )
+ // deleting the RedlineObject is enough
+ rArr.DeleteAndDestroy( rPos-- );
+ break;
+
+ default:
+ bRet = false;
+ }
+
+ if( pNew )
+ {
+ rArr.Insert( pNew ); ++rPos;
+ }
+
+ if( bReplace || ( bCheck && !pRedl->HasValidRange() ))
+ {
+ // re-insert
+ rArr.Remove( pRedl );
+ rArr.Insert( pRedl );
+ }
+
+ sw::UpdateFramesForRemoveDeleteRedline(rDoc, updatePaM);
+ }
+ break;
+
+ case RedlineType::Format:
+ case RedlineType::FmtColl:
+ case RedlineType::ParagraphFormat:
+ {
+ // tdf#52391 instead of hidden acception at the requested
+ // rejection, remove direct text formatting to get the potential
+ // original state of the text (FIXME if the original text
+ // has already contained direct text formatting: unfortunately
+ // ODF 1.2 doesn't support rejection of format-only changes)
+ if ( pRedl->GetType() == RedlineType::Format )
+ {
+ SwPaM aPam( *(pRedl->Start()), *(pRedl->End()) );
+ rDoc.ResetAttrs(aPam);
+ }
+ else if ( pRedl->GetType() == RedlineType::ParagraphFormat )
+ {
+ // handle paragraph formatting changes
+ // (range is only a full paragraph or a part of it)
+ const SwPosition* pStt = pRedl->Start();
+ SwTextNode* pTNd = pStt->GetNode().GetTextNode();
+ if( pTNd )
+ {
+ // expand range to the whole paragraph
+ // and reset only the paragraph attributes
+ SwPaM aPam( *pTNd, pTNd->GetText().getLength() );
+ o3tl::sorted_vector<sal_uInt16> aResetAttrsArray;
+
+ constexpr std::pair<sal_uInt16, sal_uInt16> aResetableSetRange[] = {
+ { RES_PARATR_BEGIN, RES_PARATR_END - 1 },
+ { RES_PARATR_LIST_BEGIN, RES_FRMATR_END - 1 },
+ };
+
+ for (const auto& [nBegin, nEnd] : aResetableSetRange)
+ {
+ for (sal_uInt16 i = nBegin; i <= nEnd; ++i)
+ aResetAttrsArray.insert( i );
+ }
+
+ rDoc.ResetAttrs(aPam, false, aResetAttrsArray);
+
+ // remove numbering
+ if ( pTNd->GetNumRule() )
+ rDoc.DelNumRules(aPam);
+ }
+ }
+
+ if( pRedl->GetExtraData() )
+ pRedl->GetExtraData()->Reject( *pRedl );
+
+ rArr.DeleteAndDestroy( rPos-- );
+ }
+ break;
+
+ default:
+ bRet = false;
+ }
+ return bRet;
+ }
+
+ bool lcl_AcceptInnerInsertRedline(SwRedlineTable& rArr, SwRedlineTable::size_type& rPos,
+ int nDepth)
+ {
+ SwRangeRedline* pRedl = rArr[rPos];
+ SwDoc& rDoc = pRedl->GetDoc();
+ SwPaM const updatePaM(*pRedl->Start(), *pRedl->End());
+
+ pRedl->PopAllDataAfter(nDepth);
+ sw::UpdateFramesForRemoveDeleteRedline(rDoc, updatePaM);
+ return true;
+ }
+
+ typedef bool (*Fn_AcceptReject)( SwRedlineTable& rArr, SwRedlineTable::size_type& rPos,
+ bool bCallDelete,
+ const SwPosition* pSttRng,
+ const SwPosition* pEndRng);
+
+
+ int lcl_AcceptRejectRedl( Fn_AcceptReject fn_AcceptReject,
+ SwRedlineTable& rArr, bool bCallDelete,
+ const SwPaM& rPam)
+ {
+ SwRedlineTable::size_type n = 0;
+ int nCount = 0;
+
+ const SwPosition* pStt = rPam.Start(),
+ * pEnd = rPam.End();
+ const SwRangeRedline* pFnd = rArr.FindAtPosition( *pStt, n );
+ if( pFnd && // Is new a part of it?
+ ( *pFnd->Start() != *pStt || *pFnd->End() > *pEnd ))
+ {
+ // Only revoke the partial selection
+ if( (*fn_AcceptReject)( rArr, n, bCallDelete, pStt, pEnd ))
+ nCount++;
+ ++n;
+ }
+
+ // tdf#119824 first we will accept only overlapping paragraph format changes
+ // in the first loop to avoid potential content changes during Redo
+ bool bHasParagraphFormatChange = false;
+ for( int m = 0 ; m < 2 && !bHasParagraphFormatChange; ++m )
+ {
+ for(SwRedlineTable::size_type o = n ; o < rArr.size(); ++o )
+ {
+ SwRangeRedline* pTmp = rArr[ o ];
+ if( pTmp->HasMark() && pTmp->IsVisible() )
+ {
+ if( *pTmp->End() <= *pEnd )
+ {
+ if( (m > 0 || RedlineType::ParagraphFormat == pTmp->GetType()) &&
+ (*fn_AcceptReject)( rArr, o, bCallDelete, nullptr, nullptr ))
+ {
+ bHasParagraphFormatChange = true;
+ nCount++;
+ }
+ }
+ else
+ {
+ if( *pTmp->Start() < *pEnd )
+ {
+ // Only revoke the partial selection
+ if( (m > 0 || RedlineType::ParagraphFormat == pTmp->GetType()) &&
+ (*fn_AcceptReject)( rArr, o, bCallDelete, pStt, pEnd ))
+ {
+ bHasParagraphFormatChange = true;
+ nCount++;
+ }
+ }
+ break;
+ }
+ }
+ }
+ }
+ return nCount;
+ }
+
+ void lcl_AdjustRedlineRange( SwPaM& rPam )
+ {
+ // The Selection is only in the ContentSection. If there are Redlines
+ // to Non-ContentNodes before or after that, then the Selections
+ // expand to them.
+ auto [pStt, pEnd] = rPam.StartEnd(); // SwPosition*
+ SwDoc& rDoc = rPam.GetDoc();
+ if( !pStt->GetContentIndex() &&
+ !rDoc.GetNodes()[ pStt->GetNodeIndex() - 1 ]->IsContentNode() )
+ {
+ const SwRangeRedline* pRedl = rDoc.getIDocumentRedlineAccess().GetRedline( *pStt, nullptr );
+ if( pRedl )
+ {
+ const SwPosition* pRStt = pRedl->Start();
+ if( !pRStt->GetContentIndex() && pRStt->GetNodeIndex() ==
+ pStt->GetNodeIndex() - 1 )
+ *pStt = *pRStt;
+ }
+ }
+ if( pEnd->GetNode().IsContentNode() &&
+ !rDoc.GetNodes()[ pEnd->GetNodeIndex() + 1 ]->IsContentNode() &&
+ pEnd->GetContentIndex() == pEnd->GetNode().GetContentNode()->Len() )
+ {
+ const SwRangeRedline* pRedl = rDoc.getIDocumentRedlineAccess().GetRedline( *pEnd, nullptr );
+ if( pRedl )
+ {
+ const SwPosition* pREnd = pRedl->End();
+ if( !pREnd->GetContentIndex() && pREnd->GetNodeIndex() ==
+ pEnd->GetNodeIndex() + 1 )
+ *pEnd = *pREnd;
+ }
+ }
+ }
+
+ /// in case some text is deleted, ensure that the not-yet-inserted
+ /// SwRangeRedline has its positions corrected not to point to deleted node
+ class TemporaryRedlineUpdater
+ {
+ private:
+ SwRangeRedline & m_rRedline;
+ std::shared_ptr<SwUnoCursor> m_pCursor;
+ public:
+ TemporaryRedlineUpdater(SwDoc & rDoc, SwRangeRedline & rRedline)
+ : m_rRedline(rRedline)
+ , m_pCursor(rDoc.CreateUnoCursor(*rRedline.GetPoint(), false))
+ {
+ if (m_rRedline.HasMark())
+ {
+ m_pCursor->SetMark();
+ *m_pCursor->GetMark() = *m_rRedline.GetMark();
+ m_rRedline.GetMark()->Assign(rDoc.GetNodes().GetEndOfContent());
+ }
+ m_rRedline.GetPoint()->Assign(rDoc.GetNodes().GetEndOfContent());
+ }
+ ~TemporaryRedlineUpdater()
+ {
+ static_cast<SwPaM&>(m_rRedline) = *m_pCursor;
+ }
+ };
+}
+
+namespace sw
+{
+
+DocumentRedlineManager::DocumentRedlineManager(SwDoc& i_rSwdoc)
+ : m_rDoc(i_rSwdoc)
+ , meRedlineFlags(RedlineFlags::ShowInsert | RedlineFlags::ShowDelete)
+ , mbIsRedlineMove(false)
+ , mnAutoFormatRedlnCommentNo(0)
+{
+}
+
+RedlineFlags DocumentRedlineManager::GetRedlineFlags() const
+{
+ return meRedlineFlags;
+}
+
+void DocumentRedlineManager::SetRedlineFlags( RedlineFlags eMode )
+{
+ if( meRedlineFlags == eMode )
+ return;
+
+ if( (RedlineFlags::ShowMask & meRedlineFlags) != (RedlineFlags::ShowMask & eMode)
+ || !(RedlineFlags::ShowMask & eMode) )
+ {
+ bool bSaveInXMLImportFlag = m_rDoc.IsInXMLImport();
+ m_rDoc.SetInXMLImport( false );
+ // and then hide/display everything
+ void (SwRangeRedline::*pFnc)(sal_uInt16, size_t, bool); // Allow compiler warn if use of
+ // uninitialized ptr is possible
+
+ RedlineFlags eShowMode = RedlineFlags::ShowMask & eMode;
+ if (eShowMode == (RedlineFlags::ShowInsert | RedlineFlags::ShowDelete))
+ pFnc = &SwRangeRedline::Show;
+ else if (eShowMode == RedlineFlags::ShowInsert)
+ pFnc = &SwRangeRedline::Hide;
+ else if (eShowMode == RedlineFlags::ShowDelete)
+ pFnc = &SwRangeRedline::ShowOriginal;
+ else
+ {
+ pFnc = &SwRangeRedline::Hide;
+ eMode |= RedlineFlags::ShowInsert;
+ }
+
+ CheckAnchoredFlyConsistency(m_rDoc);
+ CHECK_REDLINE( *this )
+
+ o3tl::sorted_vector<SwRootFrame *> hiddenLayouts;
+ if (eShowMode == (RedlineFlags::ShowInsert | RedlineFlags::ShowDelete))
+ {
+ // sw_redlinehide: the problem here is that MoveFromSection
+ // creates the frames wrongly (non-merged), because its own
+ // SwRangeRedline has wrong positions until after the nodes
+ // are all moved, so fix things up by force by re-creating
+ // all merged frames from scratch.
+ o3tl::sorted_vector<SwRootFrame *> const layouts(m_rDoc.GetAllLayouts());
+ for (SwRootFrame *const pLayout : layouts)
+ {
+ if (pLayout->IsHideRedlines())
+ {
+ pLayout->SetHideRedlines(false);
+ hiddenLayouts.insert(pLayout);
+ }
+ }
+ }
+
+ for (sal_uInt16 nLoop = 1; nLoop <= 2; ++nLoop)
+ for (size_t i = 0; i < maRedlineTable.size(); ++i)
+ {
+ SwRangeRedline *const pRedline = maRedlineTable[i];
+ (pRedline->*pFnc)(nLoop, i, false);
+ while (maRedlineTable.size() <= i
+ || maRedlineTable[i] != pRedline)
+ { // ensure current position
+ --i; // a previous redline may have been deleted
+ }
+ }
+
+ //SwRangeRedline::MoveFromSection routinely changes
+ //the keys that mpRedlineTable is sorted by
+ maRedlineTable.Resort();
+
+ CheckAnchoredFlyConsistency(m_rDoc);
+ CHECK_REDLINE( *this )
+
+ for (SwRootFrame *const pLayout : hiddenLayouts)
+ {
+ pLayout->SetHideRedlines(true);
+ }
+
+ m_rDoc.SetInXMLImport( bSaveInXMLImportFlag );
+ }
+ meRedlineFlags = eMode;
+ m_rDoc.getIDocumentState().SetModified();
+
+ // #TODO - add 'SwExtraRedlineTable' also ?
+}
+
+bool DocumentRedlineManager::IsRedlineOn() const
+{
+ return IDocumentRedlineAccess::IsRedlineOn(meRedlineFlags);
+}
+
+bool DocumentRedlineManager::IsIgnoreRedline() const
+{
+ return bool(RedlineFlags::Ignore & meRedlineFlags);
+}
+
+void DocumentRedlineManager::SetRedlineFlags_intern(RedlineFlags eMode)
+{
+ meRedlineFlags = eMode;
+}
+
+const SwRedlineTable& DocumentRedlineManager::GetRedlineTable() const
+{
+ return maRedlineTable;
+}
+
+SwRedlineTable& DocumentRedlineManager::GetRedlineTable()
+{
+ return maRedlineTable;
+}
+
+const SwExtraRedlineTable& DocumentRedlineManager::GetExtraRedlineTable() const
+{
+ return maExtraRedlineTable;
+}
+
+SwExtraRedlineTable& DocumentRedlineManager::GetExtraRedlineTable()
+{
+ return maExtraRedlineTable;
+}
+
+bool DocumentRedlineManager::IsInRedlines(const SwNode & rNode) const
+{
+ if (&rNode.GetNodes() != &m_rDoc.GetNodes())
+ return false;
+
+ SwPosition aPos(rNode);
+ SwNode & rEndOfRedlines = m_rDoc.GetNodes().GetEndOfRedlines();
+ SwPaM aPam(SwPosition(*rEndOfRedlines.StartOfSectionNode()),
+ SwPosition(rEndOfRedlines));
+
+ return aPam.ContainsPosition(aPos);
+}
+
+bool DocumentRedlineManager::IsRedlineMove() const
+{
+ return mbIsRedlineMove;
+}
+
+void DocumentRedlineManager::SetRedlineMove(bool bFlag)
+{
+ mbIsRedlineMove = bFlag;
+}
+
+/*
+Text means Text not "polluted" by Redlines.
+
+Behaviour of Insert-Redline:
+ - in the Text - insert Redline Object
+ - in InsertRedline (own) - ignore, existing is extended
+ - in InsertRedline (others) - split up InsertRedline and
+ insert Redline Object
+ - in DeleteRedline - split up DeleteRedline or
+ move at the end/beginning
+
+Behaviour of Delete-Redline:
+ - in the Text - insert Redline Object
+ - in DeleteRedline (own/others) - ignore
+ - in InsertRedline (own) - ignore, but delete character
+ - in InsertRedline (others) - split up InsertRedline and
+ insert Redline Object
+ - Text and own Insert overlap - delete Text in the own Insert,
+ extend in the other Text
+ (up to the Insert!)
+ - Text and other Insert overlap - insert Redline Object, the
+ other Insert is overlapped by
+ the Delete
+*/
+IDocumentRedlineAccess::AppendResult
+DocumentRedlineManager::AppendRedline(SwRangeRedline* pNewRedl, bool const bCallDelete,
+ sal_uInt32 nMoveIDToDelete)
+{
+ CHECK_REDLINE( *this )
+
+ if (!IsRedlineOn() || IsShowOriginal(meRedlineFlags))
+ {
+ if( bCallDelete && RedlineType::Delete == pNewRedl->GetType() )
+ {
+ RedlineFlags eOld = meRedlineFlags;
+ // Set to NONE, so that the Delete::Redo merges the Redline data correctly!
+ // The ShowMode needs to be retained!
+ meRedlineFlags = eOld & ~RedlineFlags(RedlineFlags::On | RedlineFlags::Ignore);
+ m_rDoc.getIDocumentContentOperations().DeleteAndJoin( *pNewRedl );
+ meRedlineFlags = eOld;
+ }
+ delete pNewRedl;
+ pNewRedl = nullptr;
+ CHECK_REDLINE( *this )
+ return AppendResult::IGNORED;
+ }
+
+ // Collect MoveID's of the redlines we delete.
+ // If there is only 1, then we should use its ID. (continuing the move)
+ std::set<sal_uInt32> deletedMoveIDs;
+
+ bool bMerged = false;
+
+ pNewRedl->InvalidateRange(SwRangeRedline::Invalidation::Add);
+
+ if( m_rDoc.IsAutoFormatRedline() )
+ {
+ pNewRedl->SetAutoFormat();
+ if( moAutoFormatRedlnComment && !moAutoFormatRedlnComment->isEmpty() )
+ {
+ pNewRedl->SetComment( *moAutoFormatRedlnComment );
+ pNewRedl->SetSeqNo( mnAutoFormatRedlnCommentNo );
+ }
+ }
+
+ auto [pStt, pEnd] = pNewRedl->StartEnd(); // SwPosition*
+ {
+ SwTextNode* pTextNode = pStt->GetNode().GetTextNode();
+ if( pTextNode == nullptr )
+ {
+ if( pStt->GetContentIndex() > 0 )
+ {
+ OSL_ENSURE( false, "Redline start: non-text-node with content" );
+ pStt->SetContent( 0 );
+ }
+ }
+ else
+ {
+ if( pStt->GetContentIndex() > pTextNode->Len() )
+ {
+ OSL_ENSURE( false, "Redline start: index after text" );
+ pStt->SetContent( pTextNode->Len() );
+ }
+ }
+ pTextNode = pEnd->GetNode().GetTextNode();
+ if( pTextNode == nullptr )
+ {
+ if( pEnd->GetContentIndex() > 0 )
+ {
+ OSL_ENSURE( false, "Redline end: non-text-node with content" );
+ pEnd->SetContent(0);
+ }
+ }
+ else
+ {
+ if( pEnd->GetContentIndex() > pTextNode->Len() )
+ {
+ OSL_ENSURE( false, "Redline end: index after text" );
+ pEnd->SetContent( pTextNode->Len() );
+ }
+ }
+ }
+ if( ( *pStt == *pEnd ) &&
+ ( pNewRedl->GetContentIdx() == nullptr ) )
+ { // Do not insert empty redlines
+ delete pNewRedl;
+ return AppendResult::IGNORED;
+ }
+ bool bCompress = false;
+ SwRedlineTable::size_type n = 0;
+ // look up the first Redline for the starting position
+ if( !GetRedline( *pStt, &n ) && n )
+ --n;
+ const SwRedlineTable::size_type nStartPos = n;
+ bool bDec = false;
+
+ for( ; pNewRedl && n < maRedlineTable.size(); bDec ? n : ++n )
+ {
+ bDec = false;
+
+ SwRangeRedline* pRedl = maRedlineTable[ n ];
+ auto [pRStt, pREnd] = pRedl->StartEnd();
+
+ // #i8518# remove empty redlines while we're at it
+ if( ( *pRStt == *pREnd ) &&
+ ( pRedl->GetContentIdx() == nullptr ) )
+ {
+ maRedlineTable.DeleteAndDestroy(n);
+ continue;
+ }
+
+ SwComparePosition eCmpPos = ComparePosition( *pStt, *pEnd, *pRStt, *pREnd );
+
+ if ( SwComparePosition::Before == eCmpPos && !IsPrevPos( *pEnd, *pRStt ))
+ break;
+
+ switch( pNewRedl->GetType() )
+ {
+ case RedlineType::Insert:
+ switch( pRedl->GetType() )
+ {
+ case RedlineType::Insert:
+ if( pRedl->IsOwnRedline( *pNewRedl ) &&
+ // don't join inserted characters with moved text
+ !pRedl->IsMoved() )
+ {
+ bool bDelete = false;
+ bool bMaybeNotify = false;
+
+ // Merge if applicable?
+ if( (( SwComparePosition::Behind == eCmpPos &&
+ IsPrevPos( *pREnd, *pStt ) ) ||
+ ( SwComparePosition::CollideStart == eCmpPos ) ||
+ ( SwComparePosition::OverlapBehind == eCmpPos ) ) &&
+ pRedl->CanCombine( *pNewRedl ) &&
+ ( n+1 >= maRedlineTable.size() ||
+ ( *maRedlineTable[ n+1 ]->Start() >= *pEnd &&
+ *maRedlineTable[ n+1 ]->Start() != *pREnd ) ) )
+ {
+ pRedl->SetEnd( *pEnd, pREnd );
+ if( !pRedl->HasValidRange() )
+ {
+ // re-insert
+ maRedlineTable.Remove( n );
+ maRedlineTable.Insert( pRedl );
+ }
+
+ bMerged = true;
+ bDelete = true;
+ }
+ else if( (( SwComparePosition::Before == eCmpPos &&
+ IsPrevPos( *pEnd, *pRStt ) ) ||
+ ( SwComparePosition::CollideEnd == eCmpPos ) ||
+ ( SwComparePosition::OverlapBefore == eCmpPos ) ) &&
+ pRedl->CanCombine( *pNewRedl ) &&
+ ( !n ||
+ *maRedlineTable[ n-1 ]->End() != *pRStt ))
+ {
+ pRedl->SetStart( *pStt, pRStt );
+ // re-insert
+ maRedlineTable.Remove( n );
+ maRedlineTable.Insert( pRedl );
+
+ bMerged = true;
+ bDelete = true;
+ }
+ else if ( SwComparePosition::Outside == eCmpPos )
+ {
+ // own insert-over-insert redlines:
+ // just scrap the inside ones
+ maRedlineTable.DeleteAndDestroy( n );
+ bDec = true;
+ }
+ else if( SwComparePosition::OverlapBehind == eCmpPos )
+ {
+ *pStt = *pREnd;
+ if( ( *pStt == *pEnd ) &&
+ ( pNewRedl->GetContentIdx() == nullptr ) )
+ bDelete = bMaybeNotify = true;
+ }
+ else if( SwComparePosition::OverlapBefore == eCmpPos )
+ {
+ *pEnd = *pRStt;
+ if( ( *pStt == *pEnd ) &&
+ ( pNewRedl->GetContentIdx() == nullptr ) )
+ bDelete = bMaybeNotify = true;
+ }
+ else if( SwComparePosition::Inside == eCmpPos )
+ {
+ bDelete = bMaybeNotify = true;
+ bMerged = true;
+ }
+ else if( SwComparePosition::Equal == eCmpPos )
+ bDelete = bMaybeNotify = true;
+
+ if( bDelete )
+ {
+ delete pNewRedl;
+ pNewRedl = nullptr;
+ bCompress = true;
+
+ if (bMaybeNotify)
+ MaybeNotifyRedlineModification(*pRedl, m_rDoc);
+
+ // set IsMoved checking nearby redlines
+ if (n < maRedlineTable.size()) // in case above 're-insert' failed
+ maRedlineTable.isMoved(n);
+ }
+ }
+ else if( SwComparePosition::Inside == eCmpPos )
+ {
+ // split up
+ if( *pEnd != *pREnd )
+ {
+ SwRangeRedline* pCpy = new SwRangeRedline( *pRedl );
+ pCpy->SetStart( *pEnd );
+ maRedlineTable.Insert( pCpy );
+ }
+ pRedl->SetEnd( *pStt, pREnd );
+ if( ( *pStt == *pRStt ) &&
+ ( pRedl->GetContentIdx() == nullptr ) )
+ {
+ maRedlineTable.DeleteAndDestroy( n );
+ bDec = true;
+ }
+ else if( !pRedl->HasValidRange() )
+ {
+ // re-insert
+ maRedlineTable.Remove( n );
+ maRedlineTable.Insert( pRedl );
+ }
+ }
+ else if ( SwComparePosition::Outside == eCmpPos )
+ {
+ // handle overlapping redlines in broken documents
+
+ // split up the new redline, since it covers the
+ // existing redline. Insert the first part, and
+ // progress with the remainder as usual
+ SwRangeRedline* pSplit = new SwRangeRedline( *pNewRedl );
+ pSplit->SetEnd( *pRStt );
+ pNewRedl->SetStart( *pREnd );
+ maRedlineTable.Insert( pSplit );
+ if( *pStt == *pEnd && pNewRedl->GetContentIdx() == nullptr )
+ {
+ delete pNewRedl;
+ pNewRedl = nullptr;
+ bCompress = true;
+ }
+ }
+ else if ( SwComparePosition::OverlapBehind == eCmpPos )
+ {
+ // handle overlapping redlines in broken documents
+ pNewRedl->SetStart( *pREnd );
+ }
+ else if ( SwComparePosition::OverlapBefore == eCmpPos )
+ {
+ // handle overlapping redlines in broken documents
+ *pEnd = *pRStt;
+ if( ( *pStt == *pEnd ) &&
+ ( pNewRedl->GetContentIdx() == nullptr ) )
+ {
+ delete pNewRedl;
+ pNewRedl = nullptr;
+ bCompress = true;
+
+ MaybeNotifyRedlineModification(*pRedl, m_rDoc);
+ }
+ }
+ break;
+ case RedlineType::Delete:
+ if( SwComparePosition::Inside == eCmpPos )
+ {
+ // split up
+ if( *pEnd != *pREnd )
+ {
+ SwRangeRedline* pCpy = new SwRangeRedline( *pRedl );
+ pCpy->SetStart( *pEnd );
+ maRedlineTable.Insert( pCpy );
+ }
+ pRedl->SetEnd( *pStt, pREnd );
+ if( ( *pStt == *pRStt ) &&
+ ( pRedl->GetContentIdx() == nullptr ) )
+ {
+ maRedlineTable.DeleteAndDestroy( n );
+ bDec = true;
+ }
+ else if( !pRedl->HasValidRange() )
+ {
+ // re-insert
+ maRedlineTable.Remove( n );
+ maRedlineTable.Insert( pRedl, n );
+ }
+ }
+ else if ( SwComparePosition::Outside == eCmpPos )
+ {
+ // handle overlapping redlines in broken documents
+
+ // split up the new redline, since it covers the
+ // existing redline. Insert the first part, and
+ // progress with the remainder as usual
+ SwRangeRedline* pSplit = new SwRangeRedline( *pNewRedl );
+ pSplit->SetEnd( *pRStt );
+ pNewRedl->SetStart( *pREnd );
+ maRedlineTable.Insert( pSplit );
+ if( *pStt == *pEnd && pNewRedl->GetContentIdx() == nullptr )
+ {
+ delete pNewRedl;
+ pNewRedl = nullptr;
+ bCompress = true;
+ }
+ }
+ else if ( SwComparePosition::Equal == eCmpPos )
+ {
+ // handle identical redlines in broken documents
+ // delete old (delete) redline
+ maRedlineTable.DeleteAndDestroy( n );
+ bDec = true;
+ }
+ else if ( SwComparePosition::OverlapBehind == eCmpPos )
+ { // Another workaround for broken redlines
+ pNewRedl->SetStart( *pREnd );
+ }
+ break;
+ case RedlineType::Format:
+ switch( eCmpPos )
+ {
+ case SwComparePosition::OverlapBefore:
+ pRedl->SetStart( *pEnd, pRStt );
+ // re-insert
+ maRedlineTable.Remove( n );
+ maRedlineTable.Insert( pRedl, n );
+ bDec = true;
+ break;
+
+ case SwComparePosition::OverlapBehind:
+ pRedl->SetEnd( *pStt, pREnd );
+ if( *pStt == *pRStt && pRedl->GetContentIdx() == nullptr )
+ {
+ maRedlineTable.DeleteAndDestroy( n );
+ bDec = true;
+ }
+ break;
+
+ case SwComparePosition::Equal:
+ case SwComparePosition::Outside:
+ // Overlaps the current one completely or has the
+ // same dimension, delete the old one
+ maRedlineTable.DeleteAndDestroy( n );
+ bDec = true;
+ break;
+
+ case SwComparePosition::Inside:
+ // Overlaps the current one completely,
+ // split or shorten the new one
+ if( *pEnd != *pREnd )
+ {
+ if( *pEnd != *pRStt )
+ {
+ SwRangeRedline* pNew = new SwRangeRedline( *pRedl );
+ pNew->SetStart( *pEnd );
+ pRedl->SetEnd( *pStt, pREnd );
+ if( *pStt == *pRStt && pRedl->GetContentIdx() == nullptr )
+ maRedlineTable.DeleteAndDestroy( n );
+ AppendRedline( pNew, bCallDelete );
+ n = 0; // re-initialize
+ bDec = true;
+ }
+ }
+ else
+ pRedl->SetEnd( *pStt, pREnd );
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case RedlineType::Delete:
+ switch( pRedl->GetType() )
+ {
+ case RedlineType::Delete:
+ switch( eCmpPos )
+ {
+ case SwComparePosition::Outside:
+ {
+ // Overlaps the current one completely,
+ // split the new one
+ if (*pEnd == *pREnd)
+ {
+ pNewRedl->SetEnd(*pRStt, pEnd);
+ }
+ else if (*pStt == *pRStt)
+ {
+ pNewRedl->SetStart(*pREnd, pStt);
+ }
+ else
+ {
+ SwRangeRedline* pNew = new SwRangeRedline( *pNewRedl );
+ pNew->SetStart( *pREnd );
+ pNewRedl->SetEnd( *pRStt, pEnd );
+ AppendRedline( pNew, bCallDelete );
+ n = 0; // re-initialize
+ bDec = true;
+ }
+ }
+ break;
+
+ case SwComparePosition::Inside:
+ case SwComparePosition::Equal:
+ delete pNewRedl;
+ pNewRedl = nullptr;
+ bCompress = true;
+
+ MaybeNotifyRedlineModification(*pRedl, m_rDoc);
+ break;
+
+ case SwComparePosition::OverlapBefore:
+ case SwComparePosition::OverlapBehind:
+ if( pRedl->IsOwnRedline( *pNewRedl ) &&
+ pRedl->CanCombine( *pNewRedl ))
+ {
+ // If that's the case we can merge it, meaning
+ // the new one covers this well
+ if( SwComparePosition::OverlapBehind == eCmpPos )
+ pNewRedl->SetStart( *pRStt, pStt );
+ else
+ pNewRedl->SetEnd( *pREnd, pEnd );
+ maRedlineTable.DeleteAndDestroy( n );
+ bDec = true;
+ }
+ else if( SwComparePosition::OverlapBehind == eCmpPos )
+ pNewRedl->SetStart( *pREnd, pStt );
+ else
+ pNewRedl->SetEnd( *pRStt, pEnd );
+ break;
+
+ case SwComparePosition::CollideEnd:
+ if (pRStt->GetContentIndex() != 0
+ && pRStt->GetNode() != pREnd->GetNode())
+ { // tdf#147466 HACK: don't combine in this case to avoid the tdf#119571 code from *undeleting* section nodes
+ break;
+ }
+ [[fallthrough]];
+ case SwComparePosition::CollideStart:
+ if( pRedl->IsOwnRedline( *pNewRedl ) &&
+ pRedl->CanCombine( *pNewRedl ) )
+ {
+ if( IsHideChanges( meRedlineFlags ))
+ {
+ // Before we can merge, we make it visible!
+ // We insert temporarily so that pNew is
+ // also dealt with when moving the indices.
+ maRedlineTable.Insert(pNewRedl);
+ pRedl->Show(0, maRedlineTable.GetPos(pRedl));
+ maRedlineTable.Remove( pNewRedl );
+ pRStt = pRedl->Start();
+ pREnd = pRedl->End();
+ }
+
+ // If that's the case we can merge it, meaning
+ // the new one covers this well
+ if( SwComparePosition::CollideStart == eCmpPos )
+ pNewRedl->SetStart( *pRStt, pStt );
+ else
+ pNewRedl->SetEnd( *pREnd, pEnd );
+
+ // delete current (below), and restart process with
+ // previous
+ SwRedlineTable::size_type nToBeDeleted = n;
+ bDec = true;
+
+ if( *(pNewRedl->Start()) <= *pREnd )
+ {
+ // Whoooah, we just extended the new 'redline'
+ // beyond previous redlines, so better start
+ // again. Of course this is not supposed to
+ // happen, and in an ideal world it doesn't,
+ // but unfortunately this code is buggy and
+ // totally rotten so it does happen and we
+ // better fix it.
+ n = 0;
+ bDec = true;
+ }
+
+ maRedlineTable.DeleteAndDestroy( nToBeDeleted );
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case RedlineType::Insert:
+ {
+ // b62341295: Do not throw away redlines
+ // even if they are not allowed to be combined
+ RedlineFlags eOld = meRedlineFlags;
+ if( !( eOld & RedlineFlags::DontCombineRedlines ) &&
+ pRedl->IsOwnRedline( *pNewRedl ) &&
+ // tdf#116084 tdf#121176 don't combine anonymized deletion
+ // and anonymized insertion, i.e. with the same dummy timestamp
+ !pRedl->GetRedlineData(0).IsAnonymized() )
+ {
+ // Collect MoveID's of the redlines we delete.
+ if (nMoveIDToDelete > 1 && maRedlineTable[n]->GetMoved() > 0
+ && (eCmpPos == SwComparePosition::Equal
+ || eCmpPos == SwComparePosition::Inside
+ || eCmpPos == SwComparePosition::Outside
+ || eCmpPos == SwComparePosition::OverlapBefore
+ || eCmpPos == SwComparePosition::OverlapBehind))
+ {
+ deletedMoveIDs.insert(maRedlineTable[n]->GetMoved());
+ }
+
+ // Set to NONE, so that the Delete::Redo merges the Redline data correctly!
+ // The ShowMode needs to be retained!
+ meRedlineFlags = eOld & ~RedlineFlags(RedlineFlags::On | RedlineFlags::Ignore);
+ switch( eCmpPos )
+ {
+ case SwComparePosition::Equal:
+ bCompress = true;
+ maRedlineTable.DeleteAndDestroy( n );
+ bDec = true;
+ [[fallthrough]];
+
+ case SwComparePosition::Inside:
+ if( bCallDelete )
+ {
+ // DeleteAndJoin does not yield the
+ // desired result if there is no paragraph to
+ // join with, i.e. at the end of the document.
+ // For this case, we completely delete the
+ // paragraphs (if, of course, we also start on
+ // a paragraph boundary).
+ if( (pStt->GetContentIndex() == 0) &&
+ pEnd->GetNode().IsEndNode() )
+ {
+ pEnd->Adjust(SwNodeOffset(-1));
+ m_rDoc.getIDocumentContentOperations().DelFullPara( *pNewRedl );
+ }
+ else
+ m_rDoc.getIDocumentContentOperations().DeleteAndJoin( *pNewRedl );
+
+ bCompress = true;
+ }
+ delete pNewRedl;
+ pNewRedl = nullptr;
+
+ // No need to call MaybeNotifyRedlineModification, because a notification
+ // was already sent in DocumentRedlineManager::DeleteRedline
+ break;
+
+ case SwComparePosition::Outside:
+ {
+ maRedlineTable.Remove( n );
+ bDec = true;
+ if( bCallDelete )
+ {
+ TemporaryRedlineUpdater const u(m_rDoc, *pNewRedl);
+ m_rDoc.getIDocumentContentOperations().DeleteAndJoin( *pRedl );
+ n = 0; // re-initialize
+ }
+ delete pRedl;
+ }
+ break;
+
+ case SwComparePosition::OverlapBefore:
+ {
+ SwPaM aPam( *pRStt, *pEnd );
+
+ if( *pEnd == *pREnd )
+ maRedlineTable.DeleteAndDestroy( n );
+ else
+ {
+ pRedl->SetStart( *pEnd, pRStt );
+ // re-insert
+ maRedlineTable.Remove( n );
+ maRedlineTable.Insert( pRedl, n );
+ }
+
+ if( bCallDelete )
+ {
+ TemporaryRedlineUpdater const u(m_rDoc, *pNewRedl);
+ m_rDoc.getIDocumentContentOperations().DeleteAndJoin( aPam );
+ n = 0; // re-initialize
+ }
+ bDec = true;
+ }
+ break;
+
+ case SwComparePosition::OverlapBehind:
+ {
+ SwPaM aPam( *pStt, *pREnd );
+
+ if( *pStt == *pRStt )
+ {
+ maRedlineTable.DeleteAndDestroy( n );
+ bDec = true;
+ }
+ else
+ pRedl->SetEnd( *pStt, pREnd );
+
+ if( bCallDelete )
+ {
+ TemporaryRedlineUpdater const u(m_rDoc, *pNewRedl);
+ m_rDoc.getIDocumentContentOperations().DeleteAndJoin( aPam );
+ n = 0; // re-initialize
+ bDec = true;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ meRedlineFlags = eOld;
+ }
+ else
+ {
+ // it may be necessary to split the existing redline in
+ // two. In this case, pRedl will be changed to cover
+ // only part of its former range, and pNew will cover
+ // the remainder.
+ SwRangeRedline* pNew = nullptr;
+
+ switch( eCmpPos )
+ {
+ case SwComparePosition::Equal:
+ {
+ pRedl->PushData( *pNewRedl );
+ delete pNewRedl;
+ pNewRedl = nullptr;
+ if( IsHideChanges( meRedlineFlags ))
+ {
+ pRedl->Hide(0, maRedlineTable.GetPos(pRedl));
+ }
+ bCompress = true;
+
+ // set IsMoved checking nearby redlines
+ SwRedlineTable::size_type nRIdx = maRedlineTable.GetPos(pRedl);
+ if (nRIdx < maRedlineTable.size()) // in case above 're-insert' failed
+ maRedlineTable.isMoved(nRIdx);
+
+ }
+ break;
+
+ case SwComparePosition::Inside:
+ {
+ if( *pRStt == *pStt )
+ {
+ // #i97421#
+ // redline w/out extent loops
+ if (*pStt != *pEnd)
+ {
+ pNewRedl->PushData( *pRedl, false );
+ pRedl->SetStart( *pEnd, pRStt );
+ // re-insert
+ maRedlineTable.Remove( n );
+ maRedlineTable.Insert( pRedl, n );
+ bDec = true;
+ }
+ }
+ else
+ {
+ pNewRedl->PushData( *pRedl, false );
+ if( *pREnd != *pEnd )
+ {
+ pNew = new SwRangeRedline( *pRedl );
+ pNew->SetStart( *pEnd );
+ }
+ pRedl->SetEnd( *pStt, pREnd );
+ if( !pRedl->HasValidRange() )
+ {
+ // re-insert
+ maRedlineTable.Remove( n );
+ maRedlineTable.Insert( pRedl, n );
+ }
+ }
+ }
+ break;
+
+ case SwComparePosition::Outside:
+ {
+ pRedl->PushData( *pNewRedl );
+ if( *pEnd == *pREnd )
+ {
+ pNewRedl->SetEnd( *pRStt, pEnd );
+ }
+ else if (*pStt == *pRStt)
+ {
+ pNewRedl->SetStart(*pREnd, pStt);
+ }
+ else
+ {
+ pNew = new SwRangeRedline( *pNewRedl );
+ pNew->SetEnd( *pRStt );
+ pNewRedl->SetStart( *pREnd, pStt );
+ }
+ bCompress = true;
+ }
+ break;
+
+ case SwComparePosition::OverlapBefore:
+ {
+ if( *pEnd == *pREnd )
+ {
+ pRedl->PushData( *pNewRedl );
+ pNewRedl->SetEnd( *pRStt, pEnd );
+ if( IsHideChanges( meRedlineFlags ))
+ {
+ maRedlineTable.Insert(pNewRedl);
+ pRedl->Hide(0, maRedlineTable.GetPos(pRedl));
+ maRedlineTable.Remove( pNewRedl );
+ }
+ }
+ else
+ {
+ pNew = new SwRangeRedline( *pRedl );
+ pNew->PushData( *pNewRedl );
+ pNew->SetEnd( *pEnd );
+ pNewRedl->SetEnd( *pRStt, pEnd );
+ pRedl->SetStart( *pNew->End(), pRStt ) ;
+ // re-insert
+ maRedlineTable.Remove( n );
+ maRedlineTable.Insert( pRedl );
+ bDec = true;
+ }
+ }
+ break;
+
+ case SwComparePosition::OverlapBehind:
+ {
+ if( *pStt == *pRStt )
+ {
+ pRedl->PushData( *pNewRedl );
+ pNewRedl->SetStart( *pREnd, pStt );
+ if( IsHideChanges( meRedlineFlags ))
+ {
+ maRedlineTable.Insert( pNewRedl );
+ pRedl->Hide(0, maRedlineTable.GetPos(pRedl));
+ maRedlineTable.Remove( pNewRedl );
+ }
+ }
+ else
+ {
+ pNew = new SwRangeRedline( *pRedl );
+ pNew->PushData( *pNewRedl );
+ pNew->SetStart( *pStt );
+ pNewRedl->SetStart( *pREnd, pStt );
+ pRedl->SetEnd( *pNew->Start(), pREnd );
+ if( !pRedl->HasValidRange() )
+ {
+ // re-insert
+ maRedlineTable.Remove( n );
+ maRedlineTable.Insert( pRedl );
+ }
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ // insert the pNew part (if it exists)
+ if( pNew )
+ {
+ maRedlineTable.Insert( pNew );
+
+ // pNew must be deleted if Insert() wasn't
+ // successful. But that can't happen, since pNew is
+ // part of the original pRedl redline.
+ // OSL_ENSURE( bRet, "Can't insert existing redline?" );
+
+ // restart (now with pRedl being split up)
+ n = 0;
+ bDec = true;
+ }
+ }
+ }
+ break;
+
+ case RedlineType::Format:
+ switch( eCmpPos )
+ {
+ case SwComparePosition::OverlapBefore:
+ pRedl->SetStart( *pEnd, pRStt );
+ // re-insert
+ maRedlineTable.Remove( n );
+ maRedlineTable.Insert( pRedl, n );
+ bDec = true;
+ break;
+
+ case SwComparePosition::OverlapBehind:
+ pRedl->SetEnd( *pStt, pREnd );
+ break;
+
+ case SwComparePosition::Equal:
+ case SwComparePosition::Outside:
+ // Overlaps the current one completely or has the
+ // same dimension, delete the old one
+ maRedlineTable.DeleteAndDestroy( n );
+ bDec = true;
+ break;
+
+ case SwComparePosition::Inside:
+ // Overlaps the current one completely,
+ // split or shorten the new one
+ if( *pEnd != *pREnd )
+ {
+ if( *pEnd != *pRStt )
+ {
+ SwRangeRedline* pNew = new SwRangeRedline( *pRedl );
+ pNew->SetStart( *pEnd );
+ pRedl->SetEnd( *pStt, pREnd );
+ if( ( *pStt == *pRStt ) &&
+ ( pRedl->GetContentIdx() == nullptr ) )
+ maRedlineTable.DeleteAndDestroy( n );
+ AppendRedline( pNew, bCallDelete );
+ n = 0; // re-initialize
+ bDec = true;
+ }
+ }
+ else
+ pRedl->SetEnd( *pStt, pREnd );
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case RedlineType::Format:
+ switch( pRedl->GetType() )
+ {
+ case RedlineType::Insert:
+ case RedlineType::Delete:
+ switch( eCmpPos )
+ {
+ case SwComparePosition::OverlapBefore:
+ pNewRedl->SetEnd( *pRStt, pEnd );
+ break;
+
+ case SwComparePosition::OverlapBehind:
+ pNewRedl->SetStart( *pREnd, pStt );
+ break;
+
+ case SwComparePosition::Equal:
+ case SwComparePosition::Inside:
+ delete pNewRedl;
+ pNewRedl = nullptr;
+
+ MaybeNotifyRedlineModification(*pRedl, m_rDoc);
+ break;
+
+ case SwComparePosition::Outside:
+ // Overlaps the current one completely,
+ // split or shorten the new one
+ if (*pEnd == *pREnd)
+ {
+ pNewRedl->SetEnd(*pRStt, pEnd);
+ }
+ else if (*pStt == *pRStt)
+ {
+ pNewRedl->SetStart(*pREnd, pStt);
+ }
+ else
+ {
+ SwRangeRedline* pNew = new SwRangeRedline( *pNewRedl );
+ pNew->SetStart( *pREnd );
+ pNewRedl->SetEnd( *pRStt, pEnd );
+ AppendRedline( pNew, bCallDelete );
+ n = 0; // re-initialize
+ bDec = true;
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+ case RedlineType::Format:
+ switch( eCmpPos )
+ {
+ case SwComparePosition::Outside:
+ case SwComparePosition::Equal:
+ {
+ // Overlaps the current one completely or has the
+ // same dimension, delete the old one
+ maRedlineTable.DeleteAndDestroy( n );
+ bDec = true;
+ }
+ break;
+
+ case SwComparePosition::Inside:
+ if( pRedl->IsOwnRedline( *pNewRedl ) &&
+ pRedl->CanCombine( *pNewRedl ))
+ {
+ // own one can be ignored completely
+ delete pNewRedl;
+ pNewRedl = nullptr;
+
+ MaybeNotifyRedlineModification(*pRedl, m_rDoc);
+ }
+ else if( *pREnd == *pEnd )
+ // or else only shorten the current one
+ pRedl->SetEnd( *pStt, pREnd );
+ else if( *pRStt == *pStt )
+ {
+ // or else only shorten the current one
+ pRedl->SetStart( *pEnd, pRStt );
+ // re-insert
+ maRedlineTable.Remove( n );
+ maRedlineTable.Insert( pRedl, n );
+ bDec = true;
+ }
+ else
+ {
+ // If it lies completely within the current one
+ // we need to split it
+ SwRangeRedline* pNew = new SwRangeRedline( *pRedl );
+ pNew->SetStart( *pEnd );
+ pRedl->SetEnd( *pStt, pREnd );
+ AppendRedline( pNew, bCallDelete );
+ n = 0; // re-initialize
+ bDec = true;
+ }
+ break;
+
+ case SwComparePosition::OverlapBefore:
+ case SwComparePosition::OverlapBehind:
+ if( pRedl->IsOwnRedline( *pNewRedl ) &&
+ pRedl->CanCombine( *pNewRedl ))
+ {
+ // If that's the case we can merge it, meaning
+ // the new one covers this well
+ if( SwComparePosition::OverlapBehind == eCmpPos )
+ pNewRedl->SetStart( *pRStt, pStt );
+ else
+ pNewRedl->SetEnd( *pREnd, pEnd );
+ maRedlineTable.DeleteAndDestroy( n );
+ bDec = false;
+ }
+ else if( SwComparePosition::OverlapBehind == eCmpPos )
+ pNewRedl->SetStart( *pREnd, pStt );
+ else
+ pNewRedl->SetEnd( *pRStt, pEnd );
+ break;
+
+ case SwComparePosition::CollideEnd:
+ if( pRedl->IsOwnRedline( *pNewRedl ) &&
+ pRedl->CanCombine( *pNewRedl ) &&
+ (n == 0 || *maRedlineTable[ n-1 ]->End() < *pStt))
+ {
+ // If that's the case we can merge it, meaning
+ // the new one covers this well
+ pNewRedl->SetEnd( *pREnd, pEnd );
+ maRedlineTable.DeleteAndDestroy( n );
+ bDec = true;
+ }
+ break;
+ case SwComparePosition::CollideStart:
+ if( pRedl->IsOwnRedline( *pNewRedl ) &&
+ pRedl->CanCombine( *pNewRedl ) &&
+ (n+1 >= maRedlineTable.size() ||
+ (*maRedlineTable[ n+1 ]->Start() >= *pEnd &&
+ *maRedlineTable[ n+1 ]->Start() != *pREnd)))
+ {
+ // If that's the case we can merge it, meaning
+ // the new one covers this well
+ pNewRedl->SetStart( *pRStt, pStt );
+ maRedlineTable.DeleteAndDestroy( n );
+ bDec = true;
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case RedlineType::FmtColl:
+ // How should we behave here?
+ // insert as is
+ break;
+ default:
+ break;
+ }
+ }
+
+ if( pNewRedl )
+ {
+ if( ( *pStt == *pEnd ) &&
+ ( pNewRedl->GetContentIdx() == nullptr ) )
+ { // Do not insert empty redlines
+ delete pNewRedl;
+ pNewRedl = nullptr;
+ }
+ else
+ {
+ if ( bCallDelete && RedlineType::Delete == pNewRedl->GetType() )
+ {
+ if ( pStt->GetContentIndex() != 0 )
+ {
+ // tdf#119571 update the style of the joined paragraph
+ // after a partially deleted paragraph to show its correct style
+ // in "Show changes" mode, too. All removed paragraphs
+ // get the style of the first (partially deleted) paragraph
+ // to avoid text insertion with bad style in the deleted
+ // area later (except paragraphs of the removed tables).
+
+ SwContentNode* pDelNd = pStt->GetNode().GetContentNode();
+ // start copying the style of the first paragraph from the end of the range
+ SwContentNode* pTextNd = pEnd->GetNode().GetContentNode();
+ SwNodeIndex aIdx( pEnd->GetNode() );
+ bool bFirst = true;
+
+ while (pTextNd != nullptr && pDelNd->GetIndex() < pTextNd->GetIndex())
+ {
+ if( pTextNd->IsTextNode() )
+ {
+ SwPosition aPos(aIdx);
+
+ if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ bCompress = true;
+
+ // split redline to store ExtraData per paragraphs
+ SwRangeRedline* pPar = new SwRangeRedline( *pNewRedl );
+ pPar->SetStart( aPos );
+ pNewRedl->SetEnd( aPos );
+
+ // get extradata for reset formatting of the modified paragraph
+ SwRedlineExtraData_FormatColl* pExtraData = lcl_CopyStyle(aPos, *pStt, false);
+ if (pExtraData)
+ {
+ std::unique_ptr<SwRedlineExtraData_FormatColl> xRedlineExtraData;
+ if (!bFirst)
+ pExtraData->SetFormatAll(false);
+ xRedlineExtraData.reset(pExtraData);
+ pPar->SetExtraData( xRedlineExtraData.get() );
+ }
+
+ // skip empty redlines without ExtraData
+ // FIXME: maybe checking pExtraData is redundant here
+ if ( pExtraData || *pPar->Start() != *pPar->End() )
+ maRedlineTable.Insert( pPar );
+ else
+ delete pPar;
+ }
+
+ // modify paragraph formatting
+ lcl_CopyStyle(*pStt, aPos);
+ }
+
+ if (bFirst)
+ bFirst = false;
+
+ // Jump to the previous paragraph and if needed, skip paragraphs of
+ // the removed table(s) in the range to avoid leaving empty tables
+ // because of the non-continuous redline range over the table.
+ // FIXME: this is not enough for tables with inner redlines, where
+ // tracked deletion of the text containing such a table leaves an
+ // empty table at the place of the table (a problem inherited from OOo).
+ pTextNd = nullptr;
+ while( --aIdx > *pDelNd && !aIdx.GetNode().IsContentNode() )
+ {
+ // possible table end
+ if( aIdx.GetNode().IsEndNode() && aIdx.GetNode().FindTableNode() )
+ {
+ SwNodeIndex aIdx2 = aIdx;
+ // search table start and skip table paragraphs
+ while ( pDelNd->GetIndex() < aIdx2.GetIndex() )
+ {
+ SwTableNode* pTable = aIdx2.GetNode().GetTableNode();
+ if( pTable &&
+ pTable->EndOfSectionNode()->GetIndex() == aIdx.GetIndex() )
+ {
+ aIdx = aIdx2;
+ break;
+ }
+ --aIdx2;
+ }
+ }
+ }
+
+ if (aIdx.GetNode().IsContentNode())
+ pTextNd = aIdx.GetNode().GetContentNode();
+ }
+ }
+
+ // delete tables of the deletion explicitly, to avoid
+ // remaining empty tables after accepting the rejection
+ // and visible empty tables in Hide Changes mode
+ // (this was the case, if tables have already contained
+ // other tracked changes)
+ // FIXME: because of recursive nature of AppendRedline,
+ // this doesn't work for selections with multiple tables
+ if ( m_rDoc.GetIDocumentUndoRedo().DoesUndo() )
+ {
+ SwNodeIndex aSttIdx( pStt->GetNode() );
+ SwNodeIndex aEndIdx( pEnd->GetNode() );
+ while ( aSttIdx < aEndIdx )
+ {
+ if ( aSttIdx.GetNode().IsTableNode() )
+ {
+ SvxPrintItem aHasTextChangesOnly(RES_PRINT, false);
+ SwCursor aCursor( SwPosition(aSttIdx), nullptr );
+ m_rDoc.SetRowNotTracked( aCursor, aHasTextChangesOnly, /*bAll=*/true );
+ }
+ ++aSttIdx;
+ }
+ }
+ }
+ bool const ret = maRedlineTable.Insert( pNewRedl );
+ assert(ret || !pNewRedl);
+ if (ret && !pNewRedl)
+ {
+ bMerged = true; // treat InsertWithValidRanges as "merge"
+ }
+ }
+ }
+
+ // If we deleted moved redlines, and there was only 1 MoveID, then we should use that
+ // We overwrite those that was given right now, so it cannot be deeper under other redline
+ if (nMoveIDToDelete > 1 && deletedMoveIDs.size() == 1)
+ {
+ sal_uInt32 nNewMoveID = *(deletedMoveIDs.begin());
+ if (nNewMoveID > 1) // MoveID==1 is for old, unrecognised moves, leave them alone
+ {
+ for (n = 0; n < maRedlineTable.size(); ++n)
+ {
+ if (maRedlineTable[n]->GetMoved() == nMoveIDToDelete)
+ {
+ maRedlineTable[n]->SetMoved(nNewMoveID);
+ }
+ }
+ }
+ }
+
+ if( bCompress )
+ CompressRedlines(nStartPos);
+
+ CHECK_REDLINE( *this )
+
+ return (nullptr != pNewRedl)
+ ? AppendResult::APPENDED
+ : (bMerged ? AppendResult::MERGED : AppendResult::IGNORED);
+}
+
+bool DocumentRedlineManager::AppendTableRowRedline( SwTableRowRedline* pNewRedl )
+{
+ // #TODO - equivalent for 'SwTableRowRedline'
+ /*
+ CHECK_REDLINE( this )
+ */
+
+ if (IsRedlineOn() && !IsShowOriginal(meRedlineFlags))
+ {
+ // #TODO - equivalent for 'SwTableRowRedline'
+ /*
+ pNewRedl->InvalidateRange();
+ */
+
+ // Make equivalent of 'AppendRedline' checks inside here too
+
+ maExtraRedlineTable.Insert( pNewRedl );
+ }
+ else
+ {
+ // TO DO - equivalent for 'SwTableRowRedline'
+ /*
+ if( bCallDelete && RedlineType::Delete == pNewRedl->GetType() )
+ {
+ RedlineFlags eOld = meRedlineFlags;
+ // Set to NONE, so that the Delete::Redo merges the Redline data correctly!
+ // The ShowMode needs to be retained!
+ meRedlineFlags = eOld & ~(RedlineFlags::On | RedlineFlags::Ignore);
+ DeleteAndJoin( *pNewRedl );
+ meRedlineFlags = eOld;
+ }
+ delete pNewRedl, pNewRedl = 0;
+ */
+ }
+ // #TODO - equivalent for 'SwTableRowRedline'
+ /*
+ CHECK_REDLINE( this )
+ */
+
+ return nullptr != pNewRedl;
+}
+
+bool DocumentRedlineManager::AppendTableCellRedline( SwTableCellRedline* pNewRedl )
+{
+ // #TODO - equivalent for 'SwTableCellRedline'
+ /*
+ CHECK_REDLINE( this )
+ */
+
+ if (IsRedlineOn() && !IsShowOriginal(meRedlineFlags))
+ {
+ // #TODO - equivalent for 'SwTableCellRedline'
+ /*
+ pNewRedl->InvalidateRange();
+ */
+
+ // Make equivalent of 'AppendRedline' checks inside here too
+
+ maExtraRedlineTable.Insert( pNewRedl );
+ }
+ else
+ {
+ // TO DO - equivalent for 'SwTableCellRedline'
+ /*
+ if( bCallDelete && RedlineType::Delete == pNewRedl->GetType() )
+ {
+ RedlineFlags eOld = meRedlineFlags;
+ // Set to NONE, so that the Delete::Redo merges the Redline data correctly!
+ // The ShowMode needs to be retained!
+ meRedlineFlags = eOld & ~(RedlineFlags::On | RedlineFlags::Ignore);
+ DeleteAndJoin( *pNewRedl );
+ meRedlineFlags = eOld;
+ }
+ delete pNewRedl, pNewRedl = 0;
+ */
+ }
+ // #TODO - equivalent for 'SwTableCellRedline'
+ /*
+ CHECK_REDLINE( this )
+ */
+
+ return nullptr != pNewRedl;
+}
+
+void DocumentRedlineManager::CompressRedlines(size_t nStartIndex)
+{
+ CHECK_REDLINE( *this )
+
+ void (SwRangeRedline::*pFnc)(sal_uInt16, size_t, bool) = nullptr;
+ RedlineFlags eShow = RedlineFlags::ShowMask & meRedlineFlags;
+ if( eShow == (RedlineFlags::ShowInsert | RedlineFlags::ShowDelete))
+ pFnc = &SwRangeRedline::Show;
+ else if (eShow == RedlineFlags::ShowInsert)
+ pFnc = &SwRangeRedline::Hide;
+
+ // Try to merge identical ones
+ if (nStartIndex == 0)
+ nStartIndex = 1;
+ for( SwRedlineTable::size_type n = nStartIndex; n < maRedlineTable.size(); ++n )
+ {
+ SwRangeRedline* pPrev = maRedlineTable[ n-1 ],
+ * pCur = maRedlineTable[ n ];
+ auto [pPrevStt,pPrevEnd] = pPrev->StartEnd();
+ auto [pCurStt, pCurEnd] = pCur->StartEnd();
+
+ if( *pPrevEnd == *pCurStt && pPrev->CanCombine( *pCur ) &&
+ pPrevStt->GetNode().StartOfSectionNode() ==
+ pCurEnd->GetNode().StartOfSectionNode() &&
+ !pCurEnd->GetNode().StartOfSectionNode()->IsTableNode() )
+ {
+ // we then can merge them
+ SwRedlineTable::size_type nPrevIndex = n-1;
+ pPrev->Show(0, nPrevIndex);
+ pCur->Show(0, n);
+
+ pPrev->SetEnd( *pCur->End() );
+ maRedlineTable.DeleteAndDestroy( n );
+ --n;
+ if( pFnc )
+ (pPrev->*pFnc)(0, nPrevIndex, false);
+ }
+ }
+ CHECK_REDLINE( *this )
+
+ // #TODO - add 'SwExtraRedlineTable' also ?
+}
+
+bool DocumentRedlineManager::SplitRedline( const SwPaM& rRange )
+{
+ bool bChg = false;
+ auto [pStt, pEnd] = rRange.StartEnd(); // SwPosition*
+ SwRedlineTable::size_type n = 0;
+ //FIXME overlapping problem GetRedline( *pStt, &n );
+ for ( ; n < maRedlineTable.size(); ++n)
+ {
+ SwRangeRedline * pRedline = maRedlineTable[ n ];
+ auto [pRedlineStart, pRedlineEnd] = pRedline->StartEnd();
+ if (*pRedlineStart <= *pStt && *pEnd <= *pRedlineEnd)
+ {
+ bChg = true;
+ int nn = 0;
+ if (*pStt == *pRedlineStart)
+ nn += 1;
+ if (*pEnd == *pRedlineEnd)
+ nn += 2;
+
+ SwRangeRedline* pNew = nullptr;
+ switch( nn )
+ {
+ case 0:
+ pNew = new SwRangeRedline( *pRedline );
+ pRedline->SetEnd( *pStt, pRedlineEnd );
+ pNew->SetStart( *pEnd );
+ break;
+
+ case 1:
+ *pRedlineStart = *pEnd;
+ break;
+
+ case 2:
+ *pRedlineEnd = *pStt;
+ break;
+
+ case 3:
+ pRedline->InvalidateRange(SwRangeRedline::Invalidation::Remove);
+ maRedlineTable.DeleteAndDestroy( n-- );
+ pRedline = nullptr;
+ break;
+ }
+ if (pRedline && !pRedline->HasValidRange())
+ {
+ // re-insert
+ maRedlineTable.Remove( n );
+ maRedlineTable.Insert( pRedline, n );
+ }
+ if( pNew )
+ maRedlineTable.Insert( pNew, n );
+ }
+ else if (*pEnd < *pRedlineStart)
+ break;
+ }
+ return bChg;
+
+ // #TODO - add 'SwExtraRedlineTable' also ?
+}
+
+bool DocumentRedlineManager::DeleteRedline( const SwPaM& rRange, bool bSaveInUndo,
+ RedlineType nDelType )
+{
+ if( !rRange.HasMark() || *rRange.GetMark() == *rRange.GetPoint() )
+ return false;
+
+ bool bChg = false;
+
+ if (bSaveInUndo && m_rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ std::unique_ptr<SwUndoRedline> pUndo(new SwUndoRedline( SwUndoId::REDLINE, rRange ));
+ if( pUndo->GetRedlSaveCount() )
+ {
+ m_rDoc.GetIDocumentUndoRedo().AppendUndo(std::move(pUndo));
+ }
+ }
+
+ auto [pStt, pEnd] = rRange.StartEnd(); // SwPosition*
+ SwRedlineTable::size_type n = 0;
+ GetRedline( *pStt, &n );
+ for( ; n < maRedlineTable.size() ; ++n )
+ {
+ SwRangeRedline* pRedl = maRedlineTable[ n ];
+ if( RedlineType::Any != nDelType && nDelType != pRedl->GetType() )
+ continue;
+
+ auto [pRStt, pREnd] = pRedl->StartEnd(); // SwPosition*
+ switch( ComparePosition( *pStt, *pEnd, *pRStt, *pREnd ) )
+ {
+ case SwComparePosition::Equal:
+ case SwComparePosition::Outside:
+ pRedl->InvalidateRange(SwRangeRedline::Invalidation::Remove);
+ maRedlineTable.DeleteAndDestroy( n-- );
+ bChg = true;
+ break;
+
+ case SwComparePosition::OverlapBefore:
+ pRedl->InvalidateRange(SwRangeRedline::Invalidation::Remove);
+ pRedl->SetStart( *pEnd, pRStt );
+ pRedl->InvalidateRange(SwRangeRedline::Invalidation::Add);
+ // re-insert
+ maRedlineTable.Remove( n );
+ maRedlineTable.Insert( pRedl );
+ --n;
+ break;
+
+ case SwComparePosition::OverlapBehind:
+ pRedl->InvalidateRange(SwRangeRedline::Invalidation::Remove);
+ pRedl->SetEnd( *pStt, pREnd );
+ pRedl->InvalidateRange(SwRangeRedline::Invalidation::Add);
+ if( !pRedl->HasValidRange() )
+ {
+ // re-insert
+ maRedlineTable.Remove( n );
+ maRedlineTable.Insert( pRedl );
+ --n;
+ }
+ break;
+
+ case SwComparePosition::Inside:
+ {
+ // this one needs to be split
+ pRedl->InvalidateRange(SwRangeRedline::Invalidation::Remove);
+ if( *pRStt == *pStt )
+ {
+ pRedl->SetStart( *pEnd, pRStt );
+ pRedl->InvalidateRange(SwRangeRedline::Invalidation::Add);
+ // re-insert
+ maRedlineTable.Remove( n );
+ maRedlineTable.Insert( pRedl );
+ --n;
+ }
+ else
+ {
+ SwRangeRedline* pCpy;
+ if( *pREnd != *pEnd )
+ {
+ pCpy = new SwRangeRedline( *pRedl );
+ pCpy->SetStart( *pEnd );
+ pCpy->InvalidateRange(SwRangeRedline::Invalidation::Add);
+ }
+ else
+ pCpy = nullptr;
+ pRedl->SetEnd( *pStt, pREnd );
+ pRedl->InvalidateRange(SwRangeRedline::Invalidation::Add);
+ if( !pRedl->HasValidRange() )
+ {
+ // re-insert
+ maRedlineTable.Remove( n );
+ maRedlineTable.Insert( pRedl );
+ --n;
+ }
+ if( pCpy )
+ maRedlineTable.Insert( pCpy );
+ }
+ }
+ break;
+
+ case SwComparePosition::CollideEnd:
+ // remove (not hidden) empty redlines created for fixing tdf#119571
+ // (Note: hidden redlines are all empty, i.e. start and end are equal.)
+ if ( pRedl->HasMark() && *pRedl->GetMark() == *pRedl->GetPoint() )
+ {
+ pRedl->InvalidateRange(SwRangeRedline::Invalidation::Remove);
+ maRedlineTable.DeleteAndDestroy( n-- );
+ bChg = true;
+ break;
+ }
+ [[fallthrough]];
+
+ case SwComparePosition::Before:
+ n = maRedlineTable.size();
+ break;
+ default:
+ break;
+ }
+ }
+
+ if( bChg )
+ m_rDoc.getIDocumentState().SetModified();
+
+ return bChg;
+
+ // #TODO - add 'SwExtraRedlineTable' also ?
+}
+
+bool DocumentRedlineManager::DeleteRedline( const SwStartNode& rNode, bool bSaveInUndo,
+ RedlineType nDelType )
+{
+ SwPaM aTemp(*rNode.EndOfSectionNode(), rNode);
+ return DeleteRedline(aTemp, bSaveInUndo, nDelType);
+}
+
+SwRedlineTable::size_type DocumentRedlineManager::GetRedlinePos( const SwNode& rNd, RedlineType nType ) const
+{
+ const SwNodeOffset nNdIdx = rNd.GetIndex();
+ // if the table only contains good (i.e. non-overlapping) data, we can do a binary search
+ if (!maRedlineTable.HasOverlappingElements())
+ {
+ // binary search to the first redline with end >= the needle
+ auto it = std::lower_bound(maRedlineTable.begin(), maRedlineTable.end(), rNd,
+ [&nNdIdx](const SwRangeRedline* lhs, const SwNode& /*rhs*/)
+ {
+ return lhs->End()->GetNodeIndex() < nNdIdx;
+ });
+ for( ; it != maRedlineTable.end(); ++it)
+ {
+ const SwRangeRedline* pTmp = *it;
+ auto [pStart, pEnd] = pTmp->StartEnd(); // SwPosition*
+ SwNodeOffset nStart = pStart->GetNodeIndex(),
+ nEnd = pEnd->GetNodeIndex();
+
+ if( ( RedlineType::Any == nType || nType == pTmp->GetType()) &&
+ nStart <= nNdIdx && nNdIdx <= nEnd )
+ return std::distance(maRedlineTable.begin(), it);
+
+ if( nStart > nNdIdx )
+ break;
+ }
+ }
+ else
+ {
+ for( SwRedlineTable::size_type n = 0; n < maRedlineTable.size() ; ++n )
+ {
+ const SwRangeRedline* pTmp = maRedlineTable[ n ];
+ SwNodeOffset nPt = pTmp->GetPoint()->GetNodeIndex(),
+ nMk = pTmp->GetMark()->GetNodeIndex();
+ if( nPt < nMk )
+ std::swap( nMk, nPt );
+
+ if( ( RedlineType::Any == nType || nType == pTmp->GetType()) &&
+ nMk <= nNdIdx && nNdIdx <= nPt )
+ return n;
+
+ if( nMk > nNdIdx )
+ break;
+ }
+ }
+ return SwRedlineTable::npos;
+
+ // #TODO - add 'SwExtraRedlineTable' also ?
+}
+
+SwRedlineTable::size_type
+DocumentRedlineManager::GetRedlineEndPos(SwRedlineTable::size_type nStartPos, const SwNode& rNd,
+ RedlineType nType) const
+{
+ //if the start is already invalid
+ if (nStartPos >= maRedlineTable.size())
+ return nStartPos;
+
+ const SwNodeOffset nNdIdx = rNd.GetIndex();
+ SwRedlineTable::size_type nEndPos = nStartPos;
+ SwRedlineTable::size_type nEndPosTry = nEndPos + 1;
+
+ while (nEndPosTry < maRedlineTable.size()
+ && maRedlineTable[nEndPosTry]->Start()->GetNodeIndex() <= nNdIdx)
+ {
+ if (RedlineType::Any == nType || nType == maRedlineTable[nEndPosTry]->GetType())
+ {
+ nEndPos = nEndPosTry;
+ }
+ nEndPosTry++;
+ }
+ return nEndPos;
+}
+
+void DocumentRedlineManager::UpdateRedlineContentNode(SwRedlineTable::size_type nStartPos,
+ SwRedlineTable::size_type nEndPos) const
+{
+ for (SwRedlineTable::size_type n = nStartPos; n <= nEndPos; ++n)
+ {
+ //just in case we got wrong input
+ if (n >= maRedlineTable.size())
+ return;
+
+ SwPosition* pStart = maRedlineTable[n]->Start();
+ SwPosition* pEnd = maRedlineTable[n]->End();
+ SwContentNode* pCont = pStart->GetNode().GetContentNode();
+ if (pCont)
+ {
+ pStart->nContent.Assign(pCont, pStart->nContent.GetIndex());
+ }
+ pCont = pEnd->GetNode().GetContentNode();
+ if (pCont)
+ {
+ pEnd->nContent.Assign(pCont, pEnd->nContent.GetIndex());
+ }
+ }
+}
+
+bool DocumentRedlineManager::HasRedline( const SwPaM& rPam, RedlineType nType, bool bStartOrEndInRange ) const
+{
+ SwPosition currentStart(*rPam.Start());
+ SwPosition currentEnd(*rPam.End());
+ const SwNode& rEndNode(currentEnd.GetNode());
+
+ for( SwRedlineTable::size_type n = GetRedlinePos( rPam.Start()->GetNode(), nType );
+ n < maRedlineTable.size(); ++n )
+ {
+ const SwRangeRedline* pTmp = maRedlineTable[ n ];
+
+ if ( pTmp->Start()->GetNode() > rEndNode )
+ break;
+
+ if( RedlineType::Any != nType && nType != pTmp->GetType() )
+ continue;
+
+ // redline over the range
+ if ( currentStart < *pTmp->End() && *pTmp->Start() <= currentEnd &&
+ // starting or ending within the range
+ ( !bStartOrEndInRange ||
+ ( currentStart < *pTmp->Start() || *pTmp->End() < currentEnd ) ) )
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+const SwRangeRedline* DocumentRedlineManager::GetRedline( const SwPosition& rPos,
+ SwRedlineTable::size_type* pFndPos ) const
+{
+ SwRedlineTable::size_type nO = maRedlineTable.size(), nM, nU = 0;
+ if( nO > 0 )
+ {
+ nO--;
+ while( nU <= nO )
+ {
+ nM = nU + ( nO - nU ) / 2;
+ const SwRangeRedline* pRedl = maRedlineTable[ nM ];
+ auto [pStt, pEnd] = pRedl->StartEnd();
+ if( pEnd == pStt
+ ? *pStt == rPos
+ : ( *pStt <= rPos && rPos < *pEnd ) )
+ {
+ while( nM && rPos == *maRedlineTable[ nM - 1 ]->End() &&
+ rPos == *maRedlineTable[ nM - 1 ]->Start() )
+ {
+ --nM;
+ pRedl = maRedlineTable[ nM ];
+ }
+ // if there are format and insert changes in the same position
+ // show insert change first.
+ // since the redlines are sorted by position, only check the redline
+ // before and after the current redline
+ if( RedlineType::Format == pRedl->GetType() )
+ {
+ if( nM && rPos >= *maRedlineTable[ nM - 1 ]->Start() &&
+ rPos <= *maRedlineTable[ nM - 1 ]->End() &&
+ ( RedlineType::Insert == maRedlineTable[ nM - 1 ]->GetType() ) )
+ {
+ --nM;
+ pRedl = maRedlineTable[ nM ];
+ }
+ else if( ( nM + 1 ) <= nO && rPos >= *maRedlineTable[ nM + 1 ]->Start() &&
+ rPos <= *maRedlineTable[ nM + 1 ]->End() &&
+ ( RedlineType::Insert == maRedlineTable[ nM + 1 ]->GetType() ) )
+ {
+ ++nM;
+ pRedl = maRedlineTable[ nM ];
+ }
+ }
+
+ if( pFndPos )
+ *pFndPos = nM;
+ return pRedl;
+ }
+ else if( *pEnd <= rPos )
+ nU = nM + 1;
+ else if( nM == 0 )
+ {
+ if( pFndPos )
+ *pFndPos = nU;
+ return nullptr;
+ }
+ else
+ nO = nM - 1;
+ }
+ }
+ if( pFndPos )
+ *pFndPos = nU;
+ return nullptr;
+
+ // #TODO - add 'SwExtraRedlineTable' also ?
+}
+
+bool DocumentRedlineManager::AcceptRedlineRange(SwRedlineTable::size_type nPosOrigin,
+ SwRedlineTable::size_type& nPosStart,
+ SwRedlineTable::size_type& nPosEnd,
+ bool bCallDelete)
+{
+ bool bRet = false;
+
+ SwRangeRedline* pTmp = maRedlineTable[nPosOrigin];
+ SwRedlineTable::size_type nRdlIdx = nPosEnd + 1;
+ SwRedlineData aOrigData = pTmp->GetRedlineData(0);
+
+ SwNodeOffset nPamStartNI = maRedlineTable[nPosStart]->Start()->GetNodeIndex();
+ sal_Int32 nPamStartCI = maRedlineTable[nPosStart]->Start()->GetContentIndex();
+ SwNodeOffset nPamEndtNI = maRedlineTable[nPosEnd]->End()->GetNodeIndex();
+ sal_Int32 nPamEndCI = maRedlineTable[nPosEnd]->End()->GetContentIndex();
+ do
+ {
+ nRdlIdx--;
+ pTmp = maRedlineTable[nRdlIdx];
+ if (pTmp->Start()->GetNodeIndex() < nPamStartNI
+ || (pTmp->Start()->GetNodeIndex() == nPamStartNI
+ && pTmp->Start()->GetContentIndex() < nPamStartCI))
+ break;
+
+ if (pTmp->End()->GetNodeIndex() > nPamEndtNI
+ || (pTmp->End()->GetNodeIndex() == nPamEndtNI
+ && pTmp->End()->GetContentIndex() > nPamEndCI))
+ {
+ }
+ else if (pTmp->GetRedlineData(0).CanCombineForAcceptReject(aOrigData))
+ {
+ if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ m_rDoc.GetIDocumentUndoRedo().AppendUndo(
+ std::make_unique<SwUndoAcceptRedline>(*pTmp));
+ }
+ nPamEndtNI = pTmp->Start()->GetNodeIndex();
+ nPamEndCI = pTmp->Start()->GetContentIndex();
+ bRet |= lcl_AcceptRedline(maRedlineTable, nRdlIdx, bCallDelete);
+ nRdlIdx++; //we will decrease it in the loop anyway.
+ }
+ else if (aOrigData.GetType() == RedlineType::Insert
+ && pTmp->GetType() == RedlineType::Delete && pTmp->GetStackCount() > 1
+ && pTmp->GetType(1) == RedlineType::Insert
+ && pTmp->GetRedlineData(1).CanCombineForAcceptReject(aOrigData))
+ {
+ // The Insert redline we want to accept has a deletion redline too
+ // we should leave the deletion redline, and only accept the inner insert.
+ if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ m_rDoc.GetIDocumentUndoRedo().AppendUndo(
+ std::make_unique<SwUndoAcceptRedline>(*pTmp, 1));
+ }
+ nPamEndtNI = pTmp->Start()->GetNodeIndex();
+ nPamEndCI = pTmp->Start()->GetContentIndex();
+ bRet |= lcl_AcceptInnerInsertRedline(maRedlineTable, nRdlIdx, 1);
+ nRdlIdx++; //we will decrease it in the loop anyway.
+ }
+ } while (nRdlIdx > 0);
+ return bRet;
+}
+
+bool DocumentRedlineManager::AcceptMovedRedlines(sal_uInt32 nMovedID, bool bCallDelete)
+{
+ assert(nMovedID > 1); // 0, and 1 is reserved
+ bool bRet = false;
+ SwRedlineTable::size_type nRdlIdx = maRedlineTable.size();
+
+ while (nRdlIdx > 0)
+ {
+ nRdlIdx--;
+ SwRangeRedline* pTmp = maRedlineTable[nRdlIdx];
+ if (pTmp->GetMoved(0) == nMovedID
+ || (pTmp->GetStackCount() > 1 && pTmp->GetMoved(1) == nMovedID))
+ {
+ if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ m_rDoc.GetIDocumentUndoRedo().AppendUndo(
+ std::make_unique<SwUndoAcceptRedline>(*pTmp));
+ }
+
+ if (pTmp->GetMoved(0) == nMovedID)
+ bRet |= lcl_AcceptRedline(maRedlineTable, nRdlIdx, bCallDelete);
+ else
+ bRet |= lcl_AcceptInnerInsertRedline(maRedlineTable, nRdlIdx, 1);
+
+ nRdlIdx++; //we will decrease it in the loop anyway.
+ }
+ }
+ return bRet;
+}
+
+bool DocumentRedlineManager::AcceptRedline(SwRedlineTable::size_type nPos, bool bCallDelete,
+ bool bRange)
+{
+ bool bRet = false;
+
+ // Switch to visible in any case
+ if( (RedlineFlags::ShowInsert | RedlineFlags::ShowDelete) !=
+ (RedlineFlags::ShowMask & meRedlineFlags) )
+ SetRedlineFlags( RedlineFlags::ShowInsert | RedlineFlags::ShowDelete | meRedlineFlags );
+
+ SwRangeRedline* pTmp = maRedlineTable[ nPos ];
+ bool bAnonym = pTmp->GetRedlineData(0).IsAnonymized();
+
+ pTmp->Show(0, maRedlineTable.GetPos(pTmp), /*bForced=*/true);
+ pTmp->Show(1, maRedlineTable.GetPos(pTmp), /*bForced=*/true);
+ if( pTmp->HasMark() && pTmp->IsVisible() )
+ {
+ if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ SwRewriter aRewriter;
+
+ aRewriter.AddRule(UndoArg1, pTmp->GetDescr());
+ m_rDoc.GetIDocumentUndoRedo().StartUndo(SwUndoId::ACCEPT_REDLINE, &aRewriter);
+ }
+
+ int nLoopCnt = 2;
+ sal_uInt16 nSeqNo = pTmp->GetSeqNo();
+
+ if (bRange && !nSeqNo && !bAnonym
+ && !pTmp->Start()->GetNode().StartOfSectionNode()->IsTableNode())
+ {
+ sal_uInt32 nMovedID = pTmp->GetMoved(0);
+ if (nMovedID > 1)
+ {
+ // Accept all redlineData with this unique move id
+ bRet |= AcceptMovedRedlines(nMovedID, bCallDelete);
+ }
+ else
+ {
+ SwRedlineTable::size_type nPosStart = nPos;
+ SwRedlineTable::size_type nPosEnd = nPos;
+
+ maRedlineTable.getConnectedArea(nPos, nPosStart, nPosEnd, true);
+
+ // Accept redlines between pPamStart-pPamEnd.
+ // but only those that can be combined with the selected.
+ bRet |= AcceptRedlineRange(nPos, nPosStart, nPosEnd, bCallDelete);
+ }
+ }
+ else do {
+
+ if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ m_rDoc.GetIDocumentUndoRedo().AppendUndo(
+ std::make_unique<SwUndoAcceptRedline>(*pTmp) );
+ }
+
+ bRet |= lcl_AcceptRedline( maRedlineTable, nPos, bCallDelete );
+
+ if( nSeqNo )
+ {
+ if( SwRedlineTable::npos == nPos )
+ nPos = 0;
+ SwRedlineTable::size_type nFndPos = 2 == nLoopCnt
+ ? maRedlineTable.FindNextSeqNo( nSeqNo, nPos )
+ : maRedlineTable.FindPrevSeqNo( nSeqNo, nPos );
+ if( SwRedlineTable::npos != nFndPos || ( 0 != ( --nLoopCnt ) &&
+ SwRedlineTable::npos != ( nFndPos =
+ maRedlineTable.FindPrevSeqNo( nSeqNo, nPos ))) )
+ {
+ nPos = nFndPos;
+ pTmp = maRedlineTable[ nPos ];
+ }
+ else
+ nLoopCnt = 0;
+ }
+ else
+ nLoopCnt = 0;
+
+ } while (nLoopCnt);
+
+ if( bRet )
+ {
+ CompressRedlines();
+ m_rDoc.getIDocumentState().SetModified();
+ }
+
+ if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ m_rDoc.GetIDocumentUndoRedo().EndUndo(SwUndoId::END, nullptr);
+ }
+ }
+ return bRet;
+
+ // #TODO - add 'SwExtraRedlineTable' also ?
+}
+
+bool DocumentRedlineManager::AcceptRedline( const SwPaM& rPam, bool bCallDelete, sal_Int8 nDepth )
+{
+ // Switch to visible in any case
+ if( (RedlineFlags::ShowInsert | RedlineFlags::ShowDelete) !=
+ (RedlineFlags::ShowMask & meRedlineFlags) )
+ SetRedlineFlags( RedlineFlags::ShowInsert | RedlineFlags::ShowDelete | meRedlineFlags );
+
+ // The Selection is only in the ContentSection. If there are Redlines
+ // to Non-ContentNodes before or after that, then the Selections
+ // expand to them.
+ std::shared_ptr<SwUnoCursor> const pPam(m_rDoc.CreateUnoCursor(*rPam.GetPoint(), false));
+ if (rPam.HasMark())
+ {
+ pPam->SetMark();
+ *pPam->GetMark() = *rPam.GetMark();
+ }
+ lcl_AdjustRedlineRange(*pPam);
+
+ if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ m_rDoc.GetIDocumentUndoRedo().StartUndo( SwUndoId::ACCEPT_REDLINE, nullptr );
+ m_rDoc.GetIDocumentUndoRedo().AppendUndo(
+ std::make_unique<SwUndoAcceptRedline>(*pPam, nDepth));
+ }
+
+ int nRet = 0;
+ if (nDepth == 0)
+ {
+ nRet = lcl_AcceptRejectRedl(lcl_AcceptRedline, maRedlineTable, bCallDelete, *pPam);
+ }
+ else
+ {
+ // For now it is called only if it is an Insert redline in a delete redline.
+ SwRedlineTable::size_type nRdlIdx = 0;
+ maRedlineTable.FindAtPosition(*rPam.Start(), nRdlIdx);
+ if (lcl_AcceptInnerInsertRedline(maRedlineTable, nRdlIdx, 1))
+ nRet = 1;
+ }
+ if( nRet > 0 )
+ {
+ CompressRedlines();
+ m_rDoc.getIDocumentState().SetModified();
+ }
+ if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ OUString aTmpStr;
+
+ {
+ SwRewriter aRewriter;
+ aRewriter.AddRule(UndoArg1, OUString::number(nRet));
+ aTmpStr = aRewriter.Apply(SwResId(STR_N_REDLINES));
+ }
+
+ SwRewriter aRewriter;
+ aRewriter.AddRule(UndoArg1, aTmpStr);
+
+ m_rDoc.GetIDocumentUndoRedo().EndUndo( SwUndoId::ACCEPT_REDLINE, &aRewriter );
+ }
+ return nRet != 0;
+
+ // #TODO - add 'SwExtraRedlineTable' also ?
+}
+
+void DocumentRedlineManager::AcceptRedlineParagraphFormatting( const SwPaM &rPam )
+{
+ auto [pStt, pEnd] = rPam.StartEnd(); // SwPosition*
+
+ const SwNodeOffset nSttIdx = pStt->GetNodeIndex();
+ const SwNodeOffset nEndIdx = pEnd->GetNodeIndex();
+
+ for( SwRedlineTable::size_type n = 0; n < maRedlineTable.size() ; ++n )
+ {
+ const SwRangeRedline* pTmp = maRedlineTable[ n ];
+ SwNodeOffset nPt = pTmp->GetPoint()->GetNodeIndex(),
+ nMk = pTmp->GetMark()->GetNodeIndex();
+ if( nPt < nMk )
+ std::swap( nMk, nPt );
+
+ if( RedlineType::ParagraphFormat == pTmp->GetType() &&
+ ( (nSttIdx <= nMk && nMk <= nEndIdx) || (nSttIdx <= nPt && nPt <= nEndIdx) ) )
+ AcceptRedline( n, false );
+
+ if( nMk > nEndIdx )
+ break;
+ }
+}
+
+bool DocumentRedlineManager::RejectRedlineRange(SwRedlineTable::size_type nPosOrigin,
+ SwRedlineTable::size_type& nPosStart,
+ SwRedlineTable::size_type& nPosEnd,
+ bool bCallDelete)
+{
+ bool bRet = false;
+
+ SwRangeRedline* pTmp = maRedlineTable[nPosOrigin];
+ SwRedlineTable::size_type nRdlIdx = nPosEnd + 1;
+ SwRedlineData aOrigData = pTmp->GetRedlineData(0);
+
+ SwNodeOffset nPamStartNI = maRedlineTable[nPosStart]->Start()->GetNodeIndex();
+ sal_Int32 nPamStartCI = maRedlineTable[nPosStart]->Start()->GetContentIndex();
+ SwNodeOffset nPamEndtNI = maRedlineTable[nPosEnd]->End()->GetNodeIndex();
+ sal_Int32 nPamEndCI = maRedlineTable[nPosEnd]->End()->GetContentIndex();
+ do
+ {
+ nRdlIdx--;
+ pTmp = maRedlineTable[nRdlIdx];
+ if (pTmp->Start()->GetNodeIndex() < nPamStartNI
+ || (pTmp->Start()->GetNodeIndex() == nPamStartNI
+ && pTmp->Start()->GetContentIndex() < nPamStartCI))
+ break;
+
+ if (pTmp->End()->GetNodeIndex() > nPamEndtNI
+ || (pTmp->End()->GetNodeIndex() == nPamEndtNI
+ && pTmp->End()->GetContentIndex() > nPamEndCI))
+ {
+ }
+ else if (pTmp->GetRedlineData(0).CanCombineForAcceptReject(aOrigData))
+ {
+ if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ std::unique_ptr<SwUndoRejectRedline> pUndoRdl
+ = std::make_unique<SwUndoRejectRedline>(*pTmp);
+#if OSL_DEBUG_LEVEL > 0
+ pUndoRdl->SetRedlineCountDontCheck(true);
+#endif
+ m_rDoc.GetIDocumentUndoRedo().AppendUndo(std::move(pUndoRdl));
+ }
+ nPamEndtNI = pTmp->Start()->GetNodeIndex();
+ nPamEndCI = pTmp->Start()->GetContentIndex();
+ bRet |= lcl_RejectRedline(maRedlineTable, nRdlIdx, bCallDelete);
+ nRdlIdx++; //we will decrease it in the loop anyway.
+ }
+ else if (aOrigData.GetType() == RedlineType::Insert
+ && pTmp->GetType() == RedlineType::Delete && pTmp->GetStackCount() > 1
+ && pTmp->GetType(1) == RedlineType::Insert
+ && pTmp->GetRedlineData(1).CanCombineForAcceptReject(aOrigData))
+ {
+ // The Insert redline we want to reject has a deletion redline too
+ // without the insert, the delete is meaningless
+ // so we rather just accept the deletion redline
+ if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ std::unique_ptr<SwUndoRejectRedline> pUndoRdl
+ = std::make_unique<SwUndoRejectRedline>(*pTmp, 1);
+#if OSL_DEBUG_LEVEL > 0
+ pUndoRdl->SetRedlineCountDontCheck(true);
+#endif
+ m_rDoc.GetIDocumentUndoRedo().AppendUndo(std::move(pUndoRdl));
+ }
+ nPamEndtNI = pTmp->Start()->GetNodeIndex();
+ nPamEndCI = pTmp->Start()->GetContentIndex();
+ bRet |= lcl_AcceptRedline(maRedlineTable, nRdlIdx, bCallDelete);
+ nRdlIdx++; //we will decrease it in the loop anyway.
+ }
+
+ } while (nRdlIdx > 0);
+ return bRet;
+}
+
+bool DocumentRedlineManager::RejectMovedRedlines(sal_uInt32 nMovedID, bool bCallDelete)
+{
+ assert(nMovedID > 1); // 0, and 1 is reserved
+ bool bRet = false;
+ SwRedlineTable::size_type nRdlIdx = maRedlineTable.size();
+
+ while (nRdlIdx > 0)
+ {
+ nRdlIdx--;
+ SwRangeRedline* pTmp = maRedlineTable[nRdlIdx];
+ if (pTmp->GetMoved(0) == nMovedID
+ || (pTmp->GetStackCount() > 1 && pTmp->GetMoved(1) == nMovedID))
+ {
+ if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ std::unique_ptr<SwUndoRejectRedline> pUndoRdl
+ = std::make_unique<SwUndoRejectRedline>(*pTmp);
+#if OSL_DEBUG_LEVEL > 0
+ pUndoRdl->SetRedlineCountDontCheck(true);
+#endif
+ m_rDoc.GetIDocumentUndoRedo().AppendUndo(std::move(pUndoRdl));
+ }
+
+ if (pTmp->GetMoved(0) == nMovedID)
+ bRet |= lcl_RejectRedline(maRedlineTable, nRdlIdx, bCallDelete);
+ else
+ bRet |= lcl_AcceptRedline(maRedlineTable, nRdlIdx, bCallDelete);
+
+ nRdlIdx++; //we will decrease it in the loop anyway.
+ }
+ }
+ return bRet;
+}
+
+bool DocumentRedlineManager::RejectRedline(SwRedlineTable::size_type nPos,
+ bool bCallDelete, bool bRange)
+{
+ bool bRet = false;
+
+ // Switch to visible in any case
+ if( (RedlineFlags::ShowInsert | RedlineFlags::ShowDelete) !=
+ (RedlineFlags::ShowMask & meRedlineFlags) )
+ SetRedlineFlags( RedlineFlags::ShowInsert | RedlineFlags::ShowDelete | meRedlineFlags );
+
+ SwRangeRedline* pTmp = maRedlineTable[ nPos ];
+ bool bAnonym = pTmp->GetRedlineData(0).IsAnonymized();
+
+ pTmp->Show(0, maRedlineTable.GetPos(pTmp), /*bForced=*/true);
+ pTmp->Show(1, maRedlineTable.GetPos(pTmp), /*bForced=*/true);
+ if( pTmp->HasMark() && pTmp->IsVisible() )
+ {
+ if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ SwRewriter aRewriter;
+
+ aRewriter.AddRule(UndoArg1, pTmp->GetDescr());
+ m_rDoc.GetIDocumentUndoRedo().StartUndo(SwUndoId::REJECT_REDLINE, &aRewriter);
+ }
+
+ int nLoopCnt = 2;
+ sal_uInt16 nSeqNo = pTmp->GetSeqNo();
+
+ if (bRange && !nSeqNo && !bAnonym
+ && !pTmp->Start()->GetNode().StartOfSectionNode()->IsTableNode())
+ {
+ sal_uInt32 nMovedID = pTmp->GetMoved(0);
+ if (nMovedID > 1)
+ {
+ // Reject all redlineData with this unique move id
+ bRet |= RejectMovedRedlines(nMovedID, bCallDelete);
+ }
+ else
+ {
+ SwRedlineTable::size_type nPosStart = nPos;
+ SwRedlineTable::size_type nPosEnd = nPos;
+ maRedlineTable.getConnectedArea(nPos, nPosStart, nPosEnd, true);
+
+ // Reject items between pPamStart-pPamEnd
+ // but only those that can be combined with the selected.
+
+ bRet |= RejectRedlineRange(nPos, nPosStart, nPosEnd, bCallDelete);
+ }
+ }
+ else do {
+
+ if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ m_rDoc.GetIDocumentUndoRedo().AppendUndo(
+ std::make_unique<SwUndoRejectRedline>( *pTmp ) );
+ }
+
+ bRet |= lcl_RejectRedline( maRedlineTable, nPos, bCallDelete );
+
+ if( nSeqNo )
+ {
+ if( SwRedlineTable::npos == nPos )
+ nPos = 0;
+ SwRedlineTable::size_type nFndPos = 2 == nLoopCnt
+ ? maRedlineTable.FindNextSeqNo( nSeqNo, nPos )
+ : maRedlineTable.FindPrevSeqNo( nSeqNo, nPos );
+ if( SwRedlineTable::npos != nFndPos || ( 0 != ( --nLoopCnt ) &&
+ SwRedlineTable::npos != ( nFndPos =
+ maRedlineTable.FindPrevSeqNo( nSeqNo, nPos ))) )
+ {
+ nPos = nFndPos;
+ pTmp = maRedlineTable[ nPos ];
+ }
+ else
+ nLoopCnt = 0;
+ }
+ else
+ nLoopCnt = 0;
+
+ } while (nLoopCnt);
+
+ if( bRet )
+ {
+ CompressRedlines();
+ m_rDoc.getIDocumentState().SetModified();
+ }
+
+ if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ m_rDoc.GetIDocumentUndoRedo().EndUndo(SwUndoId::END, nullptr);
+ }
+ }
+ return bRet;
+
+ // #TODO - add 'SwExtraRedlineTable' also ?
+}
+
+bool DocumentRedlineManager::RejectRedline( const SwPaM& rPam, bool bCallDelete, sal_Int8 nDepth )
+{
+ // Switch to visible in any case
+ if( (RedlineFlags::ShowInsert | RedlineFlags::ShowDelete) !=
+ (RedlineFlags::ShowMask & meRedlineFlags) )
+ SetRedlineFlags( RedlineFlags::ShowInsert | RedlineFlags::ShowDelete | meRedlineFlags );
+
+ // The Selection is only in the ContentSection. If there are Redlines
+ // to Non-ContentNodes before or after that, then the Selections
+ // expand to them.
+ SwPaM aPam( *rPam.GetMark(), *rPam.GetPoint() );
+ lcl_AdjustRedlineRange( aPam );
+
+ if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ m_rDoc.GetIDocumentUndoRedo().StartUndo( SwUndoId::REJECT_REDLINE, nullptr );
+ m_rDoc.GetIDocumentUndoRedo().AppendUndo( std::make_unique<SwUndoRejectRedline>(aPam, nDepth) );
+ }
+
+ int nRet = 0;
+ if (nDepth == 0)
+ {
+ nRet = lcl_AcceptRejectRedl(lcl_RejectRedline, maRedlineTable, bCallDelete, aPam);
+ }
+ else
+ {
+ // For now it is called only if it is an Insert redline in a delete redline.
+ SwRedlineTable::size_type nRdlIdx = 0;
+ maRedlineTable.FindAtPosition(*rPam.Start(), nRdlIdx);
+ if (lcl_AcceptRedline(maRedlineTable, nRdlIdx, bCallDelete))
+ nRet = 1;
+ }
+
+ if( nRet > 0 )
+ {
+ CompressRedlines();
+ m_rDoc.getIDocumentState().SetModified();
+ }
+ if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ OUString aTmpStr;
+
+ {
+ SwRewriter aRewriter;
+ aRewriter.AddRule(UndoArg1, OUString::number(nRet));
+ aTmpStr = aRewriter.Apply(SwResId(STR_N_REDLINES));
+ }
+
+ SwRewriter aRewriter;
+ aRewriter.AddRule(UndoArg1, aTmpStr);
+
+ m_rDoc.GetIDocumentUndoRedo().EndUndo( SwUndoId::REJECT_REDLINE, &aRewriter );
+ }
+
+ return nRet != 0;
+
+ // #TODO - add 'SwExtraRedlineTable' also ?
+}
+
+void DocumentRedlineManager::AcceptAllRedline(bool bAccept)
+{
+ bool bSuccess = true;
+ OUString sUndoStr;
+ IDocumentUndoRedo& rUndoMgr = m_rDoc.GetIDocumentUndoRedo();
+
+ if (maRedlineTable.size() > 1)
+ {
+ {
+ SwRewriter aRewriter;
+ aRewriter.AddRule(UndoArg1, OUString::number(maRedlineTable.size()));
+ sUndoStr = aRewriter.Apply(SwResId(STR_N_REDLINES));
+ }
+
+ SwRewriter aRewriter;
+ aRewriter.AddRule(UndoArg1, sUndoStr);
+ rUndoMgr.StartUndo(bAccept ? SwUndoId::ACCEPT_REDLINE : SwUndoId::REJECT_REDLINE, &aRewriter);
+ }
+
+ while (!maRedlineTable.empty() && bSuccess)
+ {
+ if (bAccept)
+ bSuccess = AcceptRedline(maRedlineTable.size() - 1, true);
+ else
+ bSuccess = RejectRedline(maRedlineTable.size() - 1, true);
+ }
+
+ if (!sUndoStr.isEmpty())
+ {
+ rUndoMgr.EndUndo(SwUndoId::EMPTY, nullptr);
+ }
+}
+
+const SwRangeRedline* DocumentRedlineManager::SelNextRedline( SwPaM& rPam ) const
+{
+ rPam.DeleteMark();
+ rPam.SetMark();
+
+ SwPosition& rSttPos = *rPam.GetPoint();
+ SwPosition aSavePos( rSttPos );
+ bool bRestart;
+
+ // If the starting position points to the last valid ContentNode,
+ // we take the next Redline in any case.
+ SwRedlineTable::size_type n = 0;
+ const SwRangeRedline* pFnd = GetRedlineTable().FindAtPosition( rSttPos, n );
+ if( pFnd )
+ {
+ const SwPosition* pEnd = pFnd->End();
+ if( !pEnd->GetNode().IsContentNode() )
+ {
+ SwNodeIndex aTmp( pEnd->GetNode() );
+ SwContentNode* pCNd = SwNodes::GoPrevSection( &aTmp );
+ if( !pCNd || ( aTmp == rSttPos.GetNode() &&
+ pCNd->Len() == rSttPos.GetContentIndex() ))
+ pFnd = nullptr;
+ }
+ if( pFnd )
+ rSttPos = *pFnd->End();
+ }
+
+ do {
+ bRestart = false;
+
+ for( ; !pFnd && n < maRedlineTable.size(); ++n )
+ {
+ pFnd = maRedlineTable[ n ];
+ if( pFnd->HasMark() && pFnd->IsVisible() )
+ {
+ *rPam.GetMark() = *pFnd->Start();
+ rSttPos = *pFnd->End();
+ break;
+ }
+ else
+ pFnd = nullptr;
+ }
+
+ if( pFnd )
+ {
+ // Merge all of the same type and author that are
+ // consecutive into one Selection.
+ const SwPosition* pPrevEnd = pFnd->End();
+ while( ++n < maRedlineTable.size() )
+ {
+ const SwRangeRedline* pTmp = maRedlineTable[ n ];
+ if( pTmp->HasMark() && pTmp->IsVisible() )
+ {
+ const SwPosition *pRStt;
+ if( pFnd->GetType() != pTmp->GetType() ||
+ pFnd->GetAuthor() != pTmp->GetAuthor() )
+ break;
+ pRStt = pTmp->Start();
+ if( *pPrevEnd == *pRStt || IsPrevPos( *pPrevEnd, *pRStt ) )
+ {
+ pPrevEnd = pTmp->End();
+ rSttPos = *pPrevEnd;
+ }
+ else
+ break;
+ }
+ }
+ }
+
+ if( pFnd )
+ {
+ const SwRangeRedline* pSaveFnd = pFnd;
+
+ SwContentNode* pCNd;
+ SwPosition* pPos = rPam.GetMark();
+ if( !pPos->GetNode().IsContentNode() )
+ {
+ pCNd = m_rDoc.GetNodes().GoNextSection( pPos );
+ if( pCNd )
+ {
+ if( pPos->GetNode() <= rPam.GetPoint()->GetNode() )
+ pPos->Assign( *pCNd, 0 );
+ else
+ pFnd = nullptr;
+ }
+ }
+
+ if( pFnd )
+ {
+ pPos = rPam.GetPoint();
+ if( !pPos->GetNode().IsContentNode() )
+ {
+ pCNd = SwNodes::GoPrevSection( pPos );
+ if( pCNd )
+ {
+ if( pPos->GetNode() >= rPam.GetMark()->GetNode() )
+ pPos->Assign( *pCNd, pCNd->Len() );
+ else
+ pFnd = nullptr;
+ }
+ }
+ }
+
+ if( !pFnd || *rPam.GetMark() == *rPam.GetPoint() )
+ {
+ if( n < maRedlineTable.size() )
+ {
+ bRestart = true;
+ *rPam.GetPoint() = *pSaveFnd->End();
+ }
+ else
+ {
+ rPam.DeleteMark();
+ *rPam.GetPoint() = aSavePos;
+ }
+ pFnd = nullptr;
+ }
+ }
+ } while( bRestart );
+
+ return pFnd;
+
+ // #TODO - add 'SwExtraRedlineTable' also ?
+}
+
+const SwRangeRedline* DocumentRedlineManager::SelPrevRedline( SwPaM& rPam ) const
+{
+ rPam.DeleteMark();
+ rPam.SetMark();
+
+ SwPosition& rSttPos = *rPam.GetPoint();
+ SwPosition aSavePos( rSttPos );
+ bool bRestart;
+
+ // If the starting position points to the last valid ContentNode,
+ // we take the previous Redline in any case.
+ SwRedlineTable::size_type n = 0;
+ const SwRangeRedline* pFnd = GetRedlineTable().FindAtPosition( rSttPos, n, false );
+ if( pFnd )
+ {
+ const SwPosition* pStt = pFnd->Start();
+ if( !pStt->GetNode().IsContentNode() )
+ {
+ SwNodeIndex aTmp( pStt->GetNode() );
+ SwContentNode* pCNd = m_rDoc.GetNodes().GoNextSection( &aTmp );
+ if( !pCNd || ( aTmp == rSttPos.GetNode() &&
+ !rSttPos.GetContentIndex() ))
+ pFnd = nullptr;
+ }
+ if( pFnd )
+ rSttPos = *pFnd->Start();
+ }
+
+ do {
+ bRestart = false;
+
+ while( !pFnd && 0 < n )
+ {
+ pFnd = maRedlineTable[ --n ];
+ if( pFnd->HasMark() && pFnd->IsVisible() )
+ {
+ *rPam.GetMark() = *pFnd->End();
+ rSttPos = *pFnd->Start();
+ }
+ else
+ pFnd = nullptr;
+ }
+
+ if( pFnd )
+ {
+ // Merge all of the same type and author that are
+ // consecutive into one Selection.
+ const SwPosition* pNextStt = pFnd->Start();
+ while( 0 < n )
+ {
+ const SwRangeRedline* pTmp = maRedlineTable[ --n ];
+ if( pTmp->HasMark() && pTmp->IsVisible() )
+ {
+ const SwPosition *pREnd;
+ if( pFnd->GetType() == pTmp->GetType() &&
+ pFnd->GetAuthor() == pTmp->GetAuthor() &&
+ ( *pNextStt == *( pREnd = pTmp->End() ) ||
+ IsPrevPos( *pREnd, *pNextStt )) )
+ {
+ pNextStt = pTmp->Start();
+ rSttPos = *pNextStt;
+ }
+ else
+ {
+ ++n;
+ break;
+ }
+ }
+ }
+ }
+
+ if( pFnd )
+ {
+ const SwRangeRedline* pSaveFnd = pFnd;
+
+ SwContentNode* pCNd;
+ SwPosition* pPos = rPam.GetMark();
+ if( !pPos->GetNode().IsContentNode() )
+ {
+ pCNd = SwNodes::GoPrevSection( pPos );
+ if( pCNd )
+ {
+ if( pPos->GetNode() >= rPam.GetPoint()->GetNode() )
+ pPos->Assign( *pCNd, pCNd->Len() );
+ else
+ pFnd = nullptr;
+ }
+ }
+
+ if( pFnd )
+ {
+ pPos = rPam.GetPoint();
+ if( !pPos->GetNode().IsContentNode() )
+ {
+ pCNd = m_rDoc.GetNodes().GoNextSection( pPos );
+ if( pCNd )
+ {
+ if( pPos->GetNode() <= rPam.GetMark()->GetNode() )
+ pPos->Assign( *pCNd, 0 );
+ else
+ pFnd = nullptr;
+ }
+ }
+ }
+
+ if( !pFnd || *rPam.GetMark() == *rPam.GetPoint() )
+ {
+ if( n )
+ {
+ bRestart = true;
+ *rPam.GetPoint() = *pSaveFnd->Start();
+ }
+ else
+ {
+ rPam.DeleteMark();
+ *rPam.GetPoint() = aSavePos;
+ }
+ pFnd = nullptr;
+ }
+ }
+ } while( bRestart );
+
+ return pFnd;
+
+ // #TODO - add 'SwExtraRedlineTable' also ?
+}
+
+// Set comment at the Redline
+bool DocumentRedlineManager::SetRedlineComment( const SwPaM& rPaM, const OUString& rS )
+{
+ bool bRet = false;
+ auto [pStt, pEnd] = rPaM.StartEnd(); // SwPosition*
+ SwRedlineTable::size_type n = 0;
+ if( GetRedlineTable().FindAtPosition( *pStt, n ) )
+ {
+ for( ; n < maRedlineTable.size(); ++n )
+ {
+ bRet = true;
+ SwRangeRedline* pTmp = maRedlineTable[ n ];
+ if( pStt != pEnd && *pTmp->Start() > *pEnd )
+ break;
+
+ pTmp->SetComment( rS );
+ if( *pTmp->End() >= *pEnd )
+ break;
+ }
+ }
+ if( bRet )
+ m_rDoc.getIDocumentState().SetModified();
+
+ return bRet;
+
+ // #TODO - add 'SwExtraRedlineTable' also ?
+}
+
+// Create a new author if necessary
+std::size_t DocumentRedlineManager::GetRedlineAuthor()
+{
+ return SW_MOD()->GetRedlineAuthor();
+}
+
+/// Insert new author into the Table for the Readers etc.
+std::size_t DocumentRedlineManager::InsertRedlineAuthor( const OUString& rNew )
+{
+ return SW_MOD()->InsertRedlineAuthor(rNew);
+}
+
+void DocumentRedlineManager::UpdateRedlineAttr()
+{
+ const SwRedlineTable& rTable = GetRedlineTable();
+ for(SwRangeRedline* pRedl : rTable)
+ {
+ if( pRedl->IsVisible() )
+ pRedl->InvalidateRange(SwRangeRedline::Invalidation::Add);
+ }
+
+ // #TODO - add 'SwExtraRedlineTable' also ?
+}
+
+const uno::Sequence <sal_Int8>& DocumentRedlineManager::GetRedlinePassword() const
+{
+ return maRedlinePasswd;
+}
+
+void DocumentRedlineManager::SetRedlinePassword(
+ /*[in]*/const uno::Sequence <sal_Int8>& rNewPassword)
+{
+ maRedlinePasswd = rNewPassword;
+ m_rDoc.getIDocumentState().SetModified();
+}
+
+/// Set comment text for the Redline, which is inserted later on via
+/// AppendRedline. Is used by Autoformat.
+/// A null pointer resets the mode. The pointer is not copied, so it
+/// needs to stay valid!
+void DocumentRedlineManager::SetAutoFormatRedlineComment( const OUString* pText, sal_uInt16 nSeqNo )
+{
+ m_rDoc.SetAutoFormatRedline( nullptr != pText );
+ if( pText )
+ {
+ moAutoFormatRedlnComment = *pText;
+ }
+ else
+ {
+ moAutoFormatRedlnComment.reset();
+ }
+
+ mnAutoFormatRedlnCommentNo = nSeqNo;
+}
+
+void DocumentRedlineManager::HideAll( bool bDeletion )
+{
+ const SwRedlineTable& rTable = GetRedlineTable();
+ for (SwRedlineTable::size_type i = rTable.size(); i > 0; --i)
+ {
+ SwRangeRedline* pRedline = rTable[i-1];
+ if ( pRedline->GetType() == RedlineType::Delete )
+ {
+ if ( bDeletion && pRedline->IsVisible() )
+ {
+ pRedline->Hide(0, rTable.GetPos(pRedline), false);
+ pRedline->Hide(1, rTable.GetPos(pRedline), false);
+ }
+ else if ( !bDeletion && !pRedline->IsVisible() )
+ {
+ pRedline->Show(0, rTable.GetPos(pRedline), true);
+ pRedline->Show(1, rTable.GetPos(pRedline), true);
+ }
+ }
+ else if ( pRedline->GetType() == RedlineType::Insert )
+ {
+ if ( !bDeletion && pRedline->IsVisible() )
+ {
+ pRedline->ShowOriginal(0, rTable.GetPos(pRedline), false);
+ pRedline->ShowOriginal(1, rTable.GetPos(pRedline), false);
+ }
+ else if ( bDeletion && !pRedline->IsVisible() )
+ {
+ pRedline->Show(0, rTable.GetPos(pRedline), true);
+ pRedline->Show(1, rTable.GetPos(pRedline), true);
+ }
+ }
+ }
+}
+
+void DocumentRedlineManager::ShowAll()
+{
+ const SwRedlineTable& rTable = GetRedlineTable();
+ for (SwRedlineTable::size_type i = rTable.size(); i > 0; --i)
+ {
+ SwRangeRedline* pRedline = rTable[i-1];
+ if ( !pRedline->IsVisible() )
+ {
+ pRedline->Show(0, rTable.GetPos(pRedline), true);
+ pRedline->Show(1, rTable.GetPos(pRedline), true);
+ }
+ }
+}
+
+DocumentRedlineManager::~DocumentRedlineManager()
+{
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/DocumentSettingManager.cxx b/sw/source/core/doc/DocumentSettingManager.cxx
new file mode 100644
index 0000000000..ebe116c682
--- /dev/null
+++ b/sw/source/core/doc/DocumentSettingManager.cxx
@@ -0,0 +1,1089 @@
+/* -*- 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 <libxml/xmlwriter.h>
+
+#include <DocumentSettingManager.hxx>
+#include <doc.hxx>
+#include <IDocumentDrawModelAccess.hxx>
+#include <IDocumentState.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <comphelper/lok.hxx>
+#include <comphelper/processfactory.hxx>
+#include <editeng/forbiddencharacterstable.hxx>
+#include <osl/diagnose.h>
+#include <svx/svdmodel.hxx>
+#include <svl/asiancfg.hxx>
+#include <unotools/compatibility.hxx>
+#include <unotools/configmgr.hxx>
+#include <drawdoc.hxx>
+#include <swmodule.hxx>
+#include <linkenum.hxx>
+#include <rootfrm.hxx>
+#include <breakit.hxx>
+#include <docary.hxx>
+
+/* IDocumentSettingAccess */
+
+sw::DocumentSettingManager::DocumentSettingManager(SwDoc &rDoc)
+ :m_rDoc(rDoc),
+ mnLinkUpdMode( GLOBALSETTING ),
+ meFieldUpdMode( AUTOUPD_GLOBALSETTING ),
+ meChrCmprType( CharCompressType::NONE ),
+ mn32DummyCompatibilityOptions1(0),
+ mn32DummyCompatibilityOptions2(0),
+ mbHTMLMode(false),
+ mbIsGlobalDoc(false),
+ mbGlblDocSaveLinks(false),
+ mbIsLabelDoc(false),
+ mbPurgeOLE(true),
+ mbKernAsianPunctuation(false),
+
+ // COMPATIBILITY FLAGS START
+
+ mbAddFlyOffsets(false),
+ mbAddVerticalFlyOffsets(false),
+ mbUseHiResolutionVirtualDevice(true),
+ mbMathBaselineAlignment(false), // default for *old* documents is 'off'
+ mbStylesNoDefault(false),
+ mEmbedFonts(false),
+ mEmbedUsedFonts(false),
+ mEmbedLatinScriptFonts(true),
+ mEmbedAsianScriptFonts(true),
+ mEmbedComplexScriptFonts(true),
+ mEmbedSystemFonts(false),
+ mbOldNumbering(false),
+ mbIgnoreFirstLineIndentInNumbering(false),
+ mbDoNotResetParaAttrsForNumFont(false),
+ mbTableRowKeep(false),
+ mbIgnoreTabsAndBlanksForLineCalculation(false),
+ mbDoNotCaptureDrawObjsOnPage(false),
+ mbClipAsCharacterAnchoredWriterFlyFrames(false),
+ mbUnixForceZeroExtLeading(false),
+ mbTabRelativeToIndent(true),
+ mbProtectForm(false), // i#78591#
+ mbMsWordCompTrailingBlanks(false), // tdf#104349 tdf#104668
+ mbMsWordCompMinLineHeightByFly(false),
+ mbInvertBorderSpacing (false),
+ mbCollapseEmptyCellPara(true),
+ mbTabAtLeftIndentForParagraphsInList(false), //#i89181#
+ mbSmallCapsPercentage66(false),
+ mbTabOverflow(true),
+ mbUnbreakableNumberings(false),
+ mbClippedPictures(false),
+ mbBackgroundParaOverDrawings(false),
+ mbTabOverMargin(false),
+ mbTabOverSpacing(false),
+ mbTreatSingleColumnBreakAsPageBreak(false),
+ mbSurroundTextWrapSmall(false),
+ mbPropLineSpacingShrinksFirstLine(true),
+ mbSubtractFlys(false),
+ mApplyParagraphMarkFormatToNumbering(false),
+ mbLastBrowseMode( false ),
+ mbDisableOffPagePositioning ( false ),
+ mbProtectBookmarks(false),
+ mbProtectFields(false),
+ mbHeaderSpacingBelowLastPara(false),
+ mbFrameAutowidthWithMorePara(false),
+ mbGutterAtTop(false),
+ mbFootnoteInColumnToPageEnd(false),
+ mnImagePreferredDPI(0),
+ mbAutoFirstLineIndentDisregardLineSpace(true),
+ mbNoNumberingShowFollowBy(false),
+ mbDropCapPunctuation(true),
+ mbUseVariableWidthNBSP(false)
+
+ // COMPATIBILITY FLAGS END
+{
+ // COMPATIBILITY FLAGS START
+
+ // Note: Any non-hidden compatibility flag should obtain its default
+ // by asking SvtCompatibilityOptions, see below.
+
+ if (!utl::ConfigManager::IsFuzzing())
+ {
+ const SvtCompatibilityOptions aOptions;
+ mbParaSpaceMax = aOptions.GetDefault( SvtCompatibilityEntry::Index::AddSpacing );
+ mbParaSpaceMaxAtPages = aOptions.GetDefault( SvtCompatibilityEntry::Index::AddSpacingAtPages );
+ mbTabCompat = !aOptions.GetDefault( SvtCompatibilityEntry::Index::UseOurTabStops );
+ mbUseVirtualDevice = true;
+ mbAddExternalLeading = !aOptions.GetDefault( SvtCompatibilityEntry::Index::NoExtLeading );
+ mbOldLineSpacing = aOptions.GetDefault( SvtCompatibilityEntry::Index::UseLineSpacing );
+ mbAddParaSpacingToTableCells = aOptions.GetDefault( SvtCompatibilityEntry::Index::AddTableSpacing );
+ mbAddParaLineSpacingToTableCells = aOptions.GetDefault( SvtCompatibilityEntry::Index::AddTableLineSpacing );
+ mbUseFormerObjectPos = aOptions.GetDefault( SvtCompatibilityEntry::Index::UseObjectPositioning );
+ mbUseFormerTextWrapping = aOptions.GetDefault( SvtCompatibilityEntry::Index::UseOurTextWrapping );
+ mbConsiderWrapOnObjPos = aOptions.GetDefault( SvtCompatibilityEntry::Index::ConsiderWrappingStyle );
+ mbDoNotJustifyLinesWithManualBreak = !aOptions.GetDefault( SvtCompatibilityEntry::Index::ExpandWordSpace );
+ mbProtectForm = aOptions.GetDefault( SvtCompatibilityEntry::Index::ProtectForm );
+ mbMsWordCompTrailingBlanks = aOptions.GetDefault( SvtCompatibilityEntry::Index::MsWordTrailingBlanks );
+ mbSubtractFlys = aOptions.GetDefault( SvtCompatibilityEntry::Index::SubtractFlysAnchoredAtFlys );
+ mbEmptyDbFieldHidesPara
+ = aOptions.GetDefault(SvtCompatibilityEntry::Index::EmptyDbFieldHidesPara);
+ mbUseVariableWidthNBSP
+ = aOptions.GetDefault(SvtCompatibilityEntry::Index::UseVariableWidthNBSP);
+ }
+ else
+ {
+ mbParaSpaceMax = false;
+ mbParaSpaceMaxAtPages = false;
+ mbTabCompat = true;
+ mbUseVirtualDevice = true;
+ mbAddExternalLeading = true;
+ mbOldLineSpacing = false;
+ mbAddParaSpacingToTableCells = false;
+ mbAddParaLineSpacingToTableCells = false;
+ mbUseFormerObjectPos = false;
+ mbUseFormerTextWrapping = false;
+ mbConsiderWrapOnObjPos = false;
+ mbDoNotJustifyLinesWithManualBreak = true;
+ mbProtectForm = false;
+ mbMsWordCompTrailingBlanks = false;
+ mbSubtractFlys = false;
+ mbEmptyDbFieldHidesPara = true;
+ mbUseVariableWidthNBSP = false;
+ }
+
+ // COMPATIBILITY FLAGS END
+
+}
+
+
+sw::DocumentSettingManager::~DocumentSettingManager()
+{
+}
+
+/* IDocumentSettingAccess */
+bool sw::DocumentSettingManager::get(/*[in]*/ DocumentSettingId id) const
+{
+ switch (id)
+ {
+ // COMPATIBILITY FLAGS START
+ case DocumentSettingId::PARA_SPACE_MAX: return mbParaSpaceMax; //(n8Dummy1 & DUMMY_PARASPACEMAX);
+ case DocumentSettingId::PARA_SPACE_MAX_AT_PAGES: return mbParaSpaceMaxAtPages; //(n8Dummy1 & DUMMY_PARASPACEMAX_AT_PAGES);
+ case DocumentSettingId::TAB_COMPAT: return mbTabCompat; //(n8Dummy1 & DUMMY_TAB_COMPAT);
+ case DocumentSettingId::ADD_FLY_OFFSETS: return mbAddFlyOffsets; //(n8Dummy2 & DUMMY_ADD_FLY_OFFSETS);
+ case DocumentSettingId::ADD_VERTICAL_FLY_OFFSETS: return mbAddVerticalFlyOffsets;
+ case DocumentSettingId::ADD_EXT_LEADING: return mbAddExternalLeading; //(n8Dummy2 & DUMMY_ADD_EXTERNAL_LEADING);
+ case DocumentSettingId::USE_VIRTUAL_DEVICE: return mbUseVirtualDevice; //(n8Dummy1 & DUMMY_USE_VIRTUAL_DEVICE);
+ case DocumentSettingId::USE_HIRES_VIRTUAL_DEVICE: return mbUseHiResolutionVirtualDevice; //(n8Dummy2 & DUMMY_USE_HIRES_VIR_DEV);
+ case DocumentSettingId::OLD_NUMBERING: return mbOldNumbering;
+ case DocumentSettingId::OLD_LINE_SPACING: return mbOldLineSpacing;
+ case DocumentSettingId::ADD_PARA_SPACING_TO_TABLE_CELLS: return mbAddParaSpacingToTableCells;
+ case DocumentSettingId::ADD_PARA_LINE_SPACING_TO_TABLE_CELLS: return mbAddParaLineSpacingToTableCells;
+ case DocumentSettingId::USE_FORMER_OBJECT_POS: return mbUseFormerObjectPos;
+ case DocumentSettingId::USE_FORMER_TEXT_WRAPPING: return mbUseFormerTextWrapping;
+ case DocumentSettingId::CONSIDER_WRAP_ON_OBJECT_POSITION: return mbConsiderWrapOnObjPos;
+ case DocumentSettingId::DO_NOT_JUSTIFY_LINES_WITH_MANUAL_BREAK: return mbDoNotJustifyLinesWithManualBreak;
+ case DocumentSettingId::IGNORE_FIRST_LINE_INDENT_IN_NUMBERING: return mbIgnoreFirstLineIndentInNumbering;
+ case DocumentSettingId::TABLE_ROW_KEEP: return mbTableRowKeep;
+ case DocumentSettingId::IGNORE_TABS_AND_BLANKS_FOR_LINE_CALCULATION: return mbIgnoreTabsAndBlanksForLineCalculation;
+ case DocumentSettingId::DO_NOT_CAPTURE_DRAW_OBJS_ON_PAGE: return mbDoNotCaptureDrawObjsOnPage;
+ // #i68949#
+ case DocumentSettingId::CLIP_AS_CHARACTER_ANCHORED_WRITER_FLY_FRAME: return mbClipAsCharacterAnchoredWriterFlyFrames;
+ case DocumentSettingId::UNIX_FORCE_ZERO_EXT_LEADING: return mbUnixForceZeroExtLeading;
+ case DocumentSettingId::TABS_RELATIVE_TO_INDENT : return mbTabRelativeToIndent;
+ case DocumentSettingId::PROTECT_FORM: return mbProtectForm;
+ // tdf#104349 tdf#104668
+ case DocumentSettingId::MS_WORD_COMP_TRAILING_BLANKS: return mbMsWordCompTrailingBlanks;
+ case DocumentSettingId::MS_WORD_COMP_MIN_LINE_HEIGHT_BY_FLY: return mbMsWordCompMinLineHeightByFly;
+ // #i89181#
+ case DocumentSettingId::TAB_AT_LEFT_INDENT_FOR_PARA_IN_LIST: return mbTabAtLeftIndentForParagraphsInList;
+ case DocumentSettingId::INVERT_BORDER_SPACING: return mbInvertBorderSpacing;
+ case DocumentSettingId::COLLAPSE_EMPTY_CELL_PARA: return mbCollapseEmptyCellPara;
+ case DocumentSettingId::SMALL_CAPS_PERCENTAGE_66: return mbSmallCapsPercentage66;
+ case DocumentSettingId::TAB_OVERFLOW: return mbTabOverflow;
+ case DocumentSettingId::UNBREAKABLE_NUMBERINGS: return mbUnbreakableNumberings;
+ case DocumentSettingId::CLIPPED_PICTURES: return mbClippedPictures;
+ case DocumentSettingId::BACKGROUND_PARA_OVER_DRAWINGS: return mbBackgroundParaOverDrawings;
+ case DocumentSettingId::TAB_OVER_MARGIN: return mbTabOverMargin;
+ case DocumentSettingId::TAB_OVER_SPACING: return mbTabOverSpacing;
+ case DocumentSettingId::TREAT_SINGLE_COLUMN_BREAK_AS_PAGE_BREAK: return mbTreatSingleColumnBreakAsPageBreak;
+ case DocumentSettingId::SURROUND_TEXT_WRAP_SMALL: return mbSurroundTextWrapSmall;
+ case DocumentSettingId::PROP_LINE_SPACING_SHRINKS_FIRST_LINE: return mbPropLineSpacingShrinksFirstLine;
+ case DocumentSettingId::SUBTRACT_FLYS: return mbSubtractFlys;
+
+ case DocumentSettingId::BROWSE_MODE: return mbLastBrowseMode; // Attention: normally the SwViewShell has to be asked!
+ case DocumentSettingId::HTML_MODE: return mbHTMLMode;
+ case DocumentSettingId::GLOBAL_DOCUMENT: return mbIsGlobalDoc;
+ case DocumentSettingId::GLOBAL_DOCUMENT_SAVE_LINKS: return mbGlblDocSaveLinks;
+ case DocumentSettingId::LABEL_DOCUMENT: return mbIsLabelDoc;
+ case DocumentSettingId::PURGE_OLE: return mbPurgeOLE;
+ case DocumentSettingId::KERN_ASIAN_PUNCTUATION: return mbKernAsianPunctuation;
+ case DocumentSettingId::DO_NOT_RESET_PARA_ATTRS_FOR_NUM_FONT: return mbDoNotResetParaAttrsForNumFont;
+ case DocumentSettingId::MATH_BASELINE_ALIGNMENT: return mbMathBaselineAlignment;
+ case DocumentSettingId::STYLES_NODEFAULT: return mbStylesNoDefault;
+ case DocumentSettingId::EMBED_FONTS: return mEmbedFonts;
+ case DocumentSettingId::EMBED_USED_FONTS: return mEmbedUsedFonts;
+ case DocumentSettingId::EMBED_LATIN_SCRIPT_FONTS: return mEmbedLatinScriptFonts;
+ case DocumentSettingId::EMBED_ASIAN_SCRIPT_FONTS: return mEmbedAsianScriptFonts;
+ case DocumentSettingId::EMBED_COMPLEX_SCRIPT_FONTS: return mEmbedComplexScriptFonts;
+ case DocumentSettingId::EMBED_SYSTEM_FONTS: return mEmbedSystemFonts;
+ case DocumentSettingId::APPLY_PARAGRAPH_MARK_FORMAT_TO_NUMBERING: return mApplyParagraphMarkFormatToNumbering;
+ case DocumentSettingId::DISABLE_OFF_PAGE_POSITIONING: return mbDisableOffPagePositioning;
+ case DocumentSettingId::EMPTY_DB_FIELD_HIDES_PARA: return mbEmptyDbFieldHidesPara;
+ case DocumentSettingId::CONTINUOUS_ENDNOTES: return mbContinuousEndnotes;
+ case DocumentSettingId::PROTECT_BOOKMARKS: return mbProtectBookmarks;
+ case DocumentSettingId::PROTECT_FIELDS: return mbProtectFields;
+ case DocumentSettingId::HEADER_SPACING_BELOW_LAST_PARA: return mbHeaderSpacingBelowLastPara;
+ case DocumentSettingId::FRAME_AUTOWIDTH_WITH_MORE_PARA: return mbFrameAutowidthWithMorePara;
+ case DocumentSettingId::GUTTER_AT_TOP:
+ return mbGutterAtTop;
+ case DocumentSettingId::FOOTNOTE_IN_COLUMN_TO_PAGEEND: return mbFootnoteInColumnToPageEnd;
+ case DocumentSettingId::AUTO_FIRST_LINE_INDENT_DISREGARD_LINE_SPACE:
+ return mbAutoFirstLineIndentDisregardLineSpace;
+ case DocumentSettingId::HYPHENATE_URLS: return mbHyphenateURLs;
+ case DocumentSettingId::DO_NOT_BREAK_WRAPPED_TABLES:
+ return mbDoNotBreakWrappedTables;
+ case DocumentSettingId::ALLOW_TEXT_AFTER_FLOATING_TABLE_BREAK:
+ return mbAllowTextAfterFloatingTableBreak;
+ case DocumentSettingId::JUSTIFY_LINES_WITH_SHRINKING:
+ return mbJustifyLinesWithShrinking;
+ case DocumentSettingId::NO_NUMBERING_SHOW_FOLLOWBY: return mbNoNumberingShowFollowBy;
+ case DocumentSettingId::DROP_CAP_PUNCTUATION: return mbDropCapPunctuation;
+ case DocumentSettingId::USE_VARIABLE_WIDTH_NBSP: return mbUseVariableWidthNBSP;
+ default:
+ OSL_FAIL("Invalid setting id");
+ }
+ return false;
+}
+
+void sw::DocumentSettingManager::set(/*[in]*/ DocumentSettingId id, /*[in]*/ bool value)
+{
+ switch (id)
+ {
+ // COMPATIBILITY FLAGS START
+ case DocumentSettingId::PARA_SPACE_MAX:
+ mbParaSpaceMax = value;
+ break;
+ case DocumentSettingId::PARA_SPACE_MAX_AT_PAGES:
+ mbParaSpaceMaxAtPages = value;
+ break;
+ case DocumentSettingId::TAB_COMPAT:
+ mbTabCompat = value;
+ break;
+ case DocumentSettingId::ADD_FLY_OFFSETS:
+ mbAddFlyOffsets = value;
+ break;
+ case DocumentSettingId::ADD_VERTICAL_FLY_OFFSETS:
+ mbAddVerticalFlyOffsets = value;
+ break;
+ case DocumentSettingId::ADD_EXT_LEADING:
+ mbAddExternalLeading = value;
+ break;
+ case DocumentSettingId::USE_VIRTUAL_DEVICE:
+ mbUseVirtualDevice = value;
+ break;
+ case DocumentSettingId::USE_HIRES_VIRTUAL_DEVICE:
+ mbUseHiResolutionVirtualDevice = value;
+ break;
+ case DocumentSettingId::OLD_NUMBERING:
+ if (mbOldNumbering != value)
+ {
+ mbOldNumbering = value;
+
+ const SwNumRuleTable& rNmTable = m_rDoc.GetNumRuleTable();
+ for( SwNumRuleTable::size_type n = 0; n < rNmTable.size(); ++n )
+ rNmTable[n]->SetInvalidRule(true);
+
+ m_rDoc.UpdateNumRule();
+
+ SwNumRule *pOutlineRule = m_rDoc.GetOutlineNumRule();
+ if (pOutlineRule)
+ {
+ pOutlineRule->Validate(m_rDoc);
+ // counting of phantoms depends on <IsOldNumbering()>
+ pOutlineRule->SetCountPhantoms( !mbOldNumbering );
+ }
+ }
+ break;
+ case DocumentSettingId::OLD_LINE_SPACING:
+ mbOldLineSpacing = value;
+ break;
+ case DocumentSettingId::ADD_PARA_SPACING_TO_TABLE_CELLS:
+ mbAddParaSpacingToTableCells = value;
+ break;
+ case DocumentSettingId::ADD_PARA_LINE_SPACING_TO_TABLE_CELLS:
+ mbAddParaLineSpacingToTableCells = value;
+ break;
+ case DocumentSettingId::USE_FORMER_OBJECT_POS:
+ mbUseFormerObjectPos = value;
+ break;
+ case DocumentSettingId::USE_FORMER_TEXT_WRAPPING:
+ mbUseFormerTextWrapping = value;
+ break;
+ case DocumentSettingId::CONSIDER_WRAP_ON_OBJECT_POSITION:
+ mbConsiderWrapOnObjPos = value;
+ break;
+ case DocumentSettingId::DO_NOT_JUSTIFY_LINES_WITH_MANUAL_BREAK:
+ mbDoNotJustifyLinesWithManualBreak = value;
+ break;
+ case DocumentSettingId::IGNORE_FIRST_LINE_INDENT_IN_NUMBERING:
+ mbIgnoreFirstLineIndentInNumbering = value;
+ break;
+
+ case DocumentSettingId::TABLE_ROW_KEEP:
+ mbTableRowKeep = value;
+ break;
+
+ case DocumentSettingId::IGNORE_TABS_AND_BLANKS_FOR_LINE_CALCULATION:
+ mbIgnoreTabsAndBlanksForLineCalculation = value;
+ break;
+
+ case DocumentSettingId::DO_NOT_CAPTURE_DRAW_OBJS_ON_PAGE:
+ mbDoNotCaptureDrawObjsOnPage = value;
+ break;
+
+ // #i68949#
+ case DocumentSettingId::CLIP_AS_CHARACTER_ANCHORED_WRITER_FLY_FRAME:
+ mbClipAsCharacterAnchoredWriterFlyFrames = value;
+ break;
+
+ case DocumentSettingId::UNIX_FORCE_ZERO_EXT_LEADING:
+ mbUnixForceZeroExtLeading = value;
+ break;
+
+ case DocumentSettingId::PROTECT_FORM:
+ mbProtectForm = value;
+ break;
+
+ // tdf#140349
+ case DocumentSettingId::MS_WORD_COMP_TRAILING_BLANKS:
+ mbMsWordCompTrailingBlanks = value;
+ break;
+
+ case DocumentSettingId::MS_WORD_COMP_MIN_LINE_HEIGHT_BY_FLY:
+ mbMsWordCompMinLineHeightByFly = value;
+ break;
+
+ case DocumentSettingId::TABS_RELATIVE_TO_INDENT:
+ mbTabRelativeToIndent = value;
+ break;
+ // #i89181#
+ case DocumentSettingId::TAB_AT_LEFT_INDENT_FOR_PARA_IN_LIST:
+ mbTabAtLeftIndentForParagraphsInList = value;
+ break;
+
+ case DocumentSettingId::INVERT_BORDER_SPACING:
+ mbInvertBorderSpacing = value;
+ break;
+
+ case DocumentSettingId::COLLAPSE_EMPTY_CELL_PARA:
+ mbCollapseEmptyCellPara = value;
+ break;
+
+ case DocumentSettingId::SMALL_CAPS_PERCENTAGE_66:
+ mbSmallCapsPercentage66 = value;
+ break;
+
+ case DocumentSettingId::TAB_OVERFLOW:
+ mbTabOverflow = value;
+ break;
+
+ case DocumentSettingId::UNBREAKABLE_NUMBERINGS:
+ mbUnbreakableNumberings = value;
+ break;
+
+ case DocumentSettingId::CLIPPED_PICTURES:
+ mbClippedPictures = value;
+ break;
+
+ case DocumentSettingId::BACKGROUND_PARA_OVER_DRAWINGS:
+ mbBackgroundParaOverDrawings = value;
+ break;
+
+ case DocumentSettingId::TAB_OVER_MARGIN:
+ mbTabOverMargin = value;
+ break;
+
+ case DocumentSettingId::TAB_OVER_SPACING:
+ mbTabOverSpacing = value;
+ break;
+
+ case DocumentSettingId::TREAT_SINGLE_COLUMN_BREAK_AS_PAGE_BREAK:
+ mbTreatSingleColumnBreakAsPageBreak = value;
+ break;
+
+ case DocumentSettingId::SURROUND_TEXT_WRAP_SMALL:
+ mbSurroundTextWrapSmall = value;
+ break;
+
+ case DocumentSettingId::PROP_LINE_SPACING_SHRINKS_FIRST_LINE:
+ mbPropLineSpacingShrinksFirstLine = value;
+ break;
+
+ case DocumentSettingId::SUBTRACT_FLYS:
+ mbSubtractFlys = value;
+ break;
+
+ case DocumentSettingId::AUTO_FIRST_LINE_INDENT_DISREGARD_LINE_SPACE:
+ mbAutoFirstLineIndentDisregardLineSpace = value;
+ break;
+
+ case DocumentSettingId::HYPHENATE_URLS:
+ mbHyphenateURLs = value;
+ break;
+
+ case DocumentSettingId::DO_NOT_BREAK_WRAPPED_TABLES:
+ mbDoNotBreakWrappedTables = value;
+ break;
+
+ case DocumentSettingId::ALLOW_TEXT_AFTER_FLOATING_TABLE_BREAK:
+ mbAllowTextAfterFloatingTableBreak = value;
+ break;
+
+ case DocumentSettingId::JUSTIFY_LINES_WITH_SHRINKING:
+ mbJustifyLinesWithShrinking = value;
+ break;
+
+ case DocumentSettingId::NO_NUMBERING_SHOW_FOLLOWBY:
+ mbNoNumberingShowFollowBy = value;
+ break;
+
+ case DocumentSettingId::DROP_CAP_PUNCTUATION:
+ mbDropCapPunctuation = value;
+ break;
+
+ case DocumentSettingId::USE_VARIABLE_WIDTH_NBSP:
+ mbUseVariableWidthNBSP = value;
+ break;
+
+ // COMPATIBILITY FLAGS END
+
+ case DocumentSettingId::BROWSE_MODE: //can be used temporary (load/save) when no SwViewShell is available
+ // Can't render in webview successfully.
+ if (comphelper::LibreOfficeKit::isActive())
+ mbLastBrowseMode = false;
+ else
+ mbLastBrowseMode = value;
+ break;
+
+ case DocumentSettingId::HTML_MODE:
+ mbHTMLMode = value;
+ break;
+
+ case DocumentSettingId::GLOBAL_DOCUMENT:
+ mbIsGlobalDoc = value;
+ break;
+
+ case DocumentSettingId::GLOBAL_DOCUMENT_SAVE_LINKS:
+ mbGlblDocSaveLinks = value;
+ break;
+
+ case DocumentSettingId::LABEL_DOCUMENT:
+ mbIsLabelDoc = value;
+ break;
+
+ case DocumentSettingId::PURGE_OLE:
+ mbPurgeOLE = value;
+ break;
+
+ case DocumentSettingId::KERN_ASIAN_PUNCTUATION:
+ mbKernAsianPunctuation = value;
+ break;
+
+ case DocumentSettingId::DO_NOT_RESET_PARA_ATTRS_FOR_NUM_FONT:
+ mbDoNotResetParaAttrsForNumFont = value;
+ break;
+ case DocumentSettingId::MATH_BASELINE_ALIGNMENT:
+ mbMathBaselineAlignment = value;
+ break;
+ case DocumentSettingId::STYLES_NODEFAULT:
+ mbStylesNoDefault = value;
+ break;
+ case DocumentSettingId::EMBED_FONTS:
+ mEmbedFonts = value;
+ break;
+ case DocumentSettingId::EMBED_USED_FONTS:
+ mEmbedUsedFonts = value;
+ break;
+ case DocumentSettingId::EMBED_LATIN_SCRIPT_FONTS:
+ mEmbedLatinScriptFonts = value;
+ break;
+ case DocumentSettingId::EMBED_ASIAN_SCRIPT_FONTS:
+ mEmbedAsianScriptFonts = value;
+ break;
+ case DocumentSettingId::EMBED_COMPLEX_SCRIPT_FONTS:
+ mEmbedComplexScriptFonts = value;
+ break;
+ case DocumentSettingId::EMBED_SYSTEM_FONTS:
+ mEmbedSystemFonts = value;
+ break;
+ case DocumentSettingId::APPLY_PARAGRAPH_MARK_FORMAT_TO_NUMBERING:
+ mApplyParagraphMarkFormatToNumbering = value;
+ break;
+ case DocumentSettingId::DISABLE_OFF_PAGE_POSITIONING:
+ mbDisableOffPagePositioning = value;
+ break;
+ case DocumentSettingId::EMPTY_DB_FIELD_HIDES_PARA:
+ mbEmptyDbFieldHidesPara = value;
+ break;
+ case DocumentSettingId::CONTINUOUS_ENDNOTES:
+ mbContinuousEndnotes = value;
+ break;
+ case DocumentSettingId::PROTECT_BOOKMARKS:
+ mbProtectBookmarks = value;
+ break;
+ case DocumentSettingId::PROTECT_FIELDS:
+ mbProtectFields = value;
+ break;
+ case DocumentSettingId::HEADER_SPACING_BELOW_LAST_PARA:
+ mbHeaderSpacingBelowLastPara = value;
+ break;
+ case DocumentSettingId::FRAME_AUTOWIDTH_WITH_MORE_PARA:
+ mbFrameAutowidthWithMorePara = value;
+ break;
+ case DocumentSettingId::GUTTER_AT_TOP:
+ mbGutterAtTop = value;
+ break;
+ case DocumentSettingId::FOOTNOTE_IN_COLUMN_TO_PAGEEND:
+ mbFootnoteInColumnToPageEnd = value;
+ break;
+ default:
+ OSL_FAIL("Invalid setting id");
+ }
+}
+
+const css::i18n::ForbiddenCharacters*
+ sw::DocumentSettingManager::getForbiddenCharacters(/*[in]*/ LanguageType nLang, /*[in]*/ bool bLocaleData ) const
+{
+ const css::i18n::ForbiddenCharacters* pRet = nullptr;
+ if (mxForbiddenCharsTable)
+ pRet = mxForbiddenCharsTable->GetForbiddenCharacters( nLang, false );
+ if( bLocaleData && !pRet && g_pBreakIt )
+ pRet = &g_pBreakIt->GetForbidden( nLang );
+ return pRet;
+}
+
+void sw::DocumentSettingManager::setForbiddenCharacters(/*[in]*/ LanguageType nLang,
+ /*[in]*/ const css::i18n::ForbiddenCharacters& rFChars )
+{
+ if (!mxForbiddenCharsTable)
+ mxForbiddenCharsTable = SvxForbiddenCharactersTable::makeForbiddenCharactersTable(::comphelper::getProcessComponentContext());
+ mxForbiddenCharsTable->SetForbiddenCharacters( nLang, rFChars );
+
+ SdrModel *pDrawModel = m_rDoc.getIDocumentDrawModelAccess().GetDrawModel();
+ if( pDrawModel )
+ {
+ pDrawModel->SetForbiddenCharsTable(mxForbiddenCharsTable);
+ if( !m_rDoc.IsInReading() )
+ pDrawModel->ReformatAllTextObjects();
+ }
+
+ SwRootFrame* pTmpRoot = m_rDoc.getIDocumentLayoutAccess().GetCurrentLayout();
+ if( pTmpRoot && !m_rDoc.IsInReading() )
+ {
+ pTmpRoot->StartAllAction();
+ for(SwRootFrame* aLayout : m_rDoc.GetAllLayouts())
+ aLayout->InvalidateAllContent(SwInvalidateFlags::Size);
+ pTmpRoot->EndAllAction();
+ }
+ m_rDoc.getIDocumentState().SetModified();
+}
+
+std::shared_ptr<SvxForbiddenCharactersTable>& sw::DocumentSettingManager::getForbiddenCharacterTable()
+{
+ if (!mxForbiddenCharsTable)
+ mxForbiddenCharsTable = SvxForbiddenCharactersTable::makeForbiddenCharactersTable(::comphelper::getProcessComponentContext());
+ return mxForbiddenCharsTable;
+}
+
+const std::shared_ptr<SvxForbiddenCharactersTable>& sw::DocumentSettingManager::getForbiddenCharacterTable() const
+{
+ return mxForbiddenCharsTable;
+}
+
+sal_uInt16 sw::DocumentSettingManager::getLinkUpdateMode( /*[in]*/bool bGlobalSettings ) const
+{
+ sal_uInt16 nRet = mnLinkUpdMode;
+ if( bGlobalSettings && GLOBALSETTING == nRet )
+ nRet = SW_MOD()->GetLinkUpdMode();
+ return nRet;
+}
+
+void sw::DocumentSettingManager::setLinkUpdateMode( /*[in]*/sal_uInt16 eMode )
+{
+ mnLinkUpdMode = eMode;
+}
+
+SwFieldUpdateFlags sw::DocumentSettingManager::getFieldUpdateFlags( /*[in]*/bool bGlobalSettings ) const
+{
+ SwFieldUpdateFlags eRet = meFieldUpdMode;
+ if( bGlobalSettings && AUTOUPD_GLOBALSETTING == eRet )
+ eRet = SW_MOD()->GetFieldUpdateFlags();
+ return eRet;
+}
+
+void sw::DocumentSettingManager::setFieldUpdateFlags(/*[in]*/SwFieldUpdateFlags eMode )
+{
+ meFieldUpdMode = eMode;
+}
+
+CharCompressType sw::DocumentSettingManager::getCharacterCompressionType() const
+{
+ return meChrCmprType;
+}
+
+void sw::DocumentSettingManager::setCharacterCompressionType( /*[in]*/CharCompressType n )
+{
+ if( meChrCmprType == n )
+ return;
+
+ meChrCmprType = n;
+
+ SdrModel *pDrawModel = m_rDoc.getIDocumentDrawModelAccess().GetDrawModel();
+ if( pDrawModel )
+ {
+ pDrawModel->SetCharCompressType( n );
+ if( !m_rDoc.IsInReading() )
+ pDrawModel->ReformatAllTextObjects();
+ }
+
+ SwRootFrame* pTmpRoot = m_rDoc.getIDocumentLayoutAccess().GetCurrentLayout();
+ if( pTmpRoot && !m_rDoc.IsInReading() )
+ {
+ pTmpRoot->StartAllAction();
+ for( auto aLayout : m_rDoc.GetAllLayouts() )
+ aLayout->InvalidateAllContent(SwInvalidateFlags::Size);
+ pTmpRoot->EndAllAction();
+ }
+ m_rDoc.getIDocumentState().SetModified();
+}
+
+
+void sw::DocumentSettingManager::ReplaceCompatibilityOptions(const DocumentSettingManager& rSource)
+{
+ Setn32DummyCompatibilityOptions1( rSource.Getn32DummyCompatibilityOptions1() );
+ Setn32DummyCompatibilityOptions2( rSource.Getn32DummyCompatibilityOptions2() );
+
+ // No mbHTMLMode: this is the subset of mbLastBrowseMode, which can be temporarily enabled even
+ // for non-SwWebDocShells.
+ // No mbIsGlobalDoc: this is true for SwGlobalDocShells.
+ mbGlblDocSaveLinks = rSource.mbGlblDocSaveLinks;
+ mbIsLabelDoc = rSource.mbIsLabelDoc;
+ mbPurgeOLE = rSource.mbPurgeOLE;
+ mbKernAsianPunctuation = rSource.mbKernAsianPunctuation;
+ mbParaSpaceMax = rSource.mbParaSpaceMax;
+ mbParaSpaceMaxAtPages = rSource.mbParaSpaceMaxAtPages;
+ mbTabCompat = rSource.mbTabCompat;
+ mbUseVirtualDevice = rSource.mbUseVirtualDevice;
+ mbAddFlyOffsets = rSource.mbAddFlyOffsets;
+ mbAddVerticalFlyOffsets = rSource.mbAddVerticalFlyOffsets;
+ mbAddExternalLeading = rSource.mbAddExternalLeading;
+ mbUseHiResolutionVirtualDevice = rSource.mbUseHiResolutionVirtualDevice;
+ mbOldLineSpacing = rSource.mbOldLineSpacing;
+ mbAddParaSpacingToTableCells = rSource.mbAddParaSpacingToTableCells;
+ mbAddParaLineSpacingToTableCells = rSource.mbAddParaLineSpacingToTableCells;
+ mbUseFormerObjectPos = rSource.mbUseFormerObjectPos;
+ mbUseFormerTextWrapping = rSource.mbUseFormerTextWrapping;
+ mbConsiderWrapOnObjPos = rSource.mbConsiderWrapOnObjPos;
+ mbMathBaselineAlignment = rSource.mbMathBaselineAlignment;
+ mbStylesNoDefault = rSource.mbStylesNoDefault;
+ mbOldNumbering = rSource.mbOldNumbering;
+ mbIgnoreFirstLineIndentInNumbering = rSource.mbIgnoreFirstLineIndentInNumbering;
+ mbDoNotJustifyLinesWithManualBreak = rSource.mbDoNotJustifyLinesWithManualBreak;
+ mbDoNotResetParaAttrsForNumFont = rSource.mbDoNotResetParaAttrsForNumFont;
+ mbTableRowKeep = rSource.mbTableRowKeep;
+ mbIgnoreTabsAndBlanksForLineCalculation = rSource.mbIgnoreTabsAndBlanksForLineCalculation;
+ mbDoNotCaptureDrawObjsOnPage = rSource.mbDoNotCaptureDrawObjsOnPage;
+ mbClipAsCharacterAnchoredWriterFlyFrames = rSource.mbClipAsCharacterAnchoredWriterFlyFrames;
+ mbUnixForceZeroExtLeading = rSource.mbUnixForceZeroExtLeading;
+ mbTabRelativeToIndent = rSource.mbTabRelativeToIndent;
+ mbProtectForm = rSource.mbProtectForm;
+ mbMsWordCompTrailingBlanks = rSource.mbMsWordCompTrailingBlanks;
+ mbMsWordCompMinLineHeightByFly = rSource.mbMsWordCompMinLineHeightByFly;
+ mbInvertBorderSpacing = rSource.mbInvertBorderSpacing;
+ mbCollapseEmptyCellPara = rSource.mbCollapseEmptyCellPara;
+ mbTabAtLeftIndentForParagraphsInList = rSource.mbTabAtLeftIndentForParagraphsInList;
+ mbSmallCapsPercentage66 = rSource.mbSmallCapsPercentage66;
+ mbTabOverflow = rSource.mbTabOverflow;
+ mbUnbreakableNumberings = rSource.mbUnbreakableNumberings;
+ mbClippedPictures = rSource.mbClippedPictures;
+ mbBackgroundParaOverDrawings = rSource.mbBackgroundParaOverDrawings;
+ mbTabOverMargin = rSource.mbTabOverMargin;
+ mbTabOverSpacing = rSource.mbTabOverSpacing;
+ mbTreatSingleColumnBreakAsPageBreak = rSource.mbTreatSingleColumnBreakAsPageBreak;
+ mbSurroundTextWrapSmall = rSource.mbSurroundTextWrapSmall;
+ mbPropLineSpacingShrinksFirstLine = rSource.mbPropLineSpacingShrinksFirstLine;
+ mbSubtractFlys = rSource.mbSubtractFlys;
+ // No mbLastBrowseMode: this is false by default everywhere
+ mbDisableOffPagePositioning = rSource.mbDisableOffPagePositioning;
+ mbEmptyDbFieldHidesPara = rSource.mbEmptyDbFieldHidesPara;
+ mbContinuousEndnotes = rSource.mbContinuousEndnotes;
+ // No mbProtectBookmarks: this is false by default everywhere
+ // No mbProtectFields: this is false by default everywhere
+ mbHeaderSpacingBelowLastPara = rSource.mbHeaderSpacingBelowLastPara;
+ mbFrameAutowidthWithMorePara = rSource.mbFrameAutowidthWithMorePara;
+ mbFootnoteInColumnToPageEnd = rSource.mbFootnoteInColumnToPageEnd;
+ mbDropCapPunctuation = rSource.mbDropCapPunctuation;
+ mbUseVariableWidthNBSP = rSource.mbUseVariableWidthNBSP;
+}
+
+sal_uInt32 sw::DocumentSettingManager::Getn32DummyCompatibilityOptions1() const
+{
+ return mn32DummyCompatibilityOptions1;
+}
+
+void sw::DocumentSettingManager::Setn32DummyCompatibilityOptions1( const sal_uInt32 CompatibilityOptions1 )
+{
+ mn32DummyCompatibilityOptions1 = CompatibilityOptions1;
+}
+
+sal_uInt32 sw::DocumentSettingManager::Getn32DummyCompatibilityOptions2() const
+{
+ return mn32DummyCompatibilityOptions2;
+}
+
+void sw::DocumentSettingManager::Setn32DummyCompatibilityOptions2( const sal_uInt32 CompatibilityOptions2 )
+{
+ mn32DummyCompatibilityOptions2 = CompatibilityOptions2;
+}
+
+void sw::DocumentSettingManager::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("DocumentSettingManager"));
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbHTMLMode"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbHTMLMode).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbIsGlobalDoc"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbIsGlobalDoc).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbGlblDocSaveLinks"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbGlblDocSaveLinks).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbIsLabelDoc"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbIsLabelDoc).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbPurgeOLE"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbPurgeOLE).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbKernAsianPunctuation"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbKernAsianPunctuation).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbParaSpaceMax"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbParaSpaceMax).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbParaSpaceMaxAtPages"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbParaSpaceMaxAtPages).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbTabCompat"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbTabCompat).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbUseVirtualDevice"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbUseVirtualDevice).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbAddFlyOffsets"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbAddFlyOffsets).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbAddVerticalFlyOffsets"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbAddVerticalFlyOffsets).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbAddExternalLeading"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbAddExternalLeading).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbUseHiResolutionVirtualDevice"));
+ (void)xmlTextWriterWriteAttribute(
+ pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbUseHiResolutionVirtualDevice).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbOldLineSpacing"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbOldLineSpacing).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbAddParaSpacingToTableCells"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbAddParaSpacingToTableCells).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbAddParaLineSpacingToTableCells"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbAddParaLineSpacingToTableCells).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbUseFormerObjectPos"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbUseFormerObjectPos).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbUseFormerTextWrapping"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbUseFormerTextWrapping).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbConsiderWrapOnObjPos"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbConsiderWrapOnObjPos).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbMathBaselineAlignment"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbMathBaselineAlignment).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbStylesNoDefault"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbStylesNoDefault).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbOldNumbering"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbOldNumbering).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbIgnoreFirstLineIndentInNumbering"));
+ (void)xmlTextWriterWriteAttribute(
+ pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbIgnoreFirstLineIndentInNumbering).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbDoNotJustifyLinesWithManualBreak"));
+ (void)xmlTextWriterWriteAttribute(
+ pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbDoNotJustifyLinesWithManualBreak).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbDoNotResetParaAttrsForNumFont"));
+ (void)xmlTextWriterWriteAttribute(
+ pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbDoNotResetParaAttrsForNumFont).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbTableRowKeep"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbTableRowKeep).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbIgnoreTabsAndBlanksForLineCalculation"));
+ (void)xmlTextWriterWriteAttribute(
+ pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbIgnoreTabsAndBlanksForLineCalculation).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbDoNotCaptureDrawObjsOnPage"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbDoNotCaptureDrawObjsOnPage).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbClipAsCharacterAnchoredWriterFlyFrames"));
+ (void)xmlTextWriterWriteAttribute(
+ pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbClipAsCharacterAnchoredWriterFlyFrames).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbUnixForceZeroExtLeading"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbUnixForceZeroExtLeading).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbTabRelativeToIndent"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbTabRelativeToIndent).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbProtectForm"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbProtectForm).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbMsWordCompTrailingBlanks"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbMsWordCompTrailingBlanks).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbMsWordCompMinLineHeightByFly"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbMsWordCompMinLineHeightByFly).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbInvertBorderSpacing"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbInvertBorderSpacing).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbCollapseEmptyCellPara"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbCollapseEmptyCellPara).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbTabAtLeftIndentForParagraphsInList"));
+ (void)xmlTextWriterWriteAttribute(
+ pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbTabAtLeftIndentForParagraphsInList).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbSmallCapsPercentage66"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbSmallCapsPercentage66).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbTabOverflow"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbTabOverflow).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbUnbreakableNumberings"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbUnbreakableNumberings).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbClippedPictures"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbClippedPictures).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbBackgroundParaOverDrawings"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbBackgroundParaOverDrawings).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbTabOverMargin"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbTabOverMargin).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbTabOverSpacing"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbTabOverSpacing).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbTreatSingleColumnBreakAsPageBreak"));
+ (void)xmlTextWriterWriteAttribute(
+ pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbTreatSingleColumnBreakAsPageBreak).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbSurroundTextWrapSmall"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbSurroundTextWrapSmall).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbPropLineSpacingShrinksFirstLine"));
+ (void)xmlTextWriterWriteAttribute(
+ pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbPropLineSpacingShrinksFirstLine).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbSubtractFlys"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbSubtractFlys).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbLastBrowseMode"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbLastBrowseMode).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbDisableOffPagePositioning"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbDisableOffPagePositioning).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbEmptyDbFieldHidesPara"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbEmptyDbFieldHidesPara).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbUseVariableWidthNBSP"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbUseVariableWidthNBSP).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbContinuousEndnotes"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbContinuousEndnotes).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbHeaderSpacingBelowLastPara"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbHeaderSpacingBelowLastPara).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbFrameAutowidthWithMorePara"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbFrameAutowidthWithMorePara).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbGutterAtTop"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbGutterAtTop).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbFootnoteInColumnToPageEnd"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbFootnoteInColumnToPageEnd).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbHyphenateURLs"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbHyphenateURLs).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbDoNotBreakWrappedTables"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbDoNotBreakWrappedTables).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbAllowTextAfterFloatingTableBreak"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbAllowTextAfterFloatingTableBreak).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbJustifyLinesWithShrinking"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbJustifyLinesWithShrinking).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mnImagePreferredDPI"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::number(mnImagePreferredDPI).getStr()));
+
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/DocumentStateManager.cxx b/sw/source/core/doc/DocumentStateManager.cxx
new file mode 100644
index 0000000000..bf965d54ba
--- /dev/null
+++ b/sw/source/core/doc/DocumentStateManager.cxx
@@ -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 .
+ */
+#include <DocumentStateManager.hxx>
+#include <doc.hxx>
+#include <DocumentStatisticsManager.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <DocumentLayoutManager.hxx>
+#include <acorrect.hxx>
+
+namespace sw
+{
+
+DocumentStateManager::DocumentStateManager( SwDoc& i_rSwdoc ) :
+ m_rDoc( i_rSwdoc ),
+ mbEnableSetModified(true),
+ mbModified(false),
+ mbUpdateExpField(false),
+ mbNewDoc(false),
+ mbInCallModified(false)
+{
+}
+
+void DocumentStateManager::SetModified()
+{
+ if (!IsEnableSetModified())
+ return;
+
+ m_rDoc.GetDocumentLayoutManager().ClearSwLayouterEntries();
+ mbModified = true;
+ m_rDoc.GetDocumentStatisticsManager().SetDocStatModified( true );
+ if( m_rDoc.GetOle2Link().IsSet() )
+ {
+ mbInCallModified = true;
+ m_rDoc.GetOle2Link().Call( true );
+ mbInCallModified = false;
+ }
+
+ if( m_rDoc.GetAutoCorrExceptWord() && !m_rDoc.GetAutoCorrExceptWord()->IsDeleted() )
+ m_rDoc.DeleteAutoCorrExceptWord();
+}
+
+void DocumentStateManager::ResetModified()
+{
+ // give the old and new modified state to the link
+ // Bit 0: -> old state
+ // Bit 1: -> new state
+ bool bOldModified = mbModified;
+ mbModified = false;
+ m_rDoc.GetDocumentStatisticsManager().SetDocStatModified( false );
+ m_rDoc.GetIDocumentUndoRedo().SetUndoNoModifiedPosition();
+ if( bOldModified && m_rDoc.GetOle2Link().IsSet() )
+ {
+ mbInCallModified = true;
+ m_rDoc.GetOle2Link().Call( false );
+ mbInCallModified = false;
+ }
+}
+
+bool DocumentStateManager::IsModified() const
+{
+ return mbModified;
+}
+
+bool DocumentStateManager::IsEnableSetModified() const
+{
+ return mbEnableSetModified;
+}
+
+void DocumentStateManager::SetEnableSetModified(bool bEnableSetModified)
+{
+ mbEnableSetModified = bEnableSetModified;
+}
+
+bool DocumentStateManager::IsInCallModified() const
+{
+ return mbInCallModified;
+}
+
+bool DocumentStateManager::IsUpdateExpField() const
+{
+ return mbUpdateExpField;
+}
+
+bool DocumentStateManager::IsNewDoc() const
+{
+ return mbNewDoc;
+}
+
+void DocumentStateManager::SetNewDoc(bool b)
+{
+ mbNewDoc = b;
+}
+
+void DocumentStateManager::SetUpdateExpFieldStat(bool b)
+{
+ mbUpdateExpField = b;
+}
+
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/DocumentStatisticsManager.cxx b/sw/source/core/doc/DocumentStatisticsManager.cxx
new file mode 100644
index 0000000000..79df62ff1d
--- /dev/null
+++ b/sw/source/core/doc/DocumentStatisticsManager.cxx
@@ -0,0 +1,217 @@
+/* -*- 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 <DocumentStatisticsManager.hxx>
+#include <doc.hxx>
+#include <fldbas.hxx>
+#include <docsh.hxx>
+#include <IDocumentFieldsAccess.hxx>
+#include <IDocumentState.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <view.hxx>
+#include <ndtxt.hxx>
+#include <fmtfld.hxx>
+#include <rootfrm.hxx>
+#include <docufld.hxx>
+#include <docstat.hxx>
+#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+
+using namespace ::com::sun::star;
+
+namespace sw
+{
+
+DocumentStatisticsManager::DocumentStatisticsManager( SwDoc& i_rSwdoc )
+ : m_rDoc( i_rSwdoc ),
+ mpDocStat( new SwDocStat ),
+ mbInitialized( false ),
+ maStatsUpdateIdle( i_rSwdoc, "sw::DocumentStatisticsManager maStatsUpdateIdle" )
+{
+ maStatsUpdateIdle.SetPriority( TaskPriority::LOWEST );
+ maStatsUpdateIdle.SetInvokeHandler( LINK( this, DocumentStatisticsManager, DoIdleStatsUpdate ) );
+}
+
+void DocumentStatisticsManager::DocInfoChgd(bool const isEnableSetModified)
+{
+ m_rDoc.getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::DocInfo )->UpdateFields();
+ m_rDoc.getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::TemplateName )->UpdateFields();
+ if (isEnableSetModified)
+ {
+ m_rDoc.getIDocumentState().SetModified();
+ }
+}
+
+const SwDocStat& DocumentStatisticsManager::GetDocStat() const
+{
+ return *mpDocStat;
+}
+
+void DocumentStatisticsManager::SetDocStatModified(bool bSet)
+{
+ mpDocStat->bModified = bSet;
+}
+
+const SwDocStat& DocumentStatisticsManager::GetUpdatedDocStat( bool bCompleteAsync, bool bFields )
+{
+ if( mpDocStat->bModified || !mbInitialized)
+ {
+ UpdateDocStat( bCompleteAsync, bFields );
+ }
+ return *mpDocStat;
+}
+
+void DocumentStatisticsManager::SetDocStat( const SwDocStat& rStat )
+{
+ *mpDocStat = rStat;
+ mbInitialized = true;
+}
+
+void DocumentStatisticsManager::UpdateDocStat( bool bCompleteAsync, bool bFields )
+{
+ if( !mpDocStat->bModified && mbInitialized)
+ return;
+
+ if (!bCompleteAsync)
+ {
+ maStatsUpdateIdle.Stop();
+ while (IncrementalDocStatCalculate(
+ std::numeric_limits<tools::Long>::max(), bFields)) {}
+ }
+ else if (IncrementalDocStatCalculate(5000, bFields))
+ maStatsUpdateIdle.Start();
+ else
+ maStatsUpdateIdle.Stop();
+}
+
+// returns true while there is more to do
+bool DocumentStatisticsManager::IncrementalDocStatCalculate(tools::Long nChars, bool bFields)
+{
+ mbInitialized = true;
+ mpDocStat->Reset();
+ mpDocStat->nPara = 0; // default is 1!
+
+ // This is the inner loop - at least while the paras are dirty.
+ for( SwNodeOffset i = m_rDoc.GetNodes().Count(); i > SwNodeOffset(0) && nChars > 0; )
+ {
+ SwNode* pNd = m_rDoc.GetNodes()[ --i ];
+ switch( pNd->GetNodeType() )
+ {
+ case SwNodeType::Text:
+ {
+ tools::Long const nOldChars(mpDocStat->nChar);
+ SwTextNode *pText = static_cast< SwTextNode * >( pNd );
+ if (pText->CountWords(*mpDocStat, 0, pText->GetText().getLength()))
+ {
+ nChars -= (mpDocStat->nChar - nOldChars);
+ }
+ break;
+ }
+ case SwNodeType::Table: ++mpDocStat->nTable; break;
+ case SwNodeType::Grf: ++mpDocStat->nGrf; break;
+ case SwNodeType::Ole: ++mpDocStat->nOLE; break;
+ case SwNodeType::Section: break;
+ default: break;
+ }
+ }
+
+ // #i93174#: notes contain paragraphs that are not nodes
+ {
+ SwFieldType * const pPostits( m_rDoc.getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::Postit) );
+ std::vector<SwFormatField*> vFields;
+ pPostits->GatherFields(vFields);
+ for(auto pFormatField : vFields)
+ {
+ const auto pField = static_cast<SwPostItField const*>(pFormatField->GetField());
+ mpDocStat->nAllPara += pField->GetNumberOfParagraphs();
+ }
+ }
+
+ mpDocStat->nPage = m_rDoc.getIDocumentLayoutAccess().GetCurrentLayout() ? m_rDoc.getIDocumentLayoutAccess().GetCurrentLayout()->GetPageNum() : 0;
+ SetDocStatModified( false );
+
+ css::uno::Sequence < css::beans::NamedValue > aStat( mpDocStat->nPage ? 8 : 7);
+ auto pStat = aStat.getArray();
+ sal_Int32 n=0;
+ pStat[n].Name = "TableCount";
+ pStat[n++].Value <<= static_cast<sal_Int32>(mpDocStat->nTable);
+ pStat[n].Name = "ImageCount";
+ pStat[n++].Value <<= static_cast<sal_Int32>(mpDocStat->nGrf);
+ pStat[n].Name = "ObjectCount";
+ pStat[n++].Value <<= static_cast<sal_Int32>(mpDocStat->nOLE);
+ if ( mpDocStat->nPage )
+ {
+ pStat[n].Name = "PageCount";
+ pStat[n++].Value <<= static_cast<sal_Int32>(mpDocStat->nPage);
+ }
+ pStat[n].Name = "ParagraphCount";
+ pStat[n++].Value <<= static_cast<sal_Int32>(mpDocStat->nPara);
+ pStat[n].Name = "WordCount";
+ pStat[n++].Value <<= static_cast<sal_Int32>(mpDocStat->nWord);
+ pStat[n].Name = "CharacterCount";
+ pStat[n++].Value <<= static_cast<sal_Int32>(mpDocStat->nChar);
+ pStat[n].Name = "NonWhitespaceCharacterCount";
+ pStat[n++].Value <<= static_cast<sal_Int32>(mpDocStat->nCharExcludingSpaces);
+
+ // For e.g. autotext documents there is no pSwgInfo (#i79945)
+ SwDocShell* pObjShell(m_rDoc.GetDocShell());
+ if (pObjShell)
+ {
+ const uno::Reference<document::XDocumentPropertiesSupplier> xDPS(
+ pObjShell->GetModel(), uno::UNO_QUERY_THROW);
+ const uno::Reference<document::XDocumentProperties> xDocProps(
+ xDPS->getDocumentProperties());
+ // #i96786#: do not set modified flag when updating statistics
+ const bool bDocWasModified( m_rDoc.getIDocumentState().IsModified() );
+ const ModifyBlocker_Impl b(pObjShell);
+ // rhbz#1081176: don't jump to cursor pos because of (temporary)
+ // activation of modified flag triggering move to input position
+ auto aViewGuard(pObjShell->LockAllViews());
+ xDocProps->setDocumentStatistics(aStat);
+ if (!bDocWasModified)
+ {
+ m_rDoc.getIDocumentState().ResetModified();
+ }
+ }
+
+ // optionally update stat. fields
+ if (bFields)
+ {
+ SwFieldType *pType = m_rDoc.getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::DocStat);
+ pType->UpdateFields();
+ }
+
+ return nChars < 0;
+}
+
+IMPL_LINK( DocumentStatisticsManager, DoIdleStatsUpdate, Timer *, pIdle, void )
+{
+ if (IncrementalDocStatCalculate(32000))
+ pIdle->Start();
+ SwView* pView = m_rDoc.GetDocShell() ? m_rDoc.GetDocShell()->GetView() : nullptr;
+ if( pView )
+ pView->UpdateDocStats();
+}
+
+DocumentStatisticsManager::~DocumentStatisticsManager()
+{
+ maStatsUpdateIdle.Stop();
+}
+
+}
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/DocumentStylePoolManager.cxx b/sw/source/core/doc/DocumentStylePoolManager.cxx
new file mode 100644
index 0000000000..56c1274a87
--- /dev/null
+++ b/sw/source/core/doc/DocumentStylePoolManager.cxx
@@ -0,0 +1,2768 @@
+/* -*- 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 <DocumentStylePoolManager.hxx>
+#include <SwStyleNameMapper.hxx>
+#include <doc.hxx>
+#include <DocumentSettingManager.hxx>
+#include <IDocumentState.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <fmtanchr.hxx>
+#include <fmtfsize.hxx>
+#include <paratr.hxx>
+#include <poolfmt.hxx>
+#include <fmtornt.hxx>
+#include <charfmt.hxx>
+#include <fmtsrnd.hxx>
+#include <docary.hxx>
+#include <pagedesc.hxx>
+#include <frmfmt.hxx>
+#include <fmtline.hxx>
+#include <numrule.hxx>
+#include <hints.hxx>
+#include <editeng/paperinf.hxx>
+#include <editeng/wghtitem.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/tstpitem.hxx>
+#include <editeng/lrspitem.hxx>
+#include <editeng/ulspitem.hxx>
+#include <editeng/lspcitem.hxx>
+#include <editeng/adjustitem.hxx>
+#include <editeng/postitem.hxx>
+#include <editeng/keepitem.hxx>
+#include <editeng/opaqitem.hxx>
+#include <editeng/boxitem.hxx>
+#include <editeng/cmapitem.hxx>
+#include <editeng/udlnitem.hxx>
+#include <editeng/colritem.hxx>
+#include <editeng/protitem.hxx>
+#include <editeng/escapementitem.hxx>
+#include <editeng/langitem.hxx>
+#include <editeng/charrotateitem.hxx>
+#include <editeng/emphasismarkitem.hxx>
+#include <editeng/scriptspaceitem.hxx>
+#include <o3tl/unit_conversion.hxx>
+#include <svx/strings.hrc>
+#include <svx/dialmgr.hxx>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+#include <strings.hrc>
+#include <frmatr.hxx>
+#include <frameformats.hxx>
+#include <com/sun/star/text/VertOrientation.hpp>
+#include <com/sun/star/text/RelOrientation.hpp>
+#include <com/sun/star/text/HoriOrientation.hpp>
+#include <unotools/syslocale.hxx>
+#include <i18nlangtag/languagetag.hxx>
+#include <comphelper/lok.hxx>
+
+using namespace ::editeng;
+using namespace ::com::sun::star;
+
+bool IsConditionalByPoolId(sal_uInt16 nId)
+{
+ // TODO: why is this style conditional?
+ // If it is changed to no longer be conditional, then a style "Text Body"
+ // will be imported without its conditions from ODF.
+ return RES_POOLCOLL_TEXT == nId;
+}
+
+namespace
+{
+ const sal_uInt16 PT_3 = 3 * 20; // 3 pt
+ const sal_uInt16 PT_6 = 6 * 20; // 6 pt
+ const sal_uInt16 PT_7 = 7 * 20; // 7 pt
+ const sal_uInt16 PT_9 = 9 * 20; // 9 pt
+ const sal_uInt16 PT_10 = 10 * 20; // 10 pt
+ const sal_uInt16 PT_12 = 12 * 20; // 12 pt
+ const sal_uInt16 PT_13 = 13 * 20; // 13 pt
+ const sal_uInt16 PT_14 = 14 * 20; // 14 pt
+ const sal_uInt16 PT_16 = 16 * 20; // 16 pt
+ const sal_uInt16 PT_18 = 18 * 20; // 18 pt
+ const sal_uInt16 PT_24 = 24 * 20; // 24 pt
+ const sal_uInt16 PT_28 = 28 * 20; // 28 pt
+
+ const sal_uInt16 HTML_PARSPACE = o3tl::convert(5, o3tl::Length::mm, o3tl::Length::twip);
+
+ const sal_uInt16 aHeadlineSizes[ 2 * MAXLEVEL ] = {
+ // we do everything percentual now:
+ PT_18, PT_16, PT_14, PT_13, PT_12,
+ PT_12, PT_10, PT_10, PT_9, PT_9, // normal
+
+ PT_24, PT_18, PT_14, PT_12, PT_10,
+ PT_7, PT_7, PT_7, PT_7, PT_7 // HTML mode
+ };
+
+ tools::Long lcl_GetRightMargin( SwDoc& rDoc )
+ {
+ // Make sure that the printer settings are taken over to the standard
+ // page style
+ const SwFrameFormat& rPgDscFormat = rDoc.GetPageDesc( 0 ).GetMaster();
+ const SvxLRSpaceItem& rLR = rPgDscFormat.GetLRSpace();
+ const tools::Long nLeft = rLR.GetLeft();
+ const tools::Long nRight = rLR.GetRight();
+ const tools::Long nWidth = rPgDscFormat.GetFrameSize().GetWidth();
+ return nWidth - nLeft - nRight;
+ }
+
+ void lcl_SetDfltFont( DefaultFontType nFntType, SfxItemSet& rSet )
+ {
+ static struct {
+ sal_uInt16 nResLngId;
+ sal_uInt16 nResFntId;
+ } aArr[ 3 ] = {
+ { RES_CHRATR_LANGUAGE, RES_CHRATR_FONT },
+ { RES_CHRATR_CJK_LANGUAGE, RES_CHRATR_CJK_FONT },
+ { RES_CHRATR_CTL_LANGUAGE, RES_CHRATR_CTL_FONT }
+ };
+ for(const auto & n : aArr)
+ {
+ LanguageType nLng = static_cast<const SvxLanguageItem&>(rSet.GetPool()->GetDefaultItem(
+ n.nResLngId )).GetLanguage();
+ vcl::Font aFnt( OutputDevice::GetDefaultFont( nFntType,
+ nLng, GetDefaultFontFlags::OnlyOne ) );
+
+ rSet.Put( SvxFontItem( aFnt.GetFamilyType(), aFnt.GetFamilyName(),
+ OUString(), aFnt.GetPitch(),
+ aFnt.GetCharSet(), n.nResFntId ));
+ }
+ }
+
+ void lcl_SetDfltFont( DefaultFontType nLatinFntType, DefaultFontType nCJKFntType,
+ DefaultFontType nCTLFntType, SfxItemSet& rSet )
+ {
+ static struct {
+ sal_uInt16 nResLngId;
+ sal_uInt16 nResFntId;
+ DefaultFontType nFntType;
+ } aArr[ 3 ] = {
+ { RES_CHRATR_LANGUAGE, RES_CHRATR_FONT, static_cast<DefaultFontType>(0) },
+ { RES_CHRATR_CJK_LANGUAGE, RES_CHRATR_CJK_FONT, static_cast<DefaultFontType>(0) },
+ { RES_CHRATR_CTL_LANGUAGE, RES_CHRATR_CTL_FONT, static_cast<DefaultFontType>(0) }
+ };
+ aArr[0].nFntType = nLatinFntType;
+ aArr[1].nFntType = nCJKFntType;
+ aArr[2].nFntType = nCTLFntType;
+
+ for(const auto & n : aArr)
+ {
+ LanguageType nLng = static_cast<const SvxLanguageItem&>(rSet.GetPool()->GetDefaultItem(
+ n.nResLngId )).GetLanguage();
+ vcl::Font aFnt( OutputDevice::GetDefaultFont( n.nFntType,
+ nLng, GetDefaultFontFlags::OnlyOne ) );
+
+ rSet.Put( SvxFontItem( aFnt.GetFamilyType(), aFnt.GetFamilyName(),
+ OUString(), aFnt.GetPitch(),
+ aFnt.GetCharSet(), n.nResFntId ));
+ }
+ }
+
+ void lcl_SetHeadline( SwDoc& rDoc, SwTextFormatColl* pColl,
+ SfxItemSet& rSet,
+ sal_uInt16 nOutLvlBits, sal_uInt8 nLevel, bool bItalic )
+ {
+ SetAllScriptItem( rSet, SvxWeightItem( WEIGHT_BOLD, RES_CHRATR_WEIGHT ) );
+ SvxFontHeightItem aHItem(240, 100, RES_CHRATR_FONTSIZE);
+ const bool bHTMLMode = rDoc.GetDocumentSettingManager().get(DocumentSettingId::HTML_MODE);
+ if( bHTMLMode )
+ aHItem.SetHeight( aHeadlineSizes[ MAXLEVEL + nLevel ] );
+ else
+ aHItem.SetHeight( aHeadlineSizes[ nLevel ] );
+ SetAllScriptItem( rSet, aHItem );
+
+ if( bItalic && !bHTMLMode )
+ SetAllScriptItem( rSet, SvxPostureItem( ITALIC_NORMAL, RES_CHRATR_POSTURE ) );
+
+ if( bHTMLMode )
+ {
+ lcl_SetDfltFont( DefaultFontType::LATIN_TEXT, DefaultFontType::CJK_TEXT,
+ DefaultFontType::CTL_TEXT, rSet );
+ }
+
+ if( !pColl )
+ return;
+
+ if( !( nOutLvlBits & ( 1 << nLevel )) )
+ {
+ pColl->AssignToListLevelOfOutlineStyle(nLevel);
+ if( !bHTMLMode )
+ {
+ SwNumRule * pOutlineRule = rDoc.GetOutlineNumRule();
+ const SwNumFormat& rNFormat = pOutlineRule->Get( nLevel );
+
+ if ( rNFormat.GetPositionAndSpaceMode() ==
+ SvxNumberFormat::LABEL_WIDTH_AND_POSITION &&
+ ( rNFormat.GetAbsLSpace() || rNFormat.GetFirstLineOffset() ) )
+ {
+ SvxFirstLineIndentItem firstLine(pColl->GetFormatAttr(RES_MARGIN_FIRSTLINE));
+ SvxTextLeftMarginItem leftMargin(pColl->GetFormatAttr(RES_MARGIN_TEXTLEFT));
+ firstLine.SetTextFirstLineOffsetValue(rNFormat.GetFirstLineOffset());
+ //TODO: overflow
+ leftMargin.SetTextLeft(rNFormat.GetAbsLSpace());
+ pColl->SetFormatAttr(firstLine);
+ pColl->SetFormatAttr(leftMargin);
+ }
+
+ // All paragraph styles, which are assigned to a level of the
+ // outline style has to have the outline style set as its list style.
+ SwNumRuleItem aItem(pOutlineRule->GetName());
+ pColl->SetFormatAttr(aItem);
+ }
+ }
+ pColl->SetNextTextFormatColl( *rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_TEXT ));
+ }
+
+ void lcl_SetRegister( SwDoc& rDoc, SfxItemSet& rSet, sal_uInt16 nFact,
+ bool bHeader, bool bTab )
+ {
+ sal_uInt16 nLeft = o3tl::convert(5 * nFact, o3tl::Length::mm, o3tl::Length::twip);
+ SvxFirstLineIndentItem const firstLine(0, RES_MARGIN_FIRSTLINE);
+ SvxTextLeftMarginItem const leftMargin(nLeft, RES_MARGIN_TEXTLEFT);
+ rSet.Put(firstLine);
+ rSet.Put(leftMargin);
+ if( bHeader )
+ {
+ SetAllScriptItem( rSet, SvxWeightItem( WEIGHT_BOLD, RES_CHRATR_WEIGHT ) );
+ SetAllScriptItem( rSet, SvxFontHeightItem( PT_16, 100, RES_CHRATR_FONTSIZE ) );
+ }
+ if( bTab )
+ {
+ tools::Long nRightMargin = lcl_GetRightMargin( rDoc );
+ SvxTabStopItem aTStops( 0, 0, SvxTabAdjust::Default, RES_PARATR_TABSTOP );
+ aTStops.Insert( SvxTabStop( nRightMargin - nLeft,
+ SvxTabAdjust::Right,
+ cDfltDecimalChar, '.' ));
+ rSet.Put( aTStops );
+ }
+ }
+
+ void lcl_SetNumBul( SwDoc& rDoc, SwTextFormatColl* pColl,
+ SfxItemSet& rSet,
+ sal_uInt16 nNxt, SwTwips nEZ, SwTwips nLeft,
+ SwTwips nUpper, SwTwips nLower )
+ {
+ SvxFirstLineIndentItem const firstLine(sal_uInt16(nEZ), RES_MARGIN_FIRSTLINE);
+ SvxTextLeftMarginItem const leftMargin(sal_uInt16(nLeft), RES_MARGIN_TEXTLEFT);
+ rSet.Put(firstLine);
+ rSet.Put(leftMargin);
+ SvxULSpaceItem aUL( RES_UL_SPACE );
+ aUL.SetUpper( sal_uInt16(nUpper) );
+ aUL.SetLower( sal_uInt16(nLower) );
+ rSet.Put( aUL );
+
+ if( pColl )
+ pColl->SetNextTextFormatColl( *rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool( nNxt ));
+ }
+
+ void lcl_PutStdPageSizeIntoItemSet( SwDoc& rDoc, SfxItemSet& rSet )
+ {
+ SwPageDesc* pStdPgDsc = rDoc.getIDocumentStylePoolAccess().GetPageDescFromPool( RES_POOLPAGE_STANDARD );
+ SwFormatFrameSize aFrameSz( pStdPgDsc->GetMaster().GetFrameSize() );
+ if( pStdPgDsc->GetLandscape() )
+ {
+ SwTwips nTmp = aFrameSz.GetHeight();
+ aFrameSz.SetHeight( aFrameSz.GetWidth() );
+ aFrameSz.SetWidth( nTmp );
+ }
+ rSet.Put( aFrameSz );
+ }
+}
+
+const TranslateId STR_POOLCOLL_TEXT_ARY[] =
+{
+ // Category Text
+ STR_POOLCOLL_STANDARD,
+ STR_POOLCOLL_TEXT,
+ STR_POOLCOLL_TEXT_IDENT,
+ STR_POOLCOLL_TEXT_NEGIDENT,
+ STR_POOLCOLL_TEXT_MOVE,
+ STR_POOLCOLL_GREETING,
+ STR_POOLCOLL_SIGNATURE,
+ STR_POOLCOLL_CONFRONTATION,
+ STR_POOLCOLL_MARGINAL,
+ // Subcategory Headlines
+ STR_POOLCOLL_HEADLINE_BASE,
+ STR_POOLCOLL_HEADLINE1,
+ STR_POOLCOLL_HEADLINE2,
+ STR_POOLCOLL_HEADLINE3,
+ STR_POOLCOLL_HEADLINE4,
+ STR_POOLCOLL_HEADLINE5,
+ STR_POOLCOLL_HEADLINE6,
+ STR_POOLCOLL_HEADLINE7,
+ STR_POOLCOLL_HEADLINE8,
+ STR_POOLCOLL_HEADLINE9,
+ STR_POOLCOLL_HEADLINE10
+};
+
+const TranslateId STR_POOLCOLL_LISTS_ARY[]
+{
+ // Category Lists
+ STR_POOLCOLL_NUMBER_BULLET_BASE,
+ // Subcategory Numbering
+ STR_POOLCOLL_NUM_LEVEL1S,
+ STR_POOLCOLL_NUM_LEVEL1,
+ STR_POOLCOLL_NUM_LEVEL1E,
+ STR_POOLCOLL_NUM_NONUM1,
+ STR_POOLCOLL_NUM_LEVEL2S,
+ STR_POOLCOLL_NUM_LEVEL2,
+ STR_POOLCOLL_NUM_LEVEL2E,
+ STR_POOLCOLL_NUM_NONUM2,
+ STR_POOLCOLL_NUM_LEVEL3S,
+ STR_POOLCOLL_NUM_LEVEL3,
+ STR_POOLCOLL_NUM_LEVEL3E,
+ STR_POOLCOLL_NUM_NONUM3,
+ STR_POOLCOLL_NUM_LEVEL4S,
+ STR_POOLCOLL_NUM_LEVEL4,
+ STR_POOLCOLL_NUM_LEVEL4E,
+ STR_POOLCOLL_NUM_NONUM4,
+ STR_POOLCOLL_NUM_LEVEL5S,
+ STR_POOLCOLL_NUM_LEVEL5,
+ STR_POOLCOLL_NUM_LEVEL5E,
+ STR_POOLCOLL_NUM_NONUM5,
+
+ // Subcategory Enumeration
+ STR_POOLCOLL_BULLET_LEVEL1S,
+ STR_POOLCOLL_BULLET_LEVEL1,
+ STR_POOLCOLL_BULLET_LEVEL1E,
+ STR_POOLCOLL_BULLET_NONUM1,
+ STR_POOLCOLL_BULLET_LEVEL2S,
+ STR_POOLCOLL_BULLET_LEVEL2,
+ STR_POOLCOLL_BULLET_LEVEL2E,
+ STR_POOLCOLL_BULLET_NONUM2,
+ STR_POOLCOLL_BULLET_LEVEL3S,
+ STR_POOLCOLL_BULLET_LEVEL3,
+ STR_POOLCOLL_BULLET_LEVEL3E,
+ STR_POOLCOLL_BULLET_NONUM3,
+ STR_POOLCOLL_BULLET_LEVEL4S,
+ STR_POOLCOLL_BULLET_LEVEL4,
+ STR_POOLCOLL_BULLET_LEVEL4E,
+ STR_POOLCOLL_BULLET_NONUM4,
+ STR_POOLCOLL_BULLET_LEVEL5S,
+ STR_POOLCOLL_BULLET_LEVEL5,
+ STR_POOLCOLL_BULLET_LEVEL5E,
+ STR_POOLCOLL_BULLET_NONUM5
+};
+
+// Special Areas
+const TranslateId STR_POOLCOLL_EXTRA_ARY[]
+{
+ // Subcategory Header
+ STR_POOLCOLL_HEADERFOOTER,
+ STR_POOLCOLL_HEADER,
+ STR_POOLCOLL_HEADERL,
+ STR_POOLCOLL_HEADERR,
+ // Subcategory Footer
+ STR_POOLCOLL_FOOTER,
+ STR_POOLCOLL_FOOTERL,
+ STR_POOLCOLL_FOOTERR,
+ // Subcategory Table
+ STR_POOLCOLL_TABLE,
+ STR_POOLCOLL_TABLE_HDLN,
+ // Subcategory Labels
+ STR_POOLCOLL_LABEL,
+ STR_POOLCOLL_LABEL_ABB,
+ STR_POOLCOLL_LABEL_TABLE,
+ STR_POOLCOLL_LABEL_FRAME,
+ STR_POOLCOLL_LABEL_FIGURE,
+ // Miscellaneous
+ STR_POOLCOLL_FRAME,
+ STR_POOLCOLL_FOOTNOTE,
+ STR_POOLCOLL_ENVELOPE_ADDRESS,
+ STR_POOLCOLL_SEND_ADDRESS,
+ STR_POOLCOLL_ENDNOTE,
+ STR_POOLCOLL_LABEL_DRAWING,
+ STR_POOLCOLL_COMMENT
+};
+
+const TranslateId STR_POOLCOLL_REGISTER_ARY[] =
+{
+ // Category Directories
+ STR_POOLCOLL_REGISTER_BASE,
+ // Subcategory Index-Directories
+ STR_POOLCOLL_TOX_IDXH,
+ STR_POOLCOLL_TOX_IDX1,
+ STR_POOLCOLL_TOX_IDX2,
+ STR_POOLCOLL_TOX_IDX3,
+ STR_POOLCOLL_TOX_IDXBREAK,
+ // Subcategory Tables of Contents
+ STR_POOLCOLL_TOX_CNTNTH,
+ STR_POOLCOLL_TOX_CNTNT1,
+ STR_POOLCOLL_TOX_CNTNT2,
+ STR_POOLCOLL_TOX_CNTNT3,
+ STR_POOLCOLL_TOX_CNTNT4,
+ STR_POOLCOLL_TOX_CNTNT5,
+ // Subcategory User-Directories:
+ STR_POOLCOLL_TOX_USERH,
+ STR_POOLCOLL_TOX_USER1,
+ STR_POOLCOLL_TOX_USER2,
+ STR_POOLCOLL_TOX_USER3,
+ STR_POOLCOLL_TOX_USER4,
+ STR_POOLCOLL_TOX_USER5,
+ // Subcategory Table of Contents more Levels 5 - 10
+ STR_POOLCOLL_TOX_CNTNT6,
+ STR_POOLCOLL_TOX_CNTNT7,
+ STR_POOLCOLL_TOX_CNTNT8,
+ STR_POOLCOLL_TOX_CNTNT9,
+ STR_POOLCOLL_TOX_CNTNT10,
+ // Illustrations Index
+ STR_POOLCOLL_TOX_ILLUSH,
+ STR_POOLCOLL_TOX_ILLUS1,
+ // Object Index
+ STR_POOLCOLL_TOX_OBJECTH,
+ STR_POOLCOLL_TOX_OBJECT1,
+ // Tables Index
+ STR_POOLCOLL_TOX_TABLESH,
+ STR_POOLCOLL_TOX_TABLES1,
+ // Index of Authorities
+ STR_POOLCOLL_TOX_AUTHORITIESH,
+ STR_POOLCOLL_TOX_AUTHORITIES1,
+ // Subcategory User-Directories more Levels 5 - 10
+ STR_POOLCOLL_TOX_USER6,
+ STR_POOLCOLL_TOX_USER7,
+ STR_POOLCOLL_TOX_USER8,
+ STR_POOLCOLL_TOX_USER9,
+ STR_POOLCOLL_TOX_USER10
+};
+
+const TranslateId STR_POOLCOLL_DOC_ARY[] =
+{
+ // Category Chapter/Document
+ STR_POOLCOLL_DOC_TITLE,
+ STR_POOLCOLL_DOC_SUBTITLE,
+ STR_POOLCOLL_DOC_APPENDIX
+};
+
+const TranslateId STR_POOLCOLL_HTML_ARY[] =
+{
+ // Category HTML-Templates
+ STR_POOLCOLL_HTML_BLOCKQUOTE,
+ STR_POOLCOLL_HTML_PRE,
+ STR_POOLCOLL_HTML_HR,
+ STR_POOLCOLL_HTML_DD,
+ STR_POOLCOLL_HTML_DT
+};
+
+const TranslateId STR_POOLCHR_ARY[] =
+{
+ STR_POOLCHR_FOOTNOTE,
+ STR_POOLCHR_PAGENO,
+ STR_POOLCHR_LABEL,
+ STR_POOLCHR_DROPCAPS,
+ STR_POOLCHR_NUM_LEVEL,
+ STR_POOLCHR_BULLET_LEVEL,
+ STR_POOLCHR_INET_NORMAL,
+ STR_POOLCHR_INET_VISIT,
+ STR_POOLCHR_JUMPEDIT,
+ STR_POOLCHR_TOXJUMP,
+ STR_POOLCHR_ENDNOTE,
+ STR_POOLCHR_LINENUM,
+ STR_POOLCHR_IDX_MAIN_ENTRY,
+ STR_POOLCHR_FOOTNOTE_ANCHOR,
+ STR_POOLCHR_ENDNOTE_ANCHOR,
+ STR_POOLCHR_RUBYTEXT,
+ STR_POOLCHR_VERT_NUM
+};
+
+const TranslateId STR_POOLCHR_HTML_ARY[] =
+{
+ STR_POOLCHR_HTML_EMPHASIS,
+ STR_POOLCHR_HTML_CITATION,
+ STR_POOLCHR_HTML_STRONG,
+ STR_POOLCHR_HTML_CODE,
+ STR_POOLCHR_HTML_SAMPLE,
+ STR_POOLCHR_HTML_KEYBOARD,
+ STR_POOLCHR_HTML_VARIABLE,
+ STR_POOLCHR_HTML_DEFINSTANCE,
+ STR_POOLCHR_HTML_TELETYPE
+};
+
+const TranslateId STR_POOLFRM_ARY[] =
+{
+ STR_POOLFRM_FRAME,
+ STR_POOLFRM_GRAPHIC,
+ STR_POOLFRM_OLE,
+ STR_POOLFRM_FORMEL,
+ STR_POOLFRM_MARGINAL,
+ STR_POOLFRM_WATERSIGN,
+ STR_POOLFRM_LABEL
+};
+
+const TranslateId STR_POOLPAGE_ARY[] =
+{
+ // Page styles
+ STR_POOLPAGE_STANDARD,
+ STR_POOLPAGE_FIRST,
+ STR_POOLPAGE_LEFT,
+ STR_POOLPAGE_RIGHT,
+ STR_POOLPAGE_ENVELOPE,
+ STR_POOLPAGE_REGISTER,
+ STR_POOLPAGE_HTML,
+ STR_POOLPAGE_FOOTNOTE,
+ STR_POOLPAGE_ENDNOTE,
+ STR_POOLPAGE_LANDSCAPE
+};
+
+const TranslateId STR_POOLNUMRULE_NUM_ARY[] =
+{
+ // Numbering styles
+ STR_POOLNUMRULE_NOLIST,
+ STR_POOLNUMRULE_NUM1,
+ STR_POOLNUMRULE_NUM2,
+ STR_POOLNUMRULE_NUM3,
+ STR_POOLNUMRULE_NUM4,
+ STR_POOLNUMRULE_NUM5,
+ STR_POOLNUMRULE_BUL1,
+ STR_POOLNUMRULE_BUL2,
+ STR_POOLNUMRULE_BUL3,
+ STR_POOLNUMRULE_BUL4,
+ STR_POOLNUMRULE_BUL5
+};
+
+// XXX MUST match the entries of TableStyleProgNameTable in
+// sw/source/core/doc/SwStyleNameMapper.cxx and MUST match the order of
+// RES_POOL_TABLESTYLE_TYPE in sw/inc/poolfmt.hxx
+const TranslateId STR_TABSTYLE_ARY[] =
+{
+ // XXX MUST be in order, Writer first, then Svx old, then Svx new
+ // 1 Writer resource string
+ STR_TABSTYLE_DEFAULT,
+ // 16 old styles Svx resource strings
+ RID_SVXSTR_TBLAFMT_3D,
+ RID_SVXSTR_TBLAFMT_BLACK1,
+ RID_SVXSTR_TBLAFMT_BLACK2,
+ RID_SVXSTR_TBLAFMT_BLUE,
+ RID_SVXSTR_TBLAFMT_BROWN,
+ RID_SVXSTR_TBLAFMT_CURRENCY,
+ RID_SVXSTR_TBLAFMT_CURRENCY_3D,
+ RID_SVXSTR_TBLAFMT_CURRENCY_GRAY,
+ RID_SVXSTR_TBLAFMT_CURRENCY_LAVENDER,
+ RID_SVXSTR_TBLAFMT_CURRENCY_TURQUOISE,
+ RID_SVXSTR_TBLAFMT_GRAY,
+ RID_SVXSTR_TBLAFMT_GREEN,
+ RID_SVXSTR_TBLAFMT_LAVENDER,
+ RID_SVXSTR_TBLAFMT_RED,
+ RID_SVXSTR_TBLAFMT_TURQUOISE,
+ RID_SVXSTR_TBLAFMT_YELLOW,
+ // 10 new styles since LibreOffice 6.0 Svx resource strings
+ RID_SVXSTR_TBLAFMT_LO6_ACADEMIC,
+ RID_SVXSTR_TBLAFMT_LO6_BOX_LIST_BLUE,
+ RID_SVXSTR_TBLAFMT_LO6_BOX_LIST_GREEN,
+ RID_SVXSTR_TBLAFMT_LO6_BOX_LIST_RED,
+ RID_SVXSTR_TBLAFMT_LO6_BOX_LIST_YELLOW,
+ RID_SVXSTR_TBLAFMT_LO6_ELEGANT,
+ RID_SVXSTR_TBLAFMT_LO6_FINANCIAL,
+ RID_SVXSTR_TBLAFMT_LO6_SIMPLE_GRID_COLUMNS,
+ RID_SVXSTR_TBLAFMT_LO6_SIMPLE_GRID_ROWS,
+ RID_SVXSTR_TBLAFMT_LO6_SIMPLE_LIST_SHADED
+};
+
+namespace sw
+{
+
+DocumentStylePoolManager::DocumentStylePoolManager( SwDoc& i_rSwdoc ) : m_rDoc( i_rSwdoc )
+{
+}
+
+SwTextFormatColl* DocumentStylePoolManager::GetTextCollFromPool( sal_uInt16 nId, bool bRegardLanguage )
+{
+ OSL_ENSURE(
+ (RES_POOLCOLL_TEXT_BEGIN <= nId && nId < RES_POOLCOLL_TEXT_END) ||
+ (RES_POOLCOLL_LISTS_BEGIN <= nId && nId < RES_POOLCOLL_LISTS_END) ||
+ (RES_POOLCOLL_EXTRA_BEGIN <= nId && nId < RES_POOLCOLL_EXTRA_END) ||
+ (RES_POOLCOLL_REGISTER_BEGIN <= nId && nId < RES_POOLCOLL_REGISTER_END) ||
+ (RES_POOLCOLL_DOC_BEGIN <= nId && nId < RES_POOLCOLL_DOC_END) ||
+ (RES_POOLCOLL_HTML_BEGIN <= nId && nId < RES_POOLCOLL_HTML_END),
+ "Wrong AutoFormat Id" );
+
+ SwTextFormatColl* pNewColl;
+ sal_uInt16 nOutLvlBits = 0;
+
+ for (size_t n = 0, nSize = m_rDoc.GetTextFormatColls()->size(); n < nSize; ++n)
+ {
+ pNewColl = (*m_rDoc.GetTextFormatColls())[ n ];
+ if( nId == pNewColl->GetPoolFormatId() )
+ {
+ // in online we can have multiple languages, use translated name
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ OUString aName;
+ SwStyleNameMapper::GetUIName(nId, aName);
+ if (!aName.isEmpty())
+ pNewColl->SetFormatName(aName);
+ }
+
+ return pNewColl;
+ }
+
+ if( pNewColl->IsAssignedToListLevelOfOutlineStyle())
+ nOutLvlBits |= ( 1 << pNewColl->GetAssignedOutlineStyleLevel() );
+ }
+
+ // Didn't find it until here -> create anew
+ TranslateId pResId;
+ if (RES_POOLCOLL_TEXT_BEGIN <= nId && nId < RES_POOLCOLL_TEXT_END)
+ {
+ static_assert(SAL_N_ELEMENTS(STR_POOLCOLL_TEXT_ARY) == RES_POOLCOLL_TEXT_END - RES_POOLCOLL_TEXT_BEGIN, "### unexpected size!");
+ pResId = STR_POOLCOLL_TEXT_ARY[nId - RES_POOLCOLL_TEXT_BEGIN];
+ }
+ else if (RES_POOLCOLL_LISTS_BEGIN <= nId && nId < RES_POOLCOLL_LISTS_END)
+ {
+ static_assert(SAL_N_ELEMENTS(STR_POOLCOLL_LISTS_ARY) == RES_POOLCOLL_LISTS_END - RES_POOLCOLL_LISTS_BEGIN, "### unexpected size!");
+ pResId = STR_POOLCOLL_LISTS_ARY[nId - RES_POOLCOLL_LISTS_BEGIN];
+ }
+ else if (RES_POOLCOLL_EXTRA_BEGIN <= nId && nId < RES_POOLCOLL_EXTRA_END)
+ {
+ static_assert(SAL_N_ELEMENTS(STR_POOLCOLL_EXTRA_ARY) == RES_POOLCOLL_EXTRA_END - RES_POOLCOLL_EXTRA_BEGIN, "### unexpected size!");
+ pResId = STR_POOLCOLL_EXTRA_ARY[nId - RES_POOLCOLL_EXTRA_BEGIN];
+ }
+ else if (RES_POOLCOLL_REGISTER_BEGIN <= nId && nId < RES_POOLCOLL_REGISTER_END)
+ {
+ static_assert(SAL_N_ELEMENTS(STR_POOLCOLL_REGISTER_ARY) == RES_POOLCOLL_REGISTER_END - RES_POOLCOLL_REGISTER_BEGIN, "### unexpected size!");
+ pResId = STR_POOLCOLL_REGISTER_ARY[nId - RES_POOLCOLL_REGISTER_BEGIN];
+ }
+ else if (RES_POOLCOLL_DOC_BEGIN <= nId && nId < RES_POOLCOLL_DOC_END)
+ {
+ static_assert(SAL_N_ELEMENTS(STR_POOLCOLL_DOC_ARY) == RES_POOLCOLL_DOC_END - RES_POOLCOLL_DOC_BEGIN, "### unexpected size!");
+ pResId = STR_POOLCOLL_DOC_ARY[nId - RES_POOLCOLL_DOC_BEGIN];
+ }
+ else if (RES_POOLCOLL_HTML_BEGIN <= nId && nId < RES_POOLCOLL_HTML_END)
+ {
+ static_assert(SAL_N_ELEMENTS(STR_POOLCOLL_HTML_ARY) == RES_POOLCOLL_HTML_END - RES_POOLCOLL_HTML_BEGIN, "### unexpected size!");
+ pResId = STR_POOLCOLL_HTML_ARY[nId - RES_POOLCOLL_HTML_BEGIN];
+ }
+
+ OSL_ENSURE(pResId, "Invalid Pool ID");
+ if (!pResId)
+ return GetTextCollFromPool(RES_POOLCOLL_STANDARD);
+
+ OUString aNm(SwResId(pResId));
+
+ // A Set for all to-be-set Attributes
+ SwAttrSet aSet( m_rDoc.GetAttrPool(), aTextFormatCollSetRange );
+ sal_uInt16 nParent = GetPoolParent( nId );
+
+ {
+
+ if(::IsConditionalByPoolId( nId ))
+ pNewColl = new SwConditionTextFormatColl( m_rDoc.GetAttrPool(), aNm, !nParent
+ ? m_rDoc.GetDfltTextFormatColl()
+ : GetTextCollFromPool( nParent ));
+ else
+ pNewColl = new SwTextFormatColl( m_rDoc.GetAttrPool(), aNm, !nParent
+ ? m_rDoc.GetDfltTextFormatColl()
+ : GetTextCollFromPool( nParent ));
+ pNewColl->SetPoolFormatId( nId );
+ m_rDoc.GetTextFormatColls()->push_back( pNewColl );
+ }
+
+ bool bNoDefault = m_rDoc.GetDocumentSettingManager().get( DocumentSettingId::STYLES_NODEFAULT );
+ if ( !bNoDefault )
+ {
+ switch( nId )
+ {
+ // General content forms
+ case RES_POOLCOLL_STANDARD:
+ /* koreans do not like SvxScriptItem(TRUE) */
+ if (bRegardLanguage)
+ {
+ LanguageType nAppLanguage = GetAppLanguage();
+ if (GetDefaultFrameDirection(nAppLanguage) ==
+ SvxFrameDirection::Horizontal_RL_TB)
+ {
+ SvxAdjustItem aAdjust(SvxAdjust::Right, RES_PARATR_ADJUST );
+ aSet.Put(aAdjust);
+ }
+ if (nAppLanguage == LANGUAGE_KOREAN)
+ {
+ SvxScriptSpaceItem aScriptSpace(false, RES_PARATR_SCRIPTSPACE);
+ aSet.Put(aScriptSpace);
+ }
+ }
+ break;
+
+ case RES_POOLCOLL_TEXT: // Text body
+ {
+ SvxLineSpacingItem aLSpc( LINE_SPACE_DEFAULT_HEIGHT, RES_PARATR_LINESPACING );
+ SvxULSpaceItem aUL( 0, PT_7, RES_UL_SPACE );
+ aLSpc.SetPropLineSpace( 115 );
+ if( m_rDoc.GetDocumentSettingManager().get(DocumentSettingId::HTML_MODE) ) aUL.SetLower( HTML_PARSPACE );
+ aSet.Put( aUL );
+ aSet.Put( aLSpc );
+ }
+ break;
+ case RES_POOLCOLL_TEXT_IDENT: // Text body indentation
+ {
+ auto const first(o3tl::convert(5, o3tl::Length::mm, o3tl::Length::twip));
+ SvxFirstLineIndentItem const firstLine(first, RES_MARGIN_FIRSTLINE);
+ SvxTextLeftMarginItem const leftMargin(0, RES_MARGIN_TEXTLEFT);
+ aSet.Put(firstLine);
+ aSet.Put(leftMargin);
+ }
+ break;
+ case RES_POOLCOLL_TEXT_NEGIDENT: // Text body neg. indentation
+ {
+ auto const first(-o3tl::convert(5, o3tl::Length::mm, o3tl::Length::twip));
+ auto const left(o3tl::convert(1, o3tl::Length::cm, o3tl::Length::twip));
+ SvxFirstLineIndentItem const firstLine(first, RES_MARGIN_FIRSTLINE);
+ SvxTextLeftMarginItem const leftMargin(left, RES_MARGIN_TEXTLEFT);
+ aSet.Put(firstLine);
+ aSet.Put(leftMargin);
+
+ SvxTabStopItem aTStops(RES_PARATR_TABSTOP);
+ aTStops.Insert( SvxTabStop( 0 ));
+ aSet.Put( aTStops );
+ }
+ break;
+ case RES_POOLCOLL_TEXT_MOVE: // Text body move
+ {
+ auto const left(o3tl::convert(5, o3tl::Length::mm, o3tl::Length::twip));
+ SvxFirstLineIndentItem const firstLine(0, RES_MARGIN_FIRSTLINE);
+ SvxTextLeftMarginItem const leftMargin(left, RES_MARGIN_TEXTLEFT);
+ aSet.Put(firstLine);
+ aSet.Put(leftMargin);
+ }
+ break;
+
+ case RES_POOLCOLL_CONFRONTATION: // Text body confrontation
+ {
+ auto const first(-o3tl::convert(45, o3tl::Length::mm, o3tl::Length::twip));
+ auto const left(o3tl::convert(5, o3tl::Length::cm, o3tl::Length::twip));
+ SvxFirstLineIndentItem const firstLine(first, RES_MARGIN_FIRSTLINE);
+ SvxTextLeftMarginItem const leftMargin(left, RES_MARGIN_TEXTLEFT);
+ aSet.Put(firstLine);
+ aSet.Put(leftMargin);
+
+ SvxTabStopItem aTStops( RES_PARATR_TABSTOP );
+ aTStops.Insert( SvxTabStop( 0 ));
+ aSet.Put( aTStops );
+ }
+ break;
+ case RES_POOLCOLL_MARGINAL: // Text body marginal
+ {
+ auto const left(o3tl::convert(4, o3tl::Length::cm, o3tl::Length::twip));
+ SvxFirstLineIndentItem const firstLine(0, RES_MARGIN_FIRSTLINE);
+ SvxTextLeftMarginItem const leftMargin(left, RES_MARGIN_TEXTLEFT);
+ aSet.Put(firstLine);
+ aSet.Put(leftMargin);
+ }
+ break;
+
+ case RES_POOLCOLL_HEADLINE_BASE: // Base headline
+ {
+ static const sal_uInt16 aFontWhich[] =
+ { RES_CHRATR_FONT,
+ RES_CHRATR_CJK_FONT,
+ RES_CHRATR_CTL_FONT
+ };
+ static const sal_uInt16 aLangTypes[] =
+ {
+ RES_CHRATR_LANGUAGE,
+ RES_CHRATR_CJK_LANGUAGE,
+ RES_CHRATR_CTL_LANGUAGE
+ };
+ static const LanguageType aLangs[] =
+ {
+ LANGUAGE_ENGLISH_US,
+ LANGUAGE_ENGLISH_US,
+ LANGUAGE_ARABIC_SAUDI_ARABIA
+ };
+ static const DefaultFontType nFontTypes[] =
+ {
+ DefaultFontType::LATIN_HEADING,
+ DefaultFontType::CJK_HEADING,
+ DefaultFontType::CTL_HEADING
+ };
+
+ for( int i = 0; i < 3; ++i )
+ {
+ LanguageType nLng = static_cast<const SvxLanguageItem&>(m_rDoc.GetDefault( aLangTypes[i] )).GetLanguage();
+ if( LANGUAGE_DONTKNOW == nLng )
+ nLng = aLangs[i];
+
+ vcl::Font aFnt( OutputDevice::GetDefaultFont( nFontTypes[i],
+ nLng, GetDefaultFontFlags::OnlyOne ) );
+
+ aSet.Put( SvxFontItem( aFnt.GetFamilyType(), aFnt.GetFamilyName(),
+ OUString(), aFnt.GetPitch(),
+ aFnt.GetCharSet(), aFontWhich[i] ));
+ }
+
+ SvxFontHeightItem aFntSize( PT_14, 100, RES_CHRATR_FONTSIZE );
+ SvxULSpaceItem aUL( PT_12, PT_6, RES_UL_SPACE );
+ if( m_rDoc.GetDocumentSettingManager().get(DocumentSettingId::HTML_MODE) )
+ aUL.SetLower( HTML_PARSPACE );
+ aSet.Put( SvxFormatKeepItem( true, RES_KEEP ));
+
+ pNewColl->SetNextTextFormatColl( *GetTextCollFromPool( RES_POOLCOLL_TEXT ));
+
+ aSet.Put( aUL );
+ SetAllScriptItem( aSet, aFntSize );
+ }
+ break;
+
+ case RES_POOLCOLL_NUMBER_BULLET_BASE: // Base Numbering
+ break;
+
+ case RES_POOLCOLL_GREETING: // Greeting
+ case RES_POOLCOLL_REGISTER_BASE: // Base indexes
+ case RES_POOLCOLL_SIGNATURE: // Signatures
+ case RES_POOLCOLL_TABLE: // Table content
+ {
+ SwFormatLineNumber aLN;
+ aLN.SetCountLines( false );
+ aSet.Put( aLN );
+ if (nId == RES_POOLCOLL_TABLE)
+ {
+ aSet.Put( SvxWidowsItem( 0, RES_PARATR_WIDOWS ) );
+ aSet.Put( SvxOrphansItem( 0, RES_PARATR_ORPHANS ) );
+ }
+ }
+ break;
+
+ case RES_POOLCOLL_HEADLINE1: // Heading 1
+ {
+ SvxULSpaceItem aUL( PT_12, PT_6, RES_UL_SPACE );
+ aSet.Put( aUL );
+ lcl_SetHeadline( m_rDoc, pNewColl, aSet, nOutLvlBits, 0, false );
+ }
+ break;
+ case RES_POOLCOLL_HEADLINE2: // Heading 2
+ {
+ SvxULSpaceItem aUL( PT_10, PT_6, RES_UL_SPACE );
+ aSet.Put( aUL );
+ lcl_SetHeadline( m_rDoc, pNewColl, aSet, nOutLvlBits, 1, false );
+ }
+ break;
+ case RES_POOLCOLL_HEADLINE3: // Heading 3
+ {
+ SvxULSpaceItem aUL( PT_7, PT_6, RES_UL_SPACE );
+ aSet.Put( aUL );
+ lcl_SetHeadline( m_rDoc, pNewColl, aSet, nOutLvlBits, 2, false );
+ }
+ break;
+ case RES_POOLCOLL_HEADLINE4: // Heading 4
+ {
+ SvxULSpaceItem aUL( PT_6, PT_6, RES_UL_SPACE );
+ aSet.Put( aUL );
+ lcl_SetHeadline( m_rDoc, pNewColl, aSet, nOutLvlBits, 3, true );
+ }
+ break;
+ case RES_POOLCOLL_HEADLINE5: // Heading 5
+ {
+ SvxULSpaceItem aUL( PT_6, PT_3, RES_UL_SPACE );
+ aSet.Put( aUL );
+ lcl_SetHeadline( m_rDoc, pNewColl, aSet, nOutLvlBits, 4, false );
+ }
+ break;
+ case RES_POOLCOLL_HEADLINE6: // Heading 6
+ {
+ SvxULSpaceItem aUL( PT_3, PT_3, RES_UL_SPACE );
+ aSet.Put( aUL );
+ lcl_SetHeadline( m_rDoc, pNewColl, aSet, nOutLvlBits, 5, true );
+ }
+ break;
+ case RES_POOLCOLL_HEADLINE7: // Heading 7
+ {
+ SvxULSpaceItem aUL( PT_3, PT_3, RES_UL_SPACE );
+ aSet.Put( aUL );
+ lcl_SetHeadline( m_rDoc, pNewColl, aSet, nOutLvlBits, 6, false );
+ }
+ break;
+ case RES_POOLCOLL_HEADLINE8: // Heading 8
+ {
+ SvxULSpaceItem aUL( PT_3, PT_3, RES_UL_SPACE );
+ aSet.Put( aUL );
+ lcl_SetHeadline( m_rDoc, pNewColl, aSet, nOutLvlBits, 7, true );
+ }
+ break;
+ case RES_POOLCOLL_HEADLINE9: // Heading 9
+ {
+ SvxULSpaceItem aUL( PT_3, PT_3, RES_UL_SPACE );
+ aSet.Put( aUL );
+ lcl_SetHeadline( m_rDoc, pNewColl, aSet, nOutLvlBits, 8, false );
+ }
+ break;
+ case RES_POOLCOLL_HEADLINE10: // Heading 10
+ {
+ SvxULSpaceItem aUL( PT_3, PT_3, RES_UL_SPACE );
+ aSet.Put( aUL );
+ lcl_SetHeadline( m_rDoc, pNewColl, aSet, nOutLvlBits, 9, false );
+ }
+ break;
+
+ // Special sections:
+ // Header
+ case RES_POOLCOLL_HEADERFOOTER:
+ case RES_POOLCOLL_HEADER:
+ case RES_POOLCOLL_HEADERL:
+ case RES_POOLCOLL_HEADERR:
+ // Footer
+ case RES_POOLCOLL_FOOTER:
+ case RES_POOLCOLL_FOOTERL:
+ case RES_POOLCOLL_FOOTERR:
+ {
+ SwFormatLineNumber aLN;
+ aLN.SetCountLines( false );
+ aSet.Put( aLN );
+
+ tools::Long nRightMargin = lcl_GetRightMargin( m_rDoc );
+
+ SvxTabStopItem aTStops( 0, 0, SvxTabAdjust::Default, RES_PARATR_TABSTOP );
+ aTStops.Insert( SvxTabStop( nRightMargin / 2, SvxTabAdjust::Center ) );
+ aTStops.Insert( SvxTabStop( nRightMargin, SvxTabAdjust::Right ) );
+
+ aSet.Put( aTStops );
+
+ if ( (nId==RES_POOLCOLL_HEADERR) || (nId==RES_POOLCOLL_FOOTERR) ) {
+ SvxAdjustItem aAdjust(SvxAdjust::Right, RES_PARATR_ADJUST );
+ aSet.Put(aAdjust);
+ }
+ }
+ break;
+
+ case RES_POOLCOLL_TABLE_HDLN:
+ {
+ SetAllScriptItem( aSet, SvxWeightItem( WEIGHT_BOLD, RES_CHRATR_WEIGHT ) );
+ aSet.Put( SvxAdjustItem( SvxAdjust::Center, RES_PARATR_ADJUST ) );
+ SwFormatLineNumber aLN;
+ aLN.SetCountLines( false );
+ aSet.Put( aLN );
+ }
+ break;
+
+ case RES_POOLCOLL_FOOTNOTE: // paragraph style Footnote
+ case RES_POOLCOLL_ENDNOTE: // paragraph style Endnote
+ {
+ auto const first(-o3tl::convert(6, o3tl::Length::mm, o3tl::Length::twip));
+ auto const left(o3tl::convert(6, o3tl::Length::mm, o3tl::Length::twip));
+ SvxFirstLineIndentItem const firstLine(first, RES_MARGIN_FIRSTLINE);
+ SvxTextLeftMarginItem const leftMargin(left, RES_MARGIN_TEXTLEFT);
+ aSet.Put(firstLine);
+ aSet.Put(leftMargin);
+
+ SetAllScriptItem( aSet, SvxFontHeightItem( PT_10, 100, RES_CHRATR_FONTSIZE ) );
+ SwFormatLineNumber aLN;
+ aLN.SetCountLines( false );
+ aSet.Put( aLN );
+ }
+ break;
+
+ case RES_POOLCOLL_LABEL: // basic caption
+ {
+ SvxULSpaceItem aUL( RES_UL_SPACE );
+ aUL.SetUpper( PT_6 );
+ aUL.SetLower( PT_6 );
+ aSet.Put( aUL );
+ SetAllScriptItem( aSet, SvxPostureItem( ITALIC_NORMAL, RES_CHRATR_POSTURE ) );
+ SetAllScriptItem( aSet, SvxFontHeightItem( PT_10, 100, RES_CHRATR_FONTSIZE ) );
+ SwFormatLineNumber aLN;
+ aLN.SetCountLines( false );
+ aSet.Put( aLN );
+ }
+ break;
+
+ case RES_POOLCOLL_FRAME: // Frame content
+ case RES_POOLCOLL_LABEL_ABB: // caption image
+ case RES_POOLCOLL_LABEL_TABLE: // caption table
+ case RES_POOLCOLL_LABEL_FRAME: // caption frame
+ case RES_POOLCOLL_LABEL_DRAWING: // caption drawing
+ case RES_POOLCOLL_LABEL_FIGURE:
+ break;
+
+ case RES_POOLCOLL_ENVELOPE_ADDRESS: // envelope address
+ {
+ SvxULSpaceItem aUL( RES_UL_SPACE );
+ aUL.SetLower( PT_3 );
+ aSet.Put( aUL );
+ SwFormatLineNumber aLN;
+ aLN.SetCountLines( false );
+ aSet.Put( aLN );
+ }
+ break;
+
+ case RES_POOLCOLL_SEND_ADDRESS: // Sender address
+ {
+ if( m_rDoc.GetDocumentSettingManager().get(DocumentSettingId::HTML_MODE) )
+ SetAllScriptItem( aSet, SvxPostureItem(ITALIC_NORMAL, RES_CHRATR_POSTURE) );
+ else
+ {
+ SvxULSpaceItem aUL( RES_UL_SPACE ); aUL.SetLower( PT_3 );
+ aSet.Put( aUL );
+ }
+ SwFormatLineNumber aLN;
+ aLN.SetCountLines( false );
+ aSet.Put( aLN );
+ }
+ break;
+
+ case RES_POOLCOLL_COMMENT: // Comment
+ {
+ SetAllScriptItem(aSet, SvxFontHeightItem(PT_10, 100, RES_CHRATR_FONTSIZE));
+ }
+ break;
+
+ // User defined indexes:
+ case RES_POOLCOLL_TOX_USERH: // Header
+ lcl_SetRegister( m_rDoc, aSet, 0, true, false );
+ {
+ SwFormatLineNumber aLN;
+ aLN.SetCountLines( false );
+ aSet.Put( aLN );
+ }
+ break;
+ case RES_POOLCOLL_TOX_USER1: // 1st level
+ lcl_SetRegister( m_rDoc, aSet, 0, false, true );
+ break;
+ case RES_POOLCOLL_TOX_USER2: // 2nd level
+ lcl_SetRegister( m_rDoc, aSet, 1, false, true );
+ break;
+ case RES_POOLCOLL_TOX_USER3: // 3rd level
+ lcl_SetRegister( m_rDoc, aSet, 2, false, true );
+ break;
+ case RES_POOLCOLL_TOX_USER4: // 4th level
+ lcl_SetRegister( m_rDoc, aSet, 3, false, true );
+ break;
+ case RES_POOLCOLL_TOX_USER5: // 5th level
+ lcl_SetRegister( m_rDoc, aSet, 4, false, true );
+ break;
+ case RES_POOLCOLL_TOX_USER6: // 6th level
+ lcl_SetRegister( m_rDoc, aSet, 5, false, true );
+ break;
+ case RES_POOLCOLL_TOX_USER7: // 7th level
+ lcl_SetRegister( m_rDoc, aSet, 6, false, true );
+ break;
+ case RES_POOLCOLL_TOX_USER8: // 8th level
+ lcl_SetRegister( m_rDoc, aSet, 7, false, true );
+ break;
+ case RES_POOLCOLL_TOX_USER9: // 9th level
+ lcl_SetRegister( m_rDoc, aSet, 8, false, true );
+ break;
+ case RES_POOLCOLL_TOX_USER10: // 10th level
+ lcl_SetRegister( m_rDoc, aSet, 9, false, true );
+ break;
+
+ // Index
+ case RES_POOLCOLL_TOX_IDXH: // Header
+ lcl_SetRegister( m_rDoc, aSet, 0, true, false );
+ {
+ SwFormatLineNumber aLN;
+ aLN.SetCountLines( false );
+ aSet.Put( aLN );
+ }
+ break;
+ case RES_POOLCOLL_TOX_IDX1: // 1st level
+ lcl_SetRegister( m_rDoc, aSet, 0, false, false );
+ break;
+ case RES_POOLCOLL_TOX_IDX2: // 2nd level
+ lcl_SetRegister( m_rDoc, aSet, 1, false, false );
+ break;
+ case RES_POOLCOLL_TOX_IDX3: // 3rd level
+ lcl_SetRegister( m_rDoc, aSet, 2, false, false );
+ break;
+ case RES_POOLCOLL_TOX_IDXBREAK: // Separator
+ lcl_SetRegister( m_rDoc, aSet, 0, false, false );
+ break;
+
+ // Table of Content
+ case RES_POOLCOLL_TOX_CNTNTH: // Header
+ lcl_SetRegister( m_rDoc, aSet, 0, true, false );
+ {
+ SwFormatLineNumber aLN;
+ aLN.SetCountLines( false );
+ aSet.Put( aLN );
+ }
+ break;
+ case RES_POOLCOLL_TOX_CNTNT1: // 1st level
+ lcl_SetRegister( m_rDoc, aSet, 0, false, true );
+ break;
+ case RES_POOLCOLL_TOX_CNTNT2: // 2nd level
+ lcl_SetRegister( m_rDoc, aSet, 1, false, true );
+ break;
+ case RES_POOLCOLL_TOX_CNTNT3: // 3rd level
+ lcl_SetRegister( m_rDoc, aSet, 2, false, true );
+ break;
+ case RES_POOLCOLL_TOX_CNTNT4: // 4th level
+ lcl_SetRegister( m_rDoc, aSet, 3, false, true );
+ break;
+ case RES_POOLCOLL_TOX_CNTNT5: // 5th level
+ lcl_SetRegister( m_rDoc, aSet, 4, false, true );
+ break;
+ case RES_POOLCOLL_TOX_CNTNT6: // 6th level
+ lcl_SetRegister( m_rDoc, aSet, 5, false, true );
+ break;
+ case RES_POOLCOLL_TOX_CNTNT7: // 7th level
+ lcl_SetRegister( m_rDoc, aSet, 6, false, true );
+ break;
+ case RES_POOLCOLL_TOX_CNTNT8: // 8th level
+ lcl_SetRegister( m_rDoc, aSet, 7, false, true );
+ break;
+ case RES_POOLCOLL_TOX_CNTNT9: // 9th level
+ lcl_SetRegister( m_rDoc, aSet, 8, false, true );
+ break;
+ case RES_POOLCOLL_TOX_CNTNT10: // 10th level
+ lcl_SetRegister( m_rDoc, aSet, 9, false, true );
+ break;
+
+ case RES_POOLCOLL_TOX_ILLUSH:
+ case RES_POOLCOLL_TOX_OBJECTH:
+ case RES_POOLCOLL_TOX_TABLESH:
+ case RES_POOLCOLL_TOX_AUTHORITIESH:
+ lcl_SetRegister( m_rDoc, aSet, 0, true, false );
+ {
+ SwFormatLineNumber aLN;
+ aLN.SetCountLines( false );
+ aSet.Put( aLN );
+ }
+ break;
+ case RES_POOLCOLL_TOX_ILLUS1:
+ case RES_POOLCOLL_TOX_OBJECT1:
+ case RES_POOLCOLL_TOX_TABLES1:
+ case RES_POOLCOLL_TOX_AUTHORITIES1:
+ lcl_SetRegister( m_rDoc, aSet, 0, false, true );
+ break;
+
+ case RES_POOLCOLL_NUM_LEVEL1S:
+ lcl_SetNumBul( m_rDoc, pNewColl, aSet, RES_POOLCOLL_NUM_LEVEL1,
+ lNumberFirstLineOffset, SwNumRule::GetNumIndent( 0 ),
+ PT_12, PT_6 );
+ break;
+ case RES_POOLCOLL_NUM_LEVEL1:
+ lcl_SetNumBul( m_rDoc, pNewColl, aSet, RES_POOLCOLL_NUM_LEVEL1,
+ lNumberFirstLineOffset, SwNumRule::GetNumIndent( 0 ),
+ 0, PT_6 );
+ break;
+ case RES_POOLCOLL_NUM_LEVEL1E:
+ lcl_SetNumBul( m_rDoc, pNewColl, aSet, RES_POOLCOLL_NUM_LEVEL1,
+ lNumberFirstLineOffset, SwNumRule::GetNumIndent( 0 ),
+ 0, PT_12 );
+ break;
+ case RES_POOLCOLL_NUM_NONUM1:
+ lcl_SetNumBul( m_rDoc, pNewColl, aSet, RES_POOLCOLL_NUM_NONUM1,
+ 0, SwNumRule::GetNumIndent( 0 ), 0, PT_6 );
+ break;
+ case RES_POOLCOLL_NUM_LEVEL2S:
+ lcl_SetNumBul( m_rDoc, pNewColl, aSet, RES_POOLCOLL_NUM_LEVEL2,
+ lNumberFirstLineOffset, SwNumRule::GetNumIndent( 1 ),
+ PT_12, PT_6 );
+ break;
+ case RES_POOLCOLL_NUM_LEVEL2:
+ lcl_SetNumBul( m_rDoc, pNewColl, aSet, RES_POOLCOLL_NUM_LEVEL2,
+ lNumberFirstLineOffset, SwNumRule::GetNumIndent( 1 ),
+ 0, PT_6 );
+ break;
+ case RES_POOLCOLL_NUM_LEVEL2E:
+ lcl_SetNumBul( m_rDoc, pNewColl, aSet, RES_POOLCOLL_NUM_LEVEL2,
+ lNumberFirstLineOffset, SwNumRule::GetNumIndent( 1 ),
+ 0, PT_12 );
+ break;
+ case RES_POOLCOLL_NUM_NONUM2:
+ lcl_SetNumBul( m_rDoc, pNewColl, aSet, RES_POOLCOLL_NUM_NONUM2,
+ 0, SwNumRule::GetNumIndent( 1 ), 0, PT_6 );
+ break;
+ case RES_POOLCOLL_NUM_LEVEL3S:
+ lcl_SetNumBul( m_rDoc, pNewColl, aSet, RES_POOLCOLL_NUM_LEVEL3,
+ lNumberFirstLineOffset, SwNumRule::GetNumIndent( 2 ),
+ PT_12, PT_6 );
+ break;
+ case RES_POOLCOLL_NUM_LEVEL3:
+ lcl_SetNumBul( m_rDoc, pNewColl, aSet, RES_POOLCOLL_NUM_LEVEL3,
+ lNumberFirstLineOffset, SwNumRule::GetNumIndent( 2 ),
+ 0, PT_6 );
+ break;
+ case RES_POOLCOLL_NUM_LEVEL3E:
+ lcl_SetNumBul( m_rDoc, pNewColl, aSet, RES_POOLCOLL_NUM_LEVEL3,
+ lNumberFirstLineOffset, SwNumRule::GetNumIndent( 2 ),
+ 0, PT_12 );
+ break;
+ case RES_POOLCOLL_NUM_NONUM3:
+ lcl_SetNumBul( m_rDoc, pNewColl, aSet, RES_POOLCOLL_NUM_NONUM3,
+ 0, SwNumRule::GetNumIndent( 2 ), 0, PT_6 );
+ break;
+ case RES_POOLCOLL_NUM_LEVEL4S:
+ lcl_SetNumBul( m_rDoc, pNewColl, aSet, RES_POOLCOLL_NUM_LEVEL4,
+ lNumberFirstLineOffset, SwNumRule::GetNumIndent( 3 ),
+ PT_12, PT_6 );
+ break;
+ case RES_POOLCOLL_NUM_LEVEL4:
+ lcl_SetNumBul( m_rDoc, pNewColl, aSet, RES_POOLCOLL_NUM_LEVEL4,
+ lNumberFirstLineOffset, SwNumRule::GetNumIndent( 3 ),
+ 0, PT_6 );
+ break;
+ case RES_POOLCOLL_NUM_LEVEL4E:
+ lcl_SetNumBul( m_rDoc, pNewColl, aSet, RES_POOLCOLL_NUM_LEVEL4,
+ lNumberFirstLineOffset, SwNumRule::GetNumIndent( 3 ),
+ 0, PT_12 );
+ break;
+ case RES_POOLCOLL_NUM_NONUM4:
+ lcl_SetNumBul( m_rDoc, pNewColl, aSet, RES_POOLCOLL_NUM_NONUM4,
+ 0, SwNumRule::GetNumIndent( 3 ), 0, PT_6 );
+ break;
+ case RES_POOLCOLL_NUM_LEVEL5S:
+ lcl_SetNumBul( m_rDoc, pNewColl, aSet, RES_POOLCOLL_NUM_LEVEL5,
+ lNumberFirstLineOffset, SwNumRule::GetNumIndent( 4 ),
+ PT_12, PT_6 );
+ break;
+ case RES_POOLCOLL_NUM_LEVEL5:
+ lcl_SetNumBul( m_rDoc, pNewColl, aSet, RES_POOLCOLL_NUM_LEVEL5,
+ lNumberFirstLineOffset, SwNumRule::GetNumIndent( 4 ),
+ 0, PT_6 );
+ break;
+ case RES_POOLCOLL_NUM_LEVEL5E:
+ lcl_SetNumBul( m_rDoc, pNewColl, aSet, RES_POOLCOLL_NUM_LEVEL5,
+ lNumberFirstLineOffset, SwNumRule::GetNumIndent( 4 ),
+ 0, PT_12 );
+ break;
+ case RES_POOLCOLL_NUM_NONUM5:
+ lcl_SetNumBul( m_rDoc, pNewColl, aSet, RES_POOLCOLL_NUM_NONUM5,
+ 0, SwNumRule::GetNumIndent( 4 ), 0, PT_6 );
+ break;
+
+ case RES_POOLCOLL_BULLET_LEVEL1S:
+ lcl_SetNumBul( m_rDoc, pNewColl, aSet, RES_POOLCOLL_BULLET_LEVEL1,
+ lBulletFirstLineOffset, SwNumRule::GetBullIndent( 0 ),
+ PT_12, PT_6 );
+ break;
+ case RES_POOLCOLL_BULLET_LEVEL1:
+ lcl_SetNumBul( m_rDoc, pNewColl, aSet, RES_POOLCOLL_BULLET_LEVEL1,
+ lBulletFirstLineOffset, SwNumRule::GetBullIndent( 0 ),
+ 0, PT_6 );
+ break;
+ case RES_POOLCOLL_BULLET_LEVEL1E:
+ lcl_SetNumBul( m_rDoc, pNewColl, aSet, RES_POOLCOLL_BULLET_LEVEL1,
+ lBulletFirstLineOffset, SwNumRule::GetBullIndent( 0 ),
+ 0, PT_12 );
+ break;
+ case RES_POOLCOLL_BULLET_NONUM1:
+ lcl_SetNumBul( m_rDoc, pNewColl, aSet, RES_POOLCOLL_BULLET_NONUM1,
+ 0, SwNumRule::GetBullIndent( 0 ), 0, PT_6 );
+ break;
+ case RES_POOLCOLL_BULLET_LEVEL2S:
+ lcl_SetNumBul( m_rDoc, pNewColl, aSet, RES_POOLCOLL_BULLET_LEVEL2,
+ lBulletFirstLineOffset, SwNumRule::GetBullIndent( 1 ),
+ PT_12, PT_6 );
+ break;
+ case RES_POOLCOLL_BULLET_LEVEL2:
+ lcl_SetNumBul( m_rDoc, pNewColl, aSet, RES_POOLCOLL_BULLET_LEVEL2,
+ lBulletFirstLineOffset, SwNumRule::GetBullIndent( 1 ),
+ 0, PT_6 );
+ break;
+ case RES_POOLCOLL_BULLET_LEVEL2E:
+ lcl_SetNumBul( m_rDoc, pNewColl, aSet, RES_POOLCOLL_BULLET_LEVEL2,
+ lBulletFirstLineOffset, SwNumRule::GetBullIndent( 1 ),
+ 0, PT_12 );
+ break;
+ case RES_POOLCOLL_BULLET_NONUM2:
+ lcl_SetNumBul( m_rDoc, pNewColl, aSet, RES_POOLCOLL_BULLET_NONUM2,
+ 0, SwNumRule::GetBullIndent( 1 ), 0, PT_6 );
+ break;
+ case RES_POOLCOLL_BULLET_LEVEL3S:
+ lcl_SetNumBul( m_rDoc, pNewColl, aSet, RES_POOLCOLL_BULLET_LEVEL3,
+ lBulletFirstLineOffset, SwNumRule::GetBullIndent( 2 ),
+ PT_12, PT_6 );
+ break;
+ case RES_POOLCOLL_BULLET_LEVEL3:
+ lcl_SetNumBul( m_rDoc, pNewColl, aSet, RES_POOLCOLL_BULLET_LEVEL3,
+ lBulletFirstLineOffset, SwNumRule::GetBullIndent( 2 ),
+ 0, PT_6 );
+ break;
+ case RES_POOLCOLL_BULLET_LEVEL3E:
+ lcl_SetNumBul( m_rDoc, pNewColl, aSet, RES_POOLCOLL_BULLET_LEVEL3,
+ lBulletFirstLineOffset, SwNumRule::GetBullIndent( 2 ),
+ 0, PT_12 );
+ break;
+ case RES_POOLCOLL_BULLET_NONUM3:
+ lcl_SetNumBul( m_rDoc, pNewColl, aSet, RES_POOLCOLL_BULLET_NONUM3,
+ 0, SwNumRule::GetBullIndent( 2 ), 0, PT_6 );
+ break;
+ case RES_POOLCOLL_BULLET_LEVEL4S:
+ lcl_SetNumBul( m_rDoc, pNewColl, aSet, RES_POOLCOLL_BULLET_LEVEL4,
+ lBulletFirstLineOffset, SwNumRule::GetBullIndent( 3 ),
+ PT_12, PT_6 );
+ break;
+ case RES_POOLCOLL_BULLET_LEVEL4:
+ lcl_SetNumBul( m_rDoc, pNewColl, aSet, RES_POOLCOLL_BULLET_LEVEL4,
+ lBulletFirstLineOffset, SwNumRule::GetBullIndent( 3 ),
+ 0, PT_6 );
+ break;
+ case RES_POOLCOLL_BULLET_LEVEL4E:
+ lcl_SetNumBul( m_rDoc, pNewColl, aSet, RES_POOLCOLL_BULLET_LEVEL4,
+ lBulletFirstLineOffset, SwNumRule::GetBullIndent( 3 ),
+ 0, PT_12 );
+ break;
+ case RES_POOLCOLL_BULLET_NONUM4:
+ lcl_SetNumBul( m_rDoc, pNewColl, aSet, RES_POOLCOLL_BULLET_NONUM4,
+ 0, SwNumRule::GetBullIndent( 3 ), 0, PT_6 );
+ break;
+ case RES_POOLCOLL_BULLET_LEVEL5S:
+ lcl_SetNumBul( m_rDoc, pNewColl, aSet, RES_POOLCOLL_BULLET_LEVEL5,
+ lBulletFirstLineOffset, SwNumRule::GetBullIndent( 4 ),
+ PT_12, PT_6 );
+ break;
+ case RES_POOLCOLL_BULLET_LEVEL5:
+ lcl_SetNumBul( m_rDoc, pNewColl, aSet, RES_POOLCOLL_BULLET_LEVEL5,
+ lBulletFirstLineOffset, SwNumRule::GetBullIndent( 4 ),
+ 0, PT_6 );
+ break;
+ case RES_POOLCOLL_BULLET_LEVEL5E:
+ lcl_SetNumBul( m_rDoc, pNewColl, aSet, RES_POOLCOLL_BULLET_LEVEL5,
+ lBulletFirstLineOffset, SwNumRule::GetBullIndent( 4 ),
+ 0, PT_12 );
+ break;
+ case RES_POOLCOLL_BULLET_NONUM5:
+ lcl_SetNumBul( m_rDoc, pNewColl, aSet, RES_POOLCOLL_BULLET_NONUM5,
+ 0, SwNumRule::GetBullIndent( 4 ), 0, PT_6 );
+ break;
+
+ case RES_POOLCOLL_DOC_TITLE: // Document Title
+ {
+ SetAllScriptItem( aSet, SvxWeightItem( WEIGHT_BOLD, RES_CHRATR_WEIGHT ) );
+ SetAllScriptItem( aSet, SvxFontHeightItem( PT_28, 100, RES_CHRATR_FONTSIZE ) );
+
+ aSet.Put( SvxAdjustItem( SvxAdjust::Center, RES_PARATR_ADJUST ) );
+
+ pNewColl->SetNextTextFormatColl( *GetTextCollFromPool( RES_POOLCOLL_TEXT ));
+ }
+ break;
+
+ case RES_POOLCOLL_DOC_SUBTITLE: // Document subtitle
+ {
+ SvxULSpaceItem aUL( PT_3, PT_6, RES_UL_SPACE );
+ aSet.Put( aUL );
+ SetAllScriptItem( aSet, SvxFontHeightItem( PT_18, 100, RES_CHRATR_FONTSIZE ));
+
+ aSet.Put( SvxAdjustItem( SvxAdjust::Center, RES_PARATR_ADJUST ));
+
+ pNewColl->SetNextTextFormatColl( *GetTextCollFromPool( RES_POOLCOLL_TEXT ));
+ }
+ break;
+
+ case RES_POOLCOLL_DOC_APPENDIX: // Document Appendix tdf#114090
+ {
+ SetAllScriptItem( aSet, SvxWeightItem( WEIGHT_BOLD, RES_CHRATR_WEIGHT ) );
+ SetAllScriptItem( aSet, SvxFontHeightItem( PT_16, 100, RES_CHRATR_FONTSIZE ) );
+
+ aSet.Put( SvxAdjustItem( SvxAdjust::Center, RES_PARATR_ADJUST ) );
+
+ pNewColl->SetNextTextFormatColl( *GetTextCollFromPool( RES_POOLCOLL_TEXT ));
+ }
+ break;
+
+ case RES_POOLCOLL_HTML_BLOCKQUOTE:
+ {
+ auto const left(o3tl::convert(1, o3tl::Length::cm, o3tl::Length::twip));
+ auto const right(o3tl::convert(1, o3tl::Length::cm, o3tl::Length::twip));
+ SvxFirstLineIndentItem const firstLine(0, RES_MARGIN_FIRSTLINE);
+ SvxTextLeftMarginItem const leftMargin(left, RES_MARGIN_TEXTLEFT);
+ SvxRightMarginItem const rightMargin(right, RES_MARGIN_RIGHT);
+ aSet.Put(firstLine);
+ aSet.Put(leftMargin);
+ aSet.Put(rightMargin);
+
+ std::unique_ptr<SvxULSpaceItem> aUL(pNewColl->GetULSpace().Clone());
+ aUL->SetLower( HTML_PARSPACE );
+ aSet.Put(std::move(aUL));
+ }
+ break;
+
+ case RES_POOLCOLL_HTML_PRE:
+ {
+ ::lcl_SetDfltFont( DefaultFontType::FIXED, aSet );
+
+ // WORKAROUND: Set PRE to 10pt
+ SetAllScriptItem( aSet, SvxFontHeightItem(PT_10, 100, RES_CHRATR_FONTSIZE) );
+
+ // The lower paragraph distance is set explicitly (makes
+ // assigning hard attributes easier)
+ std::unique_ptr<SvxULSpaceItem> aULSpaceItem(pNewColl->GetULSpace().Clone());
+ aULSpaceItem->SetLower( 0 );
+ aSet.Put(std::move(aULSpaceItem));
+ }
+ break;
+
+ case RES_POOLCOLL_HTML_HR:
+ {
+ SvxBoxItem aBox( RES_BOX );
+ Color aColor( COL_GRAY );
+ SvxBorderLine aNew(&aColor, 3, SvxBorderLineStyle::DOUBLE);
+ aBox.SetLine( &aNew, SvxBoxItemLine::BOTTOM );
+
+ aSet.Put( aBox );
+ aSet.Put( SwParaConnectBorderItem( false ) );
+ SetAllScriptItem( aSet, SvxFontHeightItem(120, 100, RES_CHRATR_FONTSIZE) );
+
+ std::unique_ptr<SvxULSpaceItem> aUL;
+ {
+ pNewColl->SetNextTextFormatColl( *GetTextCollFromPool( RES_POOLCOLL_TEXT ));
+ aUL.reset(pNewColl->GetULSpace().Clone());
+ }
+ aUL->SetLower( HTML_PARSPACE );
+ aSet.Put(std::move(aUL));
+ SwFormatLineNumber aLN;
+ aLN.SetCountLines( false );
+ aSet.Put( aLN );
+ }
+ break;
+
+ case RES_POOLCOLL_HTML_DD:
+ {
+ // We indent by 1 cm. The IDs are always 2 away from each other!
+ auto const left(o3tl::convert(1, o3tl::Length::cm, o3tl::Length::twip));
+ SvxTextLeftMarginItem const leftMargin(left, RES_MARGIN_TEXTLEFT);
+ aSet.Put(leftMargin);
+ }
+ break;
+ case RES_POOLCOLL_HTML_DT:
+ {
+ {
+ pNewColl->SetNextTextFormatColl( *GetTextCollFromPool( RES_POOLCOLL_HTML_DD ));
+ }
+ // We indent by 0 cm. The IDs are always 2 away from each other!
+ SvxTextLeftMarginItem const leftMargin(0, RES_MARGIN_TEXTLEFT);
+ aSet.Put(leftMargin);
+ }
+ break;
+ }
+ }
+
+ if( aSet.Count() )
+ pNewColl->SetFormatAttr( aSet );
+ return pNewColl;
+}
+
+/// Return the AutomaticFormat with the supplied Id. If it doesn't
+/// exist, create it.
+SwFormat* DocumentStylePoolManager::GetFormatFromPool( sal_uInt16 nId )
+{
+ SwFormat *pNewFormat = nullptr;
+ SwFormat *pDeriveFormat = nullptr;
+
+ SwFormatsBase* pArray[ 2 ];
+ sal_uInt16 nArrCnt = 1;
+ TranslateId pRCId;
+ WhichRangesContainer const* pWhichRange;
+
+ switch( nId & (COLL_GET_RANGE_BITS + POOLGRP_NOCOLLID) )
+ {
+ case POOLGRP_CHARFMT:
+ {
+ pArray[0] = m_rDoc.GetCharFormats();
+ pDeriveFormat = m_rDoc.GetDfltCharFormat();
+ pWhichRange = &aCharFormatSetRange;
+
+ if (nId >= RES_POOLCHR_HTML_BEGIN && nId < RES_POOLCHR_HTML_END)
+ pRCId = STR_POOLCHR_HTML_ARY[nId - RES_POOLCHR_HTML_BEGIN];
+ else if (nId >= RES_POOLCHR_NORMAL_BEGIN && nId < RES_POOLCHR_NORMAL_END)
+ pRCId = STR_POOLCHR_ARY[nId - RES_POOLCHR_BEGIN];
+ else
+ {
+ // Fault: unknown Format, but a CharFormat -> return the first one
+ OSL_ENSURE( false, "invalid Id" );
+ pRCId = STR_POOLCHR_ARY[0];
+ }
+ }
+ break;
+ case POOLGRP_FRAMEFMT:
+ {
+ pArray[0] = m_rDoc.GetFrameFormats();
+ pArray[1] = m_rDoc.GetSpzFrameFormats();
+ pDeriveFormat = m_rDoc.GetDfltFrameFormat();
+ nArrCnt = 2;
+ pWhichRange = &aFrameFormatSetRange;
+
+ // Fault: unknown Format, but a FrameFormat
+ // -> return the first one
+ if( RES_POOLFRM_BEGIN > nId || nId >= RES_POOLFRM_END )
+ {
+ OSL_ENSURE( false, "invalid Id" );
+ nId = RES_POOLFRM_BEGIN;
+ }
+
+ pRCId = STR_POOLFRM_ARY[nId - RES_POOLFRM_BEGIN];
+ }
+ break;
+
+ default:
+ // Fault, unknown Format
+ OSL_ENSURE( nId, "invalid Id" );
+ return nullptr;
+ }
+ OSL_ENSURE(pRCId, "invalid Id");
+
+ while( nArrCnt-- )
+ for( size_t n = 0; n < (*pArray[nArrCnt]).GetFormatCount(); ++n )
+ {
+ pNewFormat = (*pArray[ nArrCnt ] ).GetFormat( n );
+ if( nId == pNewFormat->GetPoolFormatId() )
+ {
+ return pNewFormat;
+ }
+ }
+
+ OUString aNm(SwResId(pRCId));
+ SwAttrSet aSet(m_rDoc.GetAttrPool(), *pWhichRange);
+
+ {
+ bool bIsModified = m_rDoc.getIDocumentState().IsModified();
+
+ {
+ ::sw::UndoGuard const undoGuard(m_rDoc.GetIDocumentUndoRedo());
+ switch (nId & (COLL_GET_RANGE_BITS + POOLGRP_NOCOLLID) )
+ {
+ case POOLGRP_CHARFMT:
+ pNewFormat = m_rDoc.MakeCharFormat_(aNm, pDeriveFormat, false, true);
+ break;
+ case POOLGRP_FRAMEFMT:
+ pNewFormat = m_rDoc.MakeFrameFormat_(aNm, pDeriveFormat, false, true);
+ break;
+ default:
+ break;
+ }
+ }
+
+ if( !bIsModified )
+ m_rDoc.getIDocumentState().ResetModified();
+ pNewFormat->SetPoolFormatId( nId );
+ pNewFormat->SetAuto(false); // no AutoFormat
+ }
+
+ switch( nId )
+ {
+ case RES_POOLCHR_FOOTNOTE: // Footnote
+ case RES_POOLCHR_PAGENO: // Page/Field
+ case RES_POOLCHR_LABEL: // Label
+ case RES_POOLCHR_DROPCAPS: // Dropcaps
+ case RES_POOLCHR_NUM_LEVEL: // Numbering level
+ case RES_POOLCHR_TOXJUMP: // Table of contents jump
+ case RES_POOLCHR_ENDNOTE: // Endnote
+ case RES_POOLCHR_LINENUM: // Line numbering
+ break;
+
+ case RES_POOLCHR_ENDNOTE_ANCHOR: // Endnote anchor
+ case RES_POOLCHR_FOOTNOTE_ANCHOR: // Footnote anchor
+ {
+ aSet.Put( SvxEscapementItem( DFLT_ESC_AUTO_SUPER, DFLT_ESC_PROP, RES_CHRATR_ESCAPEMENT ) );
+ }
+ break;
+
+ case RES_POOLCHR_BULLET_LEVEL: // Bullet character
+ {
+ const vcl::Font& rBulletFont = numfunc::GetDefBulletFont();
+ SetAllScriptItem( aSet, SvxFontItem( rBulletFont.GetFamilyType(),
+ rBulletFont.GetFamilyName(), rBulletFont.GetStyleName(),
+ rBulletFont.GetPitch(), rBulletFont.GetCharSet(), RES_CHRATR_FONT ));
+ }
+ break;
+
+ case RES_POOLCHR_INET_NORMAL:
+ {
+ aSet.Put( SvxColorItem( COL_BLUE, RES_CHRATR_COLOR ) );
+ aSet.Put( SvxUnderlineItem( LINESTYLE_SINGLE, RES_CHRATR_UNDERLINE ) );
+ }
+ break;
+ case RES_POOLCHR_INET_VISIT:
+ {
+ aSet.Put( SvxColorItem( COL_RED, RES_CHRATR_COLOR ) );
+ aSet.Put( SvxUnderlineItem( LINESTYLE_SINGLE, RES_CHRATR_UNDERLINE ) );
+ }
+ break;
+ case RES_POOLCHR_JUMPEDIT:
+ {
+ aSet.Put( SvxColorItem( COL_CYAN, RES_CHRATR_COLOR ) );
+ aSet.Put( SvxUnderlineItem( LINESTYLE_DOTTED, RES_CHRATR_UNDERLINE ) );
+ aSet.Put( SvxCaseMapItem( SvxCaseMap::SmallCaps, RES_CHRATR_CASEMAP ) );
+ }
+ break;
+
+ case RES_POOLCHR_RUBYTEXT:
+ {
+ tools::Long nH = GetDfltAttr( RES_CHRATR_CJK_FONTSIZE )->GetHeight() / 2;
+ SetAllScriptItem( aSet, SvxFontHeightItem( nH, 100, RES_CHRATR_FONTSIZE));
+ aSet.Put(SvxUnderlineItem( LINESTYLE_NONE, RES_CHRATR_UNDERLINE ));
+ aSet.Put(SvxEmphasisMarkItem( FontEmphasisMark::NONE, RES_CHRATR_EMPHASIS_MARK) );
+ }
+ break;
+
+ case RES_POOLCHR_HTML_EMPHASIS:
+ case RES_POOLCHR_HTML_CITATION:
+ case RES_POOLCHR_HTML_VARIABLE:
+ {
+ SetAllScriptItem( aSet, SvxPostureItem( ITALIC_NORMAL, RES_CHRATR_POSTURE) );
+ }
+ break;
+
+ case RES_POOLCHR_IDX_MAIN_ENTRY:
+ case RES_POOLCHR_HTML_STRONG:
+ {
+ SetAllScriptItem( aSet, SvxWeightItem( WEIGHT_BOLD, RES_CHRATR_WEIGHT ));
+ }
+ break;
+
+ case RES_POOLCHR_HTML_CODE:
+ case RES_POOLCHR_HTML_SAMPLE:
+ case RES_POOLCHR_HTML_KEYBOARD:
+ case RES_POOLCHR_HTML_TELETYPE:
+ {
+ ::lcl_SetDfltFont( DefaultFontType::FIXED, aSet );
+ }
+ break;
+ case RES_POOLCHR_VERT_NUM:
+ aSet.Put( SvxCharRotateItem( 900_deg10, false, RES_CHRATR_ROTATE ) );
+ break;
+
+ case RES_POOLFRM_FRAME:
+ {
+ if ( m_rDoc.GetDocumentSettingManager().get(DocumentSettingId::HTML_MODE) )
+ {
+ aSet.Put( SwFormatAnchor( RndStdIds::FLY_AS_CHAR ));
+ aSet.Put( SwFormatVertOrient( 0, text::VertOrientation::LINE_CENTER, text::RelOrientation::PRINT_AREA ) );
+ aSet.Put( SwFormatSurround( css::text::WrapTextMode_NONE ) );
+ }
+ else
+ {
+ aSet.Put( SwFormatAnchor( RndStdIds::FLY_AT_PARA ));
+ aSet.Put( SwFormatSurround( css::text::WrapTextMode_PARALLEL ) );
+ aSet.Put( SwFormatHoriOrient( 0, text::HoriOrientation::CENTER, text::RelOrientation::PRINT_AREA ) );
+ aSet.Put( SwFormatVertOrient( 0, text::VertOrientation::TOP, text::RelOrientation::PRINT_AREA ) );
+ Color aCol( COL_BLACK );
+ SvxBorderLine aLine( &aCol, SvxBorderLineWidth::Hairline );
+ SvxBoxItem aBox( RES_BOX );
+ aBox.SetLine( &aLine, SvxBoxItemLine::TOP );
+ aBox.SetLine( &aLine, SvxBoxItemLine::BOTTOM );
+ aBox.SetLine( &aLine, SvxBoxItemLine::LEFT );
+ aBox.SetLine( &aLine, SvxBoxItemLine::RIGHT );
+ aBox.SetAllDistances( 85 );
+ aSet.Put( aBox );
+ aSet.Put( SvxLRSpaceItem( 114, 114, 0, RES_LR_SPACE ) );
+ aSet.Put( SvxULSpaceItem( 114, 114, RES_UL_SPACE ) );
+ }
+
+ // for styles of FlyFrames do not set the FillStyle to make it a derived attribute
+ aSet.ClearItem(XATTR_FILLSTYLE);
+ }
+ break;
+ case RES_POOLFRM_GRAPHIC:
+ case RES_POOLFRM_OLE:
+ {
+ aSet.Put( SwFormatAnchor( RndStdIds::FLY_AT_PARA ));
+ aSet.Put( SwFormatHoriOrient( 0, text::HoriOrientation::CENTER, text::RelOrientation::FRAME ));
+ aSet.Put( SwFormatVertOrient( 0, text::VertOrientation::TOP, text::RelOrientation::FRAME ));
+ aSet.Put( SwFormatSurround( css::text::WrapTextMode_DYNAMIC ));
+ }
+ break;
+ case RES_POOLFRM_FORMEL:
+ {
+ aSet.Put( SwFormatAnchor( RndStdIds::FLY_AS_CHAR ) );
+ aSet.Put( SwFormatVertOrient( 0, text::VertOrientation::CHAR_CENTER, text::RelOrientation::FRAME ) );
+ aSet.Put( SvxLRSpaceItem( 0, 0, 0, RES_LR_SPACE ) );
+ }
+ break;
+ case RES_POOLFRM_MARGINAL:
+ {
+ aSet.Put( SwFormatAnchor( RndStdIds::FLY_AT_PARA ));
+ aSet.Put( SwFormatHoriOrient( 0, text::HoriOrientation::LEFT, text::RelOrientation::FRAME ));
+ aSet.Put( SwFormatVertOrient( 0, text::VertOrientation::TOP, text::RelOrientation::FRAME ));
+ aSet.Put( SwFormatSurround( css::text::WrapTextMode_PARALLEL ));
+ // Set the default width to 3.5 cm, use the minimum value for the height
+ aSet.Put(SwFormatFrameSize(SwFrameSize::Minimum,
+ o3tl::toTwips(35, o3tl::Length::mm),
+ o3tl::toTwips(5, o3tl::Length::mm)));
+ }
+ break;
+ case RES_POOLFRM_WATERSIGN:
+ {
+ aSet.Put( SwFormatAnchor( RndStdIds::FLY_AT_PAGE ));
+ aSet.Put( SwFormatHoriOrient( 0, text::HoriOrientation::CENTER, text::RelOrientation::FRAME ));
+ aSet.Put( SwFormatVertOrient( 0, text::VertOrientation::CENTER, text::RelOrientation::FRAME ));
+ aSet.Put( SvxOpaqueItem( RES_OPAQUE, false ));
+ aSet.Put( SwFormatSurround( css::text::WrapTextMode_THROUGH ));
+ }
+ break;
+ case RES_POOLFRM_LABEL:
+ {
+ aSet.Put( SwFormatAnchor( RndStdIds::FLY_AS_CHAR ) );
+ aSet.Put( SwFormatVertOrient( 0, text::VertOrientation::TOP, text::RelOrientation::FRAME ) );
+ aSet.Put( SvxLRSpaceItem( 114, 114, 0, RES_LR_SPACE ) );
+
+ SvxProtectItem aProtect( RES_PROTECT );
+ aProtect.SetSizeProtect( true );
+ aProtect.SetPosProtect( true );
+ aSet.Put( aProtect );
+
+ pNewFormat->SetAutoUpdateOnDirectFormat();
+ }
+ break;
+ }
+ if( aSet.Count() )
+ {
+ pNewFormat->SetFormatAttr( aSet );
+ }
+ return pNewFormat;
+}
+
+SwFrameFormat* DocumentStylePoolManager::GetFrameFormatFromPool( sal_uInt16 nId )
+{
+ return static_cast<SwFrameFormat*>(GetFormatFromPool( nId ));
+}
+
+SwCharFormat* DocumentStylePoolManager::GetCharFormatFromPool( sal_uInt16 nId )
+{
+ return static_cast<SwCharFormat*>(GetFormatFromPool( nId ));
+}
+
+SwPageDesc* DocumentStylePoolManager::GetPageDescFromPool( sal_uInt16 nId, bool bRegardLanguage )
+{
+ OSL_ENSURE( RES_POOLPAGE_BEGIN <= nId && nId < RES_POOLPAGE_END,
+ "Wrong AutoFormat Id" );
+
+ for( size_t n = 0; n < m_rDoc.GetPageDescCnt(); ++n )
+ {
+ if ( nId == m_rDoc.GetPageDesc(n).GetPoolFormatId() )
+ {
+ return &m_rDoc.GetPageDesc(n);
+ }
+ }
+
+ if( RES_POOLPAGE_BEGIN > nId || nId >= RES_POOLPAGE_END )
+ {
+ // unknown page pool ID
+ OSL_ENSURE( false, "<SwDoc::GetPageDescFromPool(..)> - unknown page pool ID" );
+ nId = RES_POOLPAGE_BEGIN;
+ }
+
+ SwPageDesc* pNewPgDsc = nullptr;
+ {
+ static_assert(SAL_N_ELEMENTS(STR_POOLPAGE_ARY) == RES_POOLPAGE_END - RES_POOLPAGE_BEGIN, "### unexpected size!");
+ const OUString aNm(SwResId(STR_POOLPAGE_ARY[nId - RES_POOLPAGE_BEGIN]));
+ const bool bIsModified = m_rDoc.getIDocumentState().IsModified();
+
+ {
+ ::sw::UndoGuard const undoGuard(m_rDoc.GetIDocumentUndoRedo());
+ pNewPgDsc = m_rDoc.MakePageDesc(aNm, nullptr, bRegardLanguage);
+ }
+
+ pNewPgDsc->SetPoolFormatId( nId );
+ if ( !bIsModified )
+ {
+ m_rDoc.getIDocumentState().ResetModified();
+ }
+ }
+
+ SvxLRSpaceItem aLR( RES_LR_SPACE );
+ {
+ aLR.SetLeft(o3tl::convert(2, o3tl::Length::cm, o3tl::Length::twip));
+ aLR.SetRight( aLR.GetLeft() );
+ }
+ SvxULSpaceItem aUL( RES_UL_SPACE );
+ {
+ aUL.SetUpper( o3tl::narrowing<sal_uInt16>(aLR.GetLeft()) );
+ aUL.SetLower( o3tl::narrowing<sal_uInt16>(aLR.GetLeft()) );
+ }
+
+ SwAttrSet aSet( m_rDoc.GetAttrPool(), aPgFrameFormatSetRange );
+ bool bSetLeft = true;
+
+ switch( nId )
+ {
+ case RES_POOLPAGE_STANDARD: // "Default"
+ {
+ aSet.Put( aLR );
+ aSet.Put( aUL );
+ pNewPgDsc->SetUseOn( UseOnPage::All | UseOnPage::FirstShare );
+ }
+ break;
+
+ case RES_POOLPAGE_FIRST: // "First Page"
+ case RES_POOLPAGE_REGISTER: // "Index"
+ {
+ lcl_PutStdPageSizeIntoItemSet( m_rDoc, aSet );
+ aSet.Put( aLR );
+ aSet.Put( aUL );
+ pNewPgDsc->SetUseOn( UseOnPage::All );
+ if( RES_POOLPAGE_FIRST == nId )
+ pNewPgDsc->SetFollow( GetPageDescFromPool( RES_POOLPAGE_STANDARD ));
+ }
+ break;
+
+ case RES_POOLPAGE_LEFT: // "Left Page"
+ {
+ lcl_PutStdPageSizeIntoItemSet( m_rDoc, aSet );
+ aSet.Put( aLR );
+ aSet.Put( aUL );
+ bSetLeft = false;
+ pNewPgDsc->SetUseOn( UseOnPage::Left );
+ // this relies on GetPageDescFromPool() not going into infinite recursion
+ // (by this point RES_POOLPAGE_LEFT will not reach this place again)
+ pNewPgDsc->SetFollow( GetPageDescFromPool( RES_POOLPAGE_RIGHT ));
+ }
+ break;
+ case RES_POOLPAGE_RIGHT: // "Right Page"
+ {
+ lcl_PutStdPageSizeIntoItemSet( m_rDoc, aSet );
+ aSet.Put( aLR );
+ aSet.Put( aUL );
+ bSetLeft = false;
+ pNewPgDsc->SetUseOn( UseOnPage::Right );
+ pNewPgDsc->SetFollow( GetPageDescFromPool( RES_POOLPAGE_LEFT ));
+ }
+ break;
+
+ case RES_POOLPAGE_ENVELOPE: // "Envelope"
+ {
+ Size aPSize( SvxPaperInfo::GetPaperSize( PAPER_ENV_C65 ) );
+ LandscapeSwap( aPSize );
+ aSet.Put( SwFormatFrameSize( SwFrameSize::Fixed, aPSize.Width(), aPSize.Height() ));
+ aLR.SetLeft( 0 ); aLR.SetRight( 0 );
+ aUL.SetUpper( 0 ); aUL.SetLower( 0 );
+ aSet.Put( aLR );
+ aSet.Put( aUL );
+
+ pNewPgDsc->SetUseOn( UseOnPage::All );
+ pNewPgDsc->SetLandscape( true );
+ }
+ break;
+
+ case RES_POOLPAGE_HTML: // "HTML"
+ {
+ lcl_PutStdPageSizeIntoItemSet( m_rDoc, aSet );
+ aLR.SetRight(o3tl::convert(1, o3tl::Length::cm, o3tl::Length::twip));
+ aUL.SetUpper( o3tl::narrowing<sal_uInt16>(aLR.GetRight()) );
+ aUL.SetLower( o3tl::narrowing<sal_uInt16>(aLR.GetRight()) );
+ aSet.Put( aLR );
+ aSet.Put( aUL );
+
+ pNewPgDsc->SetUseOn( UseOnPage::All );
+ }
+ break;
+
+ case RES_POOLPAGE_FOOTNOTE: // "Footnote"
+ case RES_POOLPAGE_ENDNOTE: // "Endnote"
+ {
+ lcl_PutStdPageSizeIntoItemSet( m_rDoc, aSet );
+ aSet.Put( aLR );
+ aSet.Put( aUL );
+ pNewPgDsc->SetUseOn( UseOnPage::All );
+ SwPageFootnoteInfo aInf( pNewPgDsc->GetFootnoteInfo() );
+ aInf.SetLineWidth( 0 );
+ aInf.SetTopDist( 0 );
+ aInf.SetBottomDist( 0 );
+ pNewPgDsc->SetFootnoteInfo( aInf );
+ }
+ break;
+
+ case RES_POOLPAGE_LANDSCAPE: // "Landscape"
+ {
+ SwPageDesc* pStdPgDsc = GetPageDescFromPool( RES_POOLPAGE_STANDARD );
+ SwFormatFrameSize aFrameSz( pStdPgDsc->GetMaster().GetFrameSize() );
+ if ( !pStdPgDsc->GetLandscape() )
+ {
+ const SwTwips nTmp = aFrameSz.GetHeight();
+ aFrameSz.SetHeight( aFrameSz.GetWidth() );
+ aFrameSz.SetWidth( nTmp );
+ }
+ aSet.Put( aFrameSz );
+ aSet.Put( aLR );
+ aSet.Put( aUL );
+ pNewPgDsc->SetUseOn( UseOnPage::All );
+ pNewPgDsc->SetLandscape( true );
+ }
+ break;
+
+ }
+
+ if( aSet.Count() )
+ {
+ if( bSetLeft )
+ {
+ pNewPgDsc->GetLeft().SetFormatAttr( aSet );
+ pNewPgDsc->GetFirstLeft().SetFormatAttr( aSet );
+ }
+ pNewPgDsc->GetMaster().SetFormatAttr( aSet );
+ pNewPgDsc->GetFirstMaster().SetFormatAttr( aSet );
+ }
+ return pNewPgDsc;
+}
+
+SwNumRule* DocumentStylePoolManager::GetNumRuleFromPool( sal_uInt16 nId )
+{
+ OSL_ENSURE( RES_POOLNUMRULE_BEGIN <= nId && nId < RES_POOLNUMRULE_END,
+ "Wrong AutoFormat Id" );
+
+ SwNumRule* pNewRule;
+
+ for (size_t n = 0; n < m_rDoc.GetNumRuleTable().size(); ++n )
+ {
+ pNewRule = m_rDoc.GetNumRuleTable()[ n ];
+ if (nId == pNewRule->GetPoolFormatId())
+ {
+ return pNewRule;
+ }
+ }
+
+ // error: unknown Pool style
+ if( RES_POOLNUMRULE_BEGIN > nId || nId >= RES_POOLNUMRULE_END )
+ {
+ OSL_ENSURE( false, "invalid Id" );
+ nId = RES_POOLNUMRULE_BEGIN;
+ }
+
+ static_assert(SAL_N_ELEMENTS(STR_POOLNUMRULE_NUM_ARY) == RES_POOLNUMRULE_END - RES_POOLNUMRULE_BEGIN, "### unexpected size!");
+ OUString aNm(SwResId(STR_POOLNUMRULE_NUM_ARY[nId - RES_POOLNUMRULE_BEGIN]));
+
+ SwCharFormat *pNumCFormat = nullptr, *pBullCFormat = nullptr;
+
+ const SvxNumberFormat::SvxNumPositionAndSpaceMode eNumberFormatPositionAndSpaceMode
+ = numfunc::GetDefaultPositionAndSpaceMode(); //#i89178#
+ {
+ bool bIsModified = m_rDoc.getIDocumentState().IsModified();
+
+ sal_uInt16 n = m_rDoc.MakeNumRule( aNm, nullptr, false, eNumberFormatPositionAndSpaceMode );
+
+ pNewRule = m_rDoc.GetNumRuleTable()[ n ];
+ pNewRule->SetPoolFormatId( nId );
+ pNewRule->SetAutoRule( false );
+
+ if( RES_POOLNUMRULE_NUM1 <= nId && nId <= RES_POOLNUMRULE_NUM5 )
+ pNumCFormat = GetCharFormatFromPool( RES_POOLCHR_NUM_LEVEL );
+
+ if( ( RES_POOLNUMRULE_BUL1 <= nId && nId <= RES_POOLNUMRULE_BUL5 ) ||
+ RES_POOLNUMRULE_NUM5 == nId )
+ pBullCFormat = GetCharFormatFromPool( RES_POOLCHR_NUM_LEVEL );
+
+ if( !bIsModified )
+ m_rDoc.getIDocumentState().ResetModified();
+ }
+
+ switch( nId )
+ {
+ case RES_POOLNUMRULE_NUM1:
+ {
+ SwNumFormat aFormat;
+ aFormat.SetPositionAndSpaceMode( eNumberFormatPositionAndSpaceMode );
+ aFormat.SetNumberingType(SVX_NUM_ARABIC);
+ aFormat.SetCharFormat( pNumCFormat );
+ aFormat.SetStart( 1 );
+ aFormat.SetIncludeUpperLevels( 1 );
+
+ static const sal_uInt16 aAbsSpace[ MAXLEVEL ] =
+ {
+// cm: 0.7 cm intervals, with 1 cm = 567
+ 397, 794, 1191, 1588, 1985, 2381, 2778, 3175, 3572, 3969
+ };
+ const sal_uInt16* pArr = aAbsSpace;
+
+ if ( eNumberFormatPositionAndSpaceMode == SvxNumberFormat::LABEL_WIDTH_AND_POSITION )
+ {
+ aFormat.SetFirstLineOffset( - (*pArr) );
+ }
+ else if ( eNumberFormatPositionAndSpaceMode == SvxNumberFormat::LABEL_ALIGNMENT )
+ {
+ aFormat.SetLabelFollowedBy( SvxNumberFormat::LISTTAB );
+ aFormat.SetFirstLineIndent( - (*pArr) );
+ }
+
+ for (sal_uInt16 n = 0; n < MAXLEVEL; ++n, ++pArr)
+ {
+ aFormat.SetListFormat("", ".", n);
+ if ( eNumberFormatPositionAndSpaceMode == SvxNumberFormat::LABEL_WIDTH_AND_POSITION )
+ {
+ aFormat.SetAbsLSpace( *pArr + 357 ); // 357 is indent of 0.63 cm
+ }
+ else if ( eNumberFormatPositionAndSpaceMode == SvxNumberFormat::LABEL_ALIGNMENT )
+ {
+ aFormat.SetListtabPos( *pArr );
+ aFormat.SetIndentAt( *pArr + 357 );
+ }
+
+ pNewRule->Set( n, aFormat );
+ }
+ }
+ break;
+
+ case RES_POOLNUMRULE_NUM2:
+ {
+ static const sal_uInt16 aAbsSpace[ MAXLEVEL ] =
+ {
+ 397, 397, 397, 397, // 0.70 cm intervals
+ 397, 397, 397, 397,
+ 397, 397
+ };
+
+ const sal_uInt16* pArr = aAbsSpace;
+ SwNumFormat aFormat;
+
+ aFormat.SetPositionAndSpaceMode( eNumberFormatPositionAndSpaceMode );
+ aFormat.SetNumberingType(SVX_NUM_CHARS_UPPER_LETTER);
+ aFormat.SetCharFormat( pNumCFormat );
+ aFormat.SetStart( 1 );
+ aFormat.SetIncludeUpperLevels( 1 );
+
+ if ( eNumberFormatPositionAndSpaceMode == SvxNumberFormat::LABEL_ALIGNMENT )
+ {
+ aFormat.SetLabelFollowedBy( SvxNumberFormat::LISTTAB );
+ }
+
+ sal_uInt16 nSpace = 357; // indent of 0.63 cm
+ for (sal_uInt16 n = 0; n < MAXLEVEL; ++n)
+ {
+ aFormat.SetListFormat("", ".", n);
+ if ( eNumberFormatPositionAndSpaceMode == SvxNumberFormat::LABEL_WIDTH_AND_POSITION )
+ {
+ nSpace += pArr[ n ];
+ aFormat.SetAbsLSpace( nSpace );
+ aFormat.SetFirstLineOffset( - pArr[ n ] );
+ }
+ else if ( eNumberFormatPositionAndSpaceMode == SvxNumberFormat::LABEL_ALIGNMENT )
+ {
+ nSpace += pArr[ n ];
+ aFormat.SetListtabPos( nSpace );
+ aFormat.SetIndentAt( nSpace );
+ aFormat.SetFirstLineIndent( - pArr[ n ] );
+ }
+
+ pNewRule->Set( n, aFormat );
+ }
+ }
+ break;
+ case RES_POOLNUMRULE_NUM3:
+ {
+ SwNumFormat aFormat;
+
+ aFormat.SetPositionAndSpaceMode( eNumberFormatPositionAndSpaceMode );
+ aFormat.SetNumberingType(SVX_NUM_CHARS_LOWER_LETTER);
+ aFormat.SetCharFormat( pNumCFormat );
+ aFormat.SetStart( 1 );
+ aFormat.SetIncludeUpperLevels( 1 );
+
+ tools::Long const nOffs = 397; // 0.70 cm
+
+ if ( eNumberFormatPositionAndSpaceMode == SvxNumberFormat::LABEL_WIDTH_AND_POSITION )
+ {
+ aFormat.SetFirstLineOffset( - nOffs );
+ }
+ else if ( eNumberFormatPositionAndSpaceMode == SvxNumberFormat::LABEL_ALIGNMENT )
+ {
+ aFormat.SetLabelFollowedBy( SvxNumberFormat::LISTTAB );
+ aFormat.SetFirstLineIndent( - nOffs );
+ }
+
+ for (sal_uInt16 n = 0; n < MAXLEVEL; ++n)
+ {
+ aFormat.SetListFormat("", ".", n);
+ if ( eNumberFormatPositionAndSpaceMode == SvxNumberFormat::LABEL_WIDTH_AND_POSITION )
+ {
+ aFormat.SetAbsLSpace( (n+1) * nOffs + 357 ); // 357 is indent of 0.63 cm
+ }
+ else if ( eNumberFormatPositionAndSpaceMode == SvxNumberFormat::LABEL_ALIGNMENT )
+ {
+ tools::Long nPos = (n+1) * nOffs;
+ aFormat.SetListtabPos(nPos + 357);
+ aFormat.SetIndentAt(nPos + 357);
+ }
+
+ pNewRule->Set( n, aFormat );
+ }
+ }
+ break;
+ case RES_POOLNUMRULE_NUM4:
+ {
+ SwNumFormat aFormat;
+
+ aFormat.SetPositionAndSpaceMode( eNumberFormatPositionAndSpaceMode );
+ aFormat.SetNumberingType(SVX_NUM_ROMAN_UPPER);
+ aFormat.SetCharFormat( pNumCFormat );
+ aFormat.SetStart( 1 );
+ aFormat.SetIncludeUpperLevels( 1 );
+ aFormat.SetNumAdjust( SvxAdjust::Right );
+
+ static const sal_uInt16 aAbsSpace[ MAXLEVEL ] =
+ {
+// cm: 1.33 cm intervals
+ 754, 1508, 2262, 3016, 3771, 4525, 5279, 6033, 6787, 7541
+ };
+ const sal_uInt16* pArr = aAbsSpace;
+
+ if ( eNumberFormatPositionAndSpaceMode == SvxNumberFormat::LABEL_WIDTH_AND_POSITION )
+ {
+ aFormat.SetFirstLineOffset( 580 - (*pArr) ); // 1 cm space
+ }
+ else if ( eNumberFormatPositionAndSpaceMode == SvxNumberFormat::LABEL_ALIGNMENT )
+ {
+ aFormat.SetLabelFollowedBy( SvxNumberFormat::LISTTAB );
+ aFormat.SetFirstLineIndent( 580 - (*pArr) );
+ }
+
+ for (sal_uInt16 n = 0; n < MAXLEVEL; ++n, ++pArr)
+ {
+ aFormat.SetListFormat("", ".", n);
+ if ( eNumberFormatPositionAndSpaceMode == SvxNumberFormat::LABEL_WIDTH_AND_POSITION )
+ {
+ aFormat.SetAbsLSpace( *pArr );
+ }
+ else if ( eNumberFormatPositionAndSpaceMode == SvxNumberFormat::LABEL_ALIGNMENT )
+ {
+ aFormat.SetListtabPos( *pArr );
+ aFormat.SetIndentAt( *pArr );
+ }
+
+ pNewRule->Set( n, aFormat );
+ }
+ }
+ break;
+ case RES_POOLNUMRULE_NUM5:
+ {
+ // [ First, LSpace ]
+ static const sal_uInt16 aAbsSpace0to2[] =
+ {
+ 174, 754, // 0.33, 1.33cm
+ 174, 1508, // 0.33, 2.66cm
+ 397, 2262 // 0.70, 4.00cm
+ };
+
+ const sal_uInt16* pArr0to2 = aAbsSpace0to2;
+ SwNumFormat aFormat;
+
+ aFormat.SetPositionAndSpaceMode( eNumberFormatPositionAndSpaceMode );
+ aFormat.SetNumberingType(SVX_NUM_ROMAN_LOWER);
+ aFormat.SetStart( 1 );
+ aFormat.SetIncludeUpperLevels( 1 );
+ aFormat.SetNumAdjust( SvxAdjust::Right );
+ aFormat.SetListFormat("", ".", 0);
+
+ if ( eNumberFormatPositionAndSpaceMode == SvxNumberFormat::LABEL_ALIGNMENT )
+ {
+ aFormat.SetLabelFollowedBy( SvxNumberFormat::LISTTAB );
+ }
+
+ if ( eNumberFormatPositionAndSpaceMode == SvxNumberFormat::LABEL_WIDTH_AND_POSITION )
+ {
+ aFormat.SetFirstLineOffset(- pArr0to2[0]); // num ends at 1.00 cm
+ aFormat.SetAbsLSpace(pArr0to2[1]); // text starts at 1.33 cm
+ }
+ else if ( eNumberFormatPositionAndSpaceMode == SvxNumberFormat::LABEL_ALIGNMENT )
+ {
+ aFormat.SetFirstLineIndent( -pArr0to2[0] );
+ aFormat.SetListtabPos( pArr0to2[1] );
+ aFormat.SetIndentAt( pArr0to2[1] );
+ }
+
+ aFormat.SetCharFormat( pNumCFormat );
+ pNewRule->Set( 0, aFormat );
+
+ aFormat.SetNumberingType(SVX_NUM_ROMAN_UPPER);
+ aFormat.SetIncludeUpperLevels( 1 );
+ aFormat.SetListFormat("", ".", 1);
+
+ if ( eNumberFormatPositionAndSpaceMode == SvxNumberFormat::LABEL_WIDTH_AND_POSITION )
+ {
+ aFormat.SetFirstLineOffset(- pArr0to2[2]); // num ends at 2.33 cm
+ aFormat.SetAbsLSpace(pArr0to2[3]); // text starts at 2.66 cm
+ }
+ else if ( eNumberFormatPositionAndSpaceMode == SvxNumberFormat::LABEL_ALIGNMENT )
+ {
+ aFormat.SetFirstLineIndent( -pArr0to2[2] );
+ aFormat.SetListtabPos( pArr0to2[3] );
+ aFormat.SetIndentAt( pArr0to2[3] );
+ }
+
+ pNewRule->Set( 1, aFormat );
+
+ aFormat.SetNumberingType(SVX_NUM_CHARS_LOWER_LETTER);
+ aFormat.SetIncludeUpperLevels( 1 );
+ aFormat.SetListFormat("", u")"_ustr, 2);
+ aFormat.SetNumAdjust( SvxAdjust::Left );
+
+ if ( eNumberFormatPositionAndSpaceMode == SvxNumberFormat::LABEL_WIDTH_AND_POSITION )
+ {
+ aFormat.SetFirstLineOffset(- pArr0to2[4]); // num starts at 3.30 cm
+ aFormat.SetAbsLSpace(pArr0to2[5]); // text starts at 4.00 cm
+ }
+ else if ( eNumberFormatPositionAndSpaceMode == SvxNumberFormat::LABEL_ALIGNMENT )
+ {
+ aFormat.SetFirstLineIndent( -pArr0to2[4] );
+ aFormat.SetListtabPos( pArr0to2[5] );
+ aFormat.SetIndentAt( pArr0to2[5] );
+ }
+
+ pNewRule->Set( 2, aFormat );
+
+ aFormat.SetNumberingType(SVX_NUM_CHAR_SPECIAL);
+ aFormat.SetCharFormat( pBullCFormat );
+ aFormat.SetBulletFont( &numfunc::GetDefBulletFont() );
+ aFormat.SetBulletChar( cBulletChar );
+ sal_Int16 nOffs = o3tl::convert(660, o3tl::Length::mm100, o3tl::Length::twip),
+ nOffs2 = o3tl::convert(4000, o3tl::Length::mm100, o3tl::Length::twip);
+
+ if ( eNumberFormatPositionAndSpaceMode == SvxNumberFormat::LABEL_WIDTH_AND_POSITION )
+ {
+ aFormat.SetFirstLineOffset( - nOffs );
+ }
+ else if ( eNumberFormatPositionAndSpaceMode == SvxNumberFormat::LABEL_ALIGNMENT )
+ {
+ aFormat.SetFirstLineIndent( - nOffs );
+ }
+
+ for (sal_uInt16 n = 3; n < MAXLEVEL; ++n)
+ {
+ aFormat.SetStart( n+1 );
+ aFormat.SetListFormat("", "", n);
+
+ if ( eNumberFormatPositionAndSpaceMode == SvxNumberFormat::LABEL_WIDTH_AND_POSITION )
+ {
+ aFormat.SetAbsLSpace(nOffs2 + ((n - 2) * static_cast<tools::Long>(nOffs)));
+ }
+ else if ( eNumberFormatPositionAndSpaceMode == SvxNumberFormat::LABEL_ALIGNMENT )
+ {
+ tools::Long nPos = nOffs2 + ((n - 2) * static_cast<tools::Long>(nOffs));
+ aFormat.SetListtabPos(nPos);
+ aFormat.SetIndentAt(nPos);
+ }
+
+ pNewRule->Set( n, aFormat );
+ }
+ }
+ break;
+
+ case RES_POOLNUMRULE_BUL1:
+ {
+ SwNumFormat aFormat;
+
+ aFormat.SetPositionAndSpaceMode( eNumberFormatPositionAndSpaceMode );
+ aFormat.SetNumberingType(SVX_NUM_CHAR_SPECIAL);
+ aFormat.SetCharFormat( pBullCFormat );
+ aFormat.SetStart( 1 );
+ aFormat.SetIncludeUpperLevels( 1 );
+ aFormat.SetBulletFont( &numfunc::GetDefBulletFont() );
+ aFormat.SetBulletChar( cBulletChar );
+
+ static const sal_uInt16 aAbsSpace[ MAXLEVEL ] =
+ {
+// cm: 0,4 0,8 1,2 1,6 2,0 2,4 2,8 3,2 3,6 4,0
+ 227, 454, 680, 907, 1134, 1361, 1587, 1814, 2041, 2268
+ };
+ const sal_uInt16* pArr = aAbsSpace;
+
+ if ( eNumberFormatPositionAndSpaceMode == SvxNumberFormat::LABEL_WIDTH_AND_POSITION )
+ {
+ aFormat.SetFirstLineOffset( - (*pArr) );
+ }
+ else if ( eNumberFormatPositionAndSpaceMode == SvxNumberFormat::LABEL_ALIGNMENT )
+ {
+ aFormat.SetLabelFollowedBy( SvxNumberFormat::LISTTAB );
+ aFormat.SetFirstLineIndent( - (*pArr) );
+ }
+
+ for (sal_uInt16 n = 0; n < MAXLEVEL; ++n, ++pArr)
+ {
+ if ( eNumberFormatPositionAndSpaceMode == SvxNumberFormat::LABEL_WIDTH_AND_POSITION )
+ {
+ aFormat.SetAbsLSpace( *pArr );
+ }
+ else if ( eNumberFormatPositionAndSpaceMode == SvxNumberFormat::LABEL_ALIGNMENT )
+ {
+ aFormat.SetListtabPos( *pArr );
+ aFormat.SetIndentAt( *pArr );
+ }
+
+ pNewRule->Set( n, aFormat );
+ }
+ }
+ break;
+ case RES_POOLNUMRULE_BUL2:
+ {
+ SwNumFormat aFormat;
+
+ aFormat.SetPositionAndSpaceMode( eNumberFormatPositionAndSpaceMode );
+ aFormat.SetNumberingType(SVX_NUM_CHAR_SPECIAL);
+ aFormat.SetCharFormat( pBullCFormat );
+ aFormat.SetStart( 1 );
+ aFormat.SetIncludeUpperLevels( 1 );
+ aFormat.SetBulletFont( &numfunc::GetDefBulletFont() );
+ aFormat.SetBulletChar( 0x2013 );
+
+ static const sal_uInt16 aAbsSpace[ MAXLEVEL ] =
+ {
+// cm: 0,3 0,6 0,9 1,2 1,5 1,8 2,1 2,4 2,7 3,0
+ 170, 340, 510, 680, 850, 1020, 1191, 1361, 1531, 1701
+ };
+ const sal_uInt16* pArr = aAbsSpace;
+
+ if ( eNumberFormatPositionAndSpaceMode == SvxNumberFormat::LABEL_WIDTH_AND_POSITION )
+ {
+ aFormat.SetFirstLineOffset( - (*pArr) );
+ }
+ else if ( eNumberFormatPositionAndSpaceMode == SvxNumberFormat::LABEL_ALIGNMENT )
+ {
+ aFormat.SetLabelFollowedBy( SvxNumberFormat::LISTTAB );
+ aFormat.SetFirstLineIndent( - (*pArr) );
+ }
+
+ for (sal_uInt16 n = 0; n < MAXLEVEL; ++n, ++pArr)
+ {
+ if ( eNumberFormatPositionAndSpaceMode == SvxNumberFormat::LABEL_WIDTH_AND_POSITION )
+ {
+ aFormat.SetAbsLSpace( *pArr );
+ }
+ else if ( eNumberFormatPositionAndSpaceMode == SvxNumberFormat::LABEL_ALIGNMENT )
+ {
+ aFormat.SetListtabPos( *pArr );
+ aFormat.SetIndentAt( *pArr );
+ }
+
+ pNewRule->Set( n, aFormat );
+ }
+ }
+ break;
+ case RES_POOLNUMRULE_BUL3:
+ {
+ SwNumFormat aFormat;
+
+ aFormat.SetPositionAndSpaceMode( eNumberFormatPositionAndSpaceMode );
+
+ aFormat.SetNumberingType(SVX_NUM_CHAR_SPECIAL);
+ aFormat.SetCharFormat( pBullCFormat );
+ aFormat.SetStart( 1 );
+ aFormat.SetIncludeUpperLevels( 1 );
+ aFormat.SetBulletFont( &numfunc::GetDefBulletFont() );
+
+ sal_uInt16 nOffs = o3tl::convert(4, o3tl::Length::mm, o3tl::Length::twip);
+
+ if ( eNumberFormatPositionAndSpaceMode == SvxNumberFormat::LABEL_WIDTH_AND_POSITION )
+ {
+ aFormat.SetFirstLineOffset( - nOffs );
+ }
+ else if ( eNumberFormatPositionAndSpaceMode == SvxNumberFormat::LABEL_ALIGNMENT )
+ {
+ aFormat.SetLabelFollowedBy( SvxNumberFormat::LISTTAB );
+ aFormat.SetFirstLineIndent( - nOffs );
+ }
+
+ for (sal_uInt16 n = 0; n < MAXLEVEL; ++n)
+ {
+ aFormat.SetBulletChar( (n & 1) ? 0x25a1 : 0x2611 );
+
+ if ( eNumberFormatPositionAndSpaceMode == SvxNumberFormat::LABEL_WIDTH_AND_POSITION )
+ {
+ aFormat.SetAbsLSpace( ((n & 1) +1) * nOffs );
+ }
+ else if ( eNumberFormatPositionAndSpaceMode == SvxNumberFormat::LABEL_ALIGNMENT )
+ {
+ tools::Long nPos = ((n & 1) +1) * static_cast<tools::Long>(nOffs);
+ aFormat.SetListtabPos(nPos);
+ aFormat.SetIndentAt(nPos);
+ }
+
+ pNewRule->Set( n, aFormat );
+ }
+ }
+ break;
+ case RES_POOLNUMRULE_BUL4:
+ {
+ SwNumFormat aFormat;
+
+ aFormat.SetPositionAndSpaceMode( eNumberFormatPositionAndSpaceMode );
+ aFormat.SetNumberingType(SVX_NUM_CHAR_SPECIAL);
+ aFormat.SetCharFormat( pBullCFormat );
+ aFormat.SetStart( 1 );
+ aFormat.SetIncludeUpperLevels( 1 );
+ aFormat.SetBulletFont( &numfunc::GetDefBulletFont() );
+
+ static const sal_uInt16 aAbsSpace[ MAXLEVEL ] =
+ {
+// cm: 0,4 0,8 1,2 1,6 2,0 2,4 2,8 3,2 3,6 4,0
+ 227, 454, 680, 907, 1134, 1361, 1587, 1814, 2041, 2268
+ };
+
+ const sal_uInt16* pArr = aAbsSpace;
+
+ if ( eNumberFormatPositionAndSpaceMode == SvxNumberFormat::LABEL_WIDTH_AND_POSITION )
+ {
+ aFormat.SetFirstLineOffset( - (*pArr) );
+ }
+ else if ( eNumberFormatPositionAndSpaceMode == SvxNumberFormat::LABEL_ALIGNMENT )
+ {
+ aFormat.SetLabelFollowedBy( SvxNumberFormat::SPACE );
+ aFormat.SetFirstLineIndent( - (*pArr) );
+ }
+
+ for (sal_uInt16 n = 0; n < MAXLEVEL; ++n, ++pArr)
+ {
+ switch( n )
+ {
+ case 0: aFormat.SetBulletChar( 0x27a2 ); break;
+ case 1: aFormat.SetBulletChar( 0xE006 ); break;
+ default: aFormat.SetBulletChar( 0xE004 ); break;
+ }
+
+ if ( eNumberFormatPositionAndSpaceMode == SvxNumberFormat::LABEL_WIDTH_AND_POSITION )
+ {
+ aFormat.SetAbsLSpace( *pArr );
+ }
+ else if ( eNumberFormatPositionAndSpaceMode == SvxNumberFormat::LABEL_ALIGNMENT )
+ {
+ aFormat.SetListtabPos( *pArr );
+ aFormat.SetIndentAt( *pArr );
+ }
+
+ pNewRule->Set( n, aFormat );
+ }
+ }
+ break;
+ case RES_POOLNUMRULE_BUL5:
+ {
+ SwNumFormat aFormat;
+
+ aFormat.SetPositionAndSpaceMode( eNumberFormatPositionAndSpaceMode );
+ aFormat.SetNumberingType(SVX_NUM_CHAR_SPECIAL);
+ aFormat.SetCharFormat( pBullCFormat );
+ aFormat.SetStart( 1 );
+ aFormat.SetIncludeUpperLevels( 1 );
+ aFormat.SetBulletChar( 0x2717 );
+ aFormat.SetBulletFont( &numfunc::GetDefBulletFont() );
+
+ static const sal_uInt16 aAbsSpace[ MAXLEVEL ] =
+ {
+// cm: 0,4 0,8 1,2 1,6 2,0 2,4 2,8 3,2 3,6 4,0
+ 227, 454, 680, 907, 1134, 1361, 1587, 1814, 2041, 2268
+ };
+ const sal_uInt16* pArr = aAbsSpace;
+
+ if ( eNumberFormatPositionAndSpaceMode == SvxNumberFormat::LABEL_WIDTH_AND_POSITION )
+ {
+ aFormat.SetFirstLineOffset( - (*pArr) );
+ }
+ else if ( eNumberFormatPositionAndSpaceMode == SvxNumberFormat::LABEL_ALIGNMENT )
+ {
+ aFormat.SetLabelFollowedBy( SvxNumberFormat::LISTTAB );
+ aFormat.SetFirstLineIndent( - (*pArr) );
+ }
+
+ for (sal_uInt16 n = 0; n < MAXLEVEL; ++n, ++pArr)
+ {
+ if ( eNumberFormatPositionAndSpaceMode == SvxNumberFormat::LABEL_WIDTH_AND_POSITION )
+ {
+ aFormat.SetAbsLSpace( *pArr );
+ }
+ else if ( eNumberFormatPositionAndSpaceMode == SvxNumberFormat::LABEL_ALIGNMENT )
+ {
+ aFormat.SetListtabPos( *pArr );
+ aFormat.SetIndentAt( *pArr );
+ }
+
+ pNewRule->Set( n, aFormat );
+ }
+ }
+ break;
+ }
+
+ return pNewRule;
+}
+
+/// Check if this AutoCollection is already/still in use in this Document
+bool DocumentStylePoolManager::IsPoolTextCollUsed( sal_uInt16 nId ) const
+{
+ OSL_ENSURE(
+ (RES_POOLCOLL_TEXT_BEGIN <= nId && nId < RES_POOLCOLL_TEXT_END) ||
+ (RES_POOLCOLL_LISTS_BEGIN <= nId && nId < RES_POOLCOLL_LISTS_END) ||
+ (RES_POOLCOLL_EXTRA_BEGIN <= nId && nId < RES_POOLCOLL_EXTRA_END) ||
+ (RES_POOLCOLL_REGISTER_BEGIN <= nId && nId < RES_POOLCOLL_REGISTER_END) ||
+ (RES_POOLCOLL_DOC_BEGIN <= nId && nId < RES_POOLCOLL_DOC_END) ||
+ (RES_POOLCOLL_HTML_BEGIN <= nId && nId < RES_POOLCOLL_HTML_END),
+ "Wrong AutoFormat Id" );
+
+ SwTextFormatColl* pNewColl = nullptr;
+ bool bFnd = false;
+ for( SwTextFormatColls::size_type n = 0; !bFnd && n < m_rDoc.GetTextFormatColls()->size(); ++n )
+ {
+ pNewColl = (*m_rDoc.GetTextFormatColls())[ n ];
+ if( nId == pNewColl->GetPoolFormatId() )
+ bFnd = true;
+ }
+
+ if( !bFnd || !pNewColl->HasWriterListeners() )
+ return false;
+
+ bool isUsed = false;
+ sw::AutoFormatUsedHint aHint(isUsed, m_rDoc.GetNodes());
+ pNewColl->CallSwClientNotify(aHint);
+ return isUsed;
+}
+
+/// Check if this AutoCollection is already/still in use
+bool DocumentStylePoolManager::IsPoolFormatUsed( sal_uInt16 nId ) const
+{
+ const SwFormat *pNewFormat = nullptr;
+ const SwFormatsBase* pArray[ 2 ];
+ sal_uInt16 nArrCnt = 1;
+ bool bFnd = true;
+
+ if (RES_POOLCHR_BEGIN <= nId && nId < RES_POOLCHR_END)
+ {
+ pArray[0] = m_rDoc.GetCharFormats();
+ }
+ else if (RES_POOLFRM_BEGIN <= nId && nId < RES_POOLFRM_END)
+ {
+ pArray[0] = m_rDoc.GetFrameFormats();
+ pArray[1] = m_rDoc.GetSpzFrameFormats();
+ nArrCnt = 2;
+ }
+ else
+ {
+ SAL_WARN("sw.core", "Invalid Pool Id: " << nId << " should be within "
+ "[" << int(RES_POOLCHR_BEGIN) << "," << int(RES_POOLCHR_END) << ") or "
+ "[" << int(RES_POOLFRM_BEGIN) << "," << int(RES_POOLFRM_END) << ")");
+ bFnd = false;
+ }
+
+ if( bFnd )
+ {
+ bFnd = false;
+ while( nArrCnt-- && !bFnd )
+ for( size_t n = 0; !bFnd && n < (*pArray[nArrCnt]).GetFormatCount(); ++n )
+ {
+ pNewFormat = (*pArray[ nArrCnt ] ).GetFormat( n );
+ if( nId == pNewFormat->GetPoolFormatId() )
+ bFnd = true;
+ }
+ }
+
+ // Not found or no dependencies?
+ if(!bFnd || !pNewFormat->HasWriterListeners() )
+ return false;
+ // Check if we have dependent ContentNodes in the Nodes array
+ // (also indirect ones for derived Formats)
+ return pNewFormat->IsUsed();
+}
+
+/// Check if this AutoCollection is already/still in use in this Document
+bool DocumentStylePoolManager::IsPoolPageDescUsed( sal_uInt16 nId ) const
+{
+ OSL_ENSURE( RES_POOLPAGE_BEGIN <= nId && nId < RES_POOLPAGE_END,
+ "Wrong AutoFormat Id" );
+ SwPageDesc *pNewPgDsc = nullptr;
+ bool bFnd = false;
+ for( size_t n = 0; !bFnd && n < m_rDoc.GetPageDescCnt(); ++n )
+ {
+ pNewPgDsc = &m_rDoc.GetPageDesc(n);
+ if( nId == pNewPgDsc->GetPoolFormatId() )
+ bFnd = true;
+ }
+
+ // Not found or no dependencies?
+ if( !bFnd || !pNewPgDsc->HasWriterListeners() ) // ??????
+ return false;
+
+ // Check if we have dependent ContentNodes in the Nodes array
+ // (also indirect ones for derived Formats)
+ bool isUsed = false;
+ sw::AutoFormatUsedHint aHint(isUsed, m_rDoc.GetNodes());
+ pNewPgDsc->CallSwClientNotify(aHint);
+ return isUsed;
+}
+
+DocumentStylePoolManager::~DocumentStylePoolManager()
+{
+}
+
+}
+
+static std::vector<OUString>
+lcl_NewUINameArray(const TranslateId* pIds, const size_t nLen, const size_t nSvxIds = 0)
+{
+ assert(nSvxIds <= nLen);
+ const size_t nWriterIds = nLen - nSvxIds;
+ std::vector<OUString> aNameArray;
+ aNameArray.reserve(nLen);
+ for (size_t i = 0; i < nWriterIds; ++i)
+ aNameArray.push_back(SwResId(pIds[i]));
+ for (size_t i = nWriterIds; i < nLen; ++i)
+ aNameArray.push_back(SvxResId(pIds[i]));
+ return aNameArray;
+}
+
+const std::vector<OUString>& SwStyleNameMapper::GetTextUINameArray()
+{
+ SvtSysLocale aSysLocale;
+ const LanguageTag& rCurrentLanguage = aSysLocale.GetUILanguageTag();
+ static std::map<LanguageTag, std::vector<OUString>> s_aTextUINameArray;
+
+ auto it = s_aTextUINameArray.find(rCurrentLanguage);
+ if (it == s_aTextUINameArray.end())
+ it = s_aTextUINameArray.emplace(rCurrentLanguage,
+ lcl_NewUINameArray(STR_POOLCOLL_TEXT_ARY, SAL_N_ELEMENTS(STR_POOLCOLL_TEXT_ARY))).first;
+
+ return it->second;
+}
+
+const std::vector<OUString>& SwStyleNameMapper::GetListsUINameArray()
+{
+ SvtSysLocale aSysLocale;
+ const LanguageTag& rCurrentLanguage = aSysLocale.GetUILanguageTag();
+ static std::map<LanguageTag, std::vector<OUString>> s_aListsUINameArray;
+
+ auto it = s_aListsUINameArray.find(rCurrentLanguage);
+ if (it == s_aListsUINameArray.end())
+ it = s_aListsUINameArray.emplace(rCurrentLanguage,
+ lcl_NewUINameArray(STR_POOLCOLL_LISTS_ARY, SAL_N_ELEMENTS(STR_POOLCOLL_LISTS_ARY))).first;
+
+ return it->second;
+}
+
+const std::vector<OUString>& SwStyleNameMapper::GetExtraUINameArray()
+{
+ SvtSysLocale aSysLocale;
+ const LanguageTag& rCurrentLanguage = aSysLocale.GetUILanguageTag();
+ static std::map<LanguageTag, std::vector<OUString>> s_aExtraUINameArray;
+
+ auto it = s_aExtraUINameArray.find(rCurrentLanguage);
+ if (it == s_aExtraUINameArray.end())
+ it = s_aExtraUINameArray.emplace(rCurrentLanguage,
+ lcl_NewUINameArray(STR_POOLCOLL_EXTRA_ARY, SAL_N_ELEMENTS(STR_POOLCOLL_EXTRA_ARY))).first;
+
+ return it->second;
+}
+
+const std::vector<OUString>& SwStyleNameMapper::GetRegisterUINameArray()
+{
+ SvtSysLocale aSysLocale;
+ const LanguageTag& rCurrentLanguage = aSysLocale.GetUILanguageTag();
+ static std::map<LanguageTag, std::vector<OUString>> s_aRegisterUINameArray;
+
+ auto it = s_aRegisterUINameArray.find(rCurrentLanguage);
+ if (it == s_aRegisterUINameArray.end())
+ it = s_aRegisterUINameArray.emplace(rCurrentLanguage,
+ lcl_NewUINameArray(STR_POOLCOLL_REGISTER_ARY, SAL_N_ELEMENTS(STR_POOLCOLL_REGISTER_ARY))).first;
+
+ return it->second;
+}
+
+const std::vector<OUString>& SwStyleNameMapper::GetDocUINameArray()
+{
+ SvtSysLocale aSysLocale;
+ const LanguageTag& rCurrentLanguage = aSysLocale.GetUILanguageTag();
+ static std::map<LanguageTag, std::vector<OUString>> s_aDocUINameArray;
+
+ auto it = s_aDocUINameArray.find(rCurrentLanguage);
+ if (it == s_aDocUINameArray.end())
+ it = s_aDocUINameArray.emplace(rCurrentLanguage,
+ lcl_NewUINameArray(STR_POOLCOLL_DOC_ARY, SAL_N_ELEMENTS(STR_POOLCOLL_DOC_ARY))).first;
+
+ return it->second;
+}
+
+const std::vector<OUString>& SwStyleNameMapper::GetHTMLUINameArray()
+{
+ SvtSysLocale aSysLocale;
+ const LanguageTag& rCurrentLanguage = aSysLocale.GetUILanguageTag();
+ static std::map<LanguageTag, std::vector<OUString>> s_aHTMLUINameArray;
+
+ auto it = s_aHTMLUINameArray.find(rCurrentLanguage);
+ if (it == s_aHTMLUINameArray.end())
+ it = s_aHTMLUINameArray.emplace(rCurrentLanguage,
+ lcl_NewUINameArray(STR_POOLCOLL_HTML_ARY, SAL_N_ELEMENTS(STR_POOLCOLL_HTML_ARY))).first;
+
+ return it->second;
+}
+
+const std::vector<OUString>& SwStyleNameMapper::GetFrameFormatUINameArray()
+{
+ SvtSysLocale aSysLocale;
+ const LanguageTag& rCurrentLanguage = aSysLocale.GetUILanguageTag();
+ static std::map<LanguageTag, std::vector<OUString>> s_aFrameFormatUINameArray;
+
+ auto it = s_aFrameFormatUINameArray.find(rCurrentLanguage);
+ if (it == s_aFrameFormatUINameArray.end())
+ it = s_aFrameFormatUINameArray.emplace(rCurrentLanguage,
+ lcl_NewUINameArray(STR_POOLFRM_ARY, SAL_N_ELEMENTS(STR_POOLFRM_ARY))).first;
+
+ return it->second;
+}
+
+const std::vector<OUString>& SwStyleNameMapper::GetChrFormatUINameArray()
+{
+ SvtSysLocale aSysLocale;
+ const LanguageTag& rCurrentLanguage = aSysLocale.GetUILanguageTag();
+ static std::map<LanguageTag, std::vector<OUString>> s_aChrFormatUINameArray;
+
+ auto it = s_aChrFormatUINameArray.find(rCurrentLanguage);
+ if (it == s_aChrFormatUINameArray.end())
+ it = s_aChrFormatUINameArray.emplace(rCurrentLanguage,
+ lcl_NewUINameArray(STR_POOLCHR_ARY, SAL_N_ELEMENTS(STR_POOLCHR_ARY))).first;
+
+ return it->second;
+}
+
+const std::vector<OUString>& SwStyleNameMapper::GetHTMLChrFormatUINameArray()
+{
+ SvtSysLocale aSysLocale;
+ const LanguageTag& rCurrentLanguage = aSysLocale.GetUILanguageTag();
+ static std::map<LanguageTag, std::vector<OUString>> s_aHTMLChrFormatUINameArray;
+
+ auto it = s_aHTMLChrFormatUINameArray.find(rCurrentLanguage);
+ if (it == s_aHTMLChrFormatUINameArray.end())
+ it = s_aHTMLChrFormatUINameArray.emplace(rCurrentLanguage,
+ lcl_NewUINameArray(STR_POOLCHR_HTML_ARY, SAL_N_ELEMENTS(STR_POOLCHR_HTML_ARY))).first;
+
+ return it->second;
+}
+
+const std::vector<OUString>& SwStyleNameMapper::GetPageDescUINameArray()
+{
+ SvtSysLocale aSysLocale;
+ const LanguageTag& rCurrentLanguage = aSysLocale.GetUILanguageTag();
+ static std::map<LanguageTag, std::vector<OUString>> s_aPageDescUINameArray;
+
+ auto it = s_aPageDescUINameArray.find(rCurrentLanguage);
+ if (it == s_aPageDescUINameArray.end())
+ it = s_aPageDescUINameArray.emplace(rCurrentLanguage,
+ lcl_NewUINameArray(STR_POOLPAGE_ARY, SAL_N_ELEMENTS(STR_POOLPAGE_ARY))).first;
+
+ return it->second;
+}
+
+const std::vector<OUString>& SwStyleNameMapper::GetNumRuleUINameArray()
+{
+ SvtSysLocale aSysLocale;
+ const LanguageTag& rCurrentLanguage = aSysLocale.GetUILanguageTag();
+ static std::map<LanguageTag, std::vector<OUString>> s_aNumRuleUINameArray;
+
+ auto it = s_aNumRuleUINameArray.find(rCurrentLanguage);
+ if (it == s_aNumRuleUINameArray.end())
+ it = s_aNumRuleUINameArray.emplace(rCurrentLanguage,
+ lcl_NewUINameArray(STR_POOLNUMRULE_NUM_ARY, SAL_N_ELEMENTS(STR_POOLNUMRULE_NUM_ARY))).first;
+
+ return it->second;
+}
+
+const std::vector<OUString>& SwStyleNameMapper::GetTableStyleUINameArray()
+{
+ SvtSysLocale aSysLocale;
+ const LanguageTag& rCurrentLanguage = aSysLocale.GetUILanguageTag();
+ static std::map<LanguageTag, std::vector<OUString>> s_aTableStyleUINameArray;
+
+ auto it = s_aTableStyleUINameArray.find(rCurrentLanguage);
+ if (it == s_aTableStyleUINameArray.end())
+ it = s_aTableStyleUINameArray.emplace(rCurrentLanguage,
+ // 1 Writer resource string (XXX if this ever changes rather use offset math)
+ lcl_NewUINameArray(STR_TABSTYLE_ARY, SAL_N_ELEMENTS(STR_TABSTYLE_ARY),
+ static_cast<size_t>(SAL_N_ELEMENTS(STR_TABSTYLE_ARY) - 1))).first;
+
+ return it->second;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/DocumentTimerManager.cxx b/sw/source/core/doc/DocumentTimerManager.cxx
new file mode 100644
index 0000000000..13f85a2026
--- /dev/null
+++ b/sw/source/core/doc/DocumentTimerManager.cxx
@@ -0,0 +1,235 @@
+/* -*- 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 <DocumentTimerManager.hxx>
+
+#include <doc.hxx>
+#include <DocumentSettingManager.hxx>
+#include <IDocumentFieldsAccess.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <rootfrm.hxx>
+#include <viewsh.hxx>
+#include <unotools/lingucfg.hxx>
+#include <unotools/linguprops.hxx>
+#include <fldupde.hxx>
+#include <sfx2/progress.hxx>
+#include <viewopt.hxx>
+#include <docsh.hxx>
+#include <docfld.hxx>
+#include <fldbas.hxx>
+#include <vcl/scheduler.hxx>
+#include <comphelper/lok.hxx>
+#include <editsh.hxx>
+
+namespace sw
+{
+DocumentTimerManager::DocumentTimerManager(SwDoc& i_rSwdoc)
+ : m_rDoc(i_rSwdoc)
+ , m_nIdleBlockCount(0)
+ , m_bStartOnUnblock(false)
+ , m_aDocIdle(i_rSwdoc, "sw::DocumentTimerManager m_aDocIdle")
+ , m_aFireIdleJobsTimer("sw::DocumentTimerManager m_aFireIdleJobsTimer")
+ , m_bWaitForLokInit(true)
+{
+ m_aDocIdle.SetPriority(TaskPriority::LOWEST);
+ m_aDocIdle.SetInvokeHandler(LINK(this, DocumentTimerManager, DoIdleJobs));
+
+ m_aFireIdleJobsTimer.SetInvokeHandler(LINK(this, DocumentTimerManager, FireIdleJobsTimeout));
+ m_aFireIdleJobsTimer.SetTimeout(1000); // Enough time for LOK to render the first tiles.
+}
+
+void DocumentTimerManager::StartIdling()
+{
+ if (m_bWaitForLokInit && comphelper::LibreOfficeKit::isActive())
+ {
+ // Start the idle jobs only after a certain delay.
+ m_bWaitForLokInit = false;
+ StopIdling();
+ m_aFireIdleJobsTimer.Start();
+ return;
+ }
+
+ m_bWaitForLokInit = false;
+ m_bStartOnUnblock = true;
+ if (0 == m_nIdleBlockCount)
+ {
+ if (!m_aDocIdle.IsActive())
+ m_aDocIdle.Start();
+ else
+ Scheduler::Wakeup();
+ }
+}
+
+void DocumentTimerManager::StopIdling()
+{
+ m_bStartOnUnblock = false;
+ m_aDocIdle.Stop();
+}
+
+void DocumentTimerManager::BlockIdling()
+{
+ assert(SAL_MAX_UINT32 != m_nIdleBlockCount);
+ ++m_nIdleBlockCount;
+}
+
+void DocumentTimerManager::UnblockIdling()
+{
+ assert(0 != m_nIdleBlockCount);
+ --m_nIdleBlockCount;
+
+ if ((0 == m_nIdleBlockCount) && m_bStartOnUnblock)
+ {
+ if (!m_aDocIdle.IsActive())
+ m_aDocIdle.Start();
+ else
+ Scheduler::Wakeup();
+ }
+}
+
+IMPL_LINK(DocumentTimerManager, FireIdleJobsTimeout, Timer*, , void)
+{
+ // Now we can run the idle jobs, assuming we finished LOK initialization.
+ StartIdling();
+}
+
+DocumentTimerManager::IdleJob DocumentTimerManager::GetNextIdleJob() const
+{
+ SwRootFrame* pTmpRoot = m_rDoc.getIDocumentLayoutAccess().GetCurrentLayout();
+ if( pTmpRoot &&
+ !SfxProgress::GetActiveProgress( m_rDoc.GetDocShell() ) )
+ {
+ SwViewShell* pShell(m_rDoc.getIDocumentLayoutAccess().GetCurrentViewShell());
+ for(const SwViewShell& rSh : pShell->GetRingContainer())
+ if( rSh.ActionPend() )
+ return IdleJob::Busy;
+
+ if( pTmpRoot->IsNeedGrammarCheck() )
+ {
+ bool bIsOnlineSpell = pShell->GetViewOptions()->IsOnlineSpell();
+ bool bIsAutoGrammar = false;
+ SvtLinguConfig().GetProperty( UPN_IS_GRAMMAR_AUTO ) >>= bIsAutoGrammar;
+
+ if( bIsOnlineSpell && bIsAutoGrammar && m_rDoc.StartGrammarChecking( true ) )
+ return IdleJob::Grammar;
+ }
+
+ // If we're dragging re-layout doesn't occur so avoid a busy loop.
+ if (!pShell->HasDrawViewDrag())
+ {
+ for ( auto pLayout : m_rDoc.GetAllLayouts() )
+ {
+ if( pLayout->IsIdleFormat() )
+ return IdleJob::Layout;
+ }
+ }
+
+ SwFieldUpdateFlags nFieldUpdFlag = m_rDoc.GetDocumentSettingManager().getFieldUpdateFlags(true);
+ if( ( AUTOUPD_FIELD_ONLY == nFieldUpdFlag
+ || AUTOUPD_FIELD_AND_CHARTS == nFieldUpdFlag )
+ && m_rDoc.getIDocumentFieldsAccess().GetUpdateFields().IsFieldsDirty() )
+ {
+ if( m_rDoc.getIDocumentFieldsAccess().GetUpdateFields().IsInUpdateFields()
+ || m_rDoc.getIDocumentFieldsAccess().IsExpFieldsLocked() )
+ return IdleJob::Busy;
+ return IdleJob::Fields;
+ }
+ }
+
+ return IdleJob::None;
+}
+
+IMPL_LINK_NOARG( DocumentTimerManager, DoIdleJobs, Timer*, void )
+{
+#ifdef TIMELOG
+ static ::rtl::Logfile* pModLogFile = new ::rtl::Logfile( "First DoIdleJobs" );
+#endif
+ BlockIdling();
+ StopIdling();
+
+ IdleJob eJob = GetNextIdleJob();
+
+ switch ( eJob )
+ {
+ case IdleJob::Grammar:
+ m_rDoc.StartGrammarChecking();
+ break;
+
+ case IdleJob::Layout:
+ for ( auto pLayout : m_rDoc.GetAllLayouts() )
+ if( pLayout->IsIdleFormat() )
+ {
+ pLayout->GetCurrShell()->LayoutIdle();
+ break;
+ }
+ break;
+
+ case IdleJob::Fields:
+ {
+ SwViewShell* pShell( m_rDoc.getIDocumentLayoutAccess().GetCurrentViewShell() );
+ SwRootFrame* pTmpRoot = m_rDoc.getIDocumentLayoutAccess().GetCurrentLayout();
+
+ // Action brackets!
+ m_rDoc.getIDocumentFieldsAccess().GetUpdateFields().SetInUpdateFields( true );
+
+ pTmpRoot->StartAllAction();
+
+ // no jump on update of fields #i85168#
+ const bool bOldLockView = pShell->IsViewLocked();
+ pShell->LockView( true );
+
+ auto pChapterFieldType = m_rDoc.getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::Chapter );
+ pChapterFieldType->CallSwClientNotify(sw::LegacyModifyHint( nullptr, nullptr )); // ChapterField
+ m_rDoc.getIDocumentFieldsAccess().UpdateExpFields( nullptr, false ); // Updates ExpressionFields
+ m_rDoc.getIDocumentFieldsAccess().UpdateTableFields(nullptr); // Tables
+ m_rDoc.getIDocumentFieldsAccess().UpdateRefFields(); // References
+
+ // Validate and update the paragraph signatures.
+ if (SwEditShell* pSh = m_rDoc.GetEditShell())
+ pSh->ValidateAllParagraphSignatures(true);
+
+ pTmpRoot->EndAllAction();
+
+ pShell->LockView( bOldLockView );
+
+ m_rDoc.getIDocumentFieldsAccess().GetUpdateFields().SetInUpdateFields( false );
+ m_rDoc.getIDocumentFieldsAccess().GetUpdateFields().SetFieldsDirty( false );
+ break;
+ }
+
+ case IdleJob::Busy:
+ break;
+ case IdleJob::None:
+ break;
+ }
+
+ if ( IdleJob::None != eJob )
+ StartIdling();
+ UnblockIdling();
+
+#ifdef TIMELOG
+ if( pModLogFile && 1 != (long)pModLogFile )
+ delete pModLogFile, static_cast<long&>(pModLogFile) = 1;
+#endif
+}
+
+DocumentTimerManager::~DocumentTimerManager() {}
+
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/SwDocIdle.cxx b/sw/source/core/doc/SwDocIdle.cxx
new file mode 100644
index 0000000000..8c37642899
--- /dev/null
+++ b/sw/source/core/doc/SwDocIdle.cxx
@@ -0,0 +1,62 @@
+/* -*- 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 <view.hxx>
+#include <wrtsh.hxx>
+#include <doc.hxx>
+#include <docsh.hxx>
+#include <viewopt.hxx>
+#include <vcl/scheduler.hxx>
+
+#include <SwDocIdle.hxx>
+#include <IDocumentTimerAccess.hxx>
+
+namespace sw
+{
+
+sal_uInt64 SwDocIdle::UpdateMinPeriod( sal_uInt64 /* nTimeNow */ ) const
+{
+ bool bReadyForSchedule = true;
+
+ SwView* pView = m_rDoc.GetDocShell() ? m_rDoc.GetDocShell()->GetView() : nullptr;
+ if( pView )
+ {
+ SwWrtShell& rWrtShell = pView->GetWrtShell();
+ bReadyForSchedule = rWrtShell.GetViewOptions()->IsIdle();
+ }
+
+ if( bReadyForSchedule && !m_rDoc.getIDocumentTimerAccess().IsDocIdle() )
+ bReadyForSchedule = false;
+
+ return bReadyForSchedule
+ ? Scheduler::ImmediateTimeoutMs : Scheduler::InfiniteTimeoutMs;
+}
+
+SwDocIdle::SwDocIdle( SwDoc &doc, const char * pDebugIdleName )
+ : Idle(pDebugIdleName), m_rDoc( doc )
+{
+}
+
+SwDocIdle::~SwDocIdle()
+{
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/SwStyleNameMapper.cxx b/sw/source/core/doc/SwStyleNameMapper.cxx
new file mode 100644
index 0000000000..17f732d6d3
--- /dev/null
+++ b/sw/source/core/doc/SwStyleNameMapper.cxx
@@ -0,0 +1,775 @@
+/* -*- 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 <SwStyleNameMapper.hxx>
+#include <poolfmt.hxx>
+#include <strings.hrc>
+#include <swtypes.hxx>
+#include <unotools/syslocale.hxx>
+#include <i18nlangtag/languagetag.hxx>
+#include <map>
+
+#ifdef _NEED_TO_DEBUG_MAPPING
+#include <stdlib.h>
+#endif
+
+namespace
+{
+
+const OUString &
+lcl_GetSpecialExtraName(const OUString& rExtraName, const bool bIsUIName )
+{
+ const std::vector<OUString>& rExtraArr = bIsUIName
+ ? SwStyleNameMapper::GetExtraUINameArray()
+ : SwStyleNameMapper::GetExtraProgNameArray();
+ static const sal_uInt16 nIds[] =
+ {
+ RES_POOLCOLL_LABEL_DRAWING - RES_POOLCOLL_EXTRA_BEGIN,
+ RES_POOLCOLL_LABEL_ABB - RES_POOLCOLL_EXTRA_BEGIN,
+ RES_POOLCOLL_LABEL_TABLE - RES_POOLCOLL_EXTRA_BEGIN,
+ RES_POOLCOLL_LABEL_FRAME- RES_POOLCOLL_EXTRA_BEGIN,
+ RES_POOLCOLL_LABEL_FIGURE-RES_POOLCOLL_EXTRA_BEGIN,
+ 0
+ };
+ const sal_uInt16 * pIds;
+ for ( pIds = nIds; *pIds; ++pIds)
+ {
+ if (rExtraName == rExtraArr[ *pIds ])
+ {
+ return bIsUIName
+ ? SwStyleNameMapper::GetExtraProgNameArray()[*pIds]
+ : SwStyleNameMapper::GetExtraUINameArray()[*pIds];
+ }
+ }
+ return rExtraName;
+}
+
+bool lcl_SuffixIsUser(const OUString & rString)
+{
+ // Interesting, why the rest must be longer than 1 character? It is so
+ // since commit 4fbc9dd48b7cebb304010e7337b1bbc3936c7923 (2001-08-16)
+ return rString.getLength() > 8 && rString.endsWith(" (user)");
+}
+
+void lcl_CheckSuffixAndDelete(OUString & rString)
+{
+ if (lcl_SuffixIsUser(rString))
+ {
+ rString = rString.copy(0, rString.getLength() - 7);
+ }
+}
+
+NameToIdHash HashFromRange(sal_uInt16 nAcc) { return NameToIdHash(nAcc); }
+template <typename... Rest>
+NameToIdHash HashFromRange(sal_uInt16 nAcc, sal_uInt16 nBegin, sal_uInt16 nEnd,
+ const std::vector<OUString>& (*pFunc)(), Rest... rest)
+{
+ NameToIdHash hash(HashFromRange(nAcc + nEnd - nBegin, rest...));
+ sal_uInt16 nIndex, nId;
+ const std::vector<OUString>& rStrings = pFunc();
+ for (nIndex = 0, nId = nBegin; nId < nEnd; nId++, nIndex++)
+ hash[rStrings[nIndex]] = nId;
+ return hash;
+}
+
+template <auto initFunc> struct TablePair
+{
+ static const NameToIdHash& getMap(bool bProgName)
+ {
+ if (bProgName)
+ {
+ static const NameToIdHash s_aProgMap(initFunc(true));
+ return s_aProgMap;
+ }
+
+ SvtSysLocale aSysLocale;
+ const LanguageTag& rCurrentLanguage = aSysLocale.GetUILanguageTag();
+ static std::map<LanguageTag, NameToIdHash> s_aUIMap;
+
+ auto it = s_aUIMap.find(rCurrentLanguage);
+ if (it == s_aUIMap.end())
+ it = s_aUIMap.emplace(rCurrentLanguage, initFunc(false)).first;
+
+ return it->second;
+ }
+};
+
+NameToIdHash GetParaMap (bool bProgName)
+{
+ return HashFromRange(0,
+ RES_POOLCOLL_TEXT_BEGIN, RES_POOLCOLL_TEXT_END, bProgName ? &SwStyleNameMapper::GetTextProgNameArray : &SwStyleNameMapper::GetTextUINameArray,
+ RES_POOLCOLL_LISTS_BEGIN, RES_POOLCOLL_LISTS_END, bProgName ? &SwStyleNameMapper::GetListsProgNameArray : &SwStyleNameMapper::GetListsUINameArray,
+ RES_POOLCOLL_EXTRA_BEGIN, RES_POOLCOLL_EXTRA_END, bProgName ? &SwStyleNameMapper::GetExtraProgNameArray : &SwStyleNameMapper::GetExtraUINameArray,
+ RES_POOLCOLL_REGISTER_BEGIN, RES_POOLCOLL_REGISTER_END, bProgName ? &SwStyleNameMapper::GetRegisterProgNameArray : &SwStyleNameMapper::GetRegisterUINameArray,
+ RES_POOLCOLL_DOC_BEGIN, RES_POOLCOLL_DOC_END, bProgName ? &SwStyleNameMapper::GetDocProgNameArray : &SwStyleNameMapper::GetDocUINameArray,
+ RES_POOLCOLL_HTML_BEGIN, RES_POOLCOLL_HTML_END, bProgName ? &SwStyleNameMapper::GetHTMLProgNameArray : &SwStyleNameMapper::GetHTMLUINameArray
+ );
+};
+
+NameToIdHash GetCharMap(bool bProgName)
+{
+ return HashFromRange(0,
+ RES_POOLCHR_NORMAL_BEGIN, RES_POOLCHR_NORMAL_END, bProgName ? &SwStyleNameMapper::GetChrFormatProgNameArray : &SwStyleNameMapper::GetChrFormatUINameArray,
+ RES_POOLCHR_HTML_BEGIN, RES_POOLCHR_HTML_END, bProgName ? &SwStyleNameMapper::GetHTMLChrFormatProgNameArray : &SwStyleNameMapper::GetHTMLChrFormatUINameArray
+ );
+};
+
+NameToIdHash GetFrameMap(bool bProgName)
+{
+ return HashFromRange(0,
+ RES_POOLFRM_BEGIN, RES_POOLFRM_END, bProgName ? &SwStyleNameMapper::GetFrameFormatProgNameArray : &SwStyleNameMapper::GetFrameFormatUINameArray
+ );
+};
+
+NameToIdHash GetPageMap(bool bProgName)
+{
+ return HashFromRange(0,
+ RES_POOLPAGE_BEGIN, RES_POOLPAGE_END, bProgName ? &SwStyleNameMapper::GetPageDescProgNameArray : &SwStyleNameMapper::GetPageDescUINameArray
+ );
+};
+
+NameToIdHash GetNumRuleMap(bool bProgName)
+{
+ return HashFromRange(0,
+ RES_POOLNUMRULE_BEGIN, RES_POOLNUMRULE_END, bProgName ? &SwStyleNameMapper::GetNumRuleProgNameArray : &SwStyleNameMapper::GetNumRuleUINameArray
+ );
+};
+
+NameToIdHash GetTableStyleMap(bool bProgName)
+{
+ return HashFromRange(0,
+ RES_POOLTABLESTYLE_BEGIN, RES_POOLTABLESTYLE_END, bProgName ? &SwStyleNameMapper::GetTableStyleProgNameArray : &SwStyleNameMapper::GetTableStyleUINameArray
+ );
+};
+
+NameToIdHash GetCellStyleMap(bool bProgName)
+{
+ return HashFromRange(0,
+ RES_POOLCELLSTYLE_BEGIN, RES_POOLCELLSTYLE_END, bProgName ? &SwStyleNameMapper::GetCellStyleProgNameArray : &SwStyleNameMapper::GetCellStyleUINameArray
+ );
+};
+
+} // namespace
+
+#ifdef _NEED_TO_DEBUG_MAPPING
+void SwStyleNameMapper::testNameTable( SwGetPoolIdFromName const nFamily, sal_uInt16 const nStartIndex, sal_uInt16 const nEndIndex )
+{
+ sal_uInt16 nIndex;
+ sal_uInt16 nId;
+
+ for ( nIndex = 0, nId = nStartIndex ; nId < nEndIndex ; nId++,nIndex++ )
+ {
+ OUString aString, bString;
+ FillUIName ( nId, aString );
+ bString = GetProgName ( nFamily, aString );
+ sal_uInt16 nNewId = GetPoolIdFromProgName ( bString, nFamily );
+ FillProgName ( nNewId, aString );
+ bString = GetUIName ( aString, nFamily );
+ nNewId = GetPoolIdFromUIName ( aString, nFamily );
+ if ( nNewId != nId )
+ abort();
+ }
+}
+#endif
+
+const NameToIdHash & SwStyleNameMapper::getHashTable ( SwGetPoolIdFromName eFlags, bool bProgName )
+{
+#ifdef _NEED_TO_DEBUG_MAPPING
+ static bool bTested = false;
+ if ( !bTested )
+ {
+ bTested = true;
+
+ testNameTable( SwGetPoolIdFromName::TxtColl, RES_POOLCOLL_TEXT_BEGIN, RES_POOLCOLL_TEXT_END );
+ testNameTable( SwGetPoolIdFromName::TxtColl, RES_POOLCOLL_LISTS_BEGIN, RES_POOLCOLL_LISTS_END );
+ testNameTable( SwGetPoolIdFromName::TxtColl, RES_POOLCOLL_EXTRA_BEGIN, RES_POOLCOLL_EXTRA_END );
+ testNameTable( SwGetPoolIdFromName::TxtColl, RES_POOLCOLL_REGISTER_BEGIN, RES_POOLCOLL_REGISTER_END );
+ testNameTable( SwGetPoolIdFromName::TxtColl, RES_POOLCOLL_DOC_BEGIN, RES_POOLCOLL_DOC_END );
+ testNameTable( SwGetPoolIdFromName::TxtColl, RES_POOLCOLL_HTML_BEGIN, RES_POOLCOLL_HTML_END );
+ testNameTable( SwGetPoolIdFromName::ChrFmt, RES_POOLCHR_NORMAL_BEGIN, RES_POOLCHR_NORMAL_END );
+ testNameTable( SwGetPoolIdFromName::ChrFmt, RES_POOLCHR_HTML_BEGIN, RES_POOLCHR_HTML_END );
+ testNameTable( SwGetPoolIdFromName::FrmFmt, RES_POOLFRM_BEGIN, RES_POOLFRM_END );
+ testNameTable( SwGetPoolIdFromName::PageDesc, RES_POOLPAGE_BEGIN, RES_POOLPAGE_END );
+ testNameTable( SwGetPoolIdFromName::NumRule, RES_POOLNUMRULE_BEGIN, RES_POOLNUMRULE_END );
+ }
+#endif
+
+ switch ( eFlags )
+ {
+ case SwGetPoolIdFromName::TxtColl:
+ return TablePair<GetParaMap>::getMap(bProgName);
+ case SwGetPoolIdFromName::ChrFmt:
+ return TablePair<GetCharMap>::getMap(bProgName);
+ case SwGetPoolIdFromName::FrmFmt:
+ return TablePair<GetFrameMap>::getMap(bProgName);
+ case SwGetPoolIdFromName::PageDesc:
+ return TablePair<GetPageMap>::getMap(bProgName);
+ case SwGetPoolIdFromName::NumRule:
+ return TablePair<GetNumRuleMap>::getMap(bProgName);
+ case SwGetPoolIdFromName::TabStyle:
+ return TablePair<GetTableStyleMap>::getMap(bProgName);
+ case SwGetPoolIdFromName::CellStyle:
+ return TablePair<GetCellStyleMap>::getMap(bProgName);
+ }
+
+ assert(false); // must not reach here
+ abort();
+}
+
+// This gets the UI name from the programmatic name
+const OUString& SwStyleNameMapper::GetUIName(const OUString& rName,
+ SwGetPoolIdFromName const eFlags)
+{
+ sal_uInt16 nId = GetPoolIdFromProgName ( rName, eFlags );
+ return nId != USHRT_MAX ? GetUIName( nId, rName ) : rName;
+}
+
+// Get the programmatic name from the UI name
+const OUString& SwStyleNameMapper::GetProgName(
+ const OUString& rName, SwGetPoolIdFromName const eFlags)
+{
+ sal_uInt16 nId = GetPoolIdFromUIName ( rName, eFlags );
+ return nId != USHRT_MAX ? GetProgName( nId, rName ) : rName;
+}
+
+// Get the programmatic name from the UI name in rName and put it into rFillName
+void SwStyleNameMapper::FillProgName(
+ const OUString& rName, OUString& rFillName,
+ SwGetPoolIdFromName const eFlags)
+{
+ sal_uInt16 nId = GetPoolIdFromUIName ( rName, eFlags );
+ if ( nId == USHRT_MAX )
+ {
+ // rName isn't in our UI name table...check if it's in the programmatic one
+ nId = GetPoolIdFromProgName ( rName, eFlags );
+
+ rFillName = rName;
+ if (nId == USHRT_MAX )
+ {
+ // It isn't ...make sure the suffix isn't already " (user)"...if it is,
+ // we need to add another one
+ if (lcl_SuffixIsUser(rFillName))
+ rFillName += " (user)";
+ }
+ else
+ {
+ // It's in the programmatic name table...append suffix
+ rFillName += " (user)";
+ }
+ }
+ else
+ {
+ // If we aren't trying to disambiguate, then just do a normal fill
+ fillNameFromId(nId, rFillName, true);
+ }
+
+ if (eFlags == SwGetPoolIdFromName::ChrFmt && rName == SwResId(STR_POOLCHR_STANDARD))
+ rFillName = "Standard";
+}
+
+// Get the UI name from the programmatic name in rName and put it into rFillName
+void SwStyleNameMapper::FillUIName(
+ const OUString& rName, OUString& rFillName,
+ SwGetPoolIdFromName const eFlags)
+{
+ OUString aName = rName;
+ if (eFlags == SwGetPoolIdFromName::ChrFmt && rName == "Standard")
+ aName = SwResId(STR_POOLCHR_STANDARD);
+
+ sal_uInt16 nId = GetPoolIdFromProgName ( aName, eFlags );
+ if ( nId == USHRT_MAX )
+ {
+ rFillName = aName;
+ // aName isn't in our Prog name table...check if it has a " (user)" suffix, if so remove it
+ lcl_CheckSuffixAndDelete ( rFillName );
+ }
+ else
+ {
+ // If we aren't trying to disambiguate, then just do a normal fill
+ fillNameFromId(nId, rFillName, false);
+ }
+}
+
+const OUString& SwStyleNameMapper::getNameFromId(
+ sal_uInt16 const nId, const OUString& rFillName, bool const bProgName)
+{
+ sal_uInt16 nStt = 0;
+ const std::vector<OUString>* pStrArr = nullptr;
+
+ switch( (USER_FMT | COLL_GET_RANGE_BITS | POOLGRP_NOCOLLID) & nId )
+ {
+ case COLL_TEXT_BITS:
+ if( RES_POOLCOLL_TEXT_BEGIN <= nId && nId < RES_POOLCOLL_TEXT_END )
+ {
+ pStrArr = bProgName ? &GetTextProgNameArray() : &GetTextUINameArray();
+ nStt = RES_POOLCOLL_TEXT_BEGIN;
+ }
+ break;
+ case COLL_LISTS_BITS:
+ if( RES_POOLCOLL_LISTS_BEGIN <= nId && nId < RES_POOLCOLL_LISTS_END )
+ {
+ pStrArr = bProgName ? &GetListsProgNameArray() : &GetListsUINameArray();
+ nStt = RES_POOLCOLL_LISTS_BEGIN;
+ }
+ break;
+ case COLL_EXTRA_BITS:
+ if( RES_POOLCOLL_EXTRA_BEGIN <= nId && nId < RES_POOLCOLL_EXTRA_END )
+ {
+ pStrArr = bProgName ? &GetExtraProgNameArray() : &GetExtraUINameArray();
+ nStt = RES_POOLCOLL_EXTRA_BEGIN;
+ }
+ break;
+ case COLL_REGISTER_BITS:
+ if( RES_POOLCOLL_REGISTER_BEGIN <= nId && nId < RES_POOLCOLL_REGISTER_END )
+ {
+ pStrArr = bProgName ? &GetRegisterProgNameArray() : &GetRegisterUINameArray();
+ nStt = RES_POOLCOLL_REGISTER_BEGIN;
+ }
+ break;
+ case COLL_DOC_BITS:
+ if( RES_POOLCOLL_DOC_BEGIN <= nId && nId < RES_POOLCOLL_DOC_END )
+ {
+ pStrArr = bProgName ? &GetDocProgNameArray() : &GetDocUINameArray();
+ nStt = RES_POOLCOLL_DOC_BEGIN;
+ }
+ break;
+ case COLL_HTML_BITS:
+ if( RES_POOLCOLL_HTML_BEGIN <= nId && nId < RES_POOLCOLL_HTML_END )
+ {
+ pStrArr = bProgName ? &GetHTMLProgNameArray() : &GetHTMLUINameArray();
+ nStt = RES_POOLCOLL_HTML_BEGIN;
+ }
+ break;
+ case POOLGRP_CHARFMT:
+ if( RES_POOLCHR_NORMAL_BEGIN <= nId && nId < RES_POOLCHR_NORMAL_END )
+ {
+ pStrArr = bProgName ? &GetChrFormatProgNameArray() : &GetChrFormatUINameArray();
+ nStt = RES_POOLCHR_NORMAL_BEGIN;
+ }
+ else if( RES_POOLCHR_HTML_BEGIN <= nId && nId < RES_POOLCHR_HTML_END )
+ {
+ pStrArr = bProgName ? &GetHTMLChrFormatProgNameArray() : &GetHTMLChrFormatUINameArray();
+ nStt = RES_POOLCHR_HTML_BEGIN;
+ }
+ break;
+ case POOLGRP_FRAMEFMT:
+ if( RES_POOLFRM_BEGIN <= nId && nId < RES_POOLFRM_END )
+ {
+ pStrArr = bProgName ? &GetFrameFormatProgNameArray() : &GetFrameFormatUINameArray();
+ nStt = RES_POOLFRM_BEGIN;
+ }
+ break;
+ case POOLGRP_PAGEDESC:
+ if( RES_POOLPAGE_BEGIN <= nId && nId < RES_POOLPAGE_END )
+ {
+ pStrArr = bProgName ? &GetPageDescProgNameArray() : &GetPageDescUINameArray();
+ nStt = RES_POOLPAGE_BEGIN;
+ }
+ break;
+ case POOLGRP_NUMRULE:
+ if( RES_POOLNUMRULE_BEGIN <= nId && nId < RES_POOLNUMRULE_END )
+ {
+ pStrArr = bProgName ? &GetNumRuleProgNameArray() : &GetNumRuleUINameArray();
+ nStt = RES_POOLNUMRULE_BEGIN;
+ }
+ break;
+ case POOLGRP_TABSTYLE:
+ if( RES_POOLTABLESTYLE_BEGIN <= nId && nId < RES_POOLTABLESTYLE_END )
+ {
+ pStrArr = bProgName ? &GetTableStyleProgNameArray() : &GetTableStyleUINameArray();
+ nStt = RES_POOLTABLESTYLE_BEGIN;
+ }
+ break;
+ }
+ return pStrArr ? pStrArr->operator[](nId - nStt) : rFillName;
+}
+
+void SwStyleNameMapper::fillNameFromId(
+ sal_uInt16 const nId, OUString& rFillName, bool bProgName)
+{
+ rFillName = getNameFromId(nId, rFillName, bProgName);
+}
+
+// Get the UI name from the pool ID
+void SwStyleNameMapper::FillUIName(sal_uInt16 const nId, OUString& rFillName)
+{
+ fillNameFromId(nId, rFillName, false);
+}
+
+// Get the UI name from the pool ID
+const OUString& SwStyleNameMapper::GetUIName(
+ sal_uInt16 const nId, const OUString& rName)
+{
+ return getNameFromId(nId, rName, false);
+}
+
+// Get the programmatic name from the pool ID
+void SwStyleNameMapper::FillProgName(sal_uInt16 nId, OUString& rFillName)
+{
+ fillNameFromId(nId, rFillName, true);
+}
+
+// Get the programmatic name from the pool ID
+const OUString&
+SwStyleNameMapper::GetProgName(sal_uInt16 const nId, const OUString& rName)
+{
+ return getNameFromId(nId, rName, true);
+}
+
+// This gets the PoolId from the UI Name
+sal_uInt16 SwStyleNameMapper::GetPoolIdFromUIName(
+ const OUString& rName, SwGetPoolIdFromName const eFlags)
+{
+ const NameToIdHash & rHashMap = getHashTable ( eFlags, false );
+ NameToIdHash::const_iterator aIter = rHashMap.find(rName);
+ return aIter != rHashMap.end() ? (*aIter).second : USHRT_MAX;
+}
+
+// Get the Pool ID from the programmatic name
+sal_uInt16 SwStyleNameMapper::GetPoolIdFromProgName(
+ const OUString& rName, SwGetPoolIdFromName const eFlags)
+{
+ const NameToIdHash & rHashMap = getHashTable ( eFlags, true );
+ NameToIdHash::const_iterator aIter = rHashMap.find(rName);
+ return aIter != rHashMap.end() ? (*aIter).second : USHRT_MAX;
+}
+
+// Hard coded Programmatic Name tables
+
+/// returns an empty array because Cell Names aren't translated
+const std::vector<OUString>& SwStyleNameMapper::GetCellStyleUINameArray()
+{
+ static const std::vector<OUString> s_aCellStyleUINameArray;
+ return s_aCellStyleUINameArray;
+}
+
+const std::vector<OUString>& SwStyleNameMapper::GetTextProgNameArray()
+{
+ static const std::vector<OUString> s_aTextProgNameArray = {
+ "Standard", // RES_POOLCOLL_STANDARD
+ "Text body",
+ "First line indent",
+ "Hanging indent",
+ "Text body indent",
+ "Salutation",
+ "Signature",
+ "List Indent", // RES_POOLCOLL_CONFRONTATION
+ "Marginalia",
+ "Heading",
+ "Heading 1",
+ "Heading 2",
+ "Heading 3",
+ "Heading 4",
+ "Heading 5",
+ "Heading 6",
+ "Heading 7",
+ "Heading 8",
+ "Heading 9",
+ "Heading 10", // RES_POOLCOLL_TEXT_END
+ };
+ return s_aTextProgNameArray;
+}
+
+const std::vector<OUString>& SwStyleNameMapper::GetListsProgNameArray()
+{
+ static const std::vector<OUString> s_aListsProgNameArray = {
+ "List", // STR_POCO_PRGM_NUMBER_BULLET_BASE
+ "Numbering 1 Start", // STR_POCO_PRGM_NUM_LEVEL1S
+ "Numbering 1",
+ "Numbering 1 End",
+ "Numbering 1 Cont.",
+ "Numbering 2 Start",
+ "Numbering 2",
+ "Numbering 2 End",
+ "Numbering 2 Cont.",
+ "Numbering 3 Start",
+ "Numbering 3",
+ "Numbering 3 End",
+ "Numbering 3 Cont.",
+ "Numbering 4 Start",
+ "Numbering 4",
+ "Numbering 4 End",
+ "Numbering 4 Cont.",
+ "Numbering 5 Start",
+ "Numbering 5",
+ "Numbering 5 End",
+ "Numbering 5 Cont.",
+ "List 1 Start",
+ "List 1",
+ "List 1 End",
+ "List 1 Cont.",
+ "List 2 Start",
+ "List 2",
+ "List 2 End",
+ "List 2 Cont.",
+ "List 3 Start",
+ "List 3",
+ "List 3 End",
+ "List 3 Cont.",
+ "List 4 Start",
+ "List 4",
+ "List 4 End",
+ "List 4 Cont.",
+ "List 5 Start",
+ "List 5",
+ "List 5 End",
+ "List 5 Cont.", // STR_POCO_PRGM_BULLET_NONUM5
+ };
+ return s_aListsProgNameArray;
+}
+
+const std::vector<OUString>& SwStyleNameMapper::GetExtraProgNameArray()
+{
+ static const std::vector<OUString> s_aExtraProgNameArray = {
+ "Header and Footer", // RES_POOLCOLL_EXTRA_BEGIN
+ "Header",
+ "Header left",
+ "Header right",
+ "Footer",
+ "Footer left",
+ "Footer right",
+ "Table Contents",
+ "Table Heading",
+ "Caption",
+ "Illustration",
+ "Table",
+ "Text",
+ "Figure", // RES_POOLCOLL_LABEL_FIGURE
+ "Frame contents",
+ "Footnote",
+ "Addressee",
+ "Sender",
+ "Endnote",
+ "Drawing",
+ "Comment", // RES_POOLCOLL_COMMENT
+ };
+ return s_aExtraProgNameArray;
+}
+
+const std::vector<OUString>& SwStyleNameMapper::GetRegisterProgNameArray()
+{
+ static const std::vector<OUString> s_aRegisterProgNameArray = {
+ "Index", // STR_POCO_PRGM_REGISTER_BASE
+ "Index Heading", // STR_POCO_PRGM_TOX_IDXH
+ "Index 1",
+ "Index 2",
+ "Index 3",
+ "Index Separator",
+ "Contents Heading",
+ "Contents 1",
+ "Contents 2",
+ "Contents 3",
+ "Contents 4",
+ "Contents 5",
+ "User Index Heading",
+ "User Index 1",
+ "User Index 2",
+ "User Index 3",
+ "User Index 4",
+ "User Index 5",
+ "Contents 6",
+ "Contents 7",
+ "Contents 8",
+ "Contents 9",
+ "Contents 10",
+ "Figure Index Heading",
+ "Figure Index 1",
+ "Object index heading",
+ "Object index 1",
+ "Table index heading",
+ "Table index 1",
+ "Bibliography Heading",
+ "Bibliography 1",
+ "User Index 6",
+ "User Index 7",
+ "User Index 8",
+ "User Index 9",
+ "User Index 10", // STR_POCO_PRGM_TOX_USER10
+ };
+ return s_aRegisterProgNameArray;
+}
+
+const std::vector<OUString>& SwStyleNameMapper::GetDocProgNameArray()
+{
+ static const std::vector<OUString> s_aDocProgNameArray = {
+ "Title", // STR_POCO_PRGM_DOC_TITLE
+ "Subtitle",
+ "Appendix",
+ };
+ return s_aDocProgNameArray;
+}
+
+const std::vector<OUString>& SwStyleNameMapper::GetHTMLProgNameArray()
+{
+ static const std::vector<OUString> s_aHTMLProgNameArray = {
+ "Quotations",
+ "Preformatted Text",
+ "Horizontal Line",
+ "List Contents",
+ "List Heading", // STR_POCO_PRGM_HTML_DT
+ };
+ return s_aHTMLProgNameArray;
+}
+
+const std::vector<OUString>& SwStyleNameMapper::GetFrameFormatProgNameArray()
+{
+ static const std::vector<OUString> s_aFrameFormatProgNameArray = {
+ "Frame", // RES_POOLFRM_FRAME
+ "Graphics",
+ "OLE",
+ "Formula",
+ "Marginalia",
+ "Watermark",
+ "Labels", // RES_POOLFRM_LABEL
+ };
+ return s_aFrameFormatProgNameArray;
+}
+
+const std::vector<OUString>& SwStyleNameMapper::GetChrFormatProgNameArray()
+{
+ static const std::vector<OUString> s_aChrFormatProgNameArray = {
+ "Footnote Symbol", // RES_POOLCHR_FOOTNOTE
+ "Page Number",
+ "Caption characters",
+ "Drop Caps",
+ "Numbering Symbols",
+ "Bullet Symbols",
+ "Internet link",
+ "Visited Internet Link",
+ "Placeholder",
+ "Index Link",
+ "Endnote Symbol",
+ "Line numbering",
+ "Main index entry",
+ "Footnote anchor",
+ "Endnote anchor",
+ "Rubies", // RES_POOLCHR_RUBYTEXT
+ "Vertical Numbering Symbols", // RES_POOLCHR_VERT_NUMBER
+ };
+ return s_aChrFormatProgNameArray;
+}
+
+const std::vector<OUString>& SwStyleNameMapper::GetHTMLChrFormatProgNameArray()
+{
+ static const std::vector<OUString> s_aHTMLChrFormatProgNameArray = {
+ "Emphasis", // RES_POOLCHR_HTML_EMPHASIS
+ "Citation",
+ "Strong Emphasis",
+ "Source Text",
+ "Example",
+ "User Entry",
+ "Variable",
+ "Definition",
+ "Teletype", // RES_POOLCHR_HTML_TELETYPE
+ };
+ return s_aHTMLChrFormatProgNameArray;
+}
+
+const std::vector<OUString>& SwStyleNameMapper::GetPageDescProgNameArray()
+{
+ static const std::vector<OUString> s_aPageDescProgNameArray = {
+ "Standard", // STR_POOLPAGE_PRGM_STANDARD
+ "First Page",
+ "Left Page",
+ "Right Page",
+ "Envelope",
+ "Index",
+ "HTML",
+ "Footnote",
+ "Endnote", // STR_POOLPAGE_PRGM_ENDNOTE
+ "Landscape",
+ };
+ return s_aPageDescProgNameArray;
+}
+
+const std::vector<OUString>& SwStyleNameMapper::GetNumRuleProgNameArray()
+{
+ static const std::vector<OUString> s_aNumRuleProgNameArray = {
+ "No List",
+ "Numbering 123", // STR_POOLNUMRULE_PRGM_NUM1
+ "Numbering ABC",
+ "Numbering abc",
+ "Numbering IVX",
+ "Numbering ivx",
+ "List 1",
+ "List 2",
+ "List 3",
+ "List 4",
+ "List 5", // STR_POOLNUMRULE_PRGM_BUL5
+ };
+ return s_aNumRuleProgNameArray;
+}
+
+const std::vector<OUString>& SwStyleNameMapper::GetTableStyleProgNameArray()
+{
+ // XXX MUST match the entries of STR_TABSTYLE_ARY in
+ // sw/source/core/doc/DocumentStylePoolManager.cxx and MUST match the order of
+ // RES_POOL_TABLESTYLE_TYPE in sw/inc/poolfmt.hxx
+ static const std::vector<OUString> s_aTableStyleProgNameArray = {
+ "Default Style", // RES_POOLTABLESTYLE_DEFAULT
+ "3D", // RES_POOLTABLESTYLE_3D
+ "Black 1", // RES_POOLTABLESTYLE_BLACK1
+ "Black 2", // RES_POOLTABLESTYLE_BLACK2
+ "Blue", // RES_POOLTABLESTYLE_BLUE
+ "Brown", // RES_POOLTABLESTYLE_BROWN
+ "Currency", // RES_POOLTABLESTYLE_CURRENCY
+ "Currency 3D", // RES_POOLTABLESTYLE_CURRENCY_3D
+ "Currency Gray", // RES_POOLTABLESTYLE_CURRENCY_GRAY
+ "Currency Lavender", // RES_POOLTABLESTYLE_CURRENCY_LAVENDER
+ "Currency Turquoise", // RES_POOLTABLESTYLE_CURRENCY_TURQUOISE
+ "Gray", // RES_POOLTABLESTYLE_GRAY
+ "Green", // RES_POOLTABLESTYLE_GREEN
+ "Lavender", // RES_POOLTABLESTYLE_LAVENDER
+ "Red", // RES_POOLTABLESTYLE_RED
+ "Turquoise", // RES_POOLTABLESTYLE_TURQUOISE
+ "Yellow", // RES_POOLTABLESTYLE_YELLOW
+ "Academic", // RES_POOLTABLESTYLE_LO6_ACADEMIC
+ "Box List Blue", // RES_POOLTABLESTYLE_LO6_BOX_LIST_BLUE
+ "Box List Green", // RES_POOLTABLESTYLE_LO6_BOX_LIST_GREEN
+ "Box List Red", // RES_POOLTABLESTYLE_LO6_BOX_LIST_RED
+ "Box List Yellow", // RES_POOLTABLESTYLE_LO6_BOX_LIST_YELLOW
+ "Elegant", // RES_POOLTABLESTYLE_LO6_ELEGANT
+ "Financial", // RES_POOLTABLESTYLE_LO6_FINANCIAL
+ "Simple Grid Columns", // RES_POOLTABLESTYLE_LO6_SIMPLE_GRID_COLUMNS
+ "Simple Grid Rows", // RES_POOLTABLESTYLE_LO6_SIMPLE_GRID_ROWS
+ "Simple List Shaded", // RES_POOLTABLESTYLE_LO6_SIMPLE_LIST_SHADED
+ };
+ return s_aTableStyleProgNameArray;
+}
+
+/// returns an empty array because Cell Names aren't translated
+const std::vector<OUString>& SwStyleNameMapper::GetCellStyleProgNameArray()
+{
+ static const std::vector<OUString> s_aCellStyleProgNameArray;
+ return s_aCellStyleProgNameArray;
+}
+
+const OUString &
+SwStyleNameMapper::GetSpecialExtraProgName(const OUString& rExtraUIName)
+{
+ return lcl_GetSpecialExtraName( rExtraUIName, true );
+}
+
+const OUString &
+SwStyleNameMapper::GetSpecialExtraUIName(const OUString& rExtraProgName)
+{
+ return lcl_GetSpecialExtraName( rExtraProgName, false );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/acmplwrd.cxx b/sw/source/core/doc/acmplwrd.cxx
new file mode 100644
index 0000000000..38011750bd
--- /dev/null
+++ b/sw/source/core/doc/acmplwrd.cxx
@@ -0,0 +1,420 @@
+/* -*- 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 <tools/urlobj.hxx>
+#include <hintids.hxx>
+#include <acmplwrd.hxx>
+#include <doc.hxx>
+#include <pagedesc.hxx>
+#include <poolfmt.hxx>
+#include <svl/listener.hxx>
+#include <IDocumentStylePoolAccess.hxx>
+#include <editeng/svxacorr.hxx>
+#include <osl/diagnose.h>
+
+#include <editeng/acorrcfg.hxx>
+#include <sfx2/docfile.hxx>
+#include <docsh.hxx>
+
+#include <cassert>
+#include <vector>
+
+class SwAutoCompleteClient : public SvtListener
+{
+ SwAutoCompleteWord* m_pAutoCompleteWord;
+ SwDoc* m_pDoc;
+#if OSL_DEBUG_LEVEL > 0
+ static sal_uLong s_nSwAutoCompleteClientCount;
+#endif
+public:
+ SwAutoCompleteClient(SwAutoCompleteWord& rToTell, SwDoc& rSwDoc);
+ SwAutoCompleteClient(const SwAutoCompleteClient& rClient);
+ virtual ~SwAutoCompleteClient() override;
+
+ SwAutoCompleteClient& operator=(const SwAutoCompleteClient& rClient);
+
+ const SwDoc& GetDoc() const {return *m_pDoc;}
+#if OSL_DEBUG_LEVEL > 0
+ static sal_uLong GetElementCount() {return s_nSwAutoCompleteClientCount;}
+#endif
+protected:
+ virtual void Notify(const SfxHint&) override;
+private:
+ void DocumentDying();
+};
+
+class SwAutoCompleteWord_Impl
+{
+ std::vector<SwAutoCompleteClient>
+ m_aClientVector;
+ SwAutoCompleteWord& m_rAutoCompleteWord;
+public:
+ explicit SwAutoCompleteWord_Impl(SwAutoCompleteWord& rParent) :
+ m_rAutoCompleteWord(rParent){}
+ void AddDocument(SwDoc& rDoc);
+ void RemoveDocument(const SwDoc& rDoc);
+};
+
+class SwAutoCompleteString
+ : public editeng::IAutoCompleteString
+{
+#if OSL_DEBUG_LEVEL > 0
+ static sal_uLong s_nSwAutoCompleteStringCount;
+#endif
+ std::vector<const SwDoc*> m_aSourceDocs;
+ public:
+ SwAutoCompleteString(const OUString& rStr, sal_Int32 nLen);
+
+ virtual ~SwAutoCompleteString() override;
+ void AddDocument(const SwDoc& rDoc);
+ //returns true if last document reference has been removed
+ bool RemoveDocument(const SwDoc& rDoc);
+#if OSL_DEBUG_LEVEL > 0
+ static sal_uLong GetElementCount() {return s_nSwAutoCompleteStringCount;}
+#endif
+};
+#if OSL_DEBUG_LEVEL > 0
+ sal_uLong SwAutoCompleteClient::s_nSwAutoCompleteClientCount = 0;
+ sal_uLong SwAutoCompleteString::s_nSwAutoCompleteStringCount = 0;
+#endif
+
+SwAutoCompleteClient::SwAutoCompleteClient(SwAutoCompleteWord& rToTell, SwDoc& rSwDoc) :
+ m_pAutoCompleteWord(&rToTell),
+ m_pDoc(&rSwDoc)
+{
+ StartListening(m_pDoc->getIDocumentStylePoolAccess().GetPageDescFromPool(RES_POOLPAGE_STANDARD)->GetNotifier());
+#if OSL_DEBUG_LEVEL > 0
+ ++s_nSwAutoCompleteClientCount;
+#endif
+}
+
+SwAutoCompleteClient::SwAutoCompleteClient(const SwAutoCompleteClient& rClient) :
+ SvtListener(),
+ m_pAutoCompleteWord(rClient.m_pAutoCompleteWord),
+ m_pDoc(rClient.m_pDoc)
+{
+ StartListening(m_pDoc->getIDocumentStylePoolAccess().GetPageDescFromPool(RES_POOLPAGE_STANDARD)->GetNotifier());
+#if OSL_DEBUG_LEVEL > 0
+ ++s_nSwAutoCompleteClientCount;
+#endif
+}
+
+SwAutoCompleteClient::~SwAutoCompleteClient()
+{
+#if OSL_DEBUG_LEVEL > 0
+ --s_nSwAutoCompleteClientCount;
+#else
+ (void) this;
+#endif
+}
+
+SwAutoCompleteClient& SwAutoCompleteClient::operator=(const SwAutoCompleteClient& rClient)
+{
+ m_pAutoCompleteWord = rClient.m_pAutoCompleteWord;
+ m_pDoc = rClient.m_pDoc;
+ CopyAllBroadcasters(rClient);
+ return *this;
+}
+
+void SwAutoCompleteClient::DocumentDying()
+{
+ EndListeningAll();
+ m_pAutoCompleteWord->DocumentDying(*m_pDoc);
+}
+
+void SwAutoCompleteClient::Notify(const SfxHint& rHint)
+{
+ switch(rHint.GetId())
+ {
+ case SfxHintId::Dying:
+ DocumentDying();
+ return;
+ case SfxHintId::SwLegacyModify:
+ {
+ auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
+ switch(pLegacy->GetWhich())
+ {
+ case RES_REMOVE_UNO_OBJECT:
+ case RES_OBJECTDYING:
+ DocumentDying();
+ return;
+ default:
+ return;
+ }
+ }
+ default:
+ return;
+ }
+}
+
+void SwAutoCompleteWord_Impl::AddDocument(SwDoc& rDoc)
+{
+ if (std::any_of(m_aClientVector.begin(), m_aClientVector.end(),
+ [&rDoc](SwAutoCompleteClient& rClient) { return &rClient.GetDoc() == &rDoc; }))
+ return;
+ m_aClientVector.emplace_back(m_rAutoCompleteWord, rDoc);
+}
+
+void SwAutoCompleteWord_Impl::RemoveDocument(const SwDoc& rDoc)
+{
+ auto aIt = std::find_if(m_aClientVector.begin(), m_aClientVector.end(),
+ [&rDoc](SwAutoCompleteClient& rClient) { return &rClient.GetDoc() == &rDoc; });
+ if (aIt != m_aClientVector.end())
+ m_aClientVector.erase(aIt);
+}
+
+SwAutoCompleteString::SwAutoCompleteString(
+ const OUString& rStr, sal_Int32 const nLen)
+ : editeng::IAutoCompleteString(rStr.copy(0, nLen))
+{
+#if OSL_DEBUG_LEVEL > 0
+ ++s_nSwAutoCompleteStringCount;
+#endif
+}
+
+SwAutoCompleteString::~SwAutoCompleteString()
+{
+#if OSL_DEBUG_LEVEL > 0
+ --s_nSwAutoCompleteStringCount;
+#else
+ (void) this;
+#endif
+}
+
+void SwAutoCompleteString::AddDocument(const SwDoc& rDoc)
+{
+ auto aIt = std::find(m_aSourceDocs.begin(), m_aSourceDocs.end(), &rDoc);
+ if (aIt != m_aSourceDocs.end())
+ return;
+ m_aSourceDocs.push_back(&rDoc);
+}
+
+bool SwAutoCompleteString::RemoveDocument(const SwDoc& rDoc)
+{
+ auto aIt = std::find(m_aSourceDocs.begin(), m_aSourceDocs.end(), &rDoc);
+ if (aIt != m_aSourceDocs.end())
+ {
+ m_aSourceDocs.erase(aIt);
+ return m_aSourceDocs.empty();
+ }
+ return false;
+}
+
+SwAutoCompleteWord::SwAutoCompleteWord(
+ editeng::SortedAutoCompleteStrings::size_type nWords, sal_uInt16 nMWrdLen ):
+ m_pImpl(new SwAutoCompleteWord_Impl(*this)),
+ m_nMaxCount( nWords ),
+ m_nMinWordLen( nMWrdLen ),
+ m_bLockWordList( false )
+{
+}
+
+SwAutoCompleteWord::~SwAutoCompleteWord()
+{
+ m_WordList.DeleteAndDestroyAll(); // so the assertion below works
+#if OSL_DEBUG_LEVEL > 0
+ sal_uLong nStrings = SwAutoCompleteString::GetElementCount();
+ sal_uLong nClients = SwAutoCompleteClient::GetElementCount();
+ OSL_ENSURE(!nStrings && !nClients, "AutoComplete: clients or string count mismatch");
+#endif
+}
+
+bool SwAutoCompleteWord::InsertWord( const OUString& rWord, SwDoc& rDoc )
+{
+ SwDocShell* pDocShell = rDoc.GetDocShell();
+ SfxMedium* pMedium = pDocShell ? pDocShell->GetMedium() : nullptr;
+ // strings from help module should not be added
+ if( pMedium )
+ {
+ const INetURLObject& rURL = pMedium->GetURLObject();
+ if ( rURL.GetProtocol() == INetProtocol::VndSunStarHelp )
+ return false;
+ }
+
+ OUString aNewWord = rWord.replaceAll(OUStringChar(CH_TXTATR_INWORD), "")
+ .replaceAll(OUStringChar(CH_TXTATR_BREAKWORD), "");
+
+ m_pImpl->AddDocument(rDoc);
+ bool bRet = false;
+ sal_Int32 nWrdLen = aNewWord.getLength();
+ while( nWrdLen && '.' == aNewWord[ nWrdLen-1 ])
+ --nWrdLen;
+
+ if( !m_bLockWordList && nWrdLen >= m_nMinWordLen )
+ {
+ SwAutoCompleteString* pNew = new SwAutoCompleteString( aNewWord, nWrdLen );
+ pNew->AddDocument(rDoc);
+ std::pair<editeng::SortedAutoCompleteStrings::const_iterator, bool>
+ aInsPair = m_WordList.insert(pNew);
+
+ m_LookupTree.insert( aNewWord.subView(0, nWrdLen) );
+
+ if (aInsPair.second)
+ {
+ bRet = true;
+ if (m_aLRUList.size() >= m_nMaxCount)
+ {
+ // the last one needs to be removed
+ // so that there is space for the first one
+ SwAutoCompleteString* pDel = m_aLRUList.back();
+ m_aLRUList.pop_back();
+ m_WordList.erase(pDel);
+ delete pDel;
+ }
+ m_aLRUList.push_front(pNew);
+ }
+ else
+ {
+ delete pNew;
+ // then move "up"
+ pNew = static_cast<SwAutoCompleteString*>(*aInsPair.first);
+
+ // add the document to the already inserted string
+ pNew->AddDocument(rDoc);
+
+ // move pNew to the front of the LRU list
+ SwAutoCompleteStringPtrDeque::iterator it = std::find( m_aLRUList.begin(), m_aLRUList.end(), pNew );
+ OSL_ENSURE( m_aLRUList.end() != it, "String not found" );
+ if ( m_aLRUList.begin() != it && m_aLRUList.end() != it )
+ {
+ m_aLRUList.erase( it );
+ m_aLRUList.push_front( pNew );
+ }
+ }
+ }
+ return bRet;
+}
+
+void SwAutoCompleteWord::SetMaxCount(
+ editeng::SortedAutoCompleteStrings::size_type nNewMax )
+{
+ if( nNewMax < m_nMaxCount && m_aLRUList.size() > nNewMax )
+ {
+ // remove the trailing ones
+ SwAutoCompleteStringPtrDeque::size_type nLRUIndex = nNewMax-1;
+ while (nNewMax < m_WordList.size() && nLRUIndex < m_aLRUList.size())
+ {
+ auto it = m_WordList.find(m_aLRUList[nLRUIndex++]);
+ OSL_ENSURE( m_WordList.end() != it, "String not found" );
+ editeng::IAutoCompleteString *const pDel = *it;
+ m_WordList.erase(it);
+ delete pDel;
+ }
+ m_aLRUList.erase( m_aLRUList.begin() + nNewMax - 1, m_aLRUList.end() );
+ }
+ m_nMaxCount = nNewMax;
+}
+
+void SwAutoCompleteWord::SetMinWordLen( sal_uInt16 n )
+{
+ // Do you really want to remove all words that are less than the minWrdLen?
+ if( n < m_nMinWordLen )
+ {
+ for (size_t nPos = 0; nPos < m_WordList.size(); ++nPos)
+ if (m_WordList[ nPos ]->GetAutoCompleteString().getLength() < n)
+ {
+ SwAutoCompleteString *const pDel =
+ dynamic_cast<SwAutoCompleteString*>(m_WordList[nPos]);
+ m_WordList.erase_at(nPos);
+
+ SwAutoCompleteStringPtrDeque::iterator it = std::find( m_aLRUList.begin(), m_aLRUList.end(), pDel );
+ OSL_ENSURE( m_aLRUList.end() != it, "String not found" );
+ m_aLRUList.erase( it );
+ --nPos;
+ delete pDel;
+ }
+ }
+
+ m_nMinWordLen = n;
+}
+
+/** Return all words matching a given prefix
+ *
+ * @param aMatch the prefix to search for
+ * @param rWords the words found matching
+ */
+bool SwAutoCompleteWord::GetWordsMatching(std::u16string_view aMatch, std::vector<OUString>& rWords) const
+{
+ assert(rWords.empty());
+ m_LookupTree.findSuggestions(aMatch, rWords);
+ return !rWords.empty();
+}
+
+void SwAutoCompleteWord::CheckChangedList(
+ const editeng::SortedAutoCompleteStrings& rNewLst)
+{
+ size_t nMyLen = m_WordList.size(), nNewLen = rNewLst.size();
+ size_t nMyPos = 0, nNewPos = 0;
+
+ for( ; nMyPos < nMyLen && nNewPos < nNewLen; ++nMyPos, ++nNewPos )
+ {
+ const editeng::IAutoCompleteString * pStr = rNewLst[ nNewPos ];
+ while (m_WordList[nMyPos] != pStr)
+ {
+ SwAutoCompleteString *const pDel =
+ dynamic_cast<SwAutoCompleteString*>(m_WordList[nMyPos]);
+ m_WordList.erase_at(nMyPos);
+ SwAutoCompleteStringPtrDeque::iterator it = std::find( m_aLRUList.begin(), m_aLRUList.end(), pDel );
+ OSL_ENSURE( m_aLRUList.end() != it, "String not found" );
+ m_aLRUList.erase( it );
+ delete pDel;
+ if( nMyPos >= --nMyLen )
+ break;
+ }
+ }
+ // remove the elements at the end of the array
+ if( nMyPos >= nMyLen )
+ return;
+
+ // clear LRU array first then delete the string object
+ for( ; nNewPos < nMyLen; ++nNewPos )
+ {
+ SwAutoCompleteString *const pDel =
+ dynamic_cast<SwAutoCompleteString*>(m_WordList[nNewPos]);
+ SwAutoCompleteStringPtrDeque::iterator it = std::find( m_aLRUList.begin(), m_aLRUList.end(), pDel );
+ OSL_ENSURE( m_aLRUList.end() != it, "String not found" );
+ m_aLRUList.erase( it );
+ delete pDel;
+ }
+ // remove from array
+ m_WordList.erase(m_WordList.begin() + nMyPos,
+ m_WordList.begin() + nMyLen);
+}
+
+void SwAutoCompleteWord::DocumentDying(const SwDoc& rDoc)
+{
+ m_pImpl->RemoveDocument(rDoc);
+
+ SvxAutoCorrect* pACorr = SvxAutoCorrCfg::Get().GetAutoCorrect();
+ const bool bDelete = !pACorr->GetSwFlags().bAutoCmpltKeepList;
+ for (size_t nPos = m_WordList.size(); nPos; nPos--)
+ {
+ SwAutoCompleteString *const pCurrent = dynamic_cast<SwAutoCompleteString*>(m_WordList[nPos - 1]);
+ if(pCurrent && pCurrent->RemoveDocument(rDoc) && bDelete)
+ {
+ m_WordList.erase_at(nPos - 1);
+ SwAutoCompleteStringPtrDeque::iterator it = std::find( m_aLRUList.begin(), m_aLRUList.end(), pCurrent );
+ OSL_ENSURE( m_aLRUList.end() != it, "word not found in LRU list" );
+ m_aLRUList.erase( it );
+ delete pCurrent;
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/dbgoutsw.cxx b/sw/source/core/doc/dbgoutsw.cxx
new file mode 100644
index 0000000000..1de4b71535
--- /dev/null
+++ b/sw/source/core/doc/dbgoutsw.cxx
@@ -0,0 +1,820 @@
+/* -*- 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 .
+ */
+
+#ifdef DBG_UTIL
+
+#include <osl/diagnose.h>
+#include <rtl/ustring.hxx>
+#include <svl/poolitem.hxx>
+#include <svl/itemiter.hxx>
+#include <map>
+#include <node.hxx>
+#include <ndtxt.hxx>
+#include <ndhints.hxx>
+#include <txatbase.hxx>
+#include <pam.hxx>
+#include <docary.hxx>
+#include <undobj.hxx>
+#include <numrule.hxx>
+#include <doc.hxx>
+#include <frmfmt.hxx>
+#include <fmtanchr.hxx>
+#include <swrect.hxx>
+#include <ndarr.hxx>
+#include <paratr.hxx>
+#include <SwNodeNum.hxx>
+#include <dbgoutsw.hxx>
+#include <frameformats.hxx>
+#include <cstdio>
+
+static OString aDbgOutResult;
+bool bDbgOutStdErr = false;
+bool bDbgOutPrintAttrSet = false;
+
+template<class T>
+static OUString lcl_dbg_out_SvPtrArr(const T & rArr)
+{
+ OUStringBuffer aStr("[ ");
+
+ for (typename T::const_iterator i(rArr.begin()); i != rArr.end(); ++i)
+ {
+ if (i != rArr.begin())
+ aStr.append(", ");
+
+ if (*i)
+ aStr.append(lcl_dbg_out(**i));
+ else
+ aStr.append("(null)");
+ }
+
+ aStr.append(" ]");
+
+ return aStr.makeStringAndClear();
+}
+
+const char * dbg_out(const void * pVoid)
+{
+ return dbg_out(OUString::number(reinterpret_cast<sal_uIntPtr>(pVoid), 16));
+}
+
+const char * dbg_out(std::u16string_view aStr)
+{
+ aDbgOutResult = OUStringToOString(aStr, RTL_TEXTENCODING_ASCII_US);
+
+ if (bDbgOutStdErr)
+ fprintf(stderr, "%s", aDbgOutResult.getStr());
+
+ return aDbgOutResult.getStr();
+}
+
+static std::map<sal_uInt16,OUString> & GetItemWhichMap()
+{
+ static std::map<sal_uInt16,OUString> aItemWhichMap
+ {
+ { RES_CHRATR_CASEMAP , "CHRATR_CASEMAP" },
+ { RES_CHRATR_CHARSETCOLOR , "CHRATR_CHARSETCOLOR" },
+ { RES_CHRATR_COLOR , "CHRATR_COLOR" },
+ { RES_CHRATR_CONTOUR , "CHRATR_CONTOUR" },
+ { RES_CHRATR_CROSSEDOUT , "CHRATR_CROSSEDOUT" },
+ { RES_CHRATR_ESCAPEMENT , "CHRATR_ESCAPEMENT" },
+ { RES_CHRATR_FONT , "CHRATR_FONT" },
+ { RES_CHRATR_FONTSIZE , "CHRATR_FONTSIZE" },
+ { RES_CHRATR_KERNING , "CHRATR_KERNING" },
+ { RES_CHRATR_LANGUAGE , "CHRATR_LANGUAGE" },
+ { RES_CHRATR_POSTURE , "CHRATR_POSTURE" },
+ { RES_CHRATR_SHADOWED , "CHRATR_SHADOWED" },
+ { RES_CHRATR_UNDERLINE , "CHRATR_UNDERLINE" },
+ { RES_CHRATR_OVERLINE , "CHRATR_OVERLINE" },
+ { RES_CHRATR_WEIGHT , "CHRATR_WEIGHT" },
+ { RES_CHRATR_WORDLINEMODE , "CHRATR_WORDLINEMODE" },
+ { RES_CHRATR_AUTOKERN , "CHRATR_AUTOKERN" },
+ { RES_CHRATR_BLINK , "CHRATR_BLINK" },
+ { RES_CHRATR_NOHYPHEN , "CHRATR_NOHYPHEN" },
+ { RES_CHRATR_BACKGROUND , "CHRATR_BACKGROUND" },
+ { RES_CHRATR_HIGHLIGHT , "CHRATR_HIGHLIGHT" },
+ { RES_CHRATR_CJK_FONT , "CHRATR_CJK_FONT" },
+ { RES_CHRATR_CJK_FONTSIZE , "CHRATR_CJK_FONTSIZE" },
+ { RES_CHRATR_CJK_LANGUAGE , "CHRATR_CJK_LANGUAGE" },
+ { RES_CHRATR_CJK_POSTURE , "CHRATR_CJK_POSTURE" },
+ { RES_CHRATR_CJK_WEIGHT , "CHRATR_CJK_WEIGHT" },
+ { RES_CHRATR_CTL_FONT , "CHRATR_CTL_FONT" },
+ { RES_CHRATR_CTL_FONTSIZE , "CHRATR_CTL_FONTSIZE" },
+ { RES_CHRATR_CTL_LANGUAGE , "CHRATR_CTL_LANGUAGE" },
+ { RES_CHRATR_CTL_POSTURE , "CHRATR_CTL_POSTURE" },
+ { RES_CHRATR_CTL_WEIGHT , "CHRATR_CTL_WEIGHT" },
+ { RES_CHRATR_ROTATE , "CHRATR_ROTATE" },
+ { RES_CHRATR_EMPHASIS_MARK , "CHRATR_EMPHASIS_MARK" },
+ { RES_CHRATR_TWO_LINES , "CHRATR_TWO_LINES" },
+ { RES_CHRATR_SCALEW , "CHRATR_SCALEW" },
+ { RES_CHRATR_RELIEF , "CHRATR_RELIEF" },
+ { RES_CHRATR_HIDDEN , "CHRATR_HIDDEN" },
+ { RES_CHRATR_BOX , "CHRATR_BOX" },
+ { RES_CHRATR_SHADOW , "CHRATR_SHADOW" },
+ { RES_TXTATR_AUTOFMT , "TXTATR_AUTOFMT" },
+ { RES_TXTATR_INETFMT , "TXTATR_INETFMT" },
+ { RES_TXTATR_REFMARK , "TXTATR_REFMARK" },
+ { RES_TXTATR_TOXMARK , "TXTATR_TOXMARK" },
+ { RES_TXTATR_CHARFMT , "TXTATR_CHARFMT" },
+ { RES_TXTATR_INPUTFIELD , "RES_TXTATR_INPUTFIELD" },
+ { RES_TXTATR_CONTENTCONTROL , "RES_TXTATR_CONTENTCONTROL" },
+ { RES_TXTATR_CJK_RUBY , "TXTATR_CJK_RUBY" },
+ { RES_TXTATR_UNKNOWN_CONTAINER , "TXTATR_UNKNOWN_CONTAINER" },
+ { RES_TXTATR_META , "TXTATR_META" },
+ { RES_TXTATR_METAFIELD , "TXTATR_METAFIELD" },
+ { RES_TXTATR_FIELD , "TXTATR_FIELD" },
+ { RES_TXTATR_FLYCNT , "TXTATR_FLYCNT" },
+ { RES_TXTATR_FTN , "TXTATR_FTN" },
+ { RES_TXTATR_ANNOTATION , "TXTATR_ANNOTATION" },
+ { RES_TXTATR_LINEBREAK , "RES_TXTATR_LINEBREAK" },
+ { RES_TXTATR_DUMMY1 , "TXTATR_DUMMY1" },
+ { RES_PARATR_LINESPACING , "PARATR_LINESPACING" },
+ { RES_PARATR_ADJUST , "PARATR_ADJUST" },
+ { RES_PARATR_SPLIT , "PARATR_SPLIT" },
+ { RES_PARATR_ORPHANS , "PARATR_ORPHANS" },
+ { RES_PARATR_WIDOWS , "PARATR_WIDOWS" },
+ { RES_PARATR_TABSTOP , "PARATR_TABSTOP" },
+ { RES_PARATR_HYPHENZONE , "PARATR_HYPHENZONE" },
+ { RES_PARATR_DROP , "PARATR_DROP" },
+ { RES_PARATR_REGISTER , "PARATR_REGISTER" },
+ { RES_PARATR_NUMRULE , "PARATR_NUMRULE" },
+ { RES_PARATR_SCRIPTSPACE , "PARATR_SCRIPTSPACE" },
+ { RES_PARATR_HANGINGPUNCTUATION , "PARATR_HANGINGPUNCTUATION" },
+ { RES_PARATR_FORBIDDEN_RULES , "PARATR_FORBIDDEN_RULES" },
+ { RES_PARATR_VERTALIGN , "PARATR_VERTALIGN" },
+ { RES_PARATR_SNAPTOGRID , "PARATR_SNAPTOGRID" },
+ { RES_PARATR_CONNECT_BORDER , "PARATR_CONNECT_BORDER" },
+ { RES_FILL_ORDER , "FILL_ORDER" },
+ { RES_FRM_SIZE , "FRM_SIZE" },
+ { RES_PAPER_BIN , "PAPER_BIN" },
+ { RES_MARGIN_FIRSTLINE, "FIRSTLINE" },
+ { RES_MARGIN_TEXTLEFT, "TEXTLEFT" },
+ { RES_MARGIN_RIGHT, "RIGHT" },
+ { RES_MARGIN_LEFT, "LEFT" },
+ { RES_MARGIN_GUTTER, "GUTTER" },
+ { RES_MARGIN_GUTTER_RIGHT, "GUTTER_RIGHT" },
+ { RES_LR_SPACE , "LR_SPACE" },
+ { RES_UL_SPACE , "UL_SPACE" },
+ { RES_PAGEDESC , "PAGEDESC" },
+ { RES_BREAK , "BREAK" },
+ { RES_CNTNT , "CNTNT" },
+ { RES_HEADER , "HEADER" },
+ { RES_FOOTER , "FOOTER" },
+ { RES_PRINT , "PRINT" },
+ { RES_OPAQUE , "OPAQUE" },
+ { RES_PROTECT , "PROTECT" },
+ { RES_SURROUND , "SURROUND" },
+ { RES_VERT_ORIENT , "VERT_ORIENT" },
+ { RES_HORI_ORIENT , "HORI_ORIENT" },
+ { RES_ANCHOR , "ANCHOR" },
+ { RES_BACKGROUND , "BACKGROUND" },
+ { RES_BOX , "BOX" },
+ { RES_SHADOW , "SHADOW" },
+ { RES_FRMMACRO , "FRMMACRO" },
+ { RES_COL , "COL" },
+ { RES_KEEP , "KEEP" },
+ { RES_URL , "URL" },
+ { RES_EDIT_IN_READONLY , "EDIT_IN_READONLY" },
+ { RES_LAYOUT_SPLIT , "LAYOUT_SPLIT" },
+ { RES_CHAIN , "CHAIN" },
+ { RES_TEXTGRID , "TEXTGRID" },
+ { RES_LINENUMBER , "LINENUMBER" },
+ { RES_FTN_AT_TXTEND , "FTN_AT_TXTEND" },
+ { RES_END_AT_TXTEND , "END_AT_TXTEND" },
+ { RES_COLUMNBALANCE , "COLUMNBALANCE" },
+ { RES_FRAMEDIR , "FRAMEDIR" },
+ { RES_HEADER_FOOTER_EAT_SPACING , "HEADER_FOOTER_EAT_SPACING" },
+ { RES_ROW_SPLIT , "ROW_SPLIT" },
+ { RES_GRFATR_MIRRORGRF , "GRFATR_MIRRORGRF" },
+ { RES_GRFATR_CROPGRF , "GRFATR_CROPGRF" },
+ { RES_GRFATR_ROTATION , "GRFATR_ROTATION" },
+ { RES_GRFATR_LUMINANCE , "GRFATR_LUMINANCE" },
+ { RES_GRFATR_CONTRAST , "GRFATR_CONTRAST" },
+ { RES_GRFATR_CHANNELR , "GRFATR_CHANNELR" },
+ { RES_GRFATR_CHANNELG , "GRFATR_CHANNELG" },
+ { RES_GRFATR_CHANNELB , "GRFATR_CHANNELB" },
+ { RES_GRFATR_GAMMA , "GRFATR_GAMMA" },
+ { RES_GRFATR_INVERT , "GRFATR_INVERT" },
+ { RES_GRFATR_TRANSPARENCY , "GRFATR_TRANSPARENCY" },
+ { RES_GRFATR_DRAWMODE , "GRFATR_DRAWMODE" },
+ { RES_BOXATR_FORMAT , "BOXATR_FORMAT" },
+ { RES_BOXATR_FORMULA , "BOXATR_FORMULA" },
+ { RES_BOXATR_VALUE , "BOXATR_VALUE" },
+ };
+
+ return aItemWhichMap;
+}
+
+static OUString lcl_dbg_out(const SfxPoolItem & rItem)
+{
+ OUString aStr("[ ");
+
+ auto & rWhichMap = GetItemWhichMap();
+ auto it = rWhichMap.find(rItem.Which());
+ if ( it != rWhichMap.end())
+ aStr += it->second;
+ else
+ aStr += OUString::number(rItem.Which());
+
+ aStr += " ]";
+
+ return aStr;
+}
+
+const char * dbg_out(const SfxPoolItem & rItem)
+{
+ return dbg_out(lcl_dbg_out(rItem));
+}
+
+const char * dbg_out(const SfxPoolItem * pItem)
+{
+ return dbg_out(pItem ? lcl_dbg_out(*pItem) : OUString("(nil)"));
+}
+
+static OUString lcl_dbg_out(const SfxItemSet & rSet)
+{
+ SfxItemIter aIter(rSet);
+ bool bFirst = true;
+ OUStringBuffer aStr = "[ ";
+
+ for (const SfxPoolItem* pItem = aIter.GetCurItem(); pItem; pItem = aIter.NextItem())
+ {
+ if (!bFirst)
+ aStr.append(", ");
+
+ if (reinterpret_cast<sal_uIntPtr>(pItem) != SAL_MAX_SIZE)
+ aStr.append(lcl_dbg_out(*pItem));
+ else
+ aStr.append("invalid");
+
+ bFirst = false;
+ }
+
+ aStr.append(" ]");
+
+ return aStr.makeStringAndClear();
+}
+
+const char * dbg_out(const SfxItemSet & rSet)
+{
+ return dbg_out(lcl_dbg_out(rSet));
+}
+
+static OUString lcl_dbg_out(const SwTextAttr & rAttr)
+{
+ OUString aStr =
+ "[ " +
+ OUString::number(rAttr.GetStart()) +
+ "->" +
+ OUString::number(*rAttr.End()) +
+ " " +
+ lcl_dbg_out(rAttr.GetAttr()) +
+ " ]";
+
+ return aStr;
+}
+
+const char * dbg_out(const SwTextAttr & rAttr)
+{
+ return dbg_out(lcl_dbg_out(rAttr));
+}
+
+static OUString lcl_dbg_out(const SwpHints & rHints)
+{
+ OUStringBuffer aStr("[ SwpHints\n");
+
+ for (size_t i = 0; i < rHints.Count(); ++i)
+ {
+ aStr.append(" " + lcl_dbg_out(*rHints.Get(i)) + "\n");
+ }
+
+ aStr.append("]\n");
+
+ return aStr.makeStringAndClear();
+}
+
+const char * dbg_out(const SwpHints &rHints)
+{
+ return dbg_out(lcl_dbg_out(rHints));
+}
+
+static OUString lcl_dbg_out(const SwPosition & rPos)
+{
+ OUString aStr =
+ "( " +
+ OUString::number(sal_Int32(rPos.GetNodeIndex())) +
+ ", " +
+ OUString::number(rPos.GetContentIndex()) +
+ ": " +
+ OUString::number(reinterpret_cast<sal_IntPtr>(rPos.GetContentNode()), 16) +
+ " )";
+
+ return aStr;
+}
+
+const char * dbg_out(const SwPosition & rPos)
+{
+ return dbg_out(lcl_dbg_out(rPos));
+}
+
+static OUString lcl_dbg_out(const SwPaM & rPam)
+{
+ OUString aStr =
+ "[ Pt: " +
+ lcl_dbg_out(*rPam.GetPoint());
+
+ if (rPam.HasMark())
+ {
+ aStr += ", Mk: " + lcl_dbg_out(*rPam.GetMark());
+ }
+
+ aStr += " ]";
+
+ return aStr;
+}
+
+const char * dbg_out(const SwPaM & rPam)
+{
+ return dbg_out(lcl_dbg_out(rPam));
+}
+
+static OUString lcl_dbg_out(const SwNodeNum & )
+{
+ return OUString();/*rNum.ToString();*/
+}
+
+const char * dbg_out(const SwNodeNum & rNum)
+{
+ return dbg_out(lcl_dbg_out(rNum));
+}
+
+static OUString lcl_dbg_out(const SwRect & rRect)
+{
+ OUString aResult =
+ "[ [" +
+ OUString::number(rRect.Left()) +
+ ", " +
+ OUString::number(rRect.Top()) +
+ "], [" +
+ OUString::number(rRect.Right()) +
+ ", " +
+ OUString::number(rRect.Bottom()) +
+ "] ]";
+
+ return aResult;
+}
+
+const char * dbg_out(const SwRect & rRect)
+{
+ return dbg_out(lcl_dbg_out(rRect));
+}
+
+static OUString lcl_dbg_out(const SwFrameFormat & rFrameFormat)
+{
+ OUString aResult = "[ " +
+ OUString::number(reinterpret_cast<sal_uIntPtr>(&rFrameFormat), 16) +
+ "(" +
+ rFrameFormat.GetName() + ")";
+
+ if (rFrameFormat.IsAuto())
+ aResult += "*";
+
+ aResult += " ," + lcl_dbg_out(rFrameFormat.FindLayoutRect()) + " ]";
+
+ return aResult;
+}
+
+const char * dbg_out(const SwFrameFormat & rFrameFormat)
+{
+ return dbg_out(lcl_dbg_out(rFrameFormat));
+}
+
+static OUString lcl_AnchoredFrames(const SwNode & rNode)
+{
+ OUStringBuffer aResult("[");
+
+ const SwDoc& rDoc = rNode.GetDoc();
+ const sw::SpzFrameFormats* pSpzs = rDoc.GetSpzFrameFormats();
+
+ if (pSpzs)
+ {
+ bool bFirst = true;
+ for(const sw::SpzFrameFormat* pSpz: *pSpzs)
+ {
+ const SwFormatAnchor& rAnchor = pSpz->GetAnchor();
+ const SwNode * pPos = rAnchor.GetAnchorNode();
+
+ if (pPos && *pPos == rNode)
+ {
+ if (! bFirst)
+ aResult.append(", ");
+
+ if (pSpz)
+ aResult.append(lcl_dbg_out(*pSpz));
+ bFirst = false;
+ }
+ }
+ }
+
+ aResult.append("]");
+
+ return aResult.makeStringAndClear();
+}
+
+static OUString lcl_dbg_out_NumType(sal_Int16 nType)
+{
+ OUString aTmpStr;
+
+ switch (nType)
+ {
+ case SVX_NUM_NUMBER_NONE:
+ aTmpStr += " NONE";
+
+ break;
+ case SVX_NUM_CHARS_UPPER_LETTER:
+ aTmpStr += " CHARS_UPPER_LETTER";
+
+ break;
+ case SVX_NUM_CHARS_LOWER_LETTER:
+ aTmpStr += " CHARS_LOWER_LETTER";
+
+ break;
+ case SVX_NUM_ROMAN_UPPER:
+ aTmpStr += " ROMAN_UPPER";
+
+ break;
+ case SVX_NUM_ROMAN_LOWER:
+ aTmpStr += " ROMAN_LOWER";
+
+ break;
+ case SVX_NUM_ARABIC:
+ aTmpStr += " ARABIC";
+
+ break;
+ default:
+ aTmpStr += " ??";
+
+ break;
+ }
+
+ return aTmpStr;
+}
+
+static OUString lcl_dbg_out(const SwNode & rNode)
+{
+ OUString aTmpStr = "<node "
+ "index=\"" +
+ OUString::number(sal_Int32(rNode.GetIndex())) +
+ "\""
+ " serial=\"" +
+ OUString::number(rNode.GetSerial()) +
+ "\""
+ " type=\"" +
+ OUString::number(sal_Int32( rNode.GetNodeType() ) ) +
+ "\""
+ " pointer=\"" +
+ OUString::number(reinterpret_cast<sal_uIntPtr>(&rNode), 16) +
+ "\">";
+
+ const SwTextNode * pTextNode = rNode.GetTextNode();
+
+ if (rNode.IsTextNode())
+ {
+ const SfxItemSet * pAttrSet = pTextNode->GetpSwAttrSet();
+
+ aTmpStr += "<txt>" + (pTextNode->GetText().getLength() > 10 ? pTextNode->GetText().copy(0, 10) : pTextNode->GetText()) + "</txt>";
+
+ if (rNode.IsTableNode())
+ aTmpStr += "<tbl/>";
+
+ aTmpStr += "<outlinelevel>" + OUString::number(pTextNode->GetAttrOutlineLevel()-1) + "</outlinelevel>";
+
+ const SwNumRule * pNumRule = pTextNode->GetNumRule();
+
+ if (pNumRule != nullptr)
+ {
+ aTmpStr += "<number>";
+ if ( pTextNode->GetNum() )
+ {
+ aTmpStr += lcl_dbg_out(*(pTextNode->GetNum()));
+ }
+ aTmpStr += "</number><rule>" +
+ pNumRule->GetName();
+
+ const SwNumRuleItem * pItem = nullptr;
+
+ if (pAttrSet &&
+ (pItem = pAttrSet->GetItemIfSet(RES_PARATR_NUMRULE, false)))
+ {
+ aTmpStr += "(" + pItem->GetValue() + ")*";
+ }
+
+ const SwNumFormat * pNumFormat = nullptr;
+ aTmpStr += "</rule>";
+
+ if (pTextNode->GetActualListLevel() > 0)
+ pNumFormat = pNumRule->GetNumFormat( static_cast< sal_uInt16 >(pTextNode->GetActualListLevel()) );
+
+ if (pNumFormat)
+ {
+ aTmpStr += "<numformat>" +
+ lcl_dbg_out_NumType(pNumFormat->GetNumberingType()) + "</numformat>";
+ }
+ }
+
+ if (pTextNode->IsCountedInList())
+ aTmpStr += "<counted/>";
+
+ SwFormatColl * pColl = pTextNode->GetFormatColl();
+
+ if (pColl)
+ {
+ aTmpStr += "<coll>" + pColl->GetName() + "(";
+
+ SwTextFormatColl *pTextColl = static_cast<SwTextFormatColl*>(pColl);
+ if (pTextColl->IsAssignedToListLevelOfOutlineStyle())
+ {
+ aTmpStr += OUString::number(pTextColl->GetAssignedOutlineStyleLevel());
+ }
+ else
+ {
+ aTmpStr += OUString::number(-1);
+ }
+
+ const SwNumRuleItem & rItem =
+ pColl->GetFormatAttr(RES_PARATR_NUMRULE);
+ const OUString& sNumruleName = rItem.GetValue();
+
+ if (!sNumruleName.isEmpty())
+ {
+ aTmpStr += ", " + sNumruleName;
+ }
+ aTmpStr += ")"
+ "</coll>";
+ }
+
+ SwFormatColl * pCColl = pTextNode->GetCondFormatColl();
+
+ if (pCColl)
+ {
+ aTmpStr += "<ccoll>" + pCColl->GetName() + "</ccoll>";
+ }
+
+ aTmpStr += "<frms>" + lcl_AnchoredFrames(rNode) + "</frms>";
+
+ if (bDbgOutPrintAttrSet)
+ {
+ aTmpStr += "<attrs>" + lcl_dbg_out(pTextNode->GetSwAttrSet()) + "</attrs>";
+ }
+ }
+ else if (rNode.IsStartNode())
+ {
+ aTmpStr += "<start end=\"";
+
+ const SwStartNode * pStartNode = rNode.GetStartNode();
+ if (pStartNode != nullptr)
+ aTmpStr += OUString::number(sal_Int32(pStartNode->EndOfSectionNode()->GetIndex()));
+
+ aTmpStr += "\"/>";
+ }
+ else if (rNode.IsEndNode())
+ aTmpStr += "<end/>";
+
+ aTmpStr += "</node>";
+
+ return aTmpStr;
+}
+
+const char * dbg_out(const SwNode & rNode)
+{
+ return dbg_out(lcl_dbg_out(rNode));
+}
+
+const char * dbg_out(const SwNode * pNode)
+{
+ if (nullptr != pNode)
+ return dbg_out(*pNode);
+ else
+ return nullptr;
+}
+
+const char * dbg_out(const SwContentNode * pNode)
+{
+ if (nullptr != pNode)
+ return dbg_out(*pNode);
+ else
+ return nullptr;
+}
+
+const char * dbg_out(const SwTextNode * pNode)
+{
+ if (nullptr != pNode)
+ return dbg_out(*pNode);
+ else
+ return nullptr;
+}
+
+static OUString lcl_dbg_out(const SwUndo & rUndo)
+{
+ return "[ " + OUString::number(static_cast<int>(rUndo.GetId()))
+ + ": " + rUndo.GetComment() + " ]";
+}
+
+const char * dbg_out(const SwUndo & rUndo)
+{
+ return dbg_out(lcl_dbg_out(rUndo));
+}
+
+static OUString lcl_dbg_out(SwOutlineNodes const & rNodes)
+{
+ OUStringBuffer aStr("[\n");
+
+ for (size_t i = 0; i < rNodes.size(); i++)
+ {
+ aStr.append(lcl_dbg_out(*rNodes[i]) + "\n");
+ }
+
+ aStr.append("]\n");
+
+ return aStr.makeStringAndClear();
+}
+
+const char * dbg_out( SwOutlineNodes const & rNodes)
+{
+ return dbg_out(lcl_dbg_out(rNodes));
+}
+
+static OUString lcl_dbg_out(const SvxNumberFormat & rFormat)
+{
+ OUString aResult = lcl_dbg_out_NumType(rFormat.GetNumberingType());
+ return aResult;
+}
+
+static OUString lcl_dbg_out(const SwNumRule & rRule)
+{
+ OUStringBuffer aResult("[ " + rRule.GetName() + " [");
+
+ for (sal_uInt8 n = 0; n < MAXLEVEL; n++)
+ {
+ if (n > 0)
+ aResult.append(", ");
+
+ aResult.append(lcl_dbg_out(rRule.Get(n)));
+ }
+
+ aResult.append("]]");
+
+ return aResult.makeStringAndClear();
+}
+
+const char * dbg_out(const SwNumRule & rRule)
+{
+ return dbg_out(lcl_dbg_out(rRule));
+}
+
+static OUString lcl_dbg_out(const SwTextFormatColl & rFormat)
+{
+ return rFormat.GetName() + "(" +
+ OUString::number(rFormat.GetAttrOutlineLevel()) + ")";
+}
+
+const char * dbg_out(const SwTextFormatColl & rFormat)
+{
+ return dbg_out(lcl_dbg_out(rFormat));
+}
+
+static OUString lcl_dbg_out(const sw::FrameFormats<sw::SpzFrameFormat*>& rFrameFormats)
+{
+ return lcl_dbg_out_SvPtrArr<sw::FrameFormats<sw::SpzFrameFormat*>>(rFrameFormats);
+}
+
+const char * dbg_out(const sw::FrameFormats<sw::SpzFrameFormat*>& rFrameFormats)
+{
+ return dbg_out(lcl_dbg_out(rFrameFormats));
+}
+
+static OUString lcl_dbg_out(const SwNumRuleTable & rTable)
+{
+ OUStringBuffer aResult("[");
+
+ for (size_t n = 0; n < rTable.size(); n++)
+ {
+ if (n > 0)
+ aResult.append(", ");
+
+ aResult.append(rTable[n]->GetName());
+
+ aResult.append("(" + OUString::number(reinterpret_cast<sal_uIntPtr>(rTable[n]), 16) + ")");
+ }
+
+ aResult.append("]");
+
+ return aResult.makeStringAndClear();
+}
+
+const char * dbg_out(const SwNumRuleTable & rTable)
+{
+ return dbg_out(lcl_dbg_out(rTable));
+}
+
+static OUString lcl_TokenType2Str(FormTokenType nType)
+{
+ switch(nType)
+ {
+ case TOKEN_ENTRY_NO:
+ return "NO";
+ case TOKEN_ENTRY_TEXT:
+ return "ENTRY_TEXT";
+ case TOKEN_ENTRY:
+ return "ENTRY";
+ case TOKEN_TAB_STOP:
+ return "TAB_STOP";
+ case TOKEN_TEXT:
+ return "TOKEN_TEXT";
+ case TOKEN_PAGE_NUMS:
+ return "NUMS";
+ case TOKEN_CHAPTER_INFO:
+ return "CHAPTER_INFO";
+ case TOKEN_LINK_START:
+ return "LINK_START";
+ case TOKEN_LINK_END:
+ return "LINK_END";
+ case TOKEN_AUTHORITY:
+ return "AUTHORITY";
+ case TOKEN_END:
+ return "END";
+ default:
+ OSL_FAIL("should not be reached");
+ return "??";
+ }
+}
+
+static OUString lcl_dbg_out(const SwFormToken & rToken)
+{
+ return rToken.GetString();
+}
+
+const char * dbg_out(const SwFormToken & rToken)
+{
+ return dbg_out(lcl_dbg_out(rToken));
+}
+
+static OUString lcl_dbg_out(const SwFormTokens & rTokens)
+{
+ OUStringBuffer aStr("[");
+
+ SwFormTokens::const_iterator aIt;
+
+ for (aIt = rTokens.begin(); aIt != rTokens.end(); ++aIt)
+ {
+ if (aIt != rTokens.begin())
+ aStr.append(", ");
+
+ aStr.append(lcl_TokenType2Str(aIt->eTokenType) + ": " + lcl_dbg_out(*aIt));
+ }
+
+ aStr.append("]");
+
+ return aStr.makeStringAndClear();
+}
+
+const char * dbg_out(const SwFormTokens & rTokens)
+{
+ return dbg_out(lcl_dbg_out(rTokens));
+}
+
+static OUString lcl_dbg_out(const SwNodeRange & rRange)
+{
+ OUString aStr =
+ "[" +
+ lcl_dbg_out(SwPosition(rRange.aStart)) +
+ ", " +
+ lcl_dbg_out(SwPosition(rRange.aEnd)) +
+ "]";
+
+ return aStr;
+}
+
+const char * dbg_out(const SwNodeRange & rRange)
+{
+ return dbg_out(lcl_dbg_out(rRange));
+}
+
+#endif // DEBUG
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/doc.cxx b/sw/source/core/doc/doc.cxx
new file mode 100644
index 0000000000..4bcb2a35e8
--- /dev/null
+++ b/sw/source/core/doc/doc.cxx
@@ -0,0 +1,1951 @@
+/* -*- 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 <config_features.h>
+
+#include <doc.hxx>
+#include <com/sun/star/script/vba/XVBAEventProcessor.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <DocumentFieldsManager.hxx>
+#include <DocumentSettingManager.hxx>
+#include <DocumentDrawModelManager.hxx>
+#include <DocumentTimerManager.hxx>
+#include <DocumentDeviceManager.hxx>
+#include <DocumentChartDataProviderManager.hxx>
+#include <DocumentLinksAdministrationManager.hxx>
+#include <DocumentListItemsManager.hxx>
+#include <DocumentListsManager.hxx>
+#include <DocumentOutlineNodesManager.hxx>
+#include <DocumentContentOperationsManager.hxx>
+#include <DocumentRedlineManager.hxx>
+#include <DocumentStatisticsManager.hxx>
+#include <DocumentStateManager.hxx>
+#include <DocumentStylePoolManager.hxx>
+#include <DocumentLayoutManager.hxx>
+#include <DocumentExternalDataManager.hxx>
+#include <UndoManager.hxx>
+#include <dbmgr.hxx>
+#include <hintids.hxx>
+
+#include <comphelper/random.hxx>
+#include <tools/multisel.hxx>
+#include <rtl/ustring.hxx>
+#include <svl/poolitem.hxx>
+#include <unotools/syslocale.hxx>
+#include <editeng/keepitem.hxx>
+#include <editeng/formatbreakitem.hxx>
+#include <editeng/pbinitem.hxx>
+#include <unotools/localedatawrapper.hxx>
+
+#include <officecfg/Office/Writer.hxx>
+
+#include <swatrset.hxx>
+#include <swmodule.hxx>
+#include <fmtrfmrk.hxx>
+#include <fmtinfmt.hxx>
+#include <fmtfld.hxx>
+#include <txtfld.hxx>
+#include <dbfld.hxx>
+#include <txtinet.hxx>
+#include <txtrfmrk.hxx>
+#include <frmatr.hxx>
+#include <pagefrm.hxx>
+#include <rootfrm.hxx>
+#include <pam.hxx>
+#include <ndtxt.hxx>
+#include <swundo.hxx>
+#include <rolbck.hxx>
+#include <UndoAttribute.hxx>
+#include <UndoCore.hxx>
+#include <UndoTable.hxx>
+#include <pagedesc.hxx>
+#include <doctxm.hxx>
+#include <poolfmt.hxx>
+#include <SwGrammarMarkUp.hxx>
+#include <scriptinfo.hxx>
+#include <mdiexp.hxx>
+#include <docary.hxx>
+#include <printdata.hxx>
+#include <strings.hrc>
+#include <SwUndoTOXChange.hxx>
+#include <unocrsr.hxx>
+#include <docfld.hxx>
+#include <docufld.hxx>
+#include <viewsh.hxx>
+#include <shellres.hxx>
+#include <txtfrm.hxx>
+#include <attrhint.hxx>
+
+#include <vector>
+#include <map>
+#include <o3tl/string_view.hxx>
+#include <osl/diagnose.h>
+#include <osl/interlck.h>
+#include <vbahelper/vbaaccesshelper.hxx>
+#include <editeng/langitem.hxx>
+#include <calbck.hxx>
+#include <crsrsh.hxx>
+
+/* @@@MAINTAINABILITY-HORROR@@@
+ Probably unwanted dependency on SwDocShell
+*/
+#include <docsh.hxx>
+
+#include <com/sun/star/text/XTextRange.hpp>
+#include <editeng/unoprnms.hxx>
+#include <unotextrange.hxx>
+#include <unoprnms.hxx>
+#include <unomap.hxx>
+
+using namespace ::com::sun::star;
+
+sal_Int32 SwDoc::acquire()
+{
+ assert(mReferenceCount >= 0);
+ return osl_atomic_increment(&mReferenceCount);
+}
+
+sal_Int32 SwDoc::release()
+{
+ assert(mReferenceCount >= 1);
+ auto x = osl_atomic_decrement(&mReferenceCount);
+ if (x == 0)
+ delete this;
+ return x;
+}
+
+sal_Int32 SwDoc::getReferenceCount() const
+{
+ assert(mReferenceCount >= 0);
+ return mReferenceCount;
+}
+
+::sw::MetaFieldManager & SwDoc::GetMetaFieldManager()
+{
+ return *m_pMetaFieldManager;
+}
+
+::SwContentControlManager& SwDoc::GetContentControlManager()
+{
+ return *m_pContentControlManager;
+}
+
+::sw::UndoManager & SwDoc::GetUndoManager()
+{
+ return *m_pUndoManager;
+}
+
+::sw::UndoManager const & SwDoc::GetUndoManager() const
+{
+ return *m_pUndoManager;
+}
+
+
+IDocumentUndoRedo & SwDoc::GetIDocumentUndoRedo()
+{
+ return *m_pUndoManager;
+}
+
+IDocumentUndoRedo const & SwDoc::GetIDocumentUndoRedo() const
+{
+ return *m_pUndoManager;
+}
+
+/* IDocumentDrawModelAccess */
+IDocumentDrawModelAccess const & SwDoc::getIDocumentDrawModelAccess() const
+{
+ return GetDocumentDrawModelManager();
+}
+
+IDocumentDrawModelAccess & SwDoc::getIDocumentDrawModelAccess()
+{
+ return GetDocumentDrawModelManager();
+}
+
+::sw::DocumentDrawModelManager const & SwDoc::GetDocumentDrawModelManager() const
+{
+ return *m_pDocumentDrawModelManager;
+}
+
+::sw::DocumentDrawModelManager & SwDoc::GetDocumentDrawModelManager()
+{
+ return *m_pDocumentDrawModelManager;
+}
+
+/* IDocumentSettingAccess */
+IDocumentSettingAccess const & SwDoc::getIDocumentSettingAccess() const
+{
+ return GetDocumentSettingManager();
+}
+
+IDocumentSettingAccess & SwDoc::getIDocumentSettingAccess()
+{
+ return GetDocumentSettingManager();
+}
+
+::sw::DocumentSettingManager & SwDoc::GetDocumentSettingManager()
+{
+ return *m_pDocumentSettingManager;
+}
+
+::sw::DocumentSettingManager const & SwDoc::GetDocumentSettingManager() const
+{
+ return *m_pDocumentSettingManager;
+}
+
+sal_uInt32 SwDoc::getRsid() const
+{
+ return mnRsid;
+}
+
+void SwDoc::setRsid( sal_uInt32 nVal )
+{
+ static bool bHack = (getenv("LIBO_ONEWAY_STABLE_ODF_EXPORT") != nullptr);
+
+ sal_uInt32 nIncrease = 0;
+ if (!bHack)
+ {
+ // Increase the rsid with a random number smaller than 2^17. This way we
+ // expect to be able to edit a document 2^12 times before rsid overflows.
+ // start from 1 to ensure the new rsid is not the same
+ nIncrease = comphelper::rng::uniform_uint_distribution(1, (1 << 17) - 1);
+ }
+ mnRsid = nVal + nIncrease;
+}
+
+sal_uInt32 SwDoc::getRsidRoot() const
+{
+ return mnRsidRoot;
+}
+
+void SwDoc::setRsidRoot( sal_uInt32 nVal )
+{
+ mnRsidRoot = nVal;
+}
+
+/* IDocumentChartDataProviderAccess */
+IDocumentChartDataProviderAccess const & SwDoc::getIDocumentChartDataProviderAccess() const
+{
+ return *m_pDocumentChartDataProviderManager;
+}
+
+IDocumentChartDataProviderAccess & SwDoc::getIDocumentChartDataProviderAccess()
+{
+ return *m_pDocumentChartDataProviderManager;
+}
+
+// IDocumentDeviceAccess
+IDocumentDeviceAccess const & SwDoc::getIDocumentDeviceAccess() const
+{
+ return *m_pDeviceAccess;
+}
+
+IDocumentDeviceAccess & SwDoc::getIDocumentDeviceAccess()
+{
+ return *m_pDeviceAccess;
+}
+
+//IDocumentTimerAccess
+IDocumentTimerAccess const & SwDoc::getIDocumentTimerAccess() const
+{
+ return *m_pDocumentTimerManager;
+}
+
+IDocumentTimerAccess & SwDoc::getIDocumentTimerAccess()
+{
+ return *m_pDocumentTimerManager;
+}
+
+// IDocumentLinksAdministration
+IDocumentLinksAdministration const & SwDoc::getIDocumentLinksAdministration() const
+{
+ return *m_pDocumentLinksAdministrationManager;
+}
+
+IDocumentLinksAdministration & SwDoc::getIDocumentLinksAdministration()
+{
+ return *m_pDocumentLinksAdministrationManager;
+}
+
+::sw::DocumentLinksAdministrationManager const & SwDoc::GetDocumentLinksAdministrationManager() const
+{
+ return *m_pDocumentLinksAdministrationManager;
+}
+
+::sw::DocumentLinksAdministrationManager & SwDoc::GetDocumentLinksAdministrationManager()
+{
+ return *m_pDocumentLinksAdministrationManager;
+}
+
+//IDocumentListItems
+IDocumentListItems const & SwDoc::getIDocumentListItems() const
+{
+ return *m_pDocumentListItemsManager;
+}
+
+//IDocumentListItems
+IDocumentListItems & SwDoc::getIDocumentListItems()
+{
+ return *m_pDocumentListItemsManager;
+}
+
+//IDocumentListsAccess
+IDocumentListsAccess const & SwDoc::getIDocumentListsAccess() const
+{
+ return *m_pDocumentListsManager;
+}
+
+IDocumentListsAccess & SwDoc::getIDocumentListsAccess()
+{
+ return *m_pDocumentListsManager;
+}
+
+//IDocumentOutlinesNodes
+IDocumentOutlineNodes const & SwDoc::getIDocumentOutlineNodes() const
+{
+ return *m_pDocumentOutlineNodesManager;
+}
+
+IDocumentOutlineNodes & SwDoc::getIDocumentOutlineNodes()
+{
+ return *m_pDocumentOutlineNodesManager;
+}
+
+//IDocumentContentOperations
+IDocumentContentOperations const & SwDoc::getIDocumentContentOperations() const
+{
+ return *m_pDocumentContentOperationsManager;
+}
+
+IDocumentContentOperations & SwDoc::getIDocumentContentOperations()
+{
+ return *m_pDocumentContentOperationsManager;
+}
+
+::sw::DocumentContentOperationsManager const & SwDoc::GetDocumentContentOperationsManager() const
+{
+ return *m_pDocumentContentOperationsManager;
+}
+::sw::DocumentContentOperationsManager & SwDoc::GetDocumentContentOperationsManager()
+{
+ return *m_pDocumentContentOperationsManager;
+}
+
+//IDocumentRedlineAccess
+IDocumentRedlineAccess const & SwDoc::getIDocumentRedlineAccess() const
+{
+ return *m_pDocumentRedlineManager;
+}
+
+IDocumentRedlineAccess& SwDoc::getIDocumentRedlineAccess()
+{
+ return *m_pDocumentRedlineManager;
+}
+
+::sw::DocumentRedlineManager const & SwDoc::GetDocumentRedlineManager() const
+{
+ return *m_pDocumentRedlineManager;
+}
+
+::sw::DocumentRedlineManager& SwDoc::GetDocumentRedlineManager()
+{
+ return *m_pDocumentRedlineManager;
+}
+
+//IDocumentFieldsAccess
+
+IDocumentFieldsAccess const & SwDoc::getIDocumentFieldsAccess() const
+{
+ return *m_pDocumentFieldsManager;
+}
+
+IDocumentFieldsAccess & SwDoc::getIDocumentFieldsAccess()
+{
+ return *m_pDocumentFieldsManager;
+}
+
+::sw::DocumentFieldsManager & SwDoc::GetDocumentFieldsManager()
+{
+ return *m_pDocumentFieldsManager;
+}
+
+//IDocumentStatistics
+IDocumentStatistics const & SwDoc::getIDocumentStatistics() const
+{
+ return *m_pDocumentStatisticsManager;
+}
+
+IDocumentStatistics & SwDoc::getIDocumentStatistics()
+{
+ return *m_pDocumentStatisticsManager;
+}
+
+::sw::DocumentStatisticsManager const & SwDoc::GetDocumentStatisticsManager() const
+{
+ return *m_pDocumentStatisticsManager;
+}
+
+::sw::DocumentStatisticsManager & SwDoc::GetDocumentStatisticsManager()
+{
+ return *m_pDocumentStatisticsManager;
+}
+
+//IDocumentState
+IDocumentState const & SwDoc::getIDocumentState() const
+{
+ return *m_pDocumentStateManager;
+}
+
+IDocumentState & SwDoc::getIDocumentState()
+{
+ return *m_pDocumentStateManager;
+}
+
+//IDocumentLayoutAccess
+IDocumentLayoutAccess const & SwDoc::getIDocumentLayoutAccess() const
+{
+ return *m_pDocumentLayoutManager;
+}
+
+IDocumentLayoutAccess & SwDoc::getIDocumentLayoutAccess()
+{
+ return *m_pDocumentLayoutManager;
+}
+
+::sw::DocumentLayoutManager const & SwDoc::GetDocumentLayoutManager() const
+{
+ return *m_pDocumentLayoutManager;
+}
+
+::sw::DocumentLayoutManager & SwDoc::GetDocumentLayoutManager()
+{
+ return *m_pDocumentLayoutManager;
+}
+
+//IDocumentStylePoolAccess
+IDocumentStylePoolAccess const & SwDoc::getIDocumentStylePoolAccess() const
+{
+ return *m_pDocumentStylePoolManager;
+}
+
+IDocumentStylePoolAccess & SwDoc::getIDocumentStylePoolAccess()
+{
+ return *m_pDocumentStylePoolManager;
+}
+
+//IDocumentExternalData
+IDocumentExternalData const & SwDoc::getIDocumentExternalData() const
+{
+ return *m_pDocumentExternalDataManager;
+}
+
+IDocumentExternalData & SwDoc::getIDocumentExternalData()
+{
+ return *m_pDocumentExternalDataManager;
+}
+
+/* Implementations the next Interface here */
+
+/*
+ * Document editing (Doc-SS) to fill the document
+ * by the RTF parser and for the EditShell.
+ */
+void SwDoc::ChgDBData(const SwDBData& rNewData)
+{
+ if( rNewData != maDBData )
+ {
+ maDBData = rNewData;
+ getIDocumentState().SetModified();
+ if (m_pDBManager)
+ m_pDBManager->CommitLastRegistrations();
+ }
+ getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::DatabaseName)->UpdateFields();
+}
+
+namespace {
+
+struct PostItField_ : public SetGetExpField
+{
+ PostItField_( const SwNode& rNd, const SwTextField* pField )
+ : SetGetExpField( rNd, pField, std::nullopt ) {}
+
+ sal_uInt16 GetPageNo( const StringRangeEnumerator &rRangeEnum,
+ const o3tl::sorted_vector< sal_Int32 > &rPossiblePages,
+ sal_uInt16& rVirtPgNo, sal_Int32& rLineNo );
+
+ const SwPostItField* GetPostIt() const
+ {
+ return static_cast<const SwPostItField*>( GetTextField()->GetFormatField().GetField() );
+ }
+};
+
+}
+
+sal_uInt16 PostItField_::GetPageNo(
+ const StringRangeEnumerator &rRangeEnum,
+ const o3tl::sorted_vector< sal_Int32 > &rPossiblePages,
+ /* out */ sal_uInt16& rVirtPgNo, /* out */ sal_Int32& rLineNo )
+{
+ //Problem: If a PostItField is contained in a Node that is represented
+ //by more than one layout instance,
+ //we have to decide whether it should be printed once or n-times.
+ //Probably only once. For the page number we don't select a random one,
+ //but the PostIt's first occurrence in the selected area.
+ rVirtPgNo = 0;
+ SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(GetTextField()->GetTextNode());
+ for( SwTextFrame* pFrame = aIter.First(); pFrame; pFrame = aIter.Next() )
+ {
+ TextFrameIndex const nPos = pFrame->MapModelToView(
+ &GetTextField()->GetTextNode(), GetContent());
+ if( pFrame->GetOffset() > nPos ||
+ (pFrame->HasFollow() && pFrame->GetFollow()->GetOffset() <= nPos) )
+ continue;
+ sal_uInt16 nPgNo = pFrame->GetPhyPageNum();
+ if( rRangeEnum.hasValue( nPgNo, &rPossiblePages ))
+ {
+ rLineNo = o3tl::narrowing<sal_Int32>(pFrame->GetLineCount( nPos ) +
+ pFrame->GetAllLines() - pFrame->GetThisLines());
+ rVirtPgNo = pFrame->GetVirtPageNum();
+ return nPgNo;
+ }
+ }
+ return 0;
+}
+
+bool sw_GetPostIts(const IDocumentFieldsAccess& rIDFA, SetGetExpFields* pSrtLst)
+{
+ SwFieldType* pFieldType = rIDFA.GetSysFieldType(SwFieldIds::Postit);
+ assert(pFieldType);
+
+ std::vector<SwFormatField*> vFields;
+ pFieldType->GatherFields(vFields);
+ if(pSrtLst)
+ for(auto pField: vFields)
+ {
+ auto pTextField = pField->GetTextField();
+ std::unique_ptr<PostItField_> pNew(new PostItField_(pTextField->GetTextNode(), pTextField));
+ pSrtLst->insert(std::move(pNew));
+
+ }
+ return vFields.size()>0;
+}
+
+static void lcl_FormatPostIt(
+ IDocumentContentOperations* pIDCO,
+ SwPaM& aPam,
+ const SwPostItField* pField,
+ bool bNewPage, bool bIsFirstPostIt,
+ sal_uInt16 nPageNo, sal_Int32 nLineNo )
+{
+ static char const sTmp[] = " : ";
+
+ assert(SwViewShell::GetShellRes());
+
+ if (bNewPage)
+ {
+ pIDCO->InsertPoolItem( aPam, SvxFormatBreakItem( SvxBreak::PageAfter, RES_BREAK ) );
+ pIDCO->SplitNode( *aPam.GetPoint(), false );
+ }
+ else if (!bIsFirstPostIt)
+ {
+ // add an empty line between different notes
+ pIDCO->SplitNode( *aPam.GetPoint(), false );
+ pIDCO->SplitNode( *aPam.GetPoint(), false );
+ }
+
+ OUString aStr = SwViewShell::GetShellRes()->aPostItPage +
+ sTmp +
+ OUString::number( nPageNo ) +
+ " ";
+ if( nLineNo )
+ {
+ aStr += SwViewShell::GetShellRes()->aPostItLine +
+ sTmp +
+ OUString::number( nLineNo ) +
+ " ";
+ }
+ SvtSysLocale aSysLocale;
+ aStr += SwViewShell::GetShellRes()->aPostItAuthor +
+ sTmp + pField->GetPar1() + " " +
+ /*(LocaleDataWrapper&)*/aSysLocale.GetLocaleData().getDate( pField->GetDate() );
+ if(pField->GetResolved())
+ aStr += " " + SwResId(STR_RESOLVED);
+ pIDCO->InsertString( aPam, aStr );
+
+ pIDCO->SplitNode( *aPam.GetPoint(), false );
+ aStr = pField->GetPar2();
+#if defined(_WIN32)
+ // Throw out all CR in Windows
+ aStr = aStr.replaceAll("\r", "");
+#endif
+ pIDCO->InsertString( aPam, aStr );
+}
+
+/// provide the paper tray to use according to the page style in use,
+/// but do that only if the respective item is NOT just the default item
+static sal_Int32 lcl_GetPaperBin( const SwPageFrame *pStartFrame )
+{
+ sal_Int32 nRes = -1;
+
+ const SwFrameFormat &rFormat = pStartFrame->GetPageDesc()->GetMaster();
+ const SfxPoolItem *pItem = nullptr;
+ SfxItemState eState = rFormat.GetItemState( RES_PAPER_BIN, false, &pItem );
+ const SvxPaperBinItem *pPaperBinItem = dynamic_cast< const SvxPaperBinItem * >(pItem);
+ if (eState > SfxItemState::DEFAULT && pPaperBinItem)
+ nRes = pPaperBinItem->GetValue();
+
+ return nRes;
+}
+
+namespace
+{
+// tdf#:114663 Translates a range string from user input (with page numbering possibly not
+// taking blank pages into account) to equivalent string which references physical page numbers.
+// rUIPages2PhyPagesMap must contain a contiguous sequence of UI page numbers
+OUString UIPages2PhyPages(const OUString& rUIPageRange, const std::map< sal_Int32, sal_Int32 >& rUIPages2PhyPagesMap)
+{
+ if (rUIPages2PhyPagesMap.empty())
+ return OUString();
+ auto iMin = rUIPages2PhyPagesMap.begin();
+ const sal_Int32 nUIPageMin = iMin->first, nPhyPageMin = iMin->second;
+ auto iMax = rUIPages2PhyPagesMap.rbegin();
+ const sal_Int32 nUIPageMax = iMax->first, nPhyPageMax = iMax->second;
+ OUStringBuffer aOut(rUIPageRange.getLength());
+ OUStringBuffer aNumber(16);
+ const sal_Unicode* pInput = rUIPageRange.getStr();
+ while (*pInput)
+ {
+ while (*pInput >= '0' && *pInput <= '9')
+ aNumber.append(*pInput++);
+ if (!aNumber.isEmpty())
+ {
+ sal_Int32 nNumber = o3tl::toInt32(aNumber);
+ aNumber.setLength(0);
+ if (nNumber < nUIPageMin)
+ nNumber = nPhyPageMin-1;
+ else if (nNumber > nUIPageMax)
+ nNumber = nPhyPageMax+1;
+ else
+ nNumber = rUIPages2PhyPagesMap.at(nNumber);
+ aOut.append(nNumber);
+ }
+
+ while (*pInput && (*pInput < '0' || *pInput > '9'))
+ aOut.append(*pInput++);
+ }
+
+ return aOut.makeStringAndClear();
+}
+}
+
+// tdf#52316 remove blank pages from page count and actual page number
+void SwDoc::CalculateNonBlankPages(
+ const SwRootFrame& rLayout,
+ sal_uInt16& nDocPageCount,
+ sal_uInt16& nActualPage)
+{
+ sal_uInt16 nDocPageCountWithBlank = nDocPageCount;
+ sal_uInt16 nActualPageWithBlank = nActualPage;
+ sal_uInt16 nPageNum = 1;
+ const SwPageFrame *pStPage = dynamic_cast<const SwPageFrame*>( rLayout.Lower() );
+ while (pStPage && nPageNum <= nDocPageCountWithBlank)
+ {
+ if ( pStPage->getFrameArea().Height() == 0 )
+ {
+ --nDocPageCount;
+ if (nPageNum <= nActualPageWithBlank)
+ --nActualPage;
+ }
+ ++nPageNum;
+ pStPage = static_cast<const SwPageFrame*>(pStPage->GetNext());
+ }
+}
+
+void SwDoc::CalculatePagesForPrinting(
+ const SwRootFrame& rLayout,
+ /* out */ SwRenderData &rData,
+ const SwPrintUIOptions &rOptions,
+ bool bIsPDFExport,
+ sal_Int32 nDocPageCount )
+{
+ const sal_Int64 nContent = rOptions.getIntValue( "PrintContent", 0 );
+ const bool bPrintSelection = nContent == 2;
+
+ // properties to take into account when calculating the set of pages
+ // (PDF export UI does not allow for selecting left or right pages only)
+ bool bPrintLeftPages = bIsPDFExport || rOptions.IsPrintLeftPages();
+ bool bPrintRightPages = bIsPDFExport || rOptions.IsPrintRightPages();
+ // #i103700# printing selections should not allow for automatic inserting empty pages
+ bool bPrintEmptyPages = !bPrintSelection && rOptions.IsPrintEmptyPages( bIsPDFExport );
+
+ std::map< sal_Int32, sal_Int32 > &rPrinterPaperTrays = rData.GetPrinterPaperTrays();
+ o3tl::sorted_vector< sal_Int32 > &rValidPages = rData.GetValidPagesSet();
+ // Map page numbers from user input (possibly ignoring blanks) to physical page numbers
+ std::map< sal_Int32, sal_Int32 > aUIPages2PhyPagesMap;
+ rValidPages.clear();
+
+ sal_Int32 nPageNum = 1, nUIPageNum = 1;
+ const SwPageFrame *pStPage = dynamic_cast<const SwPageFrame*>( rLayout.Lower() );
+ while (pStPage && nPageNum <= nDocPageCount)
+ {
+ const bool bNonEmptyPage = pStPage->getFrameArea().Height() != 0;
+ const bool bPrintThisPage =
+ ( (bPrintRightPages && pStPage->OnRightPage()) ||
+ (bPrintLeftPages && !pStPage->OnRightPage()) ) &&
+ ( bPrintEmptyPages || bNonEmptyPage );
+
+ if (bPrintThisPage)
+ {
+ rValidPages.insert( nPageNum );
+ rPrinterPaperTrays[ nPageNum ] = lcl_GetPaperBin( pStPage );
+ }
+
+ if ( bPrintEmptyPages || bNonEmptyPage )
+ {
+ aUIPages2PhyPagesMap[nUIPageNum++] = nPageNum;
+ }
+ ++nPageNum;
+ pStPage = static_cast<const SwPageFrame*>(pStPage->GetNext());
+ }
+
+ // now that we have identified the valid pages for printing according
+ // to the print settings we need to get the PageRange to use and
+ // use both results to get the actual pages to be printed
+ // (post-it settings need to be taken into account later on!)
+
+ // get PageRange value to use
+ OUString aPageRange;
+ // #i116085# - adjusting fix for i113919
+ if ( !bIsPDFExport )
+ {
+ // PageContent :
+ // 0 -> print all pages (default if aPageRange is empty)
+ // 1 -> print range according to PageRange
+ // 2 -> print selection
+ if (1 == nContent)
+ aPageRange = rOptions.getStringValue( "PageRange" );
+
+ if (2 == nContent)
+ {
+ // note that printing selections is actually implemented by copying
+ // the selection to a new temporary document and printing all of that one.
+ // Thus for Writer "PrintContent" must never be 2.
+ // See SwXTextDocument::GetRenderDoc for evaluating if a selection is to be
+ // printed and for creating the temporary document.
+ }
+
+ // please note
+ }
+ if (aPageRange.isEmpty()) // empty string -> print all
+ {
+ // set page range to print to 'all pages'
+ aPageRange = OUString::number( 1 ) + "-" + OUString::number( nDocPageCount );
+ }
+ else
+ {
+ // Convert page numbers from user input to physical page numbers
+ aPageRange = UIPages2PhyPages(aPageRange, aUIPages2PhyPagesMap);
+ }
+ rData.SetPageRange( aPageRange );
+
+ // get vector of pages to print according to PageRange and valid pages set from above
+ // (result may be an empty vector, for example if the range string is not correct)
+ // If excluding empty pages, allow range to specify range of printable pages
+ StringRangeEnumerator::getRangesFromString( aPageRange, rData.GetPagesToPrint(),
+ 1, nDocPageCount, 0, &rData.GetValidPagesSet() );
+}
+
+void SwDoc::UpdatePagesForPrintingWithPostItData(
+ /* out */ SwRenderData &rData,
+ const SwPrintUIOptions &rOptions,
+ sal_Int32 nDocPageCount )
+{
+
+ SwPostItMode nPostItMode = static_cast<SwPostItMode>( rOptions.getIntValue( "PrintAnnotationMode", 0 ) );
+ assert((nPostItMode == SwPostItMode::NONE || rData.HasPostItData())
+ && "print post-its without post-it data?");
+ const SetGetExpFields::size_type nPostItCount =
+ rData.HasPostItData() ? rData.m_pPostItFields->size() : 0;
+ if (nPostItMode == SwPostItMode::NONE || nPostItCount <= 0)
+ return;
+
+ CurrShell aCurr( rData.m_pPostItShell.get() );
+
+ // clear document and move to end of it
+ SwDoc & rPostItDoc(*rData.m_pPostItShell->GetDoc());
+ SwPaM aPam(rPostItDoc.GetNodes().GetEndOfContent());
+ aPam.Move( fnMoveBackward, GoInDoc );
+ aPam.SetMark();
+ aPam.Move( fnMoveForward, GoInDoc );
+ rPostItDoc.getIDocumentContentOperations().DeleteRange( aPam );
+
+ const StringRangeEnumerator aRangeEnum( rData.GetPageRange(), 1, nDocPageCount, 0 );
+
+ // For mode SwPostItMode::EndPage:
+ // maps a physical page number to the page number in post-it document that holds
+ // the first post-it for that physical page . Needed to relate the correct start frames
+ // from the post-it doc to the physical page of the document
+ std::map< sal_Int32, sal_Int32 > aPostItLastStartPageNum;
+
+ // add all post-its on valid pages within the page range to the
+ // temporary post-it document.
+ // Since the array of post-it fields is sorted by page and line number we will
+ // already get them in the correct order
+ sal_uInt16 nVirtPg = 0, nLastPageNum = 0, nPhyPageNum = 0;
+ sal_Int32 nLineNo = 0;
+ bool bIsFirstPostIt = true;
+ for (SetGetExpFields::size_type i = 0; i < nPostItCount; ++i)
+ {
+ PostItField_& rPostIt = static_cast<PostItField_&>(*(*rData.m_pPostItFields)[ i ]);
+ nLastPageNum = nPhyPageNum;
+ nPhyPageNum = rPostIt.GetPageNo(
+ aRangeEnum, rData.GetValidPagesSet(), nVirtPg, nLineNo );
+ if (nPhyPageNum)
+ {
+ // need to insert a page break?
+ // In SwPostItMode::EndPage mode for each document page the following
+ // post-it page needs to start on a new page
+ const bool bNewPage = nPostItMode == SwPostItMode::EndPage &&
+ !bIsFirstPostIt && nPhyPageNum != nLastPageNum;
+
+ lcl_FormatPostIt( &rData.m_pPostItShell->GetDoc()->getIDocumentContentOperations(), aPam,
+ rPostIt.GetPostIt(), bNewPage, bIsFirstPostIt, nVirtPg, nLineNo );
+ bIsFirstPostIt = false;
+
+ if (nPostItMode == SwPostItMode::EndPage)
+ {
+ // get the correct number of current pages for the post-it document
+ rData.m_pPostItShell->CalcLayout();
+ const sal_Int32 nPages = rData.m_pPostItShell->GetPageCount();
+ aPostItLastStartPageNum[ nPhyPageNum ] = nPages;
+ }
+ }
+ }
+
+ // format post-it doc to get correct number of pages
+ rData.m_pPostItShell->CalcLayout();
+
+ SwRootFrame* pPostItRoot = rData.m_pPostItShell->GetLayout();
+ //tdf#103313 print dialog maxes out cpu as Idles never get to
+ //complete this postitshell's desire to complete formatting
+ pPostItRoot->ResetIdleFormat();
+
+ const sal_Int32 nPostItDocPageCount = rData.m_pPostItShell->GetPageCount();
+
+ if (nPostItMode == SwPostItMode::Only || nPostItMode == SwPostItMode::EndDoc)
+ {
+ // now add those post-it pages to the vector of pages to print
+ // or replace them if only post-its should be printed
+
+ if (nPostItMode == SwPostItMode::Only)
+ {
+ // no document page to be printed
+ rData.GetPagesToPrint().clear();
+ }
+
+ // now we just need to add the post-it pages to be printed to the
+ // end of the vector of pages to print
+ sal_Int32 nPageNum = 0;
+ const SwPageFrame * pPageFrame = static_cast<SwPageFrame*>(pPostItRoot->Lower());
+ while( pPageFrame && nPageNum < nPostItDocPageCount )
+ {
+ ++nPageNum;
+ // negative page number indicates page is from the post-it doc
+ rData.GetPagesToPrint().push_back( -nPageNum );
+ pPageFrame = static_cast<const SwPageFrame*>(pPageFrame->GetNext());
+ }
+ OSL_ENSURE( nPageNum == nPostItDocPageCount, "unexpected number of pages" );
+ }
+ else if (nPostItMode == SwPostItMode::EndPage)
+ {
+ // the next step is to find all the pages from the post-it
+ // document that should be printed for a given physical page
+ // of the document
+
+ std::vector< sal_Int32 > aTmpPagesToPrint;
+ sal_Int32 nLastPostItPage(0);
+ const size_t nNum = rData.GetPagesToPrint().size();
+ for (size_t i = 0 ; i < nNum; ++i)
+ {
+ // add the physical page to print from the document
+ const sal_Int32 nPhysPage = rData.GetPagesToPrint()[i];
+ aTmpPagesToPrint.push_back( nPhysPage );
+
+ // add the post-it document pages to print, i.e those
+ // post-it pages that have the data for the above physical page
+ std::map<sal_Int32, sal_Int32>::const_iterator const iter(
+ aPostItLastStartPageNum.find(nPhysPage));
+ if (iter != aPostItLastStartPageNum.end())
+ {
+ for (sal_Int32 j = nLastPostItPage + 1;
+ j <= iter->second; ++j)
+ {
+ // negative page number indicates page is from the
+ aTmpPagesToPrint.push_back(-j); // post-it document
+ }
+ nLastPostItPage = iter->second;
+ }
+ }
+
+ // finally we need to assign those vectors to the resulting ones.
+ // swapping the data should be more efficient than assigning since
+ // we won't need the temporary vectors anymore
+ rData.GetPagesToPrint().swap( aTmpPagesToPrint );
+ }
+
+}
+
+void SwDoc::CalculatePagePairsForProspectPrinting(
+ const SwRootFrame& rLayout,
+ /* out */ SwRenderData &rData,
+ const SwPrintUIOptions &rOptions,
+ sal_Int32 nDocPageCount )
+{
+ std::map< sal_Int32, sal_Int32 > &rPrinterPaperTrays = rData.GetPrinterPaperTrays();
+ o3tl::sorted_vector< sal_Int32 > &rValidPagesSet = rData.GetValidPagesSet();
+ std::vector< std::pair< sal_Int32, sal_Int32 > > &rPagePairs = rData.GetPagePairsForProspectPrinting();
+ std::map< sal_Int32, const SwPageFrame * > validStartFrames;
+
+ rPagePairs.clear();
+ rValidPagesSet.clear();
+
+ OUString aPageRange;
+ // PageContent :
+ // 0 -> print all pages (default if aPageRange is empty)
+ // 1 -> print range according to PageRange
+ // 2 -> print selection
+ const sal_Int64 nContent = rOptions.getIntValue( "PrintContent", 0 );
+ if (nContent == 1)
+ aPageRange = rOptions.getStringValue( "PageRange" );
+ if (aPageRange.isEmpty()) // empty string -> print all
+ {
+ // set page range to print to 'all pages'
+ aPageRange = OUString::number( 1 ) + "-" + OUString::number( nDocPageCount );
+ }
+ StringRangeEnumerator aRange( aPageRange, 1, nDocPageCount, 0 );
+
+ if ( aRange.size() <= 0)
+ return;
+
+ const SwPageFrame *pStPage = dynamic_cast<const SwPageFrame*>( rLayout.Lower() );
+ for ( sal_Int32 i = 1; pStPage && i < nDocPageCount; ++i )
+ pStPage = static_cast<const SwPageFrame*>(pStPage->GetNext());
+ if ( !pStPage ) // Then it was that
+ return;
+
+ // currently for prospect printing all pages are valid to be printed
+ // thus we add them all to the respective map and set for later use
+ sal_Int32 nPageNum = 0;
+ const SwPageFrame *pPageFrame = dynamic_cast<const SwPageFrame*>( rLayout.Lower() );
+ while( pPageFrame && nPageNum < nDocPageCount )
+ {
+ ++nPageNum;
+ rValidPagesSet.insert( nPageNum );
+ validStartFrames[ nPageNum ] = pPageFrame;
+ pPageFrame = static_cast<const SwPageFrame*>(pPageFrame->GetNext());
+
+ rPrinterPaperTrays[ nPageNum ] = lcl_GetPaperBin( pStPage );
+ }
+ OSL_ENSURE( nPageNum == nDocPageCount, "unexpected number of pages" );
+
+ // properties to take into account when calculating the set of pages
+ // Note: here bPrintLeftPages and bPrintRightPages refer to the (virtual) resulting pages
+ // of the prospect!
+ bool bPrintLeftPages = rOptions.IsPrintLeftPages();
+ bool bPrintRightPages = rOptions.IsPrintRightPages();
+ bool bPrintProspectRTL = rOptions.getIntValue( "PrintProspectRTL", 0 ) != 0;
+
+ // get pages for prospect printing according to the 'PageRange'
+ // (duplicates and any order allowed!)
+ std::vector< sal_Int32 > aPagesToPrint;
+ StringRangeEnumerator::getRangesFromString(
+ aPageRange, aPagesToPrint, 1, nDocPageCount, 0 );
+
+ if (aPagesToPrint.empty())
+ return;
+
+ // now fill the vector for calculating the page pairs with the start frames
+ // from the above obtained vector
+ std::vector< const SwPageFrame * > aVec;
+ for (sal_Int32 nPage : aPagesToPrint)
+ {
+ const SwPageFrame *pFrame = validStartFrames[ nPage ];
+ aVec.push_back( pFrame );
+ }
+
+ // just one page is special ...
+ if ( 1 == aVec.size() )
+ {
+#if defined __GNUC__ && !defined __clang__ && __GNUC__ == 12 && __cplusplus == 202002L
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Warray-bounds"
+#pragma GCC diagnostic ignored "-Wstringop-overflow"
+#endif
+ aVec.insert( aVec.begin() + 1, nullptr ); // insert a second empty page
+#if defined __GNUC__ && !defined __clang__ && __GNUC__ == 12 && __cplusplus == 202002L
+#pragma GCC diagnostic pop
+#endif
+ }
+ else
+ {
+ // now extend the number of pages to fit a multiple of 4
+ // (4 'normal' pages are needed for a single prospect paper
+ // with back and front)
+ while( aVec.size() & 3 )
+ aVec.push_back( nullptr );
+ }
+
+ // make sure that all pages are in correct order
+ std::vector< const SwPageFrame * >::size_type nSPg = 0;
+ std::vector< const SwPageFrame * >::size_type nEPg = aVec.size();
+ sal_Int32 nStep = 1;
+ if ( 0 == (nEPg & 1 )) // there are no uneven ones!
+ --nEPg;
+
+ if ( !bPrintLeftPages )
+ ++nStep;
+ else if ( !bPrintRightPages )
+ {
+ ++nStep;
+ ++nSPg;
+ --nEPg;
+ }
+
+ // the number of 'virtual' pages to be printed
+ sal_Int32 nCntPage = (( nEPg - nSPg ) / ( 2 * nStep )) + 1;
+
+ for ( sal_Int32 nPrintCount = 0; nSPg < nEPg &&
+ nPrintCount < nCntPage; ++nPrintCount )
+ {
+ pStPage = aVec[ nSPg ];
+ const SwPageFrame* pNxtPage = nEPg < aVec.size() ? aVec[ nEPg ] : nullptr;
+
+ short nRtlOfs = bPrintProspectRTL ? 1 : 0;
+ if ( 0 == (( nSPg + nRtlOfs) & 1 ) ) // switch for odd number in LTR, even number in RTL
+ {
+ const SwPageFrame* pTmp = pStPage;
+ pStPage = pNxtPage;
+ pNxtPage = pTmp;
+ }
+
+ sal_Int32 nFirst = -1, nSecond = -1;
+ for ( int nC = 0; nC < 2; ++nC )
+ {
+ sal_Int32 nPage = -1;
+ if ( pStPage )
+ nPage = pStPage->GetPhyPageNum();
+ if (nC == 0)
+ nFirst = nPage;
+ else
+ nSecond = nPage;
+
+ pStPage = pNxtPage;
+ }
+ rPagePairs.emplace_back(nFirst, nSecond );
+
+ nSPg = nSPg + nStep;
+ nEPg = nEPg - nStep;
+ }
+ OSL_ENSURE( size_t(nCntPage) == rPagePairs.size(), "size mismatch for number of page pairs" );
+
+ // luckily prospect printing does not make use of post-its so far,
+ // thus we are done here.
+}
+
+/// @return the reference in the doc for the name
+const SwFormatRefMark* SwDoc::GetRefMark( std::u16string_view rName ) const
+{
+ for (const SfxPoolItem* pItem : GetAttrPool().GetItemSurrogates(RES_TXTATR_REFMARK))
+ {
+ auto pFormatRef = dynamic_cast<const SwFormatRefMark*>(pItem);
+ if(!pFormatRef)
+ continue;
+
+ const SwTextRefMark* pTextRef = pFormatRef->GetTextRefMark();
+ if( pTextRef && &pTextRef->GetTextNode().GetNodes() == &GetNodes() &&
+ rName == pFormatRef->GetRefName() )
+ return pFormatRef;
+ }
+ return nullptr;
+}
+
+/// @return the RefMark per index - for Uno
+const SwFormatRefMark* SwDoc::GetRefMark( sal_uInt16 nIndex ) const
+{
+ const SwFormatRefMark* pRet = nullptr;
+
+ sal_uInt32 nCount = 0;
+ for (const SfxPoolItem* pItem : GetAttrPool().GetItemSurrogates(RES_TXTATR_REFMARK))
+ {
+ auto pRefMark = dynamic_cast<const SwFormatRefMark*>(pItem);
+ if( !pRefMark )
+ continue;
+ const SwTextRefMark* pTextRef = pRefMark->GetTextRefMark();
+ if( pTextRef && &pTextRef->GetTextNode().GetNodes() == &GetNodes() )
+ {
+ if(nCount == nIndex)
+ {
+ pRet = pRefMark;
+ break;
+ }
+ nCount++;
+ }
+ }
+ return pRet;
+}
+
+/// @return the names of all set references in the Doc
+//JP 24.06.96: If the array pointer is 0, then just return whether a RefMark is set in the Doc
+// OS 25.06.96: From now on we always return the reference count
+sal_uInt16 SwDoc::GetRefMarks( std::vector<OUString>* pNames ) const
+{
+ sal_uInt16 nCount = 0;
+ for (const SfxPoolItem* pItem : GetAttrPool().GetItemSurrogates(RES_TXTATR_REFMARK))
+ {
+ auto pRefMark = dynamic_cast<const SwFormatRefMark*>(pItem);
+ if( !pRefMark )
+ continue;
+ const SwTextRefMark* pTextRef = pRefMark->GetTextRefMark();
+ if( pTextRef && &pTextRef->GetTextNode().GetNodes() == &GetNodes() )
+ {
+ if( pNames )
+ {
+ OUString aTmp(pRefMark->GetRefName());
+ pNames->insert(pNames->begin() + nCount, aTmp);
+ }
+ ++nCount;
+ }
+ }
+
+ return nCount;
+}
+
+void SwDoc::DeleteFormatRefMark(const SwFormatRefMark* pFormatRefMark)
+{
+ const SwTextRefMark* pTextRefMark = pFormatRefMark->GetTextRefMark();
+ SwTextNode& rTextNd = const_cast<SwTextNode&>(pTextRefMark->GetTextNode());
+ std::unique_ptr<SwRegHistory> aRegHistory;
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ SwUndoResetAttr* pUndo = new SwUndoResetAttr(SwPosition(rTextNd, pTextRefMark->GetStart()),
+ RES_TXTATR_REFMARK);
+ GetIDocumentUndoRedo().AppendUndo(std::unique_ptr<SwUndo>(pUndo));
+ aRegHistory.reset(new SwRegHistory(rTextNd, &pUndo->GetHistory()));
+ rTextNd.GetpSwpHints()->Register(aRegHistory.get());
+ }
+ rTextNd.DeleteAttribute(const_cast<SwTextRefMark*>(pTextRefMark));
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ if (rTextNd.GetpSwpHints())
+ rTextNd.GetpSwpHints()->DeRegister();
+ }
+ getIDocumentState().SetModified();
+}
+
+static bool lcl_SpellAndGrammarAgain( SwNode* pNd, void* pArgs )
+{
+ SwTextNode *pTextNode = pNd->GetTextNode();
+ bool bOnlyWrong = *static_cast<sal_Bool*>(pArgs);
+ if( pTextNode )
+ {
+ if( bOnlyWrong )
+ {
+ if( pTextNode->GetWrong() &&
+ pTextNode->GetWrong()->InvalidateWrong() )
+ pTextNode->SetWrongDirty(sw::WrongState::TODO);
+ if( pTextNode->GetGrammarCheck() &&
+ pTextNode->GetGrammarCheck()->InvalidateWrong() )
+ pTextNode->SetGrammarCheckDirty( true );
+ }
+ else
+ {
+ pTextNode->SetWrongDirty(sw::WrongState::TODO);
+ if( pTextNode->GetWrong() )
+ pTextNode->GetWrong()->SetInvalid( 0, COMPLETE_STRING );
+ pTextNode->SetGrammarCheckDirty( true );
+ if( pTextNode->GetGrammarCheck() )
+ pTextNode->GetGrammarCheck()->SetInvalid( 0, COMPLETE_STRING );
+ }
+ }
+ return true;
+}
+
+static bool lcl_CheckSmartTagsAgain( SwNode* pNd, void* )
+{
+ SwTextNode *pTextNode = pNd->GetTextNode();
+ if( pTextNode )
+ {
+ pTextNode->SetSmartTagDirty( true );
+ pTextNode->ClearSmartTags();
+ }
+ return true;
+}
+
+/**
+ * Re-trigger spelling in the idle handler.
+ *
+ * @param bInvalid if <true>, the WrongLists in all nodes are invalidated
+ * and the SpellInvalid flag is set on all pages.
+ * @param bOnlyWrong controls whether only the areas with wrong words are
+ * checked or the whole area.
+ * @param bSmartTags ???
+ */
+void SwDoc::SpellItAgainSam( bool bInvalid, bool bOnlyWrong, bool bSmartTags )
+{
+ o3tl::sorted_vector<SwRootFrame*> aAllLayouts = GetAllLayouts();
+ assert(getIDocumentLayoutAccess().GetCurrentLayout() && "SpellAgain: Where's my RootFrame?");
+ if( bInvalid )
+ {
+ for ( auto aLayout : aAllLayouts )
+ {
+ aLayout->AllInvalidateSmartTagsOrSpelling(bSmartTags);
+ aLayout->SetNeedGrammarCheck(true);
+ }
+ if ( bSmartTags )
+ GetNodes().ForEach( lcl_CheckSmartTagsAgain, &bOnlyWrong );
+ GetNodes().ForEach( lcl_SpellAndGrammarAgain, &bOnlyWrong );
+ }
+
+ for ( auto aLayout : aAllLayouts )
+ aLayout->SetIdleFlags();
+}
+
+void SwDoc::InvalidateAutoCompleteFlag()
+{
+ SwRootFrame* pTmpRoot = getIDocumentLayoutAccess().GetCurrentLayout();
+ if( !pTmpRoot )
+ return;
+
+ o3tl::sorted_vector<SwRootFrame*> aAllLayouts = GetAllLayouts();
+ for( auto aLayout : aAllLayouts )
+ aLayout->AllInvalidateAutoCompleteWords();
+ for( SwNodeOffset nNd(1), nCnt = GetNodes().Count(); nNd < nCnt; ++nNd )
+ {
+ SwTextNode* pTextNode = GetNodes()[ nNd ]->GetTextNode();
+ if ( pTextNode ) pTextNode->SetAutoCompleteWordDirty( true );
+ }
+
+ for( auto aLayout : aAllLayouts )
+ aLayout->SetIdleFlags();
+}
+
+const SwFormatINetFormat* SwDoc::FindINetAttr( std::u16string_view rName ) const
+{
+ for (const SfxPoolItem* pItem : GetAttrPool().GetItemSurrogates(RES_TXTATR_INETFMT))
+ {
+ auto pFormatItem = dynamic_cast<const SwFormatINetFormat*>(pItem);
+ if( !pFormatItem || pFormatItem->GetName() != rName )
+ continue;
+ const SwTextINetFormat* pTextAttr = pFormatItem->GetTextINetFormat();
+ if( !pTextAttr )
+ continue;
+ const SwTextNode* pTextNd = pTextAttr->GetpTextNode();
+ if( pTextNd && &pTextNd->GetNodes() == &GetNodes() )
+ {
+ return pFormatItem;
+ }
+ }
+ return nullptr;
+}
+
+void SwDoc::Summary(SwDoc& rExtDoc, sal_uInt8 nLevel, sal_uInt8 nPara, bool bImpress)
+{
+ const SwOutlineNodes& rOutNds = GetNodes().GetOutLineNds();
+ if (rOutNds.empty())
+ return;
+
+ ::StartProgress( STR_STATSTR_SUMMARY, 0, rOutNds.size(), GetDocShell() );
+ SwNodeIndex aEndOfDoc( rExtDoc.GetNodes().GetEndOfContent(), -1 );
+ for( SwOutlineNodes::size_type i = 0; i < rOutNds.size(); ++i )
+ {
+ ::SetProgressState( static_cast<tools::Long>(i), GetDocShell() );
+ const SwNodeOffset nIndex = rOutNds[ i ]->GetIndex();
+
+ const int nLvl = GetNodes()[ nIndex ]->GetTextNode()->GetAttrOutlineLevel()-1;
+ if( nLvl > nLevel )
+ continue;
+ SwNodeOffset nEndOfs(1);
+ sal_uInt8 nWish = nPara;
+ SwNodeOffset nNextOutNd = i + 1 < rOutNds.size() ?
+ rOutNds[ i + 1 ]->GetIndex() : GetNodes().Count();
+ bool bKeep = false;
+ while( ( nWish || bKeep ) && nIndex + nEndOfs < nNextOutNd &&
+ GetNodes()[ nIndex + nEndOfs ]->IsTextNode() )
+ {
+ SwTextNode* pTextNode = GetNodes()[ nIndex+nEndOfs ]->GetTextNode();
+ if (pTextNode->GetText().getLength() && nWish)
+ --nWish;
+ bKeep = pTextNode->GetSwAttrSet().GetKeep().GetValue();
+ ++nEndOfs;
+ }
+
+ SwNodeRange aRange( *rOutNds[ i ], SwNodeOffset(0), *rOutNds[ i ], nEndOfs );
+ GetNodes().Copy_( aRange, aEndOfDoc.GetNode() );
+ }
+ const SwTextFormatColls *pColl = rExtDoc.GetTextFormatColls();
+ for( SwTextFormatColls::size_type i = 0; i < pColl->size(); ++i )
+ (*pColl)[ i ]->ResetFormatAttr( RES_PAGEDESC, RES_BREAK );
+ SwNodeIndex aIndx( rExtDoc.GetNodes().GetEndOfExtras() );
+ ++aEndOfDoc;
+ while( aIndx < aEndOfDoc )
+ {
+ bool bDelete = false;
+ SwNode *pNode = &aIndx.GetNode();
+ if( pNode->IsTextNode() )
+ {
+ SwTextNode *pNd = pNode->GetTextNode();
+ if( pNd->HasSwAttrSet() )
+ pNd->ResetAttr( RES_PAGEDESC, RES_BREAK );
+ if( bImpress )
+ {
+ SwTextFormatColl* pMyColl = pNd->GetTextColl();
+
+ const sal_uInt16 nHeadLine = o3tl::narrowing<sal_uInt16>(
+ !pMyColl->IsAssignedToListLevelOfOutlineStyle()
+ ? RES_POOLCOLL_HEADLINE2
+ : RES_POOLCOLL_HEADLINE1 );
+ pMyColl = rExtDoc.getIDocumentStylePoolAccess().GetTextCollFromPool( nHeadLine );
+ pNd->ChgFormatColl( pMyColl );
+ }
+ if( !pNd->Len() &&
+ pNd->StartOfSectionIndex()+SwNodeOffset(2) < pNd->EndOfSectionIndex() )
+ {
+ bDelete = true;
+ rExtDoc.GetNodes().Delete( aIndx );
+ }
+ }
+ if( !bDelete )
+ ++aIndx;
+ }
+ ::EndProgress( GetDocShell() );
+}
+
+namespace
+{
+void RemoveOrDeleteContents(SwTextNode* pTextNd, IDocumentContentOperations& xOperations)
+{
+ SwPaM aPam(*pTextNd, 0, *pTextNd, pTextNd->GetText().getLength());
+
+ // Remove hidden paragraph or delete contents:
+ // Delete contents if
+ // 1. removing the paragraph would result in an empty section or
+ // 2. if the paragraph is the last paragraph in the section and
+ // there is no paragraph in front of the paragraph:
+ if ((SwNodeOffset(2) == pTextNd->EndOfSectionIndex() - pTextNd->StartOfSectionIndex())
+ || (SwNodeOffset(1) == pTextNd->EndOfSectionIndex() - pTextNd->GetIndex()
+ && !pTextNd->GetNodes()[pTextNd->GetIndex() - 1]->GetTextNode()))
+ {
+ xOperations.DeleteRange(aPam);
+ }
+ else
+ {
+ aPam.DeleteMark();
+ xOperations.DelFullPara(aPam);
+ }
+}
+// Returns if the data was actually modified
+bool HandleHidingField(SwFormatField& rFormatField, const SwNodes& rNodes,
+ IDocumentContentOperations& xOperations)
+{
+ if( !rFormatField.GetTextField() )
+ return false;
+ SwTextNode* pTextNd = rFormatField.GetTextField()->GetpTextNode();
+ if( pTextNd
+ && pTextNd->GetpSwpHints() && pTextNd->IsHiddenByParaField()
+ && &pTextNd->GetNodes() == &rNodes)
+ {
+ RemoveOrDeleteContents(pTextNd, xOperations);
+ return true;
+ }
+ return false;
+}
+}
+
+// The greater the returned value, the more weight has this field type on deciding the final
+// paragraph state
+int SwDoc::FieldCanHideParaWeight(SwFieldIds eFieldId) const
+{
+ switch (eFieldId)
+ {
+ case SwFieldIds::HiddenPara:
+ return 20;
+ case SwFieldIds::Database:
+ return GetDocumentSettingManager().get(DocumentSettingId::EMPTY_DB_FIELD_HIDES_PARA)
+ ? 10
+ : 0;
+ default:
+ return 0;
+ }
+}
+
+bool SwDoc::FieldHidesPara(const SwField& rField) const
+{
+ switch (rField.GetTyp()->Which())
+ {
+ case SwFieldIds::HiddenPara:
+ return static_cast<const SwHiddenParaField&>(rField).IsHidden();
+ case SwFieldIds::Database:
+ return FieldCanHideParaWeight(SwFieldIds::Database)
+ && rField.ExpandField(true, nullptr).isEmpty();
+ default:
+ return false;
+ }
+}
+
+/// Remove the invisible content from the document e.g. hidden areas, hidden paragraphs
+// Returns if the data was actually modified
+bool SwDoc::RemoveInvisibleContent()
+{
+ bool bRet = false;
+ GetIDocumentUndoRedo().StartUndo( SwUndoId::UI_DELETE_INVISIBLECNTNT, nullptr );
+
+ {
+ class FieldTypeGuard : public SwClient
+ {
+ public:
+ explicit FieldTypeGuard(SwFieldType* pType)
+ : SwClient(pType)
+ {
+ }
+ const SwFieldType* get() const
+ {
+ return static_cast<const SwFieldType*>(GetRegisteredIn());
+ }
+ };
+ // Removing some nodes for one SwFieldIds::Database type might remove the type from
+ // document's field types, invalidating iterators. So, we need to create own list of
+ // matching types prior to processing them.
+ std::vector<std::unique_ptr<FieldTypeGuard>> aHidingFieldTypes;
+ for (std::unique_ptr<SwFieldType> const & pType : *getIDocumentFieldsAccess().GetFieldTypes())
+ {
+ if (FieldCanHideParaWeight(pType->Which()))
+ aHidingFieldTypes.push_back(std::make_unique<FieldTypeGuard>(pType.get()));
+ }
+ for (const auto& pTypeGuard : aHidingFieldTypes)
+ {
+ if (const SwFieldType* pType = pTypeGuard->get())
+ {
+ std::vector<SwFormatField*> vFields;
+ pType->GatherFields(vFields);
+ for(auto pFormatField: vFields)
+ bRet |= HandleHidingField(*pFormatField, GetNodes(), getIDocumentContentOperations());
+ }
+ }
+ }
+
+ // Remove any hidden paragraph (hidden text attribute)
+ for( SwNodeOffset n = GetNodes().Count(); n; )
+ {
+ SwTextNode* pTextNd = GetNodes()[ --n ]->GetTextNode();
+ if ( pTextNd )
+ {
+ bool bRemoved = false;
+ if ( pTextNd->HasHiddenCharAttribute( true ) )
+ {
+ bRemoved = true;
+ bRet = true;
+
+ if (SwNodeOffset(2) == pTextNd->EndOfSectionIndex() - pTextNd->StartOfSectionIndex())
+ {
+ SwFrameFormat *const pFormat = pTextNd->StartOfSectionNode()->GetFlyFormat();
+ if (nullptr != pFormat)
+ {
+ // remove hidden text frame
+ getIDocumentLayoutAccess().DelLayoutFormat(pFormat);
+ }
+ else
+ {
+ // default, remove hidden paragraph
+ RemoveOrDeleteContents(pTextNd, getIDocumentContentOperations());
+ }
+ }
+ else
+ {
+ // default, remove hidden paragraph
+ RemoveOrDeleteContents(pTextNd, getIDocumentContentOperations());
+ }
+ }
+ else if ( pTextNd->HasHiddenCharAttribute( false ) )
+ {
+ bRemoved = true;
+ bRet = true;
+ SwScriptInfo::DeleteHiddenRanges( *pTextNd );
+ }
+
+ // Footnotes/Frames may have been removed, therefore we have
+ // to reset n:
+ if ( bRemoved )
+ {
+ // [n] has to be inside [0 .. GetNodes().Count()] range
+ if (n > GetNodes().Count())
+ n = GetNodes().Count();
+ }
+ }
+ }
+
+ {
+ // Delete/empty all hidden areas
+ o3tl::sorted_vector<SwSectionFormat*> aSectFormats;
+ SwSectionFormats& rSectFormats = GetSections();
+
+ for( SwSectionFormats::size_type n = rSectFormats.size(); n; )
+ {
+ SwSectionFormat* pSectFormat = rSectFormats[ --n ];
+ // don't add sections in Undo/Redo
+ if( !pSectFormat->IsInNodesArr())
+ continue;
+ SwSection* pSect = pSectFormat->GetSection();
+ if( pSect->CalcHiddenFlag() )
+ {
+ SwSection* pParent = pSect, *pTmp;
+ while( nullptr != (pTmp = pParent->GetParent() ))
+ {
+ if( pTmp->IsHiddenFlag() )
+ pSect = pTmp;
+ pParent = pTmp;
+ }
+
+ aSectFormats.insert( pSect->GetFormat() );
+ }
+ if( !pSect->GetCondition().isEmpty() )
+ {
+ SwSectionData aSectionData( *pSect );
+ aSectionData.SetCondition( OUString() );
+ aSectionData.SetHidden( false );
+ UpdateSection( n, aSectionData );
+ }
+ }
+
+ auto n = aSectFormats.size();
+
+ if( 0 != n )
+ {
+ while( n )
+ {
+ SwSectionFormat* pSectFormat = aSectFormats[ --n ];
+ SwSectionNode* pSectNd = pSectFormat->GetSectionNode();
+ if( pSectNd )
+ {
+ bRet = true;
+ SwPaM aPam( *pSectNd );
+
+ if( pSectNd->StartOfSectionNode()->StartOfSectionIndex() ==
+ pSectNd->GetIndex() - 1 &&
+ pSectNd->StartOfSectionNode()->EndOfSectionIndex() ==
+ pSectNd->EndOfSectionIndex() + 1 )
+ {
+ // only delete the content
+ SwContentNode* pCNd = GetNodes().GoNext( aPam.GetPoint() );
+ aPam.SetMark();
+ aPam.GetPoint()->Assign( *pSectNd->EndOfSectionNode() );
+ pCNd = SwNodes::GoPrevious( aPam.GetPoint() );
+ assert(pCNd); // keep coverity happy
+ aPam.GetPoint()->SetContent( pCNd->Len() );
+
+ getIDocumentContentOperations().DeleteRange( aPam );
+ }
+ else
+ {
+ // delete the whole section
+ aPam.SetMark();
+ aPam.GetPoint()->Assign( *pSectNd->EndOfSectionNode() );
+ getIDocumentContentOperations().DelFullPara( aPam );
+ }
+
+ }
+ }
+ }
+ }
+
+ if( bRet )
+ getIDocumentState().SetModified();
+ GetIDocumentUndoRedo().EndUndo( SwUndoId::UI_DELETE_INVISIBLECNTNT, nullptr );
+ return bRet;
+}
+
+bool SwDoc::HasInvisibleContent() const
+{
+ std::vector<SwFormatField*> vFields;
+ getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::HiddenPara)->GatherFields(vFields);
+ if(vFields.size())
+ return true;
+
+ // Search for any hidden paragraph (hidden text attribute)
+ for( SwNodeOffset n = GetNodes().Count()-SwNodeOffset(1); n; --n)
+ {
+ SwTextNode* pTextNd = GetNodes()[ n ]->GetTextNode();
+ if ( pTextNd &&
+ ( pTextNd->HasHiddenCharAttribute( true ) || pTextNd->HasHiddenCharAttribute( false ) ) )
+ return true;
+ }
+
+ for(auto pSectFormat : GetSections())
+ {
+ // don't add sections in Undo/Redo
+ if( !pSectFormat->IsInNodesArr())
+ continue;
+ SwSection* pSect = pSectFormat->GetSection();
+ if( pSect->IsHidden() )
+ return true;
+ }
+ return false;
+}
+
+bool SwDoc::RestoreInvisibleContent()
+{
+ SwUndoId nLastUndoId(SwUndoId::EMPTY);
+ if (GetIDocumentUndoRedo().GetLastUndoInfo(nullptr, & nLastUndoId)
+ && (SwUndoId::UI_DELETE_INVISIBLECNTNT == nLastUndoId))
+ {
+ GetIDocumentUndoRedo().Undo();
+ GetIDocumentUndoRedo().ClearRedo();
+ return true;
+ }
+ return false;
+}
+
+static bool IsMailMergeField(SwFieldIds fieldId)
+{
+ switch (fieldId)
+ {
+ case SwFieldIds::Database: // Mail merge fields
+ case SwFieldIds::DatabaseName: // Database name
+ case SwFieldIds::HiddenText: // Hidden text may use database fields in condition
+ case SwFieldIds::HiddenPara: // Hidden paragraph may use database fields in condition
+ case SwFieldIds::DbNextSet: // Moving to next mail merge record
+ case SwFieldIds::DbNumSet: // Moving to a specific mail merge record
+ case SwFieldIds::DbSetNumber: // Number of current mail merge record
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool SwDoc::ConvertFieldsToText(SwRootFrame const& rLayout)
+{
+ bool bRet = false;
+ getIDocumentFieldsAccess().LockExpFields();
+ GetIDocumentUndoRedo().StartUndo( SwUndoId::UI_REPLACE, nullptr );
+
+ const bool bOnlyConvertDBFields
+ = officecfg::Office::Writer::FormLetter::ConvertToTextOnlyMMFields::get();
+
+ const SwFieldTypes* pMyFieldTypes = getIDocumentFieldsAccess().GetFieldTypes();
+ const SwFieldTypes::size_type nCount = pMyFieldTypes->size();
+ //go backward, field types are removed
+ for(SwFieldTypes::size_type nType = nCount; nType > 0; --nType)
+ {
+ const SwFieldType *pCurType = (*pMyFieldTypes)[nType - 1].get();
+
+ if ( SwFieldIds::Postit == pCurType->Which() )
+ continue;
+
+ if (bOnlyConvertDBFields && !IsMailMergeField(pCurType->Which()))
+ continue;
+
+ std::vector<SwFormatField*> vFieldFormats;
+ pCurType->GatherFields(vFieldFormats, false);
+ for(const auto& rpFieldFormat : vFieldFormats)
+ {
+ const SwTextField *pTextField = rpFieldFormat->GetTextField();
+ // skip fields that are currently not in the document
+ // e.g. fields in undo or redo array
+
+ bool bSkip = !pTextField ||
+ !pTextField->GetpTextNode()->GetNodes().IsDocNodes();
+ if (bSkip)
+ continue;
+
+ bool bInHeaderFooter = IsInHeaderFooter(*pTextField->GetpTextNode());
+ const SwFormatField& rFormatField = pTextField->GetFormatField();
+ const SwField* pField = rFormatField.GetField();
+
+ //#i55595# some fields have to be excluded in headers/footers
+ SwFieldIds nWhich = pField->GetTyp()->Which();
+ if(!bInHeaderFooter ||
+ (nWhich != SwFieldIds::PageNumber &&
+ nWhich != SwFieldIds::Chapter &&
+ nWhich != SwFieldIds::GetExp&&
+ nWhich != SwFieldIds::SetExp&&
+ nWhich != SwFieldIds::Input&&
+ nWhich != SwFieldIds::RefPageGet&&
+ nWhich != SwFieldIds::RefPageSet))
+ {
+ OUString sText = pField->ExpandField(true, &rLayout);
+
+ // database fields should not convert their command into text
+ if( SwFieldIds::Database == pCurType->Which() && !static_cast<const SwDBField*>(pField)->IsInitialized())
+ sText.clear();
+
+ SwPaM aInsertPam(*pTextField->GetpTextNode(), pTextField->GetStart());
+ aInsertPam.SetMark();
+
+ // go to the end of the field
+ const SwTextField *pFieldAtEnd = sw::DocumentFieldsManager::GetTextFieldAtPos(*aInsertPam.End());
+ if (pFieldAtEnd && pFieldAtEnd->Which() == RES_TXTATR_INPUTFIELD)
+ {
+ SwPosition &rEndPos = *aInsertPam.GetPoint();
+ rEndPos.SetContent( SwCursorShell::EndOfInputFieldAtPos( *aInsertPam.End() ) );
+ }
+ else
+ {
+ aInsertPam.Move();
+ }
+
+ // first insert the text after field to keep the field's attributes,
+ // then delete the field
+ if (!sText.isEmpty())
+ {
+ // to keep the position after insert
+ SwPaM aDelPam( *aInsertPam.GetMark(), *aInsertPam.GetPoint() );
+ aDelPam.Move( fnMoveBackward );
+ aInsertPam.DeleteMark();
+
+ getIDocumentContentOperations().InsertString( aInsertPam, sText );
+
+ aDelPam.Move();
+ // finally remove the field
+ getIDocumentContentOperations().DeleteAndJoin( aDelPam );
+ }
+ else
+ {
+ getIDocumentContentOperations().DeleteAndJoin( aInsertPam );
+ }
+
+ bRet = true;
+ }
+ }
+ }
+
+ if( bRet )
+ getIDocumentState().SetModified();
+ GetIDocumentUndoRedo().EndUndo( SwUndoId::UI_REPLACE, nullptr );
+ getIDocumentFieldsAccess().UnlockExpFields();
+ return bRet;
+
+}
+
+bool SwDoc::IsInsTableFormatNum() const
+{
+ return SW_MOD()->IsInsTableFormatNum(GetDocumentSettingManager().get(DocumentSettingId::HTML_MODE));
+}
+
+bool SwDoc::IsInsTableChangeNumFormat() const
+{
+ return SW_MOD()->IsInsTableChangeNumFormat(GetDocumentSettingManager().get(DocumentSettingId::HTML_MODE));
+}
+
+bool SwDoc::IsInsTableAlignNum() const
+{
+ return SW_MOD()->IsInsTableAlignNum(GetDocumentSettingManager().get(DocumentSettingId::HTML_MODE));
+}
+
+bool SwDoc::IsSplitVerticalByDefault() const
+{
+ return SW_MOD()->IsSplitVerticalByDefault(GetDocumentSettingManager().get(DocumentSettingId::HTML_MODE));
+}
+
+void SwDoc::SetSplitVerticalByDefault(bool value)
+{
+ SW_MOD()->SetSplitVerticalByDefault(GetDocumentSettingManager().get(DocumentSettingId::HTML_MODE), value);
+}
+
+/// Set up the InsertDB as Undo table
+void SwDoc::AppendUndoForInsertFromDB( const SwPaM& rPam, bool bIsTable )
+{
+ if( bIsTable )
+ {
+ const SwTableNode* pTableNd = rPam.GetPoint()->GetNode().FindTableNode();
+ if( pTableNd )
+ {
+ std::unique_ptr<SwUndoCpyTable> pUndo(new SwUndoCpyTable(*this));
+ pUndo->SetTableSttIdx( pTableNd->GetIndex() );
+ GetIDocumentUndoRedo().AppendUndo( std::move(pUndo) );
+ }
+ }
+ else if( rPam.HasMark() )
+ {
+ std::unique_ptr<SwUndoCpyDoc> pUndo(new SwUndoCpyDoc( rPam ));
+ pUndo->SetInsertRange( rPam, false );
+ GetIDocumentUndoRedo().AppendUndo( std::move(pUndo) );
+ }
+}
+
+void SwDoc::ChangeTOX(SwTOXBase & rTOX, const SwTOXBase & rNew)
+{
+ assert(dynamic_cast<const SwTOXBaseSection*>(&rTOX));
+ SwTOXBaseSection& rTOXSect(static_cast<SwTOXBaseSection&>(rTOX));
+
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().AppendUndo(
+ std::make_unique<SwUndoTOXChange>(*this, rTOXSect, rNew));
+ }
+
+ rTOX = rNew;
+
+ // note: do not Update the ToX here - the caller will do it, with a ViewShell!
+}
+
+OUString SwDoc::GetPaMDescr(const SwPaM & rPam)
+{
+ if (&rPam.GetPointNode() == &rPam.GetMarkNode())
+ {
+ SwTextNode * pTextNode = rPam.GetPointNode().GetTextNode();
+
+ if (nullptr != pTextNode)
+ {
+ const sal_Int32 nStart = rPam.Start()->GetContentIndex();
+ const sal_Int32 nEnd = rPam.End()->GetContentIndex();
+
+ return SwResId(STR_START_QUOTE)
+ + ShortenString(pTextNode->GetText().copy(nStart, nEnd - nStart),
+ nUndoStringLength,
+ SwResId(STR_LDOTS))
+ + SwResId(STR_END_QUOTE);
+ }
+ }
+ else
+ {
+ return SwResId(STR_PARAGRAPHS);
+ }
+
+ return "??";
+}
+
+bool SwDoc::ContainsHiddenChars() const
+{
+ for( SwNodeOffset n = GetNodes().Count(); n; )
+ {
+ SwNode* pNd = GetNodes()[ --n ];
+ if ( pNd->IsTextNode() && pNd->GetTextNode()->HasHiddenCharAttribute( false ) )
+ return true;
+ }
+
+ return false;
+}
+
+std::shared_ptr<SwUnoCursor> SwDoc::CreateUnoCursor( const SwPosition& rPos, bool bTableCursor )
+{
+ std::shared_ptr<SwUnoCursor> pNew;
+ if( bTableCursor )
+ pNew = std::make_shared<SwUnoTableCursor>(rPos);
+ else
+ pNew = std::make_shared<SwUnoCursor>(rPos);
+
+ mvUnoCursorTable.push_back( pNew );
+ return pNew;
+}
+
+void SwDoc::ChkCondColls()
+{
+ for (SwTextFormatColls::size_type n = 0; n < mpTextFormatCollTable->size(); ++n)
+ {
+ SwTextFormatColl *pColl = (*mpTextFormatCollTable)[n];
+ if (RES_CONDTXTFMTCOLL == pColl->Which())
+ pColl->CallSwClientNotify( SwAttrHint() );
+ }
+}
+
+uno::Reference< script::vba::XVBAEventProcessor > const &
+SwDoc::GetVbaEventProcessor()
+{
+ return mxVbaEvents;
+}
+
+void SwDoc::SetVbaEventProcessor()
+{
+#if HAVE_FEATURE_SCRIPTING
+ if (mpDocShell && ooo::vba::isAlienWordDoc(*mpDocShell))
+ {
+ try
+ {
+ uno::Reference< frame::XModel > xModel( mpDocShell->GetModel(), uno::UNO_SET_THROW );
+ uno::Sequence< uno::Any > aArgs{ uno::Any(xModel) };
+ mxVbaEvents.set( ooo::vba::createVBAUnoAPIServiceWithArgs( mpDocShell, "com.sun.star.script.vba.VBATextEventProcessor" , aArgs ), uno::UNO_QUERY_THROW );
+ }
+ catch( uno::Exception& )
+ {
+ }
+ }
+#endif
+}
+
+void SwDoc::SetMissingDictionaries( bool bIsMissing )
+{
+ if (!bIsMissing)
+ meDictionaryMissing = MissingDictionary::False;
+ else if (meDictionaryMissing == MissingDictionary::Undefined)
+ meDictionaryMissing = MissingDictionary::True;
+};
+
+void SwDoc::SetLanguage(const LanguageType eLang, const sal_uInt16 nId)
+{
+ mpAttrPool->SetPoolDefaultItem(SvxLanguageItem(eLang, nId));
+}
+
+bool SwDoc::HasParagraphDirectFormatting(const SwPosition& rPos)
+{
+ rtl::Reference<SwXTextRange> xRange(SwXTextRange::CreateXTextRange(rPos.GetDoc(), rPos,
+ &rPos));
+ uno::Reference<container::XEnumeration> xParaEnum = xRange->createEnumeration();
+ uno::Reference<text::XTextRange> xThisParagraphRange(xParaEnum->nextElement(), uno::UNO_QUERY);
+ if (xThisParagraphRange.is())
+ {
+ const std::vector<OUString> aHiddenProperties{ UNO_NAME_RSID,
+ UNO_NAME_PARA_IS_NUMBERING_RESTART,
+ UNO_NAME_PARA_STYLE_NAME,
+ UNO_NAME_PARA_CONDITIONAL_STYLE_NAME,
+ UNO_NAME_PAGE_STYLE_NAME,
+ UNO_NAME_NUMBERING_START_VALUE,
+ UNO_NAME_NUMBERING_IS_NUMBER,
+ UNO_NAME_PARA_CONTINUEING_PREVIOUS_SUB_TREE,
+ UNO_NAME_CHAR_STYLE_NAME,
+ UNO_NAME_NUMBERING_LEVEL,
+ UNO_NAME_SORTED_TEXT_ID,
+ UNO_NAME_PARRSID,
+ UNO_NAME_CHAR_COLOR_THEME,
+ UNO_NAME_CHAR_COLOR_TINT_OR_SHADE };
+
+ SfxItemPropertySet const& rPropSet(*aSwMapProvider.GetPropertySet(
+ PROPERTY_MAP_PARA_AUTO_STYLE));
+ SfxItemPropertyMap const& rMap(rPropSet.getPropertyMap());
+
+ uno::Reference<beans::XPropertySet> xPropertySet(xThisParagraphRange,
+ uno::UNO_QUERY_THROW);
+ uno::Reference<beans::XPropertyState> xPropertyState(xThisParagraphRange,
+ uno::UNO_QUERY_THROW);
+ const uno::Sequence<beans::Property> aProperties
+ = xPropertySet->getPropertySetInfo()->getProperties();
+ for (const beans::Property& rProperty : aProperties)
+ {
+ const OUString& rPropName = rProperty.Name;
+ if (!rMap.hasPropertyByName(rPropName))
+ continue;
+ if (std::find(aHiddenProperties.begin(), aHiddenProperties.end(), rPropName)
+ != aHiddenProperties.end())
+ continue;
+ if (xPropertyState->getPropertyState(rPropName) == beans::PropertyState_DIRECT_VALUE)
+ return true;
+ }
+ }
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/docbasic.cxx b/sw/source/core/doc/docbasic.cxx
new file mode 100644
index 0000000000..697559b0b4
--- /dev/null
+++ b/sw/source/core/doc/docbasic.cxx
@@ -0,0 +1,233 @@
+/* -*- 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 <hintids.hxx>
+
+#include <rtl/ustring.hxx>
+#include <sal/log.hxx>
+#include <vcl/imap.hxx>
+#include <vcl/imapobj.hxx>
+#include <basic/sbx.hxx>
+#include <frmfmt.hxx>
+#include <fmtinfmt.hxx>
+#include <fmturl.hxx>
+#include <frmatr.hxx>
+#include <doc.hxx>
+#include <docsh.hxx>
+#include <swevent.hxx>
+#include <frameformats.hxx>
+#include <memory>
+
+using namespace ::com::sun::star::uno;
+
+static Sequence<Any> *lcl_docbasic_convertArgs( SbxArray& rArgs )
+{
+ Sequence<Any> *pRet = nullptr;
+
+ sal_uInt32 nCount = rArgs.Count();
+ if( nCount > 1 )
+ {
+ nCount--;
+ pRet = new Sequence<Any>( nCount );
+ Any *pUnoArgs = pRet->getArray();
+ for( sal_uInt32 i=0; i<nCount; i++ )
+ {
+ SbxVariable* pVar = rArgs.Get(i + 1);
+ switch( pVar->GetType() )
+ {
+ case SbxSTRING:
+ pUnoArgs[i] <<= pVar->GetOUString();
+ break;
+ case SbxCHAR:
+ pUnoArgs[i] <<= static_cast<sal_Int16>(pVar->GetChar()) ;
+ break;
+ case SbxUSHORT:
+ pUnoArgs[i] <<= static_cast<sal_Int16>(pVar->GetUShort());
+ break;
+ case SbxLONG:
+ pUnoArgs[i] <<= pVar->GetLong();
+ break;
+ default:
+ pUnoArgs[i].clear();
+ break;
+ }
+ }
+ }
+
+ return pRet;
+}
+
+void SwDoc::ExecMacro( const SvxMacro& rMacro, OUString* pRet, SbxArray* pArgs )
+{
+ switch( rMacro.GetScriptType() )
+ {
+ case STARBASIC:
+ {
+ SbxBaseRef aRef;
+ SbxValue* pRetValue = new SbxValue;
+ aRef = pRetValue;
+ mpDocShell->CallBasic( rMacro.GetMacName(),
+ rMacro.GetLibName(),
+ pArgs, pRet ? pRetValue : nullptr );
+
+ if( pRet && SbxNULL < pRetValue->GetType() &&
+ SbxVOID != pRetValue->GetType() )
+ {
+ // valid value, so set it
+ *pRet = pRetValue->GetOUString();
+ }
+ }
+ break;
+ case JAVASCRIPT:
+ // ignore JavaScript calls
+ break;
+ case EXTENDED_STYPE:
+ {
+ std::unique_ptr<Sequence<Any> > pUnoArgs;
+ if( pArgs )
+ {
+ // better to rename the local function to lcl_translateBasic2Uno and
+ // a much shorter routine can be found in sfx2/source/doc/objmisc.cxx
+ pUnoArgs.reset(lcl_docbasic_convertArgs( *pArgs ));
+ }
+
+ if (!pUnoArgs)
+ {
+ pUnoArgs.reset(new Sequence< Any > (0));
+ }
+
+ // TODO - return value is not handled
+ Any aRet;
+ Sequence< sal_Int16 > aOutArgsIndex;
+ Sequence< Any > aOutArgs;
+
+ SAL_INFO("sw", "SwDoc::ExecMacro URL is " << rMacro.GetMacName() );
+
+ mpDocShell->CallXScript(
+ rMacro.GetMacName(), *pUnoArgs, aRet, aOutArgsIndex, aOutArgs);
+
+ break;
+ }
+ }
+}
+
+sal_uInt16 SwDoc::CallEvent( SvMacroItemId nEvent, const SwCallMouseEvent& rCallEvent,
+ bool bCheckPtr )
+{
+ if( !mpDocShell ) // we can't do that without a DocShell!
+ return 0;
+
+ sal_uInt16 nRet = 0;
+ const SvxMacroTableDtor* pTable = nullptr;
+ switch( rCallEvent.eType )
+ {
+ case EVENT_OBJECT_INETATTR:
+ if( bCheckPtr )
+ {
+ for (const SfxPoolItem* pItem : GetAttrPool().GetItemSurrogates(RES_TXTATR_INETFMT))
+ {
+ auto pFormatItem = dynamic_cast<const SwFormatINetFormat*>(pItem);
+ if( pFormatItem && SfxPoolItem::areSame(rCallEvent.PTR.pINetAttr, pFormatItem) )
+ {
+ bCheckPtr = false; // misuse as a flag
+ break;
+ }
+ }
+ }
+ if( !bCheckPtr )
+ pTable = rCallEvent.PTR.pINetAttr->GetMacroTable();
+ break;
+
+ case EVENT_OBJECT_URLITEM:
+ case EVENT_OBJECT_IMAGE:
+ {
+ const auto pSpz = static_cast<const sw::SpzFrameFormat*>(rCallEvent.PTR.pFormat);
+ if( bCheckPtr )
+ {
+ if (GetSpzFrameFormats()->IsAlive(pSpz))
+ bCheckPtr = false; // misuse as a flag
+ else
+ // this shouldn't be possible now that SwCallMouseEvent
+ // listens for dying format?
+ assert(false);
+ }
+ if( !bCheckPtr )
+ pTable = &pSpz->GetMacro().GetMacroTable();
+ }
+ break;
+
+ case EVENT_OBJECT_IMAGEMAP:
+ {
+ const IMapObject* pIMapObj = rCallEvent.PTR.IMAP.pIMapObj;
+ if( bCheckPtr )
+ {
+ const auto pSpz = static_cast<const sw::SpzFrameFormat*>(rCallEvent.PTR.IMAP.pFormat);
+ if (GetSpzFrameFormats()->IsAlive(pSpz))
+ {
+ const ImageMap* pIMap = pSpz->GetURL().GetMap();
+ if (pIMap)
+ {
+ for( size_t nPos = pIMap->GetIMapObjectCount(); nPos; )
+ if( pIMapObj == pIMap->GetIMapObject( --nPos ))
+ {
+ bCheckPtr = false; // misuse as a flag
+ break;
+ }
+ }
+ }
+ }
+ if( !bCheckPtr )
+ pTable = &pIMapObj->GetMacroTable();
+ }
+ break;
+ default:
+ break;
+ }
+
+ if( pTable )
+ {
+ nRet = 0x1;
+ if( pTable->IsKeyValid( nEvent ) )
+ {
+ const SvxMacro& rMacro = *pTable->Get( nEvent );
+ if( STARBASIC == rMacro.GetScriptType() )
+ {
+ nRet += ERRCODE_NONE == mpDocShell->CallBasic( rMacro.GetMacName(),
+ rMacro.GetLibName(), nullptr ) ? 1 : 0;
+ }
+ else if( EXTENDED_STYPE == rMacro.GetScriptType() )
+ {
+ Sequence<Any> aUnoArgs;
+
+ Any aRet;
+ Sequence< sal_Int16 > aOutArgsIndex;
+ Sequence< Any > aOutArgs;
+
+ SAL_INFO("sw", "SwDoc::CallEvent URL is " << rMacro.GetMacName() );
+
+ nRet += ERRCODE_NONE == mpDocShell->CallXScript(
+ rMacro.GetMacName(), aUnoArgs, aRet, aOutArgsIndex, aOutArgs) ? 1 : 0;
+ }
+ // JavaScript calls are ignored
+ }
+ }
+ return nRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/docbm.cxx b/sw/source/core/doc/docbm.cxx
new file mode 100644
index 0000000000..00681135b4
--- /dev/null
+++ b/sw/source/core/doc/docbm.cxx
@@ -0,0 +1,2124 @@
+/* -*- 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 <utility>
+
+#include <MarkManager.hxx>
+#include <bookmark.hxx>
+#include <crossrefbookmark.hxx>
+#include <crsrsh.hxx>
+#include <annotationmark.hxx>
+#include <doc.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <IDocumentState.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <docary.hxx>
+#include <xmloff/odffields.hxx>
+#include <mvsave.hxx>
+#include <ndtxt.hxx>
+#include <node.hxx>
+#include <pam.hxx>
+#include <redline.hxx>
+#include <rolbck.hxx>
+#include <rtl/ustring.hxx>
+#include <sal/types.h>
+#include <sal/log.hxx>
+#include <UndoBookmark.hxx>
+#include <tools/datetimeutils.hxx>
+#include <txtfrm.hxx>
+#include <view.hxx>
+
+#include <libxml/xmlstring.h>
+#include <libxml/xmlwriter.h>
+#include <comphelper/lok.hxx>
+#include <strings.hrc>
+
+constexpr OUString S_ANNOTATION_BOOKMARK = u"____"_ustr;
+
+using namespace ::sw::mark;
+
+std::vector<::sw::mark::MarkBase*>::const_iterator const&
+IDocumentMarkAccess::iterator::get() const
+{
+ return *m_pIter;
+}
+
+IDocumentMarkAccess::iterator::iterator(std::vector<::sw::mark::MarkBase*>::const_iterator const& rIter)
+ : m_pIter(rIter)
+{
+}
+
+IDocumentMarkAccess::iterator::iterator(iterator const& rOther)
+ : m_pIter(rOther.m_pIter)
+{
+}
+
+auto IDocumentMarkAccess::iterator::operator=(iterator const& rOther) -> iterator&
+{
+ m_pIter = rOther.m_pIter;
+ return *this;
+}
+
+IDocumentMarkAccess::iterator::iterator(iterator && rOther) noexcept
+ : m_pIter(std::move(rOther.m_pIter))
+{
+}
+
+auto IDocumentMarkAccess::iterator::operator=(iterator && rOther) noexcept -> iterator&
+{
+ m_pIter = std::move(rOther.m_pIter);
+ return *this;
+}
+
+// ARGH why does it *need* to return const& ?
+::sw::mark::IMark* /*const&*/
+IDocumentMarkAccess::iterator::operator*() const
+{
+ return static_cast<sw::mark::IMark*>(**m_pIter);
+}
+
+auto IDocumentMarkAccess::iterator::operator++() -> iterator&
+{
+ ++(*m_pIter);
+ return *this;
+}
+auto IDocumentMarkAccess::iterator::operator++(int) -> iterator
+{
+ iterator tmp(*this);
+ ++(*m_pIter);
+ return tmp;
+}
+
+bool IDocumentMarkAccess::iterator::operator==(iterator const& rOther) const
+{
+ return *m_pIter == *rOther.m_pIter;
+}
+
+bool IDocumentMarkAccess::iterator::operator!=(iterator const& rOther) const
+{
+ return *m_pIter != *rOther.m_pIter;
+}
+
+IDocumentMarkAccess::iterator::iterator()
+ : m_pIter(std::in_place)
+{
+}
+
+auto IDocumentMarkAccess::iterator::operator--() -> iterator&
+{
+ --(*m_pIter);
+ return *this;
+}
+
+auto IDocumentMarkAccess::iterator::operator--(int) -> iterator
+{
+ iterator tmp(*this);
+ --(*m_pIter);
+ return tmp;
+}
+
+auto IDocumentMarkAccess::iterator::operator+=(difference_type const n) -> iterator&
+{
+ (*m_pIter) += n;
+ return *this;
+}
+
+auto IDocumentMarkAccess::iterator::operator+(difference_type const n) const -> iterator
+{
+ return iterator(*m_pIter + n);
+}
+
+auto IDocumentMarkAccess::iterator::operator-=(difference_type const n) -> iterator&
+{
+ (*m_pIter) -= n;
+ return *this;
+}
+
+auto IDocumentMarkAccess::iterator::operator-(difference_type const n) const -> iterator
+{
+ return iterator(*m_pIter - n);
+}
+
+auto IDocumentMarkAccess::iterator::operator-(iterator const& rOther) const -> difference_type
+{
+ return *m_pIter - *rOther.m_pIter;
+}
+
+auto IDocumentMarkAccess::iterator::operator[](difference_type const n) const -> value_type
+{
+ return static_cast<sw::mark::IMark*>((*m_pIter)[n]);
+}
+
+bool IDocumentMarkAccess::iterator::operator<(iterator const& rOther) const
+{
+ return *m_pIter < *rOther.m_pIter;
+}
+bool IDocumentMarkAccess::iterator::operator>(iterator const& rOther) const
+{
+ return *m_pIter > *rOther.m_pIter;
+}
+bool IDocumentMarkAccess::iterator::operator<=(iterator const& rOther) const
+{
+ return *m_pIter <= *rOther.m_pIter;
+}
+bool IDocumentMarkAccess::iterator::operator>=(iterator const& rOther) const
+{
+ return *m_pIter >= *rOther.m_pIter;
+}
+
+
+namespace
+{
+ bool lcl_GreaterThan( const SwPosition& rPos, const SwNode& rNdIdx, std::optional<sal_Int32> oContentIdx )
+ {
+ return oContentIdx.has_value()
+ ? ( rPos.GetNode() > rNdIdx
+ || ( rPos.GetNode() == rNdIdx
+ && rPos.GetContentIndex() >= *oContentIdx ) )
+ : rPos.GetNode() >= rNdIdx;
+ }
+
+ bool lcl_Lower( const SwPosition& rPos, const SwNode& rNdIdx, std::optional<sal_Int32> oContentIdx )
+ {
+ if (rPos.GetNode() < rNdIdx)
+ return true;
+
+ if (rPos.GetNode() != rNdIdx || !oContentIdx)
+ return false;
+
+ if (rPos.GetContentIndex() < *oContentIdx)
+ return true;
+
+ // paragraph end selected?
+ return rNdIdx.IsTextNode() && *oContentIdx == rNdIdx.GetTextNode()->Len();
+ }
+
+ bool lcl_MarkOrderingByStart(const ::sw::mark::MarkBase *const pFirst,
+ const ::sw::mark::MarkBase *const pSecond)
+ {
+ SwPosition const& rFirstStart(pFirst->GetMarkStart());
+ SwPosition const& rSecondStart(pSecond->GetMarkStart());
+ if (rFirstStart.GetNode() != rSecondStart.GetNode())
+ {
+ return rFirstStart.GetNode() < rSecondStart.GetNode();
+ }
+ const sal_Int32 nFirstContent = rFirstStart.GetContentIndex();
+ const sal_Int32 nSecondContent = rSecondStart.GetContentIndex();
+ if (nFirstContent != 0 || nSecondContent != 0)
+ {
+ return nFirstContent < nSecondContent;
+ }
+ SwContentNode const*const pFirstNode(rFirstStart.nContent.GetContentNode());
+ SwContentNode const*const pSecondNode(rSecondStart.nContent.GetContentNode());
+ if ((pFirstNode != nullptr) != (pSecondNode != nullptr))
+ { // consistency with SwPosition::operator<
+ return pSecondNode != nullptr;
+ }
+ auto *const pCRFirst (dynamic_cast<::sw::mark::CrossRefBookmark const*>(pFirst));
+ auto *const pCRSecond(dynamic_cast<::sw::mark::CrossRefBookmark const*>(pSecond));
+ if ((pCRFirst == nullptr) == (pCRSecond == nullptr))
+ {
+ return false; // equal
+ }
+ return pCRFirst != nullptr; // cross-ref sorts *before*
+ }
+
+ bool lcl_MarkOrderingByEnd(const ::sw::mark::MarkBase *const pFirst,
+ const ::sw::mark::MarkBase *const pSecond)
+ {
+ return pFirst->GetMarkEnd() < pSecond->GetMarkEnd();
+ }
+
+ void lcl_InsertMarkSorted(MarkManager::container_t& io_vMarks,
+ ::sw::mark::MarkBase *const pMark)
+ {
+ io_vMarks.insert(
+ lower_bound(
+ io_vMarks.begin(),
+ io_vMarks.end(),
+ pMark,
+ &lcl_MarkOrderingByStart),
+ pMark);
+ }
+
+ void lcl_PositionFromContentNode(
+ std::optional<SwPosition>& rFoundPos,
+ const SwContentNode * const pContentNode,
+ const bool bAtEnd)
+ {
+ rFoundPos.emplace(*pContentNode, bAtEnd ? pContentNode->Len() : 0);
+ }
+
+ // return a position at the begin of rEnd, if it is a ContentNode
+ // else set it to the begin of the Node after rEnd, if there is one
+ // else set it to the end of the node before rStt
+ // else set it to the ContentNode of the Pos outside the Range
+ void lcl_FindExpelPosition(
+ std::optional<SwPosition>& rFoundPos,
+ const SwNode& rStt,
+ const SwNode& rEnd,
+ const SwPosition& rOtherPosition)
+ {
+ const SwContentNode * pNode = rEnd.GetContentNode();
+ bool bPosAtEndOfNode = false;
+ if ( pNode == nullptr)
+ {
+ SwNodeIndex aEnd(rEnd);
+ pNode = rEnd.GetNodes().GoNext( &aEnd );
+ bPosAtEndOfNode = false;
+ }
+ if ( pNode == nullptr )
+ {
+ SwNodeIndex aStt(rStt);
+ pNode = SwNodes::GoPrevious(&aStt);
+ bPosAtEndOfNode = true;
+ }
+ if ( pNode != nullptr )
+ {
+ lcl_PositionFromContentNode( rFoundPos, pNode, bPosAtEndOfNode );
+ return;
+ }
+
+ rFoundPos = rOtherPosition;
+ }
+
+ struct CompareIMarkStartsBefore
+ {
+ bool operator()(SwPosition const& rPos,
+ const sw::mark::IMark* pMark)
+ {
+ return rPos < pMark->GetMarkStart();
+ }
+ bool operator()(const sw::mark::IMark* pMark,
+ SwPosition const& rPos)
+ {
+ return pMark->GetMarkStart() < rPos;
+ }
+ };
+
+ // Apple llvm-g++ 4.2.1 with _GLIBCXX_DEBUG won't eat boost::bind for this
+ // Neither will MSVC 2008 with _DEBUG
+ struct CompareIMarkStartsAfter
+ {
+ bool operator()(SwPosition const& rPos,
+ const sw::mark::IMark* pMark)
+ {
+ return pMark->GetMarkStart() > rPos;
+ }
+ };
+
+
+ IMark* lcl_getMarkAfter(const MarkManager::container_t& rMarks, const SwPosition& rPos,
+ bool bLoop)
+ {
+ auto const pMarkAfter = upper_bound(
+ rMarks.begin(),
+ rMarks.end(),
+ rPos,
+ CompareIMarkStartsAfter());
+ if(pMarkAfter == rMarks.end())
+ {
+ if (bLoop && rMarks.begin() != rMarks.end())
+ return *rMarks.begin();
+
+ return nullptr;
+ }
+ return *pMarkAfter;
+ };
+
+ IMark* lcl_getMarkBefore(const MarkManager::container_t& rMarks, const SwPosition& rPos,
+ bool bLoop)
+ {
+ // candidates from which to choose the mark before
+ MarkManager::container_t vCandidates;
+ // no need to consider marks starting after rPos
+ auto const pCandidatesEnd = upper_bound(
+ rMarks.begin(),
+ rMarks.end(),
+ rPos,
+ CompareIMarkStartsAfter());
+ vCandidates.reserve(pCandidatesEnd - rMarks.begin());
+ // only marks ending before are candidates
+ remove_copy_if(
+ rMarks.begin(),
+ pCandidatesEnd,
+ back_inserter(vCandidates),
+ [&rPos] (const ::sw::mark::MarkBase *const pMark) { return !(pMark->GetMarkEnd() < rPos); } );
+ // no candidate left => we are in front of the first mark or there are none
+ if(vCandidates.empty())
+ {
+ if (bLoop && rMarks.begin() != rMarks.end())
+ return *(rMarks.end() - 1);
+
+ return nullptr;
+ }
+ // return the highest (last) candidate using mark end ordering
+ return *max_element(vCandidates.begin(), vCandidates.end(), &lcl_MarkOrderingByEnd);
+ }
+
+ bool lcl_FixCorrectedMark(
+ const bool bChangedPos,
+ const bool bChangedOPos,
+ MarkBase* io_pMark )
+ {
+ if ( IDocumentMarkAccess::GetType(*io_pMark) == IDocumentMarkAccess::MarkType::ANNOTATIONMARK )
+ {
+ // annotation marks are allowed to span a table cell range.
+ // but trigger sorting to be save
+ return true;
+ }
+
+ if ( ( bChangedPos || bChangedOPos )
+ && io_pMark->IsExpanded()
+ && io_pMark->GetOtherMarkPos().GetNode().FindTableBoxStartNode() !=
+ io_pMark->GetMarkPos().GetNode().FindTableBoxStartNode() )
+ {
+ if ( !bChangedOPos )
+ {
+ io_pMark->SetMarkPos( io_pMark->GetOtherMarkPos() );
+ }
+ io_pMark->ClearOtherMarkPos();
+ DdeBookmark * const pDdeBkmk = dynamic_cast< DdeBookmark*>(io_pMark);
+ if ( pDdeBkmk != nullptr
+ && pDdeBkmk->IsServer() )
+ {
+ pDdeBkmk->SetRefObject(nullptr);
+ }
+ return true;
+ }
+ return false;
+ }
+
+ bool lcl_MarkEqualByStart(const ::sw::mark::MarkBase *const pFirst,
+ const ::sw::mark::MarkBase *const pSecond)
+ {
+ return !lcl_MarkOrderingByStart(pFirst, pSecond) &&
+ !lcl_MarkOrderingByStart(pSecond, pFirst);
+ }
+
+ MarkManager::container_t::const_iterator lcl_FindMark(
+ MarkManager::container_t& rMarks,
+ const ::sw::mark::MarkBase *const pMarkToFind)
+ {
+ auto ppCurrentMark = lower_bound(
+ rMarks.begin(), rMarks.end(),
+ pMarkToFind, &lcl_MarkOrderingByStart);
+ // since there are usually not too many marks on the same start
+ // position, we are not doing a bisect search for the upper bound
+ // but instead start to iterate from pMarkLow directly
+ while (ppCurrentMark != rMarks.end() && lcl_MarkEqualByStart(*ppCurrentMark, pMarkToFind))
+ {
+ if(*ppCurrentMark == pMarkToFind)
+ {
+ return MarkManager::container_t::const_iterator(std::move(ppCurrentMark));
+ }
+ ++ppCurrentMark;
+ }
+ // reached a mark starting on a later start pos or the end of the
+ // vector => not found
+ return rMarks.end();
+ };
+
+ MarkManager::container_t::const_iterator lcl_FindMarkAtPos(
+ MarkManager::container_t& rMarks,
+ const SwPosition& rPos,
+ const IDocumentMarkAccess::MarkType eType)
+ {
+ for (auto ppCurrentMark = lower_bound(
+ rMarks.begin(), rMarks.end(),
+ rPos,
+ CompareIMarkStartsBefore());
+ ppCurrentMark != rMarks.end();
+ ++ppCurrentMark)
+ {
+ // Once we reach a mark starting after the target pos
+ // we do not need to continue
+ if((*ppCurrentMark)->GetMarkStart() > rPos)
+ break;
+ if(IDocumentMarkAccess::GetType(**ppCurrentMark) == eType)
+ {
+ return MarkManager::container_t::const_iterator(std::move(ppCurrentMark));
+ }
+ }
+ // reached a mark starting on a later start pos or the end of the
+ // vector => not found
+ return rMarks.end();
+ };
+
+ MarkManager::container_t::const_iterator lcl_FindMarkByName(
+ const OUString& rName,
+ const MarkManager::container_t::const_iterator& ppMarksBegin,
+ const MarkManager::container_t::const_iterator& ppMarksEnd)
+ {
+ return find_if(
+ ppMarksBegin,
+ ppMarksEnd,
+ [&rName] (::sw::mark::MarkBase const*const pMark) { return pMark->GetName() == rName; } );
+ }
+
+ void lcl_DebugMarks(MarkManager::container_t const& rMarks)
+ {
+#if OSL_DEBUG_LEVEL > 0
+ SAL_INFO("sw.core", rMarks.size() << " Marks");
+ for (auto ppMark = rMarks.begin();
+ ppMark != rMarks.end();
+ ++ppMark)
+ {
+ IMark* pMark = *ppMark;
+ const SwPosition* const pStPos = &pMark->GetMarkStart();
+ const SwPosition* const pEndPos = &pMark->GetMarkEnd();
+ SAL_INFO("sw.core",
+ sal_Int32(pStPos->GetNodeIndex()) << "," <<
+ pStPos->GetContentIndex() << " " <<
+ sal_Int32(pEndPos->GetNodeIndex()) << "," <<
+ pEndPos->GetContentIndex() << " " <<
+ typeid(*pMark).name() << " " <<
+ pMark->GetName());
+ }
+#else
+ (void) rMarks;
+#endif
+ assert(std::is_sorted(rMarks.begin(), rMarks.end(), lcl_MarkOrderingByStart));
+ };
+}
+
+IDocumentMarkAccess::MarkType IDocumentMarkAccess::GetType(const IMark& rBkmk)
+{
+ const std::type_info* const pMarkTypeInfo = &typeid(rBkmk);
+ // not using dynamic_cast<> here for performance
+ if(*pMarkTypeInfo == typeid(UnoMark))
+ return MarkType::UNO_BOOKMARK;
+ else if(*pMarkTypeInfo == typeid(DdeBookmark))
+ return MarkType::DDE_BOOKMARK;
+ else if(*pMarkTypeInfo == typeid(Bookmark))
+ return MarkType::BOOKMARK;
+ else if(*pMarkTypeInfo == typeid(CrossRefHeadingBookmark))
+ return MarkType::CROSSREF_HEADING_BOOKMARK;
+ else if(*pMarkTypeInfo == typeid(CrossRefNumItemBookmark))
+ return MarkType::CROSSREF_NUMITEM_BOOKMARK;
+ else if(*pMarkTypeInfo == typeid(AnnotationMark))
+ return MarkType::ANNOTATIONMARK;
+ else if(*pMarkTypeInfo == typeid(TextFieldmark))
+ return MarkType::TEXT_FIELDMARK;
+ else if(*pMarkTypeInfo == typeid(CheckboxFieldmark))
+ return MarkType::CHECKBOX_FIELDMARK;
+ else if(*pMarkTypeInfo == typeid(DropDownFieldmark))
+ return MarkType::DROPDOWN_FIELDMARK;
+ else if(*pMarkTypeInfo == typeid(DateFieldmark))
+ return MarkType::DATE_FIELDMARK;
+ else if(*pMarkTypeInfo == typeid(NavigatorReminder))
+ return MarkType::NAVIGATOR_REMINDER;
+ else
+ {
+ assert(false && "IDocumentMarkAccess::GetType(..)"
+ " - unknown MarkType. This needs to be fixed!");
+ return MarkType::UNO_BOOKMARK;
+ }
+}
+
+OUString IDocumentMarkAccess::GetCrossRefHeadingBookmarkNamePrefix()
+{
+ return "__RefHeading__";
+}
+
+bool IDocumentMarkAccess::IsLegalPaMForCrossRefHeadingBookmark( const SwPaM& rPaM )
+{
+ return rPaM.Start()->GetNode().IsTextNode() &&
+ rPaM.Start()->GetContentIndex() == 0 &&
+ ( !rPaM.HasMark() ||
+ ( rPaM.GetMark()->GetNode() == rPaM.GetPoint()->GetNode() &&
+ rPaM.End()->GetContentIndex() == rPaM.End()->GetNode().GetTextNode()->Len() ) );
+}
+
+void IDocumentMarkAccess::DeleteFieldmarkCommand(::sw::mark::IFieldmark const& rMark)
+{
+ if (GetType(rMark) != MarkType::TEXT_FIELDMARK)
+ {
+ return; // TODO FORMDATE has no command?
+ }
+ SwPaM pam(sw::mark::FindFieldSep(rMark), rMark.GetMarkStart());
+ pam.GetPoint()->AdjustContent(+1); // skip CH_TXT_ATR_FIELDSTART
+ pam.GetDoc().getIDocumentContentOperations().DeleteAndJoin(pam);
+}
+
+namespace sw::mark
+{
+ MarkManager::MarkManager(SwDoc& rDoc)
+ : m_rDoc(rDoc)
+ , m_pLastActiveFieldmark(nullptr)
+ { }
+
+ ::sw::mark::IMark* MarkManager::makeMark(const SwPaM& rPaM,
+ const OUString& rName,
+ const IDocumentMarkAccess::MarkType eType,
+ sw::mark::InsertMode const eMode,
+ SwPosition const*const pSepPos)
+ {
+#if OSL_DEBUG_LEVEL > 0
+ {
+ const SwPosition* const pPos1 = rPaM.GetPoint();
+ const SwPosition* pPos2 = pPos1;
+ if(rPaM.HasMark())
+ pPos2 = rPaM.GetMark();
+ SAL_INFO("sw.core",
+ rName << " " <<
+ sal_Int32(pPos1->GetNodeIndex() )<< "," <<
+ pPos1->GetContentIndex() << " " <<
+ sal_Int32(pPos2->GetNodeIndex()) << "," <<
+ pPos2->GetContentIndex());
+ }
+#endif
+ if ( (!rPaM.GetPoint()->GetNode().IsTextNode()
+ && (eType != MarkType::UNO_BOOKMARK
+ // SwXTextRange can be on table node or plain start node (FLY_AT_FLY)
+ || !rPaM.GetPoint()->GetNode().IsStartNode()))
+ || (!rPaM.GetMark()->GetNode().IsTextNode()
+ && (eType != MarkType::UNO_BOOKMARK
+ || !rPaM.GetMark()->GetNode().IsStartNode())))
+ {
+ SAL_WARN("sw.core", "MarkManager::makeMark(..)"
+ " - refusing to create mark on non-textnode");
+ return nullptr;
+ }
+ // There should only be one CrossRefBookmark per Textnode per Type
+ if ((eType == MarkType::CROSSREF_NUMITEM_BOOKMARK || eType == MarkType::CROSSREF_HEADING_BOOKMARK)
+ && (lcl_FindMarkAtPos(m_vBookmarks, *rPaM.Start(), eType) != m_vBookmarks.end()))
+ { // this can happen via UNO API
+ SAL_WARN("sw.core", "MarkManager::makeMark(..)"
+ " - refusing to create duplicate CrossRefBookmark");
+ return nullptr;
+ }
+
+ if ((eType == MarkType::CHECKBOX_FIELDMARK || eType == MarkType::DROPDOWN_FIELDMARK)
+ && (eMode == InsertMode::New
+ ? *rPaM.GetPoint() != *rPaM.GetMark()
+ // CopyText: pam covers CH_TXT_ATR_FORMELEMENT
+ : (rPaM.GetPoint()->GetNode() != rPaM.GetMark()->GetNode()
+ || rPaM.Start()->GetContentIndex() + 1 != rPaM.End()->GetContentIndex())))
+ {
+ SAL_WARN("sw.core", "MarkManager::makeMark(..)"
+ " - invalid range on point fieldmark");
+ return nullptr;
+ }
+
+ if ((eType == MarkType::TEXT_FIELDMARK || eType == MarkType::DATE_FIELDMARK)
+ && (rPaM.GetPoint()->GetNode().StartOfSectionNode() != rPaM.GetMark()->GetNode().StartOfSectionNode()
+ || (pSepPos && rPaM.GetPoint()->GetNode().StartOfSectionNode() != pSepPos->GetNode().StartOfSectionNode())))
+ {
+ SAL_WARN("sw.core", "MarkManager::makeMark(..)"
+ " - invalid range on fieldmark, different nodes array sections");
+ return nullptr;
+ }
+
+ if ((eType == MarkType::TEXT_FIELDMARK || eType == MarkType::DATE_FIELDMARK)
+ // can't check for Copy - it asserts - but it's also obviously unnecessary
+ && eMode == InsertMode::New
+ && sw::mark::IsFieldmarkOverlap(rPaM))
+ {
+ SAL_WARN("sw.core", "MarkManager::makeMark(..)"
+ " - invalid range on fieldmark, overlaps existing fieldmark or meta-field");
+ return nullptr;
+ }
+
+ // create mark
+ std::unique_ptr<::sw::mark::MarkBase> pMark;
+ switch(eType)
+ {
+ case IDocumentMarkAccess::MarkType::TEXT_FIELDMARK:
+ pMark = std::make_unique<TextFieldmark>(rPaM, rName);
+ break;
+ case IDocumentMarkAccess::MarkType::CHECKBOX_FIELDMARK:
+ pMark = std::make_unique<CheckboxFieldmark>(rPaM, rName);
+ break;
+ case IDocumentMarkAccess::MarkType::DROPDOWN_FIELDMARK:
+ pMark = std::make_unique<DropDownFieldmark>(rPaM, rName);
+ break;
+ case IDocumentMarkAccess::MarkType::DATE_FIELDMARK:
+ pMark = std::make_unique<DateFieldmark>(rPaM);
+ break;
+ case IDocumentMarkAccess::MarkType::NAVIGATOR_REMINDER:
+ pMark = std::make_unique<NavigatorReminder>(rPaM);
+ break;
+ case IDocumentMarkAccess::MarkType::BOOKMARK:
+ pMark = std::make_unique<Bookmark>(rPaM, vcl::KeyCode(), rName);
+ break;
+ case IDocumentMarkAccess::MarkType::DDE_BOOKMARK:
+ pMark = std::make_unique<DdeBookmark>(rPaM);
+ break;
+ case IDocumentMarkAccess::MarkType::CROSSREF_HEADING_BOOKMARK:
+ pMark = std::make_unique<CrossRefHeadingBookmark>(rPaM, vcl::KeyCode(), rName);
+ break;
+ case IDocumentMarkAccess::MarkType::CROSSREF_NUMITEM_BOOKMARK:
+ pMark = std::make_unique<CrossRefNumItemBookmark>(rPaM, vcl::KeyCode(), rName);
+ break;
+ case IDocumentMarkAccess::MarkType::UNO_BOOKMARK:
+ pMark = std::make_unique<UnoMark>(rPaM);
+ break;
+ case IDocumentMarkAccess::MarkType::ANNOTATIONMARK:
+ pMark = std::make_unique<AnnotationMark>( rPaM, rName );
+ break;
+ }
+ assert(pMark && "MarkManager::makeMark(..) - Mark was not created.");
+
+ if(pMark->GetMarkPos() != pMark->GetMarkStart())
+ pMark->Swap();
+
+ // for performance reasons, we trust UnoMarks to have a (generated) unique name
+ if ( eType != IDocumentMarkAccess::MarkType::UNO_BOOKMARK )
+ pMark->SetName( getUniqueMarkName( pMark->GetName() ) );
+
+ // insert any dummy chars before inserting into sorted vectors
+ pMark->InitDoc(m_rDoc, eMode, pSepPos);
+
+ // register mark
+ lcl_InsertMarkSorted(m_vAllMarks, pMark.get());
+ switch(eType)
+ {
+ case IDocumentMarkAccess::MarkType::BOOKMARK:
+ case IDocumentMarkAccess::MarkType::CROSSREF_NUMITEM_BOOKMARK:
+ case IDocumentMarkAccess::MarkType::CROSSREF_HEADING_BOOKMARK:
+ lcl_InsertMarkSorted(m_vBookmarks, pMark.get());
+ break;
+ case IDocumentMarkAccess::MarkType::TEXT_FIELDMARK:
+ case IDocumentMarkAccess::MarkType::CHECKBOX_FIELDMARK:
+ case IDocumentMarkAccess::MarkType::DROPDOWN_FIELDMARK:
+ case IDocumentMarkAccess::MarkType::DATE_FIELDMARK:
+ lcl_InsertMarkSorted(m_vFieldmarks, pMark.get());
+ break;
+ case IDocumentMarkAccess::MarkType::ANNOTATIONMARK:
+ lcl_InsertMarkSorted( m_vAnnotationMarks, pMark.get() );
+ break;
+ case IDocumentMarkAccess::MarkType::NAVIGATOR_REMINDER:
+ case IDocumentMarkAccess::MarkType::DDE_BOOKMARK:
+ case IDocumentMarkAccess::MarkType::UNO_BOOKMARK:
+ // no special array for these
+ break;
+ }
+ if (eMode == InsertMode::New
+ && (eType == IDocumentMarkAccess::MarkType::TEXT_FIELDMARK
+ || eType == IDocumentMarkAccess::MarkType::DATE_FIELDMARK))
+ {
+ // due to sw::InsertText notifications everything is visible now - tell
+ // layout to hide as appropriate
+ // note: we don't know how many layouts there are and which
+ // parts they hide, so just notify the entire fieldmark, it
+ // should give the right result if not in the most efficient way
+ // note2: can't be done in InitDoc() because it requires the mark
+ // to be inserted in the vectors.
+ SwPaM const tmp(pMark->GetMarkPos(), pMark->GetOtherMarkPos());
+ sw::UpdateFramesForAddDeleteRedline(m_rDoc, tmp);
+ }
+
+ SAL_INFO("sw.core", "--- makeType ---");
+ SAL_INFO("sw.core", "Marks");
+ lcl_DebugMarks(m_vAllMarks);
+ SAL_INFO("sw.core", "Bookmarks");
+ lcl_DebugMarks(m_vBookmarks);
+ SAL_INFO("sw.core", "Fieldmarks");
+ lcl_DebugMarks(m_vFieldmarks);
+
+ return pMark.release();
+ }
+
+ ::sw::mark::IFieldmark* MarkManager::makeFieldBookmark(
+ const SwPaM& rPaM,
+ const OUString& rName,
+ const OUString& rType,
+ SwPosition const*const pSepPos)
+ {
+
+ // Disable undo, because we handle it using SwUndoInsTextFieldmark
+ bool bUndoIsEnabled = m_rDoc.GetIDocumentUndoRedo().DoesUndo();
+ m_rDoc.GetIDocumentUndoRedo().DoUndo(false);
+
+ sw::mark::IMark* pMark = nullptr;
+ if(rType == ODF_FORMDATE)
+ {
+ pMark = makeMark(rPaM, rName,
+ IDocumentMarkAccess::MarkType::DATE_FIELDMARK,
+ sw::mark::InsertMode::New,
+ pSepPos);
+ }
+ else
+ {
+ pMark = makeMark(rPaM, rName,
+ IDocumentMarkAccess::MarkType::TEXT_FIELDMARK,
+ sw::mark::InsertMode::New,
+ pSepPos);
+ }
+ sw::mark::IFieldmark* pFieldMark = dynamic_cast<sw::mark::IFieldmark*>( pMark );
+ if (pFieldMark)
+ pFieldMark->SetFieldname( rType );
+
+ if (bUndoIsEnabled)
+ {
+ m_rDoc.GetIDocumentUndoRedo().DoUndo(bUndoIsEnabled);
+ if (pFieldMark)
+ m_rDoc.GetIDocumentUndoRedo().AppendUndo(std::make_unique<SwUndoInsTextFieldmark>(*pFieldMark));
+ }
+
+ return pFieldMark;
+ }
+
+ ::sw::mark::IFieldmark* MarkManager::makeNoTextFieldBookmark(
+ const SwPaM& rPaM,
+ const OUString& rName,
+ const OUString& rType)
+ {
+ // Disable undo, because we handle it using SwUndoInsNoTextFieldmark
+ bool bUndoIsEnabled = m_rDoc.GetIDocumentUndoRedo().DoesUndo();
+ m_rDoc.GetIDocumentUndoRedo().DoUndo(false);
+
+ bool bEnableSetModified = m_rDoc.getIDocumentState().IsEnableSetModified();
+ m_rDoc.getIDocumentState().SetEnableSetModified(false);
+
+ sw::mark::IMark* pMark = nullptr;
+ if(rType == ODF_FORMCHECKBOX)
+ {
+ pMark = makeMark( rPaM, rName,
+ IDocumentMarkAccess::MarkType::CHECKBOX_FIELDMARK,
+ sw::mark::InsertMode::New);
+ }
+ else if(rType == ODF_FORMDROPDOWN)
+ {
+ pMark = makeMark( rPaM, rName,
+ IDocumentMarkAccess::MarkType::DROPDOWN_FIELDMARK,
+ sw::mark::InsertMode::New);
+ }
+ else if(rType == ODF_FORMDATE)
+ {
+ pMark = makeMark( rPaM, rName,
+ IDocumentMarkAccess::MarkType::DATE_FIELDMARK,
+ sw::mark::InsertMode::New);
+ }
+
+ sw::mark::IFieldmark* pFieldMark = dynamic_cast<sw::mark::IFieldmark*>( pMark );
+ if (pFieldMark)
+ pFieldMark->SetFieldname( rType );
+
+ if (bUndoIsEnabled)
+ {
+ m_rDoc.GetIDocumentUndoRedo().DoUndo(bUndoIsEnabled);
+ if (pFieldMark)
+ m_rDoc.GetIDocumentUndoRedo().AppendUndo(std::make_unique<SwUndoInsNoTextFieldmark>(*pFieldMark));
+ }
+
+ m_rDoc.getIDocumentState().SetEnableSetModified(bEnableSetModified);
+ m_rDoc.getIDocumentState().SetModified();
+
+ return pFieldMark;
+ }
+
+ ::sw::mark::IMark* MarkManager::getMarkForTextNode(
+ const SwTextNode& rTextNode,
+ const IDocumentMarkAccess::MarkType eType )
+ {
+ SwPosition aPos(rTextNode);
+ auto const ppExistingMark = lcl_FindMarkAtPos(m_vBookmarks, aPos, eType);
+ if(ppExistingMark != m_vBookmarks.end())
+ return *ppExistingMark;
+ const SwPaM aPaM(aPos);
+ return makeMark(aPaM, OUString(), eType, sw::mark::InsertMode::New);
+ }
+
+ sw::mark::IMark* MarkManager::makeAnnotationMark(
+ const SwPaM& rPaM,
+ const OUString& rName )
+ {
+ return makeMark(rPaM, rName, IDocumentMarkAccess::MarkType::ANNOTATIONMARK,
+ sw::mark::InsertMode::New);
+ }
+
+ void MarkManager::repositionMark(
+ ::sw::mark::IMark* const io_pMark,
+ const SwPaM& rPaM)
+ {
+ assert(&io_pMark->GetMarkPos().GetDoc() == &m_rDoc &&
+ "<MarkManager::repositionMark(..)>"
+ " - Mark is not in my doc.");
+ MarkBase* const pMarkBase = dynamic_cast< MarkBase* >(io_pMark);
+ if (!pMarkBase)
+ return;
+
+ pMarkBase->InvalidateFrames();
+
+ pMarkBase->SetMarkPos(*(rPaM.GetPoint()));
+ if(rPaM.HasMark())
+ pMarkBase->SetOtherMarkPos(*(rPaM.GetMark()));
+ else
+ pMarkBase->ClearOtherMarkPos();
+
+ if(pMarkBase->GetMarkPos() != pMarkBase->GetMarkStart())
+ pMarkBase->Swap();
+
+ pMarkBase->InvalidateFrames();
+
+ sortMarks();
+ }
+
+ bool MarkManager::renameMark(
+ ::sw::mark::IMark* io_pMark,
+ const OUString& rNewName )
+ {
+ assert(&io_pMark->GetMarkPos().GetDoc() == &m_rDoc &&
+ "<MarkManager::renameMark(..)>"
+ " - Mark is not in my doc.");
+ if ( io_pMark->GetName() == rNewName )
+ return true;
+ if (lcl_FindMarkByName(rNewName, m_vAllMarks.begin(), m_vAllMarks.end()) != m_vAllMarks.end())
+ return false;
+ if (::sw::mark::MarkBase* pMarkBase = dynamic_cast< ::sw::mark::MarkBase* >(io_pMark))
+ {
+ const OUString sOldName(pMarkBase->GetName());
+ pMarkBase->SetName(rNewName);
+
+ if (dynamic_cast< ::sw::mark::Bookmark* >(io_pMark))
+ {
+ if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ m_rDoc.GetIDocumentUndoRedo().AppendUndo(
+ std::make_unique<SwUndoRenameBookmark>(sOldName, rNewName, m_rDoc));
+ }
+ m_rDoc.getIDocumentState().SetModified();
+ }
+ }
+ return true;
+ }
+
+ void MarkManager::correctMarksAbsolute(
+ const SwNode& rOldNode,
+ const SwPosition& rNewPos,
+ const sal_Int32 nOffset)
+ {
+ const SwNode* const pOldNode = &rOldNode;
+ SwPosition aNewPos(rNewPos);
+ aNewPos.AdjustContent(nOffset);
+ bool isSortingNeeded = false;
+
+ for (auto ppMark = m_vAllMarks.begin();
+ ppMark != m_vAllMarks.end();
+ ++ppMark)
+ {
+ ::sw::mark::MarkBase *const pMark = *ppMark;
+ // correction of non-existent non-MarkBase instances cannot be done
+ assert(pMark);
+ // is on position ??
+ bool bChangedPos = false;
+ if(&pMark->GetMarkPos().GetNode() == pOldNode)
+ {
+ pMark->SetMarkPos(aNewPos);
+ bChangedPos = true;
+ isSortingNeeded = true;
+ }
+ bool bChangedOPos = false;
+ if (pMark->IsExpanded() &&
+ &pMark->GetOtherMarkPos().GetNode() == pOldNode)
+ {
+ // shift the OtherMark to aNewPos
+ pMark->SetOtherMarkPos(aNewPos);
+ bChangedOPos= true;
+ isSortingNeeded = true;
+ }
+ // illegal selection? collapse the mark and restore sorting later
+ isSortingNeeded |= lcl_FixCorrectedMark(bChangedPos, bChangedOPos, pMark);
+ }
+
+ // restore sorting if needed
+ if(isSortingNeeded)
+ sortMarks();
+
+ SAL_INFO("sw.core", "correctMarksAbsolute");
+ lcl_DebugMarks(m_vAllMarks);
+ }
+
+ void MarkManager::correctMarksRelative(const SwNode& rOldNode, const SwPosition& rNewPos, const sal_Int32 nOffset)
+ {
+ const SwNode* const pOldNode = &rOldNode;
+ SwPosition aNewPos(rNewPos);
+ aNewPos.AdjustContent(nOffset);
+ bool isSortingNeeded = false;
+
+ for (auto ppMark = m_vAllMarks.begin();
+ ppMark != m_vAllMarks.end();
+ ++ppMark)
+ {
+ // is on position ??
+ bool bChangedPos = false, bChangedOPos = false;
+ ::sw::mark::MarkBase* const pMark = *ppMark;
+ // correction of non-existent non-MarkBase instances cannot be done
+ assert(pMark);
+ if(&pMark->GetMarkPos().GetNode() == pOldNode)
+ {
+ SwPosition aNewPosRel(aNewPos);
+ if (dynamic_cast< ::sw::mark::CrossRefBookmark *>(pMark))
+ {
+ // ensure that cross ref bookmark always starts at 0
+ aNewPosRel.SetContent(0); // HACK for WW8 import
+ isSortingNeeded = true; // and sort them to be safe...
+ }
+ aNewPosRel.AdjustContent(pMark->GetMarkPos().GetContentIndex());
+ pMark->SetMarkPos(aNewPosRel);
+ bChangedPos = true;
+ }
+ if(pMark->IsExpanded() &&
+ &pMark->GetOtherMarkPos().GetNode() == pOldNode)
+ {
+ SwPosition aNewPosRel(aNewPos);
+ aNewPosRel.AdjustContent(pMark->GetOtherMarkPos().GetContentIndex());
+ pMark->SetOtherMarkPos(aNewPosRel);
+ bChangedOPos = true;
+ }
+ // illegal selection? collapse the mark and restore sorting later
+ isSortingNeeded |= lcl_FixCorrectedMark(bChangedPos, bChangedOPos, pMark);
+ }
+
+ // restore sorting if needed
+ if(isSortingNeeded)
+ sortMarks();
+
+ SAL_INFO("sw.core", "correctMarksRelative");
+ lcl_DebugMarks(m_vAllMarks);
+ }
+
+ static bool isDeleteMark(
+ ::sw::mark::MarkBase const*const pMark,
+ bool const isReplace,
+ SwNode const& rStt,
+ SwNode const& rEnd,
+ std::optional<sal_Int32> oStartContentIdx,
+ std::optional<sal_Int32> oEndContentIdx,
+ bool & rbIsPosInRange,
+ bool & rbIsOtherPosInRange)
+ {
+ assert(pMark);
+ // navigator marks should not be moved
+ // TODO: Check if this might make them invalid
+ if (IDocumentMarkAccess::GetType(*pMark) == IDocumentMarkAccess::MarkType::NAVIGATOR_REMINDER)
+ {
+ return false;
+ }
+
+ // on position ??
+ rbIsPosInRange = lcl_GreaterThan(pMark->GetMarkPos(), rStt, oStartContentIdx)
+ && lcl_Lower(pMark->GetMarkPos(), rEnd, oEndContentIdx);
+ rbIsOtherPosInRange = pMark->IsExpanded()
+ && lcl_GreaterThan(pMark->GetOtherMarkPos(), rStt, oStartContentIdx)
+ && lcl_Lower(pMark->GetOtherMarkPos(), rEnd, oEndContentIdx);
+ // special case: completely in range, touching the end?
+ if ( oEndContentIdx.has_value()
+ && !(isReplace && IDocumentMarkAccess::GetType(*pMark)
+ == IDocumentMarkAccess::MarkType::BOOKMARK)
+ && ( ( rbIsOtherPosInRange
+ && pMark->GetMarkPos().GetNode() == rEnd
+ && pMark->GetMarkPos().GetContentIndex() == *oEndContentIdx )
+ || ( rbIsPosInRange
+ && pMark->IsExpanded()
+ && pMark->GetOtherMarkPos().GetNode() == rEnd
+ && pMark->GetOtherMarkPos().GetContentIndex() == *oEndContentIdx ) ) )
+ {
+ rbIsPosInRange = true;
+ rbIsOtherPosInRange = true;
+ }
+
+ if (rbIsPosInRange
+ && (rbIsOtherPosInRange
+ || !pMark->IsExpanded()))
+ {
+ // completely in range
+
+ bool bDeleteMark = true;
+ {
+ switch ( IDocumentMarkAccess::GetType( *pMark ) )
+ {
+ case IDocumentMarkAccess::MarkType::CROSSREF_HEADING_BOOKMARK:
+ case IDocumentMarkAccess::MarkType::CROSSREF_NUMITEM_BOOKMARK:
+ // no delete of cross-reference bookmarks, if range is inside one paragraph
+ bDeleteMark = &rStt != &rEnd;
+ break;
+ case IDocumentMarkAccess::MarkType::UNO_BOOKMARK:
+ // no delete of UNO mark, if it is not expanded and only touches the start of the range
+ bDeleteMark = rbIsOtherPosInRange
+ || pMark->IsExpanded()
+ || !oStartContentIdx.has_value()
+ || pMark->GetMarkPos().GetNode() != rStt
+ || pMark->GetMarkPos().GetContentIndex() != *oStartContentIdx;
+ break;
+ default:
+ bDeleteMark = true;
+ break;
+ }
+ }
+ return bDeleteMark;
+ }
+ return false;
+ }
+
+ bool MarkManager::isBookmarkDeleted(SwPaM const& rPaM, bool const isReplace) const
+ {
+ SwPosition const& rStart(*rPaM.Start());
+ SwPosition const& rEnd(*rPaM.End());
+ for (auto ppMark = m_vBookmarks.begin();
+ ppMark != m_vBookmarks.end();
+ ++ppMark)
+ {
+ bool bIsPosInRange(false);
+ bool bIsOtherPosInRange(false);
+ bool const bDeleteMark = isDeleteMark(*ppMark, isReplace,
+ rStart.GetNode(), rEnd.GetNode(), rStart.GetContentIndex(), rEnd.GetContentIndex(),
+ bIsPosInRange, bIsOtherPosInRange);
+ if (bDeleteMark
+ && IDocumentMarkAccess::GetType(**ppMark) == MarkType::BOOKMARK)
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ void MarkManager::deleteMarks(
+ const SwNode& rStt,
+ const SwNode& rEnd,
+ std::vector<SaveBookmark>* pSaveBkmk,
+ std::optional<sal_Int32> oStartContentIdx,
+ std::optional<sal_Int32> oEndContentIdx,
+ bool const isReplace)
+ {
+ std::vector<const_iterator_t> vMarksToDelete;
+ bool bIsSortingNeeded = false;
+
+ // boolean indicating, if at least one mark has been moved while collecting marks for deletion
+ bool bMarksMoved = false;
+ // have marks in the range been skipped instead of deleted
+ bool bMarksSkipDeletion = false;
+
+ // copy all bookmarks in the move area to a vector storing all position data as offset
+ // reassignment is performed after the move
+ for (auto ppMark = m_vAllMarks.begin();
+ ppMark != m_vAllMarks.end();
+ ++ppMark)
+ {
+ ::sw::mark::MarkBase *const pMark = *ppMark;
+ bool bIsPosInRange(false);
+ bool bIsOtherPosInRange(false);
+ bool const bDeleteMark = isDeleteMark(pMark, isReplace, rStt, rEnd,
+ oStartContentIdx, oEndContentIdx, bIsPosInRange, bIsOtherPosInRange);
+
+ if ( bIsPosInRange
+ && ( bIsOtherPosInRange
+ || !pMark->IsExpanded() ) )
+ {
+ if ( bDeleteMark )
+ {
+ if ( pSaveBkmk )
+ {
+ pSaveBkmk->push_back( SaveBookmark( *pMark, rStt, oStartContentIdx ) );
+ }
+ vMarksToDelete.emplace_back(ppMark);
+ }
+ else
+ {
+ bMarksSkipDeletion = true;
+ }
+ }
+ else if ( bIsPosInRange != bIsOtherPosInRange )
+ {
+ // the bookmark is partially in the range
+ // move position of that is in the range out of it
+
+ std::optional< SwPosition > oNewPos;
+ if ( oEndContentIdx )
+ {
+ oNewPos.emplace( *rEnd.GetContentNode(), *oEndContentIdx );
+ }
+ else
+ {
+ lcl_FindExpelPosition( oNewPos, rStt, rEnd, bIsPosInRange ? pMark->GetOtherMarkPos() : pMark->GetMarkPos() );
+ }
+
+ bool bMoveMark = true;
+ {
+ switch ( IDocumentMarkAccess::GetType( *pMark ) )
+ {
+ case IDocumentMarkAccess::MarkType::CROSSREF_HEADING_BOOKMARK:
+ case IDocumentMarkAccess::MarkType::CROSSREF_NUMITEM_BOOKMARK:
+ // no move of cross-reference bookmarks, if move occurs inside a certain node
+ bMoveMark = pMark->GetMarkPos().GetNode() != oNewPos->GetNode();
+ break;
+ case IDocumentMarkAccess::MarkType::ANNOTATIONMARK:
+ // no move of annotation marks, if method is called to collect deleted marks
+ bMoveMark = pSaveBkmk == nullptr;
+ break;
+ default:
+ bMoveMark = true;
+ break;
+ }
+ }
+ if ( bMoveMark )
+ {
+ if ( bIsPosInRange )
+ pMark->SetMarkPos(*oNewPos);
+ else
+ pMark->SetOtherMarkPos(*oNewPos);
+ bMarksMoved = true;
+
+ // illegal selection? collapse the mark and restore sorting later
+ bIsSortingNeeded |= lcl_FixCorrectedMark( bIsPosInRange, bIsOtherPosInRange, pMark );
+ }
+ }
+ }
+
+ {
+ // fdo#61016 delay the deletion of the fieldmark characters
+ // to prevent that from deleting the marks on that position
+ // which would invalidate the iterators in vMarksToDelete
+ std::vector< std::unique_ptr<ILazyDeleter> > vDelay;
+ vDelay.reserve(vMarksToDelete.size());
+
+ // If needed, sort mark containers containing subsets of the marks
+ // in order to assure sorting. The sorting is critical for the
+ // deletion of a mark as it is searched in these container for
+ // deletion.
+ if ( !vMarksToDelete.empty() && bMarksMoved )
+ {
+ sortSubsetMarks();
+ }
+ // we just remembered the iterators to delete, so we do not need to search
+ // for the shared_ptr<> (the entry in m_vAllMarks) again
+ // reverse iteration, since erasing an entry invalidates iterators
+ // behind it (the iterators in vMarksToDelete are sorted)
+ for ( std::vector< const_iterator_t >::reverse_iterator pppMark = vMarksToDelete.rbegin();
+ pppMark != vMarksToDelete.rend();
+ ++pppMark )
+ {
+ vDelay.push_back(deleteMark(*pppMark, pSaveBkmk != nullptr));
+ }
+ } // scope to kill vDelay
+
+ // also need to sort if both marks were moved and not-deleted because
+ // the not-deleted marks could be in wrong order vs. the moved ones
+ if (bIsSortingNeeded || (bMarksMoved && bMarksSkipDeletion))
+ {
+ sortMarks();
+ }
+
+ SAL_INFO("sw.core", "deleteMarks");
+ lcl_DebugMarks(m_vAllMarks);
+ }
+
+ namespace {
+
+ struct LazyFieldmarkDeleter : public IDocumentMarkAccess::ILazyDeleter
+ {
+ std::unique_ptr<Fieldmark> m_pFieldmark;
+ SwDoc& m_rDoc;
+ bool const m_isMoveNodes;
+ LazyFieldmarkDeleter(Fieldmark *const pMark, SwDoc& rDoc, bool const isMoveNodes)
+ : m_pFieldmark(pMark), m_rDoc(rDoc), m_isMoveNodes(isMoveNodes)
+ {
+ assert(m_pFieldmark);
+ }
+ virtual ~LazyFieldmarkDeleter() override
+ {
+ // note: because of the call chain from SwUndoDelete, the field
+ // command *cannot* be deleted here as it would create a separate
+ // SwUndoDelete that's interleaved with the SwHistory of the outer
+ // one - only delete the CH_TXT_ATR_FIELD*!
+ if (!m_isMoveNodes)
+ {
+ m_pFieldmark->ReleaseDoc(m_rDoc);
+ }
+ }
+ };
+
+ // Call DeregisterFromDoc() lazily, because it can call selection change listeners, which
+ // may mutate the marks container
+ struct LazyDdeBookmarkDeleter : public IDocumentMarkAccess::ILazyDeleter
+ {
+ std::unique_ptr<DdeBookmark> m_pDdeBookmark;
+ SwDoc& m_rDoc;
+ LazyDdeBookmarkDeleter(DdeBookmark *const pDdeBookmark, SwDoc& rDoc)
+ : m_pDdeBookmark(pDdeBookmark), m_rDoc(rDoc)
+ {
+ assert(pDdeBookmark);
+ }
+ virtual ~LazyDdeBookmarkDeleter() override
+ {
+ m_pDdeBookmark->DeregisterFromDoc(m_rDoc);
+ }
+ };
+
+ }
+
+ std::unique_ptr<IDocumentMarkAccess::ILazyDeleter>
+ MarkManager::deleteMark(const const_iterator_t& ppMark, bool const isMoveNodes)
+ {
+ std::unique_ptr<ILazyDeleter> ret;
+ if (ppMark.get() == m_vAllMarks.end())
+ return ret;
+ IMark* pMark = *ppMark;
+
+ switch(IDocumentMarkAccess::GetType(*pMark))
+ {
+ case IDocumentMarkAccess::MarkType::BOOKMARK:
+ {
+ auto const ppBookmark = lcl_FindMark(m_vBookmarks, *ppMark.get());
+ if ( ppBookmark != m_vBookmarks.end() )
+ {
+ Bookmark* pBookmark = dynamic_cast<Bookmark*>(*ppBookmark);
+
+ if(pBookmark)
+ pBookmark->sendLOKDeleteCallback();
+
+ m_vBookmarks.erase(ppBookmark);
+ }
+ else
+ {
+ assert(false &&
+ "<MarkManager::deleteMark(..)> - Bookmark not found in Bookmark container.");
+ }
+ }
+ break;
+ case IDocumentMarkAccess::MarkType::CROSSREF_HEADING_BOOKMARK:
+ case IDocumentMarkAccess::MarkType::CROSSREF_NUMITEM_BOOKMARK:
+ {
+ auto const ppBookmark = lcl_FindMark(m_vBookmarks, *ppMark.get());
+ if ( ppBookmark != m_vBookmarks.end() )
+ {
+ m_vBookmarks.erase(ppBookmark);
+ }
+ else
+ {
+ assert(false &&
+ "<MarkManager::deleteMark(..)> - Bookmark not found in Bookmark container.");
+ }
+ }
+ break;
+
+ case IDocumentMarkAccess::MarkType::TEXT_FIELDMARK:
+ case IDocumentMarkAccess::MarkType::CHECKBOX_FIELDMARK:
+ case IDocumentMarkAccess::MarkType::DROPDOWN_FIELDMARK:
+ case IDocumentMarkAccess::MarkType::DATE_FIELDMARK:
+ {
+ auto const ppFieldmark = lcl_FindMark(m_vFieldmarks, *ppMark.get());
+ if ( ppFieldmark != m_vFieldmarks.end() )
+ {
+ if(m_pLastActiveFieldmark == *ppFieldmark)
+ ClearFieldActivation();
+
+ m_vFieldmarks.erase(ppFieldmark);
+ ret.reset(new LazyFieldmarkDeleter(dynamic_cast<Fieldmark*>(pMark), m_rDoc, isMoveNodes));
+ }
+ else
+ {
+ assert(false &&
+ "<MarkManager::deleteMark(..)> - Fieldmark not found in Fieldmark container.");
+ }
+ }
+ break;
+
+ case IDocumentMarkAccess::MarkType::ANNOTATIONMARK:
+ {
+ auto const ppAnnotationMark = lcl_FindMark(m_vAnnotationMarks, *ppMark.get());
+ assert(ppAnnotationMark != m_vAnnotationMarks.end() &&
+ "<MarkManager::deleteMark(..)> - Annotation Mark not found in Annotation Mark container.");
+ m_vAnnotationMarks.erase(ppAnnotationMark);
+ }
+ break;
+
+ case IDocumentMarkAccess::MarkType::DDE_BOOKMARK:
+ case IDocumentMarkAccess::MarkType::NAVIGATOR_REMINDER:
+ case IDocumentMarkAccess::MarkType::UNO_BOOKMARK:
+ // no special marks container
+ break;
+ }
+ //Effective STL Item 27, get a non-const iterator aI at the same
+ //position as const iterator ppMark was
+ auto aI = m_vAllMarks.begin();
+ std::advance(aI, std::distance<container_t::const_iterator>(aI, ppMark.get()));
+ DdeBookmark* const pDdeBookmark = dynamic_cast<DdeBookmark*>(pMark);
+ if (pDdeBookmark)
+ {
+ ret.reset(new LazyDdeBookmarkDeleter(pDdeBookmark, m_rDoc));
+ }
+
+ m_vAllMarks.erase(aI);
+ // If we don't have a lazy deleter
+ if (!ret)
+ // delete after we remove from the list, because the destructor can
+ // recursively call into this method.
+ delete pMark;
+ return ret;
+ }
+
+ void MarkManager::deleteMark(const IMark* const pMark)
+ {
+ assert(&pMark->GetMarkPos().GetDoc() == &m_rDoc &&
+ "<MarkManager::deleteMark(..)>"
+ " - Mark is not in my doc.");
+ // finds the last Mark that is starting before pMark
+ // (pMarkLow < pMark)
+ auto [it, endIt] = equal_range(
+ m_vAllMarks.begin(),
+ m_vAllMarks.end(),
+ pMark->GetMarkStart(),
+ CompareIMarkStartsBefore());
+ for ( ; it != endIt; ++it)
+ if (*it == pMark)
+ {
+ deleteMark(iterator(it), false);
+ break;
+ }
+ }
+
+ void MarkManager::clearAllMarks()
+ {
+ ClearFieldActivation();
+ m_vFieldmarks.clear();
+ m_vBookmarks.clear();
+ m_vAnnotationMarks.clear();
+ for (const auto & p : m_vAllMarks)
+ delete p;
+ m_vAllMarks.clear();
+ }
+
+ IDocumentMarkAccess::const_iterator_t MarkManager::findMark(const OUString& rName) const
+ {
+ auto const ret = lcl_FindMarkByName(rName, m_vAllMarks.begin(), m_vAllMarks.end());
+ return IDocumentMarkAccess::iterator(ret);
+ }
+
+ IDocumentMarkAccess::const_iterator_t MarkManager::findBookmark(const OUString& rName) const
+ {
+ auto const ret = lcl_FindMarkByName(rName, m_vBookmarks.begin(), m_vBookmarks.end());
+ return IDocumentMarkAccess::iterator(ret);
+ }
+
+ IDocumentMarkAccess::const_iterator_t MarkManager::getAllMarksBegin() const
+ { return m_vAllMarks.begin(); }
+
+ IDocumentMarkAccess::const_iterator_t MarkManager::getAllMarksEnd() const
+ { return m_vAllMarks.end(); }
+
+ sal_Int32 MarkManager::getAllMarksCount() const
+ { return m_vAllMarks.size(); }
+
+ IDocumentMarkAccess::const_iterator_t MarkManager::getBookmarksBegin() const
+ { return m_vBookmarks.begin(); }
+
+ IDocumentMarkAccess::const_iterator_t MarkManager::getBookmarksEnd() const
+ { return m_vBookmarks.end(); }
+
+ sal_Int32 MarkManager::getBookmarksCount() const
+ { return m_vBookmarks.size(); }
+
+ IDocumentMarkAccess::const_iterator_t MarkManager::getFieldmarksBegin() const
+ { return m_vFieldmarks.begin(); }
+
+ IDocumentMarkAccess::const_iterator_t MarkManager::getFieldmarksEnd() const
+ { return m_vFieldmarks.end(); }
+
+ sal_Int32 MarkManager::getFieldmarksCount() const { return m_vFieldmarks.size(); }
+
+
+ // finds the first that is starting after
+ IDocumentMarkAccess::const_iterator_t MarkManager::findFirstBookmarkStartsAfter(const SwPosition& rPos) const
+ {
+ return std::upper_bound(
+ m_vBookmarks.begin(),
+ m_vBookmarks.end(),
+ rPos,
+ CompareIMarkStartsAfter());
+ }
+
+ IFieldmark* MarkManager::getFieldmarkAt(const SwPosition& rPos) const
+ {
+ auto const pFieldmark = find_if(
+ m_vFieldmarks.begin(),
+ m_vFieldmarks.end(),
+ [&rPos] (::sw::mark::MarkBase const*const pMark) {
+ return pMark->GetMarkStart() == rPos
+ // end position includes the CH_TXT_ATR_FIELDEND
+ || (pMark->GetMarkEnd().GetContentIndex() == rPos.GetContentIndex() + 1
+ && pMark->GetMarkEnd().GetNode() == rPos.GetNode());
+ } );
+ return (pFieldmark == m_vFieldmarks.end())
+ ? nullptr
+ : dynamic_cast<IFieldmark*>(*pFieldmark);
+ }
+
+ IFieldmark* MarkManager::getInnerFieldmarkFor(const SwPosition& rPos) const
+ {
+ auto itFieldmark = find_if(
+ m_vFieldmarks.begin(),
+ m_vFieldmarks.end(),
+ [&rPos] (const ::sw::mark::MarkBase *const pMark) { return pMark->IsCoveringPosition(rPos); } );
+ if (itFieldmark == m_vFieldmarks.end())
+ return nullptr;
+ auto pFieldmark(*itFieldmark);
+
+ // See if any fieldmarks after the first hit are closer to rPos.
+ ++itFieldmark;
+ for ( ; itFieldmark != m_vFieldmarks.end()
+ && (**itFieldmark).GetMarkStart() <= rPos; ++itFieldmark)
+ { // find the innermost fieldmark
+ if (rPos < (**itFieldmark).GetMarkEnd()
+ && (pFieldmark->GetMarkStart() < (**itFieldmark).GetMarkStart()
+ || (**itFieldmark).GetMarkEnd() < pFieldmark->GetMarkEnd()))
+ {
+ pFieldmark = *itFieldmark;
+ }
+ }
+ return dynamic_cast<IFieldmark*>(pFieldmark);
+ }
+
+ IMark* MarkManager::getOneInnermostBookmarkFor(const SwPosition& rPos) const
+ {
+ auto it = std::find_if(m_vBookmarks.begin(), m_vBookmarks.end(),
+ [&rPos](const sw::mark::MarkBase* pMark)
+ { return pMark->IsCoveringPosition(rPos); });
+ if (it == m_vBookmarks.end())
+ {
+ return nullptr;
+ }
+ sw::mark::IMark* pBookmark = *it;
+
+ // See if any bookmarks after the first hit are closer to rPos.
+ ++it;
+
+ for (; it != m_vBookmarks.end() && (*it)->GetMarkStart() <= rPos; ++it)
+ {
+ // Find the innermost bookmark.
+ if (rPos < (*it)->GetMarkEnd()
+ && (pBookmark->GetMarkStart() < (*it)->GetMarkStart()
+ || (*it)->GetMarkEnd() < pBookmark->GetMarkEnd()))
+ {
+ pBookmark = *it;
+ }
+ }
+ return pBookmark;
+ }
+
+ void MarkManager::deleteFieldmarkAt(const SwPosition& rPos)
+ {
+ auto const pFieldmark = dynamic_cast<Fieldmark*>(getFieldmarkAt(rPos));
+ assert(pFieldmark); // currently all callers require it to be there
+
+ deleteMark(lcl_FindMark(m_vAllMarks, pFieldmark), false);
+ }
+
+ ::sw::mark::IFieldmark* MarkManager::changeFormFieldmarkType(::sw::mark::IFieldmark* pFieldmark, const OUString& rNewType)
+ {
+ bool bActualChange = false;
+ if(rNewType == ODF_FORMDROPDOWN)
+ {
+ if (!dynamic_cast<::sw::mark::DropDownFieldmark*>(pFieldmark))
+ bActualChange = true;
+ if (!dynamic_cast<::sw::mark::CheckboxFieldmark*>(pFieldmark)) // only allowed converting between checkbox <-> dropdown
+ return nullptr;
+ }
+ else if(rNewType == ODF_FORMCHECKBOX)
+ {
+ if (!dynamic_cast<::sw::mark::CheckboxFieldmark*>(pFieldmark))
+ bActualChange = true;
+ if (!dynamic_cast<::sw::mark::DropDownFieldmark*>(pFieldmark)) // only allowed converting between checkbox <-> dropdown
+ return nullptr;
+ }
+ else if(rNewType == ODF_FORMDATE)
+ {
+ if (!dynamic_cast<::sw::mark::DateFieldmark*>(pFieldmark))
+ bActualChange = true;
+ if (!dynamic_cast<::sw::mark::TextFieldmark*>(pFieldmark)) // only allowed converting between date field <-> text field
+ return nullptr;
+ }
+
+ if (!bActualChange)
+ return nullptr;
+
+ // Store attributes needed to create the new fieldmark
+ OUString sName = pFieldmark->GetName();
+ SwPaM const aPaM(pFieldmark->GetMarkStart());
+
+ // Remove the old fieldmark and create a new one with the new type
+ if (rNewType == ODF_FORMDROPDOWN || rNewType == ODF_FORMCHECKBOX)
+ {
+ SwPosition aNewPos (*aPaM.GetPoint());
+ deleteFieldmarkAt(aNewPos);
+ return makeNoTextFieldBookmark(aPaM, sName, rNewType);
+ }
+ else if(rNewType == ODF_FORMDATE)
+ {
+ SwPosition aPos (*aPaM.GetPoint());
+ SwPaM aNewPaM(pFieldmark->GetMarkStart(), pFieldmark->GetMarkEnd());
+ deleteFieldmarkAt(aPos);
+ // HACK: hard-code the separator position here at the start because
+ // writerfilter put it in the wrong place (at the end) on attach()
+ SwPosition const sepPos(*aNewPaM.Start());
+ return makeFieldBookmark(aNewPaM, sName, rNewType, &sepPos);
+ }
+ return nullptr;
+ }
+
+ void MarkManager::NotifyCursorUpdate(const SwCursorShell& rCursorShell)
+ {
+ SwView* pSwView = dynamic_cast<SwView *>(rCursorShell.GetSfxViewShell());
+ if(!pSwView)
+ return;
+
+ SwEditWin& rEditWin = pSwView->GetEditWin();
+ SwPosition aPos(*rCursorShell.GetCursor()->GetPoint());
+ IFieldmark* pFieldBM = getInnerFieldmarkFor(aPos);
+ FieldmarkWithDropDownButton* pNewActiveFieldmark = nullptr;
+ if ((!pFieldBM || (pFieldBM->GetFieldname() != ODF_FORMDROPDOWN && pFieldBM->GetFieldname() != ODF_FORMDATE))
+ && aPos.GetContentIndex() > 0 )
+ {
+ aPos.AdjustContent(-1);
+ pFieldBM = getInnerFieldmarkFor(aPos);
+ }
+
+ if ( pFieldBM && (pFieldBM->GetFieldname() == ODF_FORMDROPDOWN ||
+ pFieldBM->GetFieldname() == ODF_FORMDATE))
+ {
+ if (m_pLastActiveFieldmark != pFieldBM)
+ {
+ FieldmarkWithDropDownButton& rFormField = dynamic_cast<FieldmarkWithDropDownButton&>(*pFieldBM);
+ pNewActiveFieldmark = &rFormField;
+ }
+ else
+ {
+ pNewActiveFieldmark = m_pLastActiveFieldmark;
+ }
+ }
+
+ if(pNewActiveFieldmark != m_pLastActiveFieldmark)
+ {
+ ClearFieldActivation();
+ m_pLastActiveFieldmark = pNewActiveFieldmark;
+ if(pNewActiveFieldmark)
+ pNewActiveFieldmark->ShowButton(&rEditWin);
+ }
+
+ LOKUpdateActiveField(pSwView);
+ }
+
+ void MarkManager::ClearFieldActivation()
+ {
+ if(m_pLastActiveFieldmark)
+ m_pLastActiveFieldmark->RemoveButton();
+
+ m_pLastActiveFieldmark = nullptr;
+ }
+
+ void MarkManager::LOKUpdateActiveField(const SfxViewShell* pViewShell)
+ {
+ if (!comphelper::LibreOfficeKit::isActive())
+ return;
+
+ if (m_pLastActiveFieldmark)
+ {
+ if (auto pDrowDown = m_pLastActiveFieldmark->GetFieldname() == ODF_FORMDROPDOWN ?
+ dynamic_cast<::sw::mark::DropDownFieldmark*>(m_pLastActiveFieldmark) :
+ nullptr)
+ {
+ pDrowDown->SendLOKShowMessage(pViewShell);
+ }
+ }
+ else
+ {
+ // Check whether we have any drop down fieldmark at all.
+ bool bDropDownFieldExist = false;
+ for (auto aIter = m_vFieldmarks.begin(); aIter != m_vFieldmarks.end(); ++aIter)
+ {
+ IFieldmark *pMark = dynamic_cast<IFieldmark*>(*aIter);
+ if (pMark && pMark->GetFieldname() == ODF_FORMDROPDOWN)
+ {
+ bDropDownFieldExist = true;
+ break;
+ }
+ }
+
+ if (bDropDownFieldExist)
+ ::sw::mark::DropDownFieldmark::SendLOKHideMessage(pViewShell);
+ }
+ }
+
+ IFieldmark* MarkManager::getDropDownFor(const SwPosition& rPos) const
+ {
+ IFieldmark *pMark = getFieldmarkAt(rPos);
+ if (!pMark || pMark->GetFieldname() != ODF_FORMDROPDOWN)
+ return nullptr;
+ return pMark;
+ }
+
+ std::vector<IFieldmark*> MarkManager::getNoTextFieldmarksIn(const SwPaM &rPaM) const
+ {
+ std::vector<IFieldmark*> aRet;
+
+ for (auto aI = m_vFieldmarks.begin(),
+ aEnd = m_vFieldmarks.end(); aI != aEnd; ++aI)
+ {
+ ::sw::mark::IMark* pI = *aI;
+ const SwPosition &rStart = pI->GetMarkPos();
+ if (!rPaM.ContainsPosition(rStart))
+ continue;
+
+ IFieldmark *pMark = dynamic_cast<IFieldmark*>(pI);
+ if (!pMark || (pMark->GetFieldname() != ODF_FORMDROPDOWN
+ && pMark->GetFieldname() != ODF_FORMCHECKBOX))
+ {
+ continue;
+ }
+
+ aRet.push_back(pMark);
+ }
+
+ return aRet;
+ }
+
+ IFieldmark* MarkManager::getFieldmarkAfter(const SwPosition& rPos, bool bLoop) const
+ { return dynamic_cast<IFieldmark*>(lcl_getMarkAfter(m_vFieldmarks, rPos, bLoop)); }
+
+ IFieldmark* MarkManager::getFieldmarkBefore(const SwPosition& rPos, bool bLoop) const
+ { return dynamic_cast<IFieldmark*>(lcl_getMarkBefore(m_vFieldmarks, rPos, bLoop)); }
+
+ IDocumentMarkAccess::const_iterator_t MarkManager::getAnnotationMarksBegin() const
+ {
+ return m_vAnnotationMarks.begin();
+ }
+
+ IDocumentMarkAccess::const_iterator_t MarkManager::getAnnotationMarksEnd() const
+ {
+ return m_vAnnotationMarks.end();
+ }
+
+ sal_Int32 MarkManager::getAnnotationMarksCount() const
+ {
+ return m_vAnnotationMarks.size();
+ }
+
+ IDocumentMarkAccess::const_iterator_t MarkManager::findAnnotationMark( const OUString& rName ) const
+ {
+ auto const ret = lcl_FindMarkByName( rName, m_vAnnotationMarks.begin(), m_vAnnotationMarks.end() );
+ return IDocumentMarkAccess::iterator(ret);
+ }
+
+ IMark* MarkManager::getAnnotationMarkFor(const SwPosition& rPos) const
+ {
+ auto const pAnnotationMark = find_if(
+ m_vAnnotationMarks.begin(),
+ m_vAnnotationMarks.end(),
+ [&rPos] (const ::sw::mark::MarkBase *const pMark) { return pMark->IsCoveringPosition(rPos); } );
+ if (pAnnotationMark == m_vAnnotationMarks.end())
+ return nullptr;
+ return *pAnnotationMark;
+ }
+
+ // finds the first that is starting after
+ IDocumentMarkAccess::const_iterator_t MarkManager::findFirstAnnotationStartsAfter(const SwPosition& rPos) const
+ {
+ return std::upper_bound(
+ m_vAnnotationMarks.begin(),
+ m_vAnnotationMarks.end(),
+ rPos,
+ CompareIMarkStartsAfter());
+ }
+
+ // create helper bookmark for annotations on tracked deletions
+ ::sw::mark::IMark* MarkManager::makeAnnotationBookmark(const SwPaM& rPaM,
+ const OUString& rName,
+ const IDocumentMarkAccess::MarkType eType,
+ sw::mark::InsertMode const eMode,
+ SwPosition const*const pSepPos)
+ {
+ OUString sAnnotationBookmarkName(rName + S_ANNOTATION_BOOKMARK);
+ return makeMark( rPaM, sAnnotationBookmarkName, eType, eMode, pSepPos);
+ }
+
+ // find helper bookmark of annotations on tracked deletions
+ IDocumentMarkAccess::const_iterator_t MarkManager::findAnnotationBookmark(const OUString& rName) const
+ {
+ OUString sAnnotationBookmarkName(rName + S_ANNOTATION_BOOKMARK);
+ return findBookmark(sAnnotationBookmarkName);
+ }
+
+ // restore text ranges of annotations on tracked deletions
+ // based on the helper bookmarks (which can survive I/O and hiding redlines)
+ void MarkManager::restoreAnnotationMarks(bool bDelete)
+ {
+ for (auto iter = getBookmarksBegin();
+ iter != getBookmarksEnd(); )
+ {
+ const OUString & rBookmarkName = (**iter).GetName();
+ sal_Int32 nPos;
+ if ( rBookmarkName.startsWith("__Annotation__") &&
+ (nPos = rBookmarkName.indexOf(S_ANNOTATION_BOOKMARK)) > -1 )
+ {
+ ::sw::UndoGuard const undoGuard(m_rDoc.GetIDocumentUndoRedo());
+ IDocumentMarkAccess::const_iterator_t pMark = findAnnotationMark(rBookmarkName.copy(0, nPos));
+ if ( pMark != getAnnotationMarksEnd() )
+ {
+ const SwPaM aPam((**iter).GetMarkStart(), (**pMark).GetMarkEnd());
+ repositionMark(*pMark, aPam);
+ }
+ if (bDelete)
+ {
+ deleteMark(&**iter);
+ // this invalidates iter, have to start over...
+ iter = getBookmarksBegin();
+ }
+ else
+ ++iter;
+ }
+ else
+ ++iter;
+ }
+ }
+
+ OUString MarkManager::getUniqueMarkName(const OUString& rName) const
+ {
+ OSL_ENSURE(rName.getLength(),
+ "<MarkManager::getUniqueMarkName(..)> - a name should be proposed");
+ if( m_rDoc.IsInMailMerge())
+ {
+ OUString newName = rName + "MailMergeMark"
+ + OStringToOUString( DateTimeToOString( DateTime( DateTime::SYSTEM )), RTL_TEXTENCODING_ASCII_US )
+ + OUString::number( m_vAllMarks.size() + 1 );
+ return newName;
+ }
+
+ if (lcl_FindMarkByName(rName, m_vAllMarks.begin(), m_vAllMarks.end()) == m_vAllMarks.end())
+ {
+ return rName;
+ }
+ OUString sTmp;
+
+ // try the name "<rName>XXX" (where XXX is a number starting from 1) unless there is
+ // an unused name. Due to performance-reasons (especially in mailmerge-scenarios) there
+ // is a map m_aMarkBasenameMapUniqueOffset which holds the next possible offset (XXX) for
+ // rName (so there is no need to test for nCnt-values smaller than the offset).
+ sal_Int32 nCnt = 1;
+ MarkBasenameMapUniqueOffset_t::const_iterator aIter = m_aMarkBasenameMapUniqueOffset.find(rName);
+ if(aIter != m_aMarkBasenameMapUniqueOffset.end()) nCnt = aIter->second;
+ OUString aPrefix = SwResId(STR_MARK_COPY).replaceFirst("%1", rName);
+ while(nCnt < SAL_MAX_INT32)
+ {
+ sTmp = aPrefix + OUString::number(nCnt);
+ nCnt++;
+ if (lcl_FindMarkByName(sTmp, m_vAllMarks.begin(), m_vAllMarks.end()) == m_vAllMarks.end())
+ {
+ break;
+ }
+ }
+ m_aMarkBasenameMapUniqueOffset[rName] = nCnt;
+
+ return sTmp;
+ }
+
+ void MarkManager::assureSortedMarkContainers() const
+ {
+ const_cast< MarkManager* >(this)->sortMarks();
+ }
+
+ void MarkManager::sortSubsetMarks()
+ {
+ stable_sort(m_vBookmarks.begin(), m_vBookmarks.end(), &lcl_MarkOrderingByStart);
+ sort(m_vFieldmarks.begin(), m_vFieldmarks.end(), &lcl_MarkOrderingByStart);
+ sort(m_vAnnotationMarks.begin(), m_vAnnotationMarks.end(), &lcl_MarkOrderingByStart);
+ }
+
+ void MarkManager::sortMarks()
+ {
+ sort(m_vAllMarks.begin(), m_vAllMarks.end(), &lcl_MarkOrderingByStart);
+ sortSubsetMarks();
+ }
+
+void MarkManager::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ struct
+ {
+ const char* pName;
+ const container_t* pContainer;
+ } aContainers[] =
+ {
+ // UNO marks are only part of all marks.
+ {"allmarks", &m_vAllMarks},
+ {"bookmarks", &m_vBookmarks},
+ {"fieldmarks", &m_vFieldmarks},
+ {"annotationmarks", &m_vAnnotationMarks}
+ };
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("MarkManager"));
+ for (const auto & rContainer : aContainers)
+ {
+ if (!rContainer.pContainer->empty())
+ {
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST(rContainer.pName));
+ for (auto it = rContainer.pContainer->begin(); it != rContainer.pContainer->end(); ++it)
+ (*it)->dumpAsXml(pWriter);
+ (void)xmlTextWriterEndElement(pWriter);
+ }
+ }
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+} // namespace ::sw::mark
+
+namespace
+{
+ bool lcl_Greater( const SwPosition& rPos, const SwNode& rNdIdx, std::optional<sal_Int32> oContentIdx )
+ {
+ return rPos.GetNode() > rNdIdx ||
+ ( oContentIdx && rPos.GetNode() == rNdIdx && rPos.GetContentIndex() > *oContentIdx );
+ }
+}
+
+// IDocumentMarkAccess for SwDoc
+IDocumentMarkAccess* SwDoc::getIDocumentMarkAccess()
+ { return static_cast< IDocumentMarkAccess* >(mpMarkManager.get()); }
+
+const IDocumentMarkAccess* SwDoc::getIDocumentMarkAccess() const
+ { return static_cast< IDocumentMarkAccess* >(mpMarkManager.get()); }
+
+SaveBookmark::SaveBookmark(
+ const IMark& rBkmk,
+ const SwNode& rMvPos,
+ std::optional<sal_Int32> oContentIdx)
+ : m_aName(rBkmk.GetName())
+ , m_bHidden(false)
+ , m_eOrigBkmType(IDocumentMarkAccess::GetType(rBkmk))
+{
+ const IBookmark* const pBookmark = dynamic_cast< const IBookmark* >(&rBkmk);
+ if(pBookmark)
+ {
+ m_aShortName = pBookmark->GetShortName();
+ m_aCode = pBookmark->GetKeyCode();
+ m_bHidden = pBookmark->IsHidden();
+ m_aHideCondition = pBookmark->GetHideCondition();
+
+ ::sfx2::Metadatable const*const pMetadatable(
+ dynamic_cast< ::sfx2::Metadatable const* >(pBookmark));
+ if (pMetadatable)
+ {
+ m_pMetadataUndo = pMetadatable->CreateUndo();
+ }
+ }
+ m_nNode1 = rBkmk.GetMarkPos().GetNodeIndex();
+ m_nContent1 = rBkmk.GetMarkPos().GetContentIndex();
+
+ m_nNode1 -= rMvPos.GetIndex();
+ if(oContentIdx && !m_nNode1)
+ m_nContent1 -= *oContentIdx;
+
+ if(rBkmk.IsExpanded())
+ {
+ m_nNode2 = rBkmk.GetOtherMarkPos().GetNodeIndex();
+ m_nContent2 = rBkmk.GetOtherMarkPos().GetContentIndex();
+
+ m_nNode2 -= rMvPos.GetIndex();
+ if(oContentIdx && !m_nNode2)
+ m_nContent2 -= *oContentIdx;
+ }
+ else
+ {
+ m_nNode2 = NODE_OFFSET_MAX;
+ m_nContent2 = -1;
+ }
+}
+
+void SaveBookmark::SetInDoc(
+ SwDoc* pDoc,
+ const SwNode& rNewPos,
+ std::optional<sal_Int32> oContentIdx)
+{
+ SwPaM aPam(rNewPos);
+ if(oContentIdx)
+ {
+ if (aPam.GetPoint()->GetNode().IsContentNode())
+ aPam.GetPoint()->SetContent( *oContentIdx );
+ else
+ SAL_WARN("sw", "trying to sent content index, but point node is not a content node");
+ }
+
+ if(NODE_OFFSET_MAX != m_nNode2)
+ {
+ aPam.SetMark();
+
+ aPam.GetMark()->Adjust(m_nNode2);
+ if (aPam.GetMark()->GetNode().IsContentNode())
+ {
+ if(oContentIdx && !m_nNode2)
+ aPam.GetMark()->SetContent(*oContentIdx + m_nContent2);
+ else
+ aPam.GetMark()->SetContent(m_nContent2);
+ }
+ else
+ SAL_WARN("sw", "trying to sent content index, but mark node is not a content node");
+ }
+
+ aPam.GetPoint()->Adjust(m_nNode1);
+
+ if (aPam.GetPoint()->GetNode().IsContentNode())
+ {
+ if(oContentIdx && !m_nNode1)
+ aPam.GetPoint()->SetContent(*oContentIdx + m_nContent1);
+ else
+ aPam.GetPoint()->SetContent(m_nContent1);
+ }
+
+ if(aPam.HasMark()
+ && !CheckNodesRange(aPam.GetPoint()->GetNode(), aPam.GetMark()->GetNode(), true))
+ return;
+
+ ::sw::mark::IBookmark* const pBookmark = dynamic_cast<::sw::mark::IBookmark*>(
+ pDoc->getIDocumentMarkAccess()->makeMark(aPam, m_aName,
+ m_eOrigBkmType, sw::mark::InsertMode::CopyText));
+ if(!pBookmark)
+ return;
+
+ pBookmark->SetKeyCode(m_aCode);
+ pBookmark->SetShortName(m_aShortName);
+ pBookmark->Hide(m_bHidden);
+ pBookmark->SetHideCondition(m_aHideCondition);
+
+ if (m_pMetadataUndo)
+ {
+ ::sfx2::Metadatable * const pMeta(
+ dynamic_cast< ::sfx2::Metadatable* >(pBookmark));
+ assert(pMeta && "metadata undo, but not metadatable?");
+ if (pMeta)
+ {
+ pMeta->RestoreMetadata(m_pMetadataUndo);
+ }
+ }
+}
+
+// DelBookmarks
+
+void DelBookmarks(
+ SwNode& rStt,
+ const SwNode& rEnd,
+ std::vector<SaveBookmark> * pSaveBkmk,
+ std::optional<sal_Int32> oStartContentIdx,
+ std::optional<sal_Int32> oEndContentIdx,
+ bool const isReplace)
+{
+ // illegal range ??
+ if(rStt.GetIndex() > rEnd.GetIndex()
+ || (&rStt == &rEnd && (!oStartContentIdx || !oEndContentIdx || *oStartContentIdx >= *oEndContentIdx)))
+ return;
+ SwDoc& rDoc = rStt.GetDoc();
+
+ rDoc.getIDocumentMarkAccess()->deleteMarks(rStt, rEnd, pSaveBkmk,
+ oStartContentIdx,
+ oEndContentIdx,
+ isReplace);
+
+ // Copy all Redlines which are in the move area into an array
+ // which holds all position information as offset.
+ // Assignment happens after moving.
+ SwRedlineTable& rTable = rDoc.getIDocumentRedlineAccess().GetRedlineTable();
+ for(SwRangeRedline* pRedl : rTable)
+ {
+ // Is at position?
+ auto [pRStt, pREnd] = pRedl->StartEnd();
+
+ if( lcl_Greater( *pRStt, rStt, oStartContentIdx ) && lcl_Lower( *pRStt, rEnd, oEndContentIdx ))
+ {
+ pRStt->Assign( rEnd );
+ if( oEndContentIdx )
+ pRStt->SetContent( *oEndContentIdx );
+ else
+ {
+ bool bStt = true;
+ SwContentNode* pCNd = pRStt->GetNode().GetContentNode();
+ if( !pCNd )
+ pCNd = rDoc.GetNodes().GoNext( pRStt );
+ if (!pCNd)
+ {
+ bStt = false;
+ pRStt->Assign(rStt);
+ pCNd = SwNodes::GoPrevious( pRStt );
+ if( !pCNd )
+ {
+ *pRStt = *pREnd;
+ pCNd = pRStt->GetNode().GetContentNode();
+ }
+ }
+ if (pCNd && !bStt)
+ pRStt->AssignEndIndex( *pCNd );
+ }
+ }
+ if( lcl_Greater( *pREnd, rStt, oStartContentIdx ) && lcl_Lower( *pREnd, rEnd, oEndContentIdx ))
+ {
+ pREnd->Assign( rStt );
+ if (oStartContentIdx && rStt.IsContentNode())
+ pREnd->SetContent( *oStartContentIdx );
+ else
+ {
+ bool bStt = false;
+ SwContentNode* pCNd = pREnd->GetNode().GetContentNode();
+ if( !pCNd )
+ pCNd = SwNodes::GoPrevious( pREnd );
+ if( !pCNd )
+ {
+ bStt = true;
+ pREnd->Assign(rEnd);
+ pCNd = rDoc.GetNodes().GoNext( pREnd );
+ if( !pCNd )
+ {
+ *pREnd = *pRStt;
+ pCNd = pREnd->GetNode().GetContentNode();
+ }
+ }
+ if (pCNd && !bStt)
+ pREnd->AssignEndIndex( *pCNd );
+ }
+ }
+ }
+}
+
+namespace sw {
+
+InsertText MakeInsertText(SwTextNode& rNode, const sal_Int32 nPos, const sal_Int32 nLen)
+{
+ SwCursor cursor(SwPosition(rNode, nPos), nullptr);
+ bool isInsideFieldmarkCommand(false);
+ bool isInsideFieldmarkResult(false);
+ while (auto const*const pMark = rNode.GetDoc().getIDocumentMarkAccess()->getInnerFieldmarkFor(*cursor.GetPoint()))
+ {
+ if (sw::mark::FindFieldSep(*pMark) < *cursor.GetPoint())
+ {
+ isInsideFieldmarkResult = true;
+ }
+ else
+ {
+ isInsideFieldmarkCommand = true;
+ }
+ *cursor.GetPoint() = pMark->GetMarkStart();
+ if (!cursor.Left(1))
+ {
+ break;
+ }
+ }
+ return InsertText(nPos, nLen, isInsideFieldmarkCommand, isInsideFieldmarkResult);
+}
+
+} // namespace sw
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/docchart.cxx b/sw/source/core/doc/docchart.cxx
new file mode 100644
index 0000000000..e421ac9ad0
--- /dev/null
+++ b/sw/source/core/doc/docchart.cxx
@@ -0,0 +1,196 @@
+/* -*- 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 <doc.hxx>
+#include <IDocumentChartDataProviderAccess.hxx>
+#include <IDocumentState.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <ndindex.hxx>
+#include <swtable.hxx>
+#include <viewsh.hxx>
+#include <ndole.hxx>
+#include <swtblfmt.hxx>
+#include <tblsel.hxx>
+#include <frameformats.hxx>
+#include <unochart.hxx>
+#include <osl/diagnose.h>
+
+void SwTable::UpdateCharts() const
+{
+ GetFrameFormat()->GetDoc()->UpdateCharts( GetFrameFormat()->GetName() );
+}
+
+bool SwTable::IsTableComplexForChart( std::u16string_view aSelection ) const
+{
+ const SwTableBox* pSttBox, *pEndBox;
+ if( 2 < aSelection.size() )
+ {
+ const size_t nSeparator = aSelection.find( u':' );
+ OSL_ENSURE( std::u16string_view::npos != nSeparator, "no valid selection" );
+
+ // Remove brackets at the beginning and from the end
+ const sal_Int32 nOffset = '<' == aSelection[0] ? 1 : 0;
+ const sal_Int32 nLength = '>' == aSelection[ aSelection.size()-1 ]
+ ? aSelection.size()-1 : aSelection.size();
+
+ pSttBox = GetTableBox(OUString(aSelection.substr( nOffset, nSeparator - nOffset )));
+ pEndBox = GetTableBox(OUString(aSelection.substr( nSeparator+1, nLength - (nSeparator+1) )));
+ }
+ else
+ {
+ const SwTableLines* pLns = &GetTabLines();
+ pSttBox = (*pLns)[ 0 ]->GetTabBoxes().front();
+ while( !pSttBox->GetSttNd() )
+ // Until the Content Box!
+ pSttBox = pSttBox->GetTabLines().front()->GetTabBoxes().front();
+
+ const SwTableBoxes* pBoxes = &pLns->back()->GetTabBoxes();
+ pEndBox = pBoxes->back();
+ while( !pEndBox->GetSttNd() )
+ {
+ // Until the Content Box!
+ pLns = &pEndBox->GetTabLines();
+ pBoxes = &pLns->back()->GetTabBoxes();
+ pEndBox = pBoxes->back();
+ }
+ }
+
+ return !pSttBox || !pEndBox || !::ChkChartSel( *pSttBox->GetSttNd(),
+ *pEndBox->GetSttNd() );
+}
+
+void SwDoc::DoUpdateAllCharts()
+{
+ SwViewShell* pVSh = getIDocumentLayoutAccess().GetCurrentViewShell();
+ if( !pVSh )
+ return;
+
+ for(const SwTableFormat* pFormat: *GetTableFrameFormats())
+ {
+ if( SwTable* pTmpTable = SwTable::FindTable( pFormat ) )
+ if( const SwTableNode* pTableNd = pTmpTable->GetTableNode() )
+ if( pTableNd->GetNodes().IsDocNodes() )
+ {
+ UpdateCharts_( *pTmpTable, *pVSh );
+ }
+ }
+}
+
+void SwDoc::UpdateCharts_( const SwTable& rTable, SwViewShell const & rVSh ) const
+{
+ OUString aName( rTable.GetFrameFormat()->GetName() );
+ SwStartNode *pStNd;
+ SwNodeIndex aIdx( *GetNodes().GetEndOfAutotext().StartOfSectionNode(), 1 );
+ while( nullptr != (pStNd = aIdx.GetNode().GetStartNode()) )
+ {
+ ++aIdx;
+ SwOLENode *pONd = aIdx.GetNode().GetOLENode();
+ if( pONd &&
+ aName == pONd->GetChartTableName() &&
+ pONd->getLayoutFrame( rVSh.GetLayout() ) )
+ {
+ // tdf#122995 for OLE/Charts in SW we do not (yet) have a refresh
+ // mechanism or embedding of the primitive representation, so this
+ // needs to be done locally here (simplest solution).
+ bool bImmediateMode(false);
+
+ if(pONd->IsChart())
+ {
+ // refresh to trigger repaint
+ const SwRect aChartRect(pONd->FindLayoutRect());
+ if(!aChartRect.IsEmpty())
+ const_cast<SwViewShell &>(rVSh).InvalidateWindows(aChartRect);
+
+ // forced refresh of the chart's primitive representation
+ pONd->GetOLEObj().resetBufferedData();
+
+ // InvalidateTable using the Immediate-Mode, else the chart will
+ // not yet know that it is invalidated at the next repaint and create
+ // the same graphical representation again
+ bImmediateMode = true;
+ }
+
+ SwChartDataProvider *pPCD = getIDocumentChartDataProviderAccess().GetChartDataProvider();
+ if (pPCD)
+ pPCD->InvalidateTable( &rTable, bImmediateMode );
+ // following this the framework will now take care of repainting
+ // the chart or it's replacement image...
+ }
+ aIdx.Assign( *pStNd->EndOfSectionNode(), + 1 );
+ }
+}
+
+void SwDoc::UpdateCharts( const OUString& rName ) const
+{
+ SwTable* pTmpTable = SwTable::FindTable( FindTableFormatByName( rName ) );
+ if( pTmpTable )
+ {
+ SwViewShell const * pVSh = getIDocumentLayoutAccess().GetCurrentViewShell();
+
+ if( pVSh )
+ UpdateCharts_( *pTmpTable, *pVSh );
+ }
+}
+
+void SwDoc::SetTableName( SwFrameFormat& rTableFormat, const OUString &rNewName )
+{
+ const OUString aOldName( rTableFormat.GetName() );
+
+ bool bNameFound = rNewName.isEmpty();
+ if( !bNameFound )
+ {
+ for(const SwTableFormat* pFormat: *GetTableFrameFormats())
+ {
+ if( !pFormat->IsDefault() &&
+ pFormat->GetName() == rNewName && IsUsed( *pFormat ) )
+ {
+ bNameFound = true;
+ break;
+ }
+ }
+ }
+
+ if( !bNameFound )
+ rTableFormat.SetFormatName( rNewName, true );
+ else
+ rTableFormat.SetFormatName( GetUniqueTableName(), true );
+
+ SwStartNode *pStNd;
+ SwNodeIndex aIdx( *GetNodes().GetEndOfAutotext().StartOfSectionNode(), 1 );
+ while ( nullptr != (pStNd = aIdx.GetNode().GetStartNode()) )
+ {
+ ++aIdx;
+ SwOLENode *pNd = aIdx.GetNode().GetOLENode();
+ if( pNd && aOldName == pNd->GetChartTableName() )
+ {
+ pNd->SetChartTableName( rNewName );
+
+ SwTable* pTable = SwTable::FindTable( &rTableFormat );
+ SwChartDataProvider *pPCD = getIDocumentChartDataProviderAccess().GetChartDataProvider();
+ if (pPCD)
+ pPCD->InvalidateTable( pTable );
+ // following this the framework will now take care of repainting
+ // the chart or it's replacement image...
+ }
+ aIdx.Assign( *pStNd->EndOfSectionNode(), + 1 );
+ }
+ getIDocumentState().SetModified();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/doccomp.cxx b/sw/source/core/doc/doccomp.cxx
new file mode 100644
index 0000000000..d813e08965
--- /dev/null
+++ b/sw/source/core/doc/doccomp.cxx
@@ -0,0 +1,2691 @@
+/* -*- 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 <sal/config.h>
+
+#include <rtl/ustrbuf.hxx>
+#include <o3tl/safeint.hxx>
+#include <osl/diagnose.h>
+#include <rtl/character.hxx>
+#include <swmodule.hxx>
+#include <doc.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <DocumentContentOperationsManager.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <IDocumentState.hxx>
+#include <docary.hxx>
+#include <pam.hxx>
+#include <ndtxt.hxx>
+#include <redline.hxx>
+#include <UndoRedline.hxx>
+#include <section.hxx>
+#include <tox.hxx>
+#include <docsh.hxx>
+#include <fmtcntnt.hxx>
+#include <modcfg.hxx>
+#include <frameformats.hxx>
+
+#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
+#include <com/sun/star/document/XDocumentProperties.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+
+#include <cstddef>
+#include <memory>
+#include <type_traits>
+#include <vector>
+
+using namespace ::com::sun::star;
+
+using std::vector;
+
+namespace {
+
+class SwCompareLine final
+{
+ const SwNode* m_pNode;
+public:
+ explicit SwCompareLine( const SwNode& rNd ) : m_pNode( &rNd ) {}
+ SwCompareLine() : m_pNode( nullptr ) {}
+
+ sal_uLong GetHashValue() const;
+ bool Compare( const SwCompareLine& rLine ) const;
+
+ static sal_uLong GetTextNodeHashValue( const SwTextNode& rNd, sal_uLong nVal );
+ static bool CompareNode( const SwNode& rDstNd, const SwNode& rSrcNd );
+ static bool CompareTextNd( const SwTextNode& rDstNd,
+ const SwTextNode& rSrcNd );
+
+ bool ChangesInLine( const SwCompareLine& rLine,
+ std::unique_ptr<SwPaM>& rpInsRing, std::unique_ptr<SwPaM>& rpDelRing ) const;
+
+ const SwNode& GetNode() const { return *m_pNode; }
+
+ const SwNode& GetEndNode() const;
+
+ // for debugging
+ OUString GetText() const;
+};
+
+
+class CompareData
+{
+protected:
+ SwDoc& m_rDoc;
+private:
+ std::unique_ptr<size_t[]> m_pIndex;
+ std::unique_ptr<bool[]> m_pChangedFlag;
+
+ std::unique_ptr<SwPaM> m_pInsertRing, m_pDelRing;
+
+ static SwNodeOffset PrevIdx( const SwNode* pNd );
+ static SwNodeOffset NextIdx( const SwNode* pNd );
+
+ vector<SwCompareLine> m_aLines;
+ bool m_bRecordDiff;
+
+ // Truncate beginning and end and add all others to the LinesArray
+ void CheckRanges( CompareData& );
+
+ virtual const SwNode& GetEndOfContent() = 0;
+
+public:
+ CompareData(SwDoc& rD, bool bRecordDiff)
+ : m_rDoc( rD )
+ , m_bRecordDiff(bRecordDiff)
+ {
+ }
+ virtual ~CompareData();
+
+ // Are there differences?
+ bool HasDiffs( const CompareData& rData ) const;
+
+ // Triggers the comparison and creation of two documents
+ void CompareLines( CompareData& rData );
+ // Display the differences - calls the methods ShowInsert and ShowDelete.
+ // These are passed the start and end line number.
+ // Displaying the actually content is to be handled by the subclass!
+ sal_uLong ShowDiffs( const CompareData& rData );
+
+ void ShowInsert( sal_uLong nStt, sal_uLong nEnd );
+ void ShowDelete( const CompareData& rData, sal_uLong nStt,
+ sal_uLong nEnd, sal_uLong nInsPos );
+ void CheckForChangesInLine( const CompareData& rData,
+ sal_uLong nStt, sal_uLong nEnd,
+ sal_uLong nThisStt, sal_uLong nThisEnd );
+
+ // Set non-ambiguous index for a line. Same lines have the same index, even in the other CompareData!
+ void SetIndex( size_t nLine, size_t nIndex );
+ size_t GetIndex( size_t nLine ) const
+ { return nLine < m_aLines.size() ? m_pIndex[ nLine ] : 0; }
+
+ // Set/get of a line has changed
+ void SetChanged( size_t nLine, bool bFlag = true );
+ bool GetChanged( size_t nLine ) const
+ {
+ return (m_pChangedFlag && nLine < m_aLines.size())
+ && m_pChangedFlag[ nLine ];
+ }
+
+ size_t GetLineCount() const { return m_aLines.size(); }
+ const SwCompareLine& GetLine( size_t nLine ) const
+ { return m_aLines[ nLine ]; }
+ void InsertLine( SwCompareLine aLine )
+ { m_aLines.push_back( aLine ); }
+
+ void SetRedlinesToDoc( bool bUseDocInfo );
+};
+
+class CompareMainText : public CompareData
+{
+public:
+ CompareMainText(SwDoc &rD, bool bRecordDiff)
+ : CompareData(rD, bRecordDiff)
+ {
+ }
+
+ virtual const SwNode& GetEndOfContent() override
+ {
+ return m_rDoc.GetNodes().GetEndOfContent();
+ }
+};
+
+class CompareFrameFormatText : public CompareData
+{
+ const SwNodeIndex &m_rIndex;
+public:
+ CompareFrameFormatText(SwDoc &rD, const SwNodeIndex &rIndex)
+ : CompareData(rD, true/*bRecordDiff*/)
+ , m_rIndex(rIndex)
+ {
+ }
+
+ virtual const SwNode& GetEndOfContent() override
+ {
+ return *m_rIndex.GetNode().EndOfSectionNode();
+ }
+};
+
+class Hash
+{
+ struct HashData
+ {
+ sal_uLong nNext, nHash;
+ SwCompareLine aLine;
+
+ HashData()
+ : nNext( 0 ), nHash( 0 ) {}
+ };
+
+ std::unique_ptr<sal_uLong[]> m_pHashArr;
+ std::unique_ptr<HashData[]> m_pDataArr;
+ sal_uLong m_nCount, m_nPrime;
+
+public:
+ explicit Hash( sal_uLong nSize );
+
+ void CalcHashValue( CompareData& rData );
+
+ sal_uLong GetCount() const { return m_nCount; }
+};
+
+class Compare
+{
+public:
+ class MovedData
+ {
+ std::unique_ptr<sal_uLong[]> m_pIndex;
+ std::unique_ptr<sal_uLong[]> m_pLineNum;
+ sal_uLong m_nCount;
+
+ public:
+ MovedData( CompareData& rData, const char* pDiscard );
+
+ sal_uLong GetIndex( sal_uLong n ) const { return m_pIndex[ n ]; }
+ sal_uLong GetLineNum( sal_uLong n ) const { return m_pLineNum[ n ]; }
+ sal_uLong GetCount() const { return m_nCount; }
+ };
+
+private:
+ /// Look for the moved lines
+ class CompareSequence
+ {
+ CompareData &m_rData1, &m_rData2;
+ const MovedData &m_rMoved1, &m_rMoved2;
+ std::unique_ptr<tools::Long[]> m_pMemory;
+ tools::Long *m_pFDiag, *m_pBDiag;
+
+ void Compare( sal_uLong nStt1, sal_uLong nEnd1, sal_uLong nStt2, sal_uLong nEnd2 );
+ sal_uLong CheckDiag( sal_uLong nStt1, sal_uLong nEnd1,
+ sal_uLong nStt2, sal_uLong nEnd2, sal_uLong* pCost );
+ public:
+ CompareSequence( CompareData& rD1, CompareData& rD2,
+ const MovedData& rMD1, const MovedData& rMD2 );
+ };
+
+ static void CountDifference( const CompareData& rData, sal_uLong* pCounts );
+ static void SetDiscard( const CompareData& rData,
+ char* pDiscard, const sal_uLong* pCounts );
+ static void CheckDiscard( size_t nLen, char* pDiscard );
+ static void ShiftBoundaries( CompareData& rData1, CompareData& rData2 );
+
+public:
+ Compare( sal_uLong nDiff, CompareData& rData1, CompareData& rData2 );
+};
+
+class ArrayComparator
+{
+public:
+ virtual bool Compare( int nIdx1, int nIdx2 ) const = 0;
+ virtual int GetLen1() const = 0;
+ virtual int GetLen2() const = 0;
+ virtual ~ArrayComparator() {}
+};
+
+/// Consider two lines equal if similar enough (e.g. look like different
+/// versions of the same paragraph)
+class LineArrayComparator : public ArrayComparator
+{
+private:
+ int m_nLen1, m_nLen2;
+ const CompareData &m_rData1, &m_rData2;
+ int m_nFirst1, m_nFirst2;
+
+public:
+ LineArrayComparator( const CompareData &rD1, const CompareData &rD2,
+ int nStt1, int nEnd1, int nStt2, int nEnd2 );
+
+ virtual bool Compare( int nIdx1, int nIdx2 ) const override;
+ virtual int GetLen1() const override { return m_nLen1; }
+ virtual int GetLen2() const override { return m_nLen2; }
+};
+
+class WordArrayComparator : public ArrayComparator
+{
+private:
+ const SwTextNode *m_pTextNode1, *m_pTextNode2;
+ std::unique_ptr<int[]> m_pPos1, m_pPos2;
+ int m_nCount1, m_nCount2; // number of words
+
+ static void CalcPositions( int *pPos, const SwTextNode *pTextNd, int &nCnt );
+
+public:
+ WordArrayComparator( const SwTextNode *pNode1, const SwTextNode *pNode2 );
+
+ virtual bool Compare( int nIdx1, int nIdx2 ) const override;
+ virtual int GetLen1() const override { return m_nCount1; }
+ virtual int GetLen2() const override { return m_nCount2; }
+ int GetCharSequence( const int *pWordLcs1, const int *pWordLcs2,
+ int *pSubseq1, int *pSubseq2, int nLcsLen );
+};
+
+class CharArrayComparator : public ArrayComparator
+{
+private:
+ const SwTextNode *m_pTextNode1, *m_pTextNode2;
+
+public:
+ CharArrayComparator( const SwTextNode *pNode1, const SwTextNode *pNode2 )
+ : m_pTextNode1( pNode1 ), m_pTextNode2( pNode2 )
+ {
+ }
+
+ virtual bool Compare( int nIdx1, int nIdx2 ) const override;
+ virtual int GetLen1() const override { return m_pTextNode1->GetText().getLength(); }
+ virtual int GetLen2() const override { return m_pTextNode2->GetText().getLength(); }
+};
+
+/// Options set in Tools->Options->Writer->Comparison
+struct CmpOptionsContainer
+{
+ SwCompareMode eCmpMode;
+ int nIgnoreLen;
+ bool bUseRsid;
+};
+
+}
+
+static CmpOptionsContainer CmpOptions;
+
+namespace {
+
+class CommonSubseq
+{
+private:
+ std::unique_ptr<int[]> m_pData;
+
+protected:
+ ArrayComparator &m_rComparator;
+
+ CommonSubseq( ArrayComparator &rComparator, int nMaxSize )
+ : m_rComparator( rComparator )
+ {
+ m_pData.reset( new int[ nMaxSize ] );
+ }
+
+ int FindLCS( int *pLcs1, int *pLcs2, int nStt1,
+ int nEnd1, int nStt2, int nEnd2 );
+
+public:
+ static int IgnoreIsolatedPieces( int *pLcs1, int *pLcs2, int nLen1, int nLen2,
+ int nLcsLen, int nPieceLen );
+};
+
+/// Use Hirschberg's algorithm to find LCS in linear space
+class LgstCommonSubseq: public CommonSubseq
+{
+private:
+ static const int CUTOFF = 1<<20; // Stop recursion at this value
+
+ std::unique_ptr<int[]> m_pL1, m_pL2;
+ std::unique_ptr<int[]> m_pBuff1, m_pBuff2;
+
+ void FindL( int *pL, int nStt1, int nEnd1, int nStt2, int nEnd2 );
+ int HirschbergLCS( int *pLcs1, int *pLcs2, int nStt1, int nEnd1,
+ int nStt2, int nEnd2 );
+
+public:
+ explicit LgstCommonSubseq( ArrayComparator &rComparator );
+
+ int Find( int *pSubseq1, int *pSubseq2 );
+};
+
+/// Find a common subsequence in linear time
+class FastCommonSubseq: private CommonSubseq
+{
+private:
+ static const int CUTOFF = 2056;
+
+ int FindFastCS( int *pSeq1, int *pSeq2, int nStt1, int nEnd1,
+ int nStt2, int nEnd2 );
+
+public:
+ explicit FastCommonSubseq( ArrayComparator &rComparator )
+ : CommonSubseq( rComparator, CUTOFF )
+ {
+ }
+
+ int Find( int *pSubseq1, int *pSubseq2 )
+ {
+ return FindFastCS( pSubseq1, pSubseq2, 0, m_rComparator.GetLen1(),
+ 0, m_rComparator.GetLen2() );
+ }
+};
+
+}
+
+CompareData::~CompareData()
+{
+ if( m_pDelRing )
+ {
+ while( m_pDelRing->GetNext() != m_pDelRing.get() )
+ delete m_pDelRing->GetNext();
+ m_pDelRing.reset();
+ }
+ if( m_pInsertRing )
+ {
+ while( m_pInsertRing->GetNext() != m_pInsertRing.get() )
+ delete m_pInsertRing->GetNext();
+ m_pInsertRing.reset();
+ }
+}
+
+void CompareData::SetIndex( size_t nLine, size_t nIndex )
+{
+ if( !m_pIndex )
+ {
+ m_pIndex.reset( new size_t[ m_aLines.size() ] );
+ memset( m_pIndex.get(), 0, m_aLines.size() * sizeof( size_t ) );
+ }
+ if( nLine < m_aLines.size() )
+ m_pIndex[ nLine ] = nIndex;
+}
+
+void CompareData::SetChanged( size_t nLine, bool bFlag )
+{
+ if( !m_pChangedFlag )
+ {
+ m_pChangedFlag.reset( new bool[ m_aLines.size() +1 ] );
+ memset( m_pChangedFlag.get(), 0, (m_aLines.size() +1) * sizeof( bool ) );
+ }
+ if( nLine < m_aLines.size() )
+ m_pChangedFlag[ nLine ] = bFlag;
+}
+
+void CompareData::CompareLines( CompareData& rData )
+{
+ CheckRanges( rData );
+
+ sal_uLong nDifferent;
+ {
+ Hash aH( GetLineCount() + rData.GetLineCount() + 1 );
+ aH.CalcHashValue( *this );
+ aH.CalcHashValue( rData );
+ nDifferent = aH.GetCount();
+ }
+ {
+ Compare aComp( nDifferent, *this, rData );
+ }
+}
+
+sal_uLong CompareData::ShowDiffs( const CompareData& rData )
+{
+ sal_uLong nLen1 = rData.GetLineCount(), nLen2 = GetLineCount();
+ sal_uLong nStt1 = 0, nStt2 = 0;
+ sal_uLong nCnt = 0;
+
+ while( nStt1 < nLen1 || nStt2 < nLen2 )
+ {
+ if( rData.GetChanged( nStt1 ) || GetChanged( nStt2 ) )
+ {
+ // Find a region of different lines between two pairs of identical
+ // lines.
+ sal_uLong nSav1 = nStt1, nSav2 = nStt2;
+ while( nStt1 < nLen1 && rData.GetChanged( nStt1 )) ++nStt1;
+ while( nStt2 < nLen2 && GetChanged( nStt2 )) ++nStt2;
+
+ if (m_bRecordDiff)
+ {
+ // Check if there are changed lines (only slightly different) and
+ // compare them in detail.
+ CheckForChangesInLine( rData, nSav1, nStt1, nSav2, nStt2 );
+ }
+
+ ++nCnt;
+ }
+ ++nStt1;
+ ++nStt2;
+ }
+ return nCnt;
+}
+
+bool CompareData::HasDiffs( const CompareData& rData ) const
+{
+ bool bRet = false;
+ sal_uLong nLen1 = rData.GetLineCount(), nLen2 = GetLineCount();
+ sal_uLong nStt1 = 0, nStt2 = 0;
+
+ while( nStt1 < nLen1 || nStt2 < nLen2 )
+ {
+ if( rData.GetChanged( nStt1 ) || GetChanged( nStt2 ) )
+ {
+ bRet = true;
+ break;
+ }
+ ++nStt1;
+ ++nStt2;
+ }
+ return bRet;
+}
+
+Hash::Hash( sal_uLong nSize )
+ : m_nCount(1)
+{
+
+ static const sal_uLong primes[] =
+ {
+ 509,
+ 1021,
+ 2039,
+ 4093,
+ 8191,
+ 16381,
+ 32749,
+ 65521,
+ 131071,
+ 262139,
+ 524287,
+ 1048573,
+ 2097143,
+ 4194301,
+ 8388593,
+ 16777213,
+ 33554393,
+ 67108859, /* Preposterously large . . . */
+ 134217689,
+ 268435399,
+ 536870909,
+ 1073741789,
+ 2147483647,
+ 0
+ };
+ int i;
+
+ m_pDataArr.reset( new HashData[ nSize ] );
+ m_pDataArr[0].nNext = 0;
+ m_pDataArr[0].nHash = 0;
+ m_nPrime = primes[0];
+
+ for( i = 0; primes[i] < nSize / 3; i++)
+ if( !primes[i] )
+ {
+ m_pHashArr = nullptr;
+ return;
+ }
+ m_nPrime = primes[ i ];
+ m_pHashArr.reset( new sal_uLong[ m_nPrime ] );
+ memset( m_pHashArr.get(), 0, m_nPrime * sizeof( sal_uLong ) );
+}
+
+void Hash::CalcHashValue( CompareData& rData )
+{
+ if( !m_pHashArr )
+ return;
+
+ for( size_t n = 0; n < rData.GetLineCount(); ++n )
+ {
+ const SwCompareLine aLine = rData.GetLine( n );
+ sal_uLong nH = aLine.GetHashValue();
+
+ sal_uLong* pFound = &m_pHashArr[ nH % m_nPrime ];
+ size_t i;
+ for( i = *pFound; ; i = m_pDataArr[i].nNext )
+ if( !i )
+ {
+ i = m_nCount++;
+ m_pDataArr[i].nNext = *pFound;
+ m_pDataArr[i].nHash = nH;
+ m_pDataArr[i].aLine = aLine;
+ *pFound = i;
+ break;
+ }
+ else if( m_pDataArr[i].nHash == nH &&
+ m_pDataArr[i].aLine.Compare( aLine ))
+ break;
+
+ rData.SetIndex( n, i );
+ }
+}
+
+Compare::Compare( sal_uLong nDiff, CompareData& rData1, CompareData& rData2 )
+{
+ std::unique_ptr<MovedData> pMD1, pMD2;
+ // Look for the differing lines
+ {
+ std::unique_ptr<char[]> pDiscard1( new char[ rData1.GetLineCount() ] );
+ std::unique_ptr<char[]> pDiscard2( new char[ rData2.GetLineCount() ] );
+
+ std::unique_ptr<sal_uLong[]> pCount1(new sal_uLong[ nDiff ]);
+ std::unique_ptr<sal_uLong[]> pCount2(new sal_uLong[ nDiff ]);
+ memset( pCount1.get(), 0, nDiff * sizeof( sal_uLong ));
+ memset( pCount2.get(), 0, nDiff * sizeof( sal_uLong ));
+
+ // find indices in CompareData which have been assigned multiple times
+ CountDifference( rData1, pCount1.get() );
+ CountDifference( rData2, pCount2.get() );
+
+ // All which occur only once now have either been inserted or deleted.
+ // All which are also contained in the other one have been moved.
+ SetDiscard( rData1, pDiscard1.get(), pCount2.get() );
+ SetDiscard( rData2, pDiscard2.get(), pCount1.get() );
+
+ CheckDiscard( rData1.GetLineCount(), pDiscard1.get() );
+ CheckDiscard( rData2.GetLineCount(), pDiscard2.get() );
+
+ pMD1.reset(new MovedData( rData1, pDiscard1.get() ));
+ pMD2.reset(new MovedData( rData2, pDiscard2.get() ));
+ }
+
+ {
+ CompareSequence aTmp( rData1, rData2, *pMD1, *pMD2 );
+ }
+
+ ShiftBoundaries( rData1, rData2 );
+}
+
+void Compare::CountDifference( const CompareData& rData, sal_uLong* pCounts )
+{
+ sal_uLong nLen = rData.GetLineCount();
+ for( sal_uLong n = 0; n < nLen; ++n )
+ {
+ sal_uLong nIdx = rData.GetIndex( n );
+ ++pCounts[ nIdx ];
+ }
+}
+
+void Compare::SetDiscard( const CompareData& rData,
+ char* pDiscard, const sal_uLong* pCounts )
+{
+ const sal_uLong nLen = rData.GetLineCount();
+
+ // calculate Max with respect to the line count
+ sal_uLong nMax = 5;
+
+ for( sal_uLong n = nLen / 64; ( n = n >> 2 ) > 0; )
+ nMax <<= 1;
+
+ for( sal_uLong n = 0; n < nLen; ++n )
+ {
+ sal_uLong nIdx = rData.GetIndex( n );
+ if( nIdx )
+ {
+ nIdx = pCounts[ nIdx ];
+ pDiscard[ n ] = !nIdx ? 1 : nIdx > nMax ? 2 : 0;
+ }
+ else
+ pDiscard[ n ] = 0;
+ }
+}
+
+void Compare::CheckDiscard( size_t nLen, char* pDiscard )
+{
+ for( size_t n = 0; n < nLen; ++n )
+ {
+ if( 2 == pDiscard[ n ] )
+ pDiscard[n] = 0;
+ else if( pDiscard[ n ] )
+ {
+ size_t j;
+ size_t length;
+ size_t provisional = 0;
+
+ /* Find end of this run of discardable lines.
+ Count how many are provisionally discardable. */
+ for (j = n; j < nLen; j++)
+ {
+ if( !pDiscard[j] )
+ break;
+ if( 2 == pDiscard[j] )
+ ++provisional;
+ }
+
+ /* Cancel provisional discards at end, and shrink the run. */
+ while( j > n && 2 == pDiscard[j - 1] )
+ {
+ pDiscard[ --j ] = 0;
+ --provisional;
+ }
+
+ /* Now we have the length of a run of discardable lines
+ whose first and last are not provisional. */
+ length = j - n;
+
+ /* If 1/4 of the lines in the run are provisional,
+ cancel discarding of all provisional lines in the run. */
+ if (provisional * 4 > length)
+ {
+ while (j > n)
+ if (pDiscard[--j] == 2)
+ pDiscard[j] = 0;
+ }
+ else
+ {
+ size_t consec;
+ size_t minimum = 1;
+ size_t tem = length / 4;
+
+ /* MINIMUM is approximate square root of LENGTH/4.
+ A subrun of two or more provisionals can stand
+ when LENGTH is at least 16.
+ A subrun of 4 or more can stand when LENGTH >= 64. */
+ while ((tem = tem >> 2) > 0)
+ minimum *= 2;
+ minimum++;
+
+ /* Cancel any subrun of MINIMUM or more provisionals
+ within the larger run. */
+ for (j = 0, consec = 0; j < length; j++)
+ if (pDiscard[n + j] != 2)
+ consec = 0;
+ else if (minimum == ++consec)
+ /* Back up to start of subrun, to cancel it all. */
+ j -= consec;
+ else if (minimum < consec)
+ pDiscard[n + j] = 0;
+
+ /* Scan from beginning of run
+ until we find 3 or more nonprovisionals in a row
+ or until the first nonprovisional at least 8 lines in.
+ Until that point, cancel any provisionals. */
+ for (j = 0, consec = 0; j < length; j++)
+ {
+ if (j >= 8 && pDiscard[n + j] == 1)
+ break;
+ if (pDiscard[n + j] == 2)
+ {
+ consec = 0;
+ pDiscard[n + j] = 0;
+ }
+ else if (pDiscard[n + j] == 0)
+ consec = 0;
+ else
+ consec++;
+ if (consec == 3)
+ break;
+ }
+
+ /* I advances to the last line of the run. */
+ n += length - 1;
+
+ /* Same thing, from end. */
+ for (j = 0, consec = 0; j < length; j++)
+ {
+ if (j >= 8 && pDiscard[n - j] == 1)
+ break;
+ if (pDiscard[n - j] == 2)
+ {
+ consec = 0;
+ pDiscard[n - j] = 0;
+ }
+ else if (pDiscard[n - j] == 0)
+ consec = 0;
+ else
+ consec++;
+ if (consec == 3)
+ break;
+ }
+ }
+ }
+ }
+}
+
+Compare::MovedData::MovedData( CompareData& rData, const char* pDiscard )
+ : m_nCount( 0 )
+{
+ sal_uLong nLen = rData.GetLineCount();
+ sal_uLong n;
+
+ for( n = 0; n < nLen; ++n )
+ if( pDiscard[ n ] )
+ rData.SetChanged( n );
+ else
+ ++m_nCount;
+
+ if( m_nCount )
+ {
+ m_pIndex.reset( new sal_uLong[ m_nCount ] );
+ m_pLineNum.reset( new sal_uLong[ m_nCount ] );
+
+ for( n = 0, m_nCount = 0; n < nLen; ++n )
+ if( !pDiscard[ n ] )
+ {
+ m_pIndex[ m_nCount ] = rData.GetIndex( n );
+ m_pLineNum[ m_nCount++ ] = n;
+ }
+ }
+}
+
+/// Find the differing lines
+Compare::CompareSequence::CompareSequence(
+ CompareData& rD1, CompareData& rD2,
+ const MovedData& rMD1, const MovedData& rMD2 )
+ : m_rData1( rD1 ), m_rData2( rD2 ), m_rMoved1( rMD1 ), m_rMoved2( rMD2 )
+{
+ sal_uLong nSize = rMD1.GetCount() + rMD2.GetCount() + 3;
+ m_pMemory.reset( new tools::Long[ nSize * 2 ] );
+ m_pFDiag = m_pMemory.get() + ( rMD2.GetCount() + 1 );
+ m_pBDiag = m_pMemory.get() + ( nSize + rMD2.GetCount() + 1 );
+
+ Compare( 0, rMD1.GetCount(), 0, rMD2.GetCount() );
+}
+
+void Compare::CompareSequence::Compare( sal_uLong nStt1, sal_uLong nEnd1,
+ sal_uLong nStt2, sal_uLong nEnd2 )
+{
+ /* Slide down the bottom initial diagonal. */
+ while( nStt1 < nEnd1 && nStt2 < nEnd2 &&
+ m_rMoved1.GetIndex( nStt1 ) == m_rMoved2.GetIndex( nStt2 ))
+ {
+ ++nStt1;
+ ++nStt2;
+ }
+
+ /* Slide up the top initial diagonal. */
+ while( nEnd1 > nStt1 && nEnd2 > nStt2 &&
+ m_rMoved1.GetIndex( nEnd1 - 1 ) == m_rMoved2.GetIndex( nEnd2 - 1 ))
+ {
+ --nEnd1;
+ --nEnd2;
+ }
+
+ /* Handle simple cases. */
+ if( nStt1 == nEnd1 )
+ while( nStt2 < nEnd2 )
+ m_rData2.SetChanged( m_rMoved2.GetLineNum( nStt2++ ));
+
+ else if (nStt2 == nEnd2)
+ while (nStt1 < nEnd1)
+ m_rData1.SetChanged( m_rMoved1.GetLineNum( nStt1++ ));
+
+ else
+ {
+ sal_uLong c, d, b;
+
+ /* Find a point of correspondence in the middle of the files. */
+
+ d = CheckDiag( nStt1, nEnd1, nStt2, nEnd2, &c );
+ b = m_pBDiag[ std::make_signed_t<decltype(d)>(d) ];
+
+ if( 1 != c )
+ {
+ /* Use that point to split this problem into two subproblems. */
+ Compare( nStt1, b, nStt2, b - d );
+ /* This used to use f instead of b,
+ but that is incorrect!
+ It is not necessarily the case that diagonal d
+ has a snake from b to f. */
+ Compare( b, nEnd1, b - d, nEnd2 );
+ }
+ }
+}
+
+sal_uLong Compare::CompareSequence::CheckDiag( sal_uLong nStt1, sal_uLong nEnd1,
+ sal_uLong nStt2, sal_uLong nEnd2, sal_uLong* pCost )
+{
+ const tools::Long dmin = nStt1 - nEnd2; /* Minimum valid diagonal. */
+ const tools::Long dmax = nEnd1 - nStt2; /* Maximum valid diagonal. */
+ const tools::Long fmid = nStt1 - nStt2; /* Center diagonal of top-down search. */
+ const tools::Long bmid = nEnd1 - nEnd2; /* Center diagonal of bottom-up search. */
+
+ tools::Long fmin = fmid, fmax = fmid; /* Limits of top-down search. */
+ tools::Long bmin = bmid, bmax = bmid; /* Limits of bottom-up search. */
+
+ tools::Long c; /* Cost. */
+ tools::Long odd = (fmid - bmid) & 1; /* True if southeast corner is on an odd
+ diagonal with respect to the northwest. */
+
+ m_pFDiag[fmid] = nStt1;
+ m_pBDiag[bmid] = nEnd1;
+
+ for (c = 1;; ++c)
+ {
+ tools::Long d; /* Active diagonal. */
+
+ /* Extend the top-down search by an edit step in each diagonal. */
+ if (fmin > dmin)
+ m_pFDiag[--fmin - 1] = -1;
+ else
+ ++fmin;
+ if (fmax < dmax)
+ m_pFDiag[++fmax + 1] = -1;
+ else
+ --fmax;
+ for (d = fmax; d >= fmin; d -= 2)
+ {
+ tools::Long x, y, tlo = m_pFDiag[d - 1], thi = m_pFDiag[d + 1];
+
+ if (tlo >= thi)
+ x = tlo + 1;
+ else
+ x = thi;
+ y = x - d;
+ while( o3tl::make_unsigned(x) < nEnd1 && o3tl::make_unsigned(y) < nEnd2 &&
+ m_rMoved1.GetIndex( x ) == m_rMoved2.GetIndex( y ))
+ {
+ ++x;
+ ++y;
+ }
+ m_pFDiag[d] = x;
+ if( odd && bmin <= d && d <= bmax && m_pBDiag[d] <= m_pFDiag[d] )
+ {
+ *pCost = 2 * c - 1;
+ return d;
+ }
+ }
+
+ /* Similar extend the bottom-up search. */
+ if (bmin > dmin)
+ m_pBDiag[--bmin - 1] = INT_MAX;
+ else
+ ++bmin;
+ if (bmax < dmax)
+ m_pBDiag[++bmax + 1] = INT_MAX;
+ else
+ --bmax;
+ for (d = bmax; d >= bmin; d -= 2)
+ {
+ tools::Long x, y, tlo = m_pBDiag[d - 1], thi = m_pBDiag[d + 1];
+
+ if (tlo < thi)
+ x = tlo;
+ else
+ x = thi - 1;
+ y = x - d;
+ while( o3tl::make_unsigned(x) > nStt1 && o3tl::make_unsigned(y) > nStt2 &&
+ m_rMoved1.GetIndex( x - 1 ) == m_rMoved2.GetIndex( y - 1 ))
+ {
+ --x;
+ --y;
+ }
+ m_pBDiag[d] = x;
+ if (!odd && fmin <= d && d <= fmax && m_pBDiag[d] <= m_pFDiag[d])
+ {
+ *pCost = 2 * c;
+ return d;
+ }
+ }
+ }
+}
+
+namespace
+{
+ void lcl_ShiftBoundariesOneway( CompareData* const pData, CompareData const * const pOtherData)
+ {
+ sal_uLong i = 0;
+ sal_uLong j = 0;
+ sal_uLong i_end = pData->GetLineCount();
+ sal_uLong preceding = ULONG_MAX;
+ sal_uLong other_preceding = ULONG_MAX;
+
+ while (true)
+ {
+ sal_uLong start, other_start;
+
+ /* Scan forwards to find beginning of another run of changes.
+ Also keep track of the corresponding point in the other file. */
+
+ while( i < i_end && !pData->GetChanged( i ) )
+ {
+ while( pOtherData->GetChanged( j++ ))
+ /* Non-corresponding lines in the other file
+ will count as the preceding batch of changes. */
+ other_preceding = j;
+ i++;
+ }
+
+ if (i == i_end)
+ break;
+
+ start = i;
+ other_start = j;
+
+ while (true)
+ {
+ /* Now find the end of this run of changes. */
+
+ while( pData->GetChanged( ++i ))
+ ;
+
+ /* If the first changed line matches the following unchanged one,
+ and this run does not follow right after a previous run,
+ and there are no lines deleted from the other file here,
+ then classify the first changed line as unchanged
+ and the following line as changed in its place. */
+
+ /* You might ask, how could this run follow right after another?
+ Only because the previous run was shifted here. */
+
+ if( i != i_end &&
+ pData->GetIndex( start ) == pData->GetIndex( i ) &&
+ !pOtherData->GetChanged( j ) &&
+ start != preceding && other_start != other_preceding )
+ {
+ pData->SetChanged( start++, false );
+ pData->SetChanged( i );
+ /* Since one line-that-matches is now before this run
+ instead of after, we must advance in the other file
+ to keep in sync. */
+ ++j;
+ }
+ else
+ break;
+ }
+
+ preceding = i;
+ other_preceding = j;
+ }
+ }
+}
+
+void Compare::ShiftBoundaries( CompareData& rData1, CompareData& rData2 )
+{
+ lcl_ShiftBoundariesOneway(&rData1, &rData2);
+ lcl_ShiftBoundariesOneway(&rData2, &rData1);
+}
+
+sal_uLong SwCompareLine::GetHashValue() const
+{
+ sal_uLong nRet = 0;
+ switch( m_pNode->GetNodeType() )
+ {
+ case SwNodeType::Text:
+ nRet = GetTextNodeHashValue( *m_pNode->GetTextNode(), nRet );
+ break;
+
+ case SwNodeType::Table:
+ {
+ const SwNode* pEndNd = m_pNode->EndOfSectionNode();
+ SwNodeIndex aIdx( *m_pNode );
+ while( &aIdx.GetNode() != pEndNd )
+ {
+ if( aIdx.GetNode().IsTextNode() )
+ nRet = GetTextNodeHashValue( *aIdx.GetNode().GetTextNode(), nRet );
+ ++aIdx;
+ }
+ }
+ break;
+
+ case SwNodeType::Section:
+ {
+ OUString sStr( GetText() );
+ for( sal_Int32 n = 0; n < sStr.getLength(); ++n )
+ nRet = (nRet << 1) + sStr[ n ];
+ }
+ break;
+
+ case SwNodeType::Grf:
+ case SwNodeType::Ole:
+ // Fixed ID? Should never occur ...
+ break;
+ default: break;
+ }
+ return nRet;
+}
+
+const SwNode& SwCompareLine::GetEndNode() const
+{
+ const SwNode* pNd = m_pNode;
+ switch( m_pNode->GetNodeType() )
+ {
+ case SwNodeType::Table:
+ pNd = m_pNode->EndOfSectionNode();
+ break;
+
+ case SwNodeType::Section:
+ {
+ const SwSectionNode& rSNd = static_cast<const SwSectionNode&>(*m_pNode);
+ const SwSection& rSect = rSNd.GetSection();
+ if( SectionType::Content != rSect.GetType() || rSect.IsProtect() )
+ pNd = m_pNode->EndOfSectionNode();
+ }
+ break;
+ default: break;
+ }
+ return *pNd;
+}
+
+bool SwCompareLine::Compare( const SwCompareLine& rLine ) const
+{
+ return CompareNode( *m_pNode, *rLine.m_pNode );
+}
+
+namespace
+{
+ OUString SimpleTableToText(const SwNode &rNode)
+ {
+ OUStringBuffer sRet;
+ const SwNode* pEndNd = rNode.EndOfSectionNode();
+ SwNodeIndex aIdx( rNode );
+ while (&aIdx.GetNode() != pEndNd)
+ {
+ if (aIdx.GetNode().IsTextNode())
+ {
+ if (sRet.getLength())
+ {
+ sRet.append( '\n' );
+ }
+ sRet.append( aIdx.GetNode().GetTextNode()->GetExpandText(nullptr) );
+ }
+ ++aIdx;
+ }
+ return sRet.makeStringAndClear();
+ }
+}
+
+bool SwCompareLine::CompareNode( const SwNode& rDstNd, const SwNode& rSrcNd )
+{
+ if( rSrcNd.GetNodeType() != rDstNd.GetNodeType() )
+ return false;
+
+ bool bRet = false;
+
+ switch( rDstNd.GetNodeType() )
+ {
+ case SwNodeType::Text:
+ bRet = CompareTextNd( *rDstNd.GetTextNode(), *rSrcNd.GetTextNode() )
+ && ( !CmpOptions.bUseRsid || rDstNd.GetTextNode()->CompareParRsid( *rSrcNd.GetTextNode() ) );
+ break;
+
+ case SwNodeType::Table:
+ {
+ const SwTableNode& rTSrcNd = static_cast<const SwTableNode&>(rSrcNd);
+ const SwTableNode& rTDstNd = static_cast<const SwTableNode&>(rDstNd);
+
+ bRet = ( rTSrcNd.EndOfSectionIndex() - rTSrcNd.GetIndex() ) ==
+ ( rTDstNd.EndOfSectionIndex() - rTDstNd.GetIndex() );
+
+ // --> #i107826#: compare actual table content
+ if (bRet)
+ {
+ bRet = (SimpleTableToText(rSrcNd) == SimpleTableToText(rDstNd));
+ }
+ }
+ break;
+
+ case SwNodeType::Section:
+ {
+ const SwSectionNode& rSSrcNd = static_cast<const SwSectionNode&>(rSrcNd),
+ & rSDstNd = static_cast<const SwSectionNode&>(rDstNd);
+ const SwSection& rSrcSect = rSSrcNd.GetSection(),
+ & rDstSect = rSDstNd.GetSection();
+ SectionType eSrcSectType = rSrcSect.GetType(),
+ eDstSectType = rDstSect.GetType();
+ switch( eSrcSectType )
+ {
+ case SectionType::Content:
+ bRet = SectionType::Content == eDstSectType &&
+ rSrcSect.IsProtect() == rDstSect.IsProtect();
+ if( bRet && rSrcSect.IsProtect() )
+ {
+ // the only have they both the same size
+ bRet = ( rSSrcNd.EndOfSectionIndex() - rSSrcNd.GetIndex() ) ==
+ ( rSDstNd.EndOfSectionIndex() - rSDstNd.GetIndex() );
+ }
+ break;
+
+ case SectionType::ToxHeader:
+ case SectionType::ToxContent:
+ if( SectionType::ToxHeader == eDstSectType ||
+ SectionType::ToxContent == eDstSectType )
+ {
+ // the same type of TOX?
+ const SwTOXBase* pSrcTOX = rSrcSect.GetTOXBase();
+ const SwTOXBase* pDstTOX = rDstSect.GetTOXBase();
+ bRet = pSrcTOX && pDstTOX
+ && pSrcTOX->GetType() == pDstTOX->GetType()
+ && pSrcTOX->GetTitle() == pDstTOX->GetTitle()
+ && pSrcTOX->GetTypeName() == pDstTOX->GetTypeName()
+ ;
+ }
+ break;
+
+ case SectionType::DdeLink:
+ case SectionType::FileLink:
+ bRet = eSrcSectType == eDstSectType &&
+ rSrcSect.GetLinkFileName() ==
+ rDstSect.GetLinkFileName();
+ break;
+ }
+ }
+ break;
+
+ case SwNodeType::End:
+ bRet = rSrcNd.StartOfSectionNode()->GetNodeType() ==
+ rDstNd.StartOfSectionNode()->GetNodeType();
+
+ // --> #i107826#: compare actual table content
+ if (bRet && rSrcNd.StartOfSectionNode()->GetNodeType() == SwNodeType::Table)
+ {
+ bRet = CompareNode(
+ *rSrcNd.StartOfSectionNode(), *rDstNd.StartOfSectionNode());
+ }
+
+ break;
+
+ default: break;
+ }
+ return bRet;
+}
+
+OUString SwCompareLine::GetText() const
+{
+ OUString sRet;
+ switch( m_pNode->GetNodeType() )
+ {
+ case SwNodeType::Text:
+ sRet = m_pNode->GetTextNode()->GetExpandText(nullptr);
+ break;
+
+ case SwNodeType::Table:
+ {
+ sRet = "Tabelle: " + SimpleTableToText(*m_pNode);
+ }
+ break;
+
+ case SwNodeType::Section:
+ {
+ sRet = "Section - Node:";
+
+ const SwSectionNode& rSNd = static_cast<const SwSectionNode&>(*m_pNode);
+ const SwSection& rSect = rSNd.GetSection();
+ switch( rSect.GetType() )
+ {
+ case SectionType::Content:
+ if( rSect.IsProtect() )
+ sRet += OUString::number(
+ sal_Int32(rSNd.EndOfSectionIndex() - rSNd.GetIndex()) );
+ break;
+
+ case SectionType::ToxHeader:
+ case SectionType::ToxContent:
+ {
+ const SwTOXBase* pTOX = rSect.GetTOXBase();
+ if( pTOX )
+ sRet += pTOX->GetTitle() + pTOX->GetTypeName() +
+ OUString::number(pTOX->GetType());
+ }
+ break;
+
+ case SectionType::DdeLink:
+ case SectionType::FileLink:
+ sRet += rSect.GetLinkFileName();
+ break;
+ }
+ }
+ break;
+
+ case SwNodeType::Grf:
+ sRet = "Grafik - Node:";
+ break;
+ case SwNodeType::Ole:
+ sRet = "OLE - Node:";
+ break;
+ default: break;
+ }
+ return sRet;
+}
+
+sal_uLong SwCompareLine::GetTextNodeHashValue( const SwTextNode& rNd, sal_uLong nVal )
+{
+ OUString sStr( rNd.GetExpandText(nullptr) );
+ for( sal_Int32 n = 0; n < sStr.getLength(); ++n )
+ nVal = (nVal << 1 ) + sStr[ n ];
+ return nVal;
+}
+
+bool SwCompareLine::CompareTextNd( const SwTextNode& rDstNd,
+ const SwTextNode& rSrcNd )
+{
+ bool bRet = false;
+ // Very simple at first
+ if( rDstNd.GetText() == rSrcNd.GetText() )
+ {
+ // The text is the same, but are the "special attributes" (0xFF) also the same?
+ bRet = true;
+ }
+ return bRet;
+}
+
+bool SwCompareLine::ChangesInLine( const SwCompareLine& rLine,
+ std::unique_ptr<SwPaM>& rpInsRing, std::unique_ptr<SwPaM>& rpDelRing ) const
+{
+ bool bRet = false;
+
+ // Only compare textnodes
+ if( SwNodeType::Text == m_pNode->GetNodeType() &&
+ SwNodeType::Text == rLine.GetNode().GetNodeType() )
+ {
+ SwTextNode& rDstNd = *const_cast<SwTextNode*>(m_pNode->GetTextNode());
+ const SwTextNode& rSrcNd = *rLine.GetNode().GetTextNode();
+ SwDoc& rDstDoc = rDstNd.GetDoc();
+
+ int nLcsLen = 0;
+
+ int nDstLen = rDstNd.GetText().getLength();
+ int nSrcLen = rSrcNd.GetText().getLength();
+
+ int nMinLen = std::min( nDstLen , nSrcLen );
+ int nAvgLen = ( nDstLen + nSrcLen )/2;
+
+ std::vector<int> aLcsDst( nMinLen + 1 );
+ std::vector<int> aLcsSrc( nMinLen + 1 );
+
+ if( CmpOptions.eCmpMode == SwCompareMode::ByWord )
+ {
+ std::vector<int> aTmpLcsDst( nMinLen + 1 );
+ std::vector<int> aTmpLcsSrc( nMinLen + 1 );
+
+ WordArrayComparator aCmp( &rDstNd, &rSrcNd );
+
+ LgstCommonSubseq aSeq( aCmp );
+
+ nLcsLen = aSeq.Find( aTmpLcsDst.data(), aTmpLcsSrc.data() );
+
+ if( CmpOptions.nIgnoreLen )
+ {
+ nLcsLen = CommonSubseq::IgnoreIsolatedPieces( aTmpLcsDst.data(), aTmpLcsSrc.data(),
+ aCmp.GetLen1(), aCmp.GetLen2(),
+ nLcsLen, CmpOptions.nIgnoreLen );
+ }
+
+ nLcsLen = aCmp.GetCharSequence( aTmpLcsDst.data(), aTmpLcsSrc.data(),
+ aLcsDst.data(), aLcsSrc.data(), nLcsLen );
+ }
+ else
+ {
+ CharArrayComparator aCmp( &rDstNd, &rSrcNd );
+ LgstCommonSubseq aSeq( aCmp );
+
+ nLcsLen = aSeq.Find( aLcsDst.data(), aLcsSrc.data() );
+
+ if( CmpOptions.nIgnoreLen )
+ {
+ nLcsLen = CommonSubseq::IgnoreIsolatedPieces( aLcsDst.data(), aLcsSrc.data(), nDstLen,
+ nSrcLen, nLcsLen,
+ CmpOptions.nIgnoreLen );
+ }
+ }
+
+ // find the sum of the squares of the continuous substrings
+ int nSqSum = 0;
+ int nCnt = 1;
+ for( int i = 0; i < nLcsLen; i++ )
+ {
+ if( i != nLcsLen - 1 && aLcsDst[i] + 1 == aLcsDst[i + 1]
+ && aLcsSrc[i] + 1 == aLcsSrc[i + 1] )
+ {
+ nCnt++;
+ }
+ else
+ {
+ nSqSum += nCnt*nCnt;
+ nCnt = 1;
+ }
+ }
+
+ // Don't compare if there aren't enough similarities
+ if ( nAvgLen >= 8 && nSqSum*32 < nAvgLen*nAvgLen )
+ {
+ return false;
+ }
+
+ // Show the differences
+ int nSkip = 0;
+ for( int i = 0; i <= nLcsLen; i++ )
+ {
+ int nDstFrom = i ? (aLcsDst[i - 1] + 1) : 0;
+ int nDstTo = ( i == nLcsLen ) ? nDstLen : aLcsDst[i];
+ int nSrcFrom = i ? (aLcsSrc[i - 1] + 1) : 0;
+ int nSrcTo = ( i == nLcsLen ) ? nSrcLen : aLcsSrc[i];
+
+ SwPaM aPam( rDstNd, nDstTo + nSkip );
+
+ if ( nDstFrom < nDstTo )
+ {
+ SwPaM* pTmp = new SwPaM( *aPam.GetPoint(), rpInsRing.get() );
+ if( !rpInsRing )
+ rpInsRing.reset(pTmp);
+ pTmp->SetMark();
+ pTmp->GetMark()->SetContent(nDstFrom + nSkip);
+ }
+
+ if ( nSrcFrom < nSrcTo )
+ {
+ bool bUndo = rDstDoc.GetIDocumentUndoRedo().DoesUndo();
+ rDstDoc.GetIDocumentUndoRedo().DoUndo( false );
+ SwPaM aCpyPam( rSrcNd, nSrcFrom );
+ aCpyPam.SetMark();
+ aCpyPam.GetPoint()->SetContent(nSrcTo);
+ aCpyPam.GetDoc().getIDocumentContentOperations().CopyRange( aCpyPam, *aPam.GetPoint(),
+ SwCopyFlags::CheckPosInFly);
+ rDstDoc.GetIDocumentUndoRedo().DoUndo( bUndo );
+
+ SwPaM* pTmp = new SwPaM( *aPam.GetPoint(), rpDelRing.get() );
+ if( !rpDelRing )
+ rpDelRing.reset(pTmp);
+
+ pTmp->SetMark();
+ pTmp->GetMark()->SetContent(nDstTo + nSkip);
+ nSkip += nSrcTo - nSrcFrom;
+
+ if( rpInsRing )
+ {
+ SwPaM* pCorr = rpInsRing->GetPrev();
+ if( *pCorr->GetPoint() == *pTmp->GetPoint() )
+ *pCorr->GetPoint() = *pTmp->GetMark();
+ }
+ }
+ }
+
+ bRet = true;
+ }
+
+ return bRet;
+}
+
+SwNodeOffset CompareData::NextIdx( const SwNode* pNd )
+{
+ if( pNd->IsStartNode() )
+ {
+ if( pNd->IsTableNode() )
+ pNd = pNd->EndOfSectionNode();
+ else
+ {
+ const SwSectionNode* pSNd = pNd->GetSectionNode();
+ if( pSNd &&
+ ( SectionType::Content != pSNd->GetSection().GetType() ||
+ pSNd->GetSection().IsProtect() ) )
+ pNd = pNd->EndOfSectionNode();
+ }
+ }
+ return pNd->GetIndex() + 1;
+}
+
+SwNodeOffset CompareData::PrevIdx( const SwNode* pNd )
+{
+ if( pNd->IsEndNode() )
+ {
+ if( pNd->StartOfSectionNode()->IsTableNode() )
+ pNd = pNd->StartOfSectionNode();
+ else
+ {
+ const SwSectionNode* pSNd = pNd->StartOfSectionNode()->GetSectionNode();
+ if( pSNd &&
+ ( SectionType::Content != pSNd->GetSection().GetType() ||
+ pSNd->GetSection().IsProtect() ) )
+ pNd = pNd->StartOfSectionNode();
+ }
+ }
+ return pNd->GetIndex() - 1;
+}
+
+void CompareData::CheckRanges( CompareData& rData )
+{
+ const SwNodes& rSrcNds = rData.m_rDoc.GetNodes();
+ const SwNodes& rDstNds = m_rDoc.GetNodes();
+
+ const SwNode& rSrcEndNd = rData.GetEndOfContent();
+ const SwNode& rDstEndNd = GetEndOfContent();
+
+ SwNodeOffset nSrcSttIdx = NextIdx( rSrcEndNd.StartOfSectionNode() );
+ SwNodeOffset nSrcEndIdx = rSrcEndNd.GetIndex();
+
+ SwNodeOffset nDstSttIdx = NextIdx( rDstEndNd.StartOfSectionNode() );
+ SwNodeOffset nDstEndIdx = rDstEndNd.GetIndex();
+
+ while( nSrcSttIdx < nSrcEndIdx && nDstSttIdx < nDstEndIdx )
+ {
+ const SwNode* pSrcNd = rSrcNds[ nSrcSttIdx ];
+ const SwNode* pDstNd = rDstNds[ nDstSttIdx ];
+ if( !SwCompareLine::CompareNode( *pSrcNd, *pDstNd ))
+ break;
+
+ nSrcSttIdx = NextIdx( pSrcNd );
+ nDstSttIdx = NextIdx( pDstNd );
+ }
+
+ nSrcEndIdx = PrevIdx( &rSrcEndNd );
+ nDstEndIdx = PrevIdx( &rDstEndNd );
+ while( nSrcSttIdx < nSrcEndIdx && nDstSttIdx < nDstEndIdx )
+ {
+ const SwNode* pSrcNd = rSrcNds[ nSrcEndIdx ];
+ const SwNode* pDstNd = rDstNds[ nDstEndIdx ];
+ if( !SwCompareLine::CompareNode( *pSrcNd, *pDstNd ))
+ break;
+
+ nSrcEndIdx = PrevIdx( pSrcNd );
+ nDstEndIdx = PrevIdx( pDstNd );
+ }
+
+ while( nSrcSttIdx <= nSrcEndIdx )
+ {
+ const SwNode* pNd = rSrcNds[ nSrcSttIdx ];
+ rData.InsertLine( SwCompareLine( *pNd ) );
+ nSrcSttIdx = NextIdx( pNd );
+ }
+
+ while( nDstSttIdx <= nDstEndIdx )
+ {
+ const SwNode* pNd = rDstNds[ nDstSttIdx ];
+ InsertLine( SwCompareLine( *pNd ) );
+ nDstSttIdx = NextIdx( pNd );
+ }
+}
+
+void CompareData::ShowInsert( sal_uLong nStt, sal_uLong nEnd )
+{
+ SwPaM* pTmp = new SwPaM( GetLine( nStt ).GetNode(), 0,
+ GetLine( nEnd-1 ).GetEndNode(), 0,
+ m_pInsertRing.get() );
+ if( !m_pInsertRing )
+ m_pInsertRing.reset( pTmp );
+
+ // #i65201#: These SwPaMs are calculated smaller than needed, see comment below
+}
+
+void CompareData::ShowDelete(
+ const CompareData& rData,
+ sal_uLong nStt,
+ sal_uLong nEnd,
+ sal_uLong nInsPos )
+{
+ SwNodeRange aRg(
+ rData.GetLine( nStt ).GetNode(), SwNodeOffset(0),
+ rData.GetLine( nEnd-1 ).GetEndNode(), SwNodeOffset(1) );
+
+ SwNodeOffset nOffset(0);
+ std::optional<SwCompareLine> xLine;
+ if( nInsPos >= 1 )
+ {
+ if( GetLineCount() == nInsPos )
+ {
+ xLine = GetLine( nInsPos-1 );
+ nOffset = SwNodeOffset(1);
+ }
+ else
+ xLine = GetLine( nInsPos );
+ }
+
+ const SwNode* pLineNd;
+ if( xLine )
+ {
+ if( nOffset )
+ pLineNd = &xLine->GetEndNode();
+ else
+ pLineNd = &xLine->GetNode();
+ }
+ else
+ {
+ pLineNd = &GetEndOfContent();
+ nOffset = SwNodeOffset(0);
+ }
+
+ SwNodeIndex aInsPos( *pLineNd, nOffset );
+ SwNodeIndex aSavePos( aInsPos, -1 );
+
+ rData.m_rDoc.GetDocumentContentOperationsManager().CopyWithFlyInFly(aRg, aInsPos.GetNode());
+ m_rDoc.getIDocumentState().SetModified();
+ ++aSavePos;
+
+ // #i65201#: These SwPaMs are calculated when the (old) delete-redlines are hidden,
+ // they will be inserted when the delete-redlines are shown again.
+ // To avoid unwanted insertions of delete-redlines into these new redlines, what happens
+ // especially at the end of the document, I reduce the SwPaM by one node.
+ // Before the new redlines are inserted, they have to expand again.
+ SwPaM* pTmp = new SwPaM( aSavePos.GetNode(), aInsPos.GetNode(), SwNodeOffset(0), SwNodeOffset(-1), m_pDelRing.get() );
+ if( !m_pDelRing )
+ m_pDelRing.reset(pTmp);
+
+ if( m_pInsertRing )
+ {
+ SwPaM* pCorr = m_pInsertRing->GetPrev();
+ if( *pCorr->GetPoint() == *pTmp->GetPoint() )
+ {
+ SwNodeIndex aTmpPos( pTmp->GetMark()->GetNode(), -1 );
+ pCorr->GetPoint()->Assign( aTmpPos );
+ }
+ }
+}
+
+void CompareData::CheckForChangesInLine( const CompareData& rData,
+ sal_uLong nStt, sal_uLong nEnd,
+ sal_uLong nThisStt, sal_uLong nThisEnd )
+{
+ LineArrayComparator aCmp( *this, rData, nThisStt, nThisEnd,
+ nStt, nEnd );
+
+ int nMinLen = std::min( aCmp.GetLen1(), aCmp.GetLen2() );
+ std::unique_ptr<int[]> pLcsDst(new int[ nMinLen ]);
+ std::unique_ptr<int[]> pLcsSrc(new int[ nMinLen ]);
+
+ FastCommonSubseq subseq( aCmp );
+ int nLcsLen = subseq.Find( pLcsDst.get(), pLcsSrc.get() );
+ for (int i = 0; i <= nLcsLen; i++)
+ {
+ // Beginning of inserted lines (inclusive)
+ int nDstFrom = i ? pLcsDst[i - 1] + 1 : 0;
+ // End of inserted lines (exclusive)
+ int nDstTo = ( i == nLcsLen ) ? aCmp.GetLen1() : pLcsDst[i];
+ // Beginning of deleted lines (inclusive)
+ int nSrcFrom = i ? pLcsSrc[i - 1] + 1 : 0;
+ // End of deleted lines (exclusive)
+ int nSrcTo = ( i == nLcsLen ) ? aCmp.GetLen2() : pLcsSrc[i];
+
+ if( i )
+ {
+ const SwCompareLine aDstLn = GetLine( nThisStt + nDstFrom - 1 );
+ const SwCompareLine aSrcLn = rData.GetLine( nStt + nSrcFrom - 1 );
+
+ // Show differences in detail for lines that
+ // were matched as only slightly different
+ if( !aDstLn.ChangesInLine( aSrcLn, m_pInsertRing, m_pDelRing ) )
+ {
+ ShowInsert( nThisStt + nDstFrom - 1, nThisStt + nDstFrom );
+ ShowDelete( rData, nStt + nSrcFrom - 1, nStt + nSrcFrom,
+ nThisStt + nDstFrom );
+ }
+ }
+
+ // Lines missing from source are inserted
+ if( nDstFrom != nDstTo )
+ {
+ ShowInsert( nThisStt + nDstFrom, nThisStt + nDstTo );
+ }
+
+ // Lines missing from destination are deleted
+ if( nSrcFrom != nSrcTo )
+ {
+ ShowDelete( rData, nStt + nSrcFrom, nStt + nSrcTo, nThisStt + nDstTo );
+ }
+ }
+}
+
+void CompareData::SetRedlinesToDoc( bool bUseDocInfo )
+{
+ SwPaM* pTmp = m_pDelRing.get();
+
+ // get the Author / TimeStamp from the "other" document info
+ std::size_t nAuthor = m_rDoc.getIDocumentRedlineAccess().GetRedlineAuthor();
+ DateTime aTimeStamp( DateTime::SYSTEM );
+ SwDocShell *pDocShell(m_rDoc.GetDocShell());
+ OSL_ENSURE(pDocShell, "no SwDocShell");
+ if (pDocShell) {
+ uno::Reference<document::XDocumentPropertiesSupplier> xDPS(
+ pDocShell->GetModel(), uno::UNO_QUERY_THROW);
+ uno::Reference<document::XDocumentProperties> xDocProps(
+ xDPS->getDocumentProperties());
+ OSL_ENSURE(xDocProps.is(), "Doc has no DocumentProperties");
+
+ if( bUseDocInfo && xDocProps.is() ) {
+ OUString aTmp( 1 == xDocProps->getEditingCycles()
+ ? xDocProps->getAuthor()
+ : xDocProps->getModifiedBy() );
+ util::DateTime uDT( 1 == xDocProps->getEditingCycles()
+ ? xDocProps->getCreationDate()
+ : xDocProps->getModificationDate() );
+
+ if( !aTmp.isEmpty() )
+ {
+ nAuthor = m_rDoc.getIDocumentRedlineAccess().InsertRedlineAuthor( aTmp );
+ aTimeStamp = DateTime(uDT);
+ }
+ }
+ }
+
+ if( pTmp )
+ {
+ SwRedlineData aRedlnData( RedlineType::Delete, nAuthor, aTimeStamp, 0,
+ OUString(), nullptr );
+ do {
+ // #i65201#: Expand again, see comment above.
+ if( pTmp->GetPoint()->GetContentIndex() == 0 )
+ {
+ pTmp->GetPoint()->Adjust(SwNodeOffset(1));
+ }
+ // #i101009#
+ // prevent redlines that end on structural end node
+ if (& GetEndOfContent() ==
+ & pTmp->GetPoint()->GetNode())
+ {
+ pTmp->GetPoint()->Adjust(SwNodeOffset(-1));
+ SwContentNode *const pContentNode( pTmp->GetPointContentNode() );
+ if( pContentNode )
+ pTmp->GetPoint()->SetContent( pContentNode->Len() );
+ // tdf#106218 try to avoid losing a paragraph break here:
+ if (pTmp->GetMark()->GetContentIndex() == 0)
+ {
+ SwNodeIndex const prev(pTmp->GetMark()->GetNode(), -1);
+ if (prev.GetNode().IsTextNode())
+ {
+ pTmp->GetMark()->Assign(
+ *prev.GetNode().GetTextNode(),
+ prev.GetNode().GetTextNode()->Len());
+ }
+ }
+ }
+
+ m_rDoc.getIDocumentRedlineAccess().DeleteRedline( *pTmp, false, RedlineType::Any );
+
+ if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ m_rDoc.GetIDocumentUndoRedo().AppendUndo(
+ std::make_unique<SwUndoCompDoc>( *pTmp, false ));
+ }
+ m_rDoc.getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( aRedlnData, *pTmp ), true );
+
+ } while( m_pDelRing.get() != ( pTmp = pTmp->GetNext()) );
+ }
+
+ pTmp = m_pInsertRing.get();
+ if( !pTmp )
+ return;
+
+ do {
+ if( pTmp->GetPoint()->GetContentIndex() == 0 )
+ {
+ pTmp->GetPoint()->Adjust(SwNodeOffset(1));
+ }
+ // #i101009#
+ // prevent redlines that end on structural end node
+ if (& GetEndOfContent() ==
+ & pTmp->GetPoint()->GetNode())
+ {
+ pTmp->GetPoint()->Adjust(SwNodeOffset(-1));
+ SwContentNode *const pContentNode( pTmp->GetPointContentNode() );
+ if( pContentNode )
+ pTmp->GetPoint()->SetContent( pContentNode->Len() );
+ // tdf#106218 try to avoid losing a paragraph break here:
+ if (pTmp->GetMark()->GetContentIndex() == 0)
+ {
+ SwNodeIndex const prev(pTmp->GetMark()->GetNode(), -1);
+ if (prev.GetNode().IsTextNode())
+ {
+ pTmp->GetMark()->Assign(
+ *prev.GetNode().GetTextNode(),
+ prev.GetNode().GetTextNode()->Len());
+ }
+ }
+ }
+ } while( m_pInsertRing.get() != ( pTmp = pTmp->GetNext()) );
+ SwRedlineData aRedlnData( RedlineType::Insert, nAuthor, aTimeStamp, 0,
+ OUString(), nullptr );
+
+ // combine consecutive
+ if( pTmp->GetNext() != m_pInsertRing.get() )
+ {
+ do {
+ // coverity[deref_arg] - the SwPaM delete moves a new entry into GetNext()
+ SwPosition& rSttEnd = *pTmp->End(),
+ & rEndStt = *pTmp->GetNext()->Start();
+ const SwContentNode* pCNd;
+ if( rSttEnd == rEndStt ||
+ (!rEndStt.GetContentIndex() &&
+ rEndStt.GetNodeIndex() - 1 == rSttEnd.GetNodeIndex() &&
+ nullptr != ( pCNd = rSttEnd.GetNode().GetContentNode() ) &&
+ rSttEnd.GetContentIndex() == pCNd->Len()))
+ {
+ if( pTmp->GetNext() == m_pInsertRing.get() )
+ {
+ // are consecutive, so combine
+ rEndStt = *pTmp->Start();
+ delete pTmp;
+ pTmp = m_pInsertRing.get();
+ }
+ else
+ {
+ // are consecutive, so combine
+ rSttEnd = *pTmp->GetNext()->End();
+ delete pTmp->GetNext();
+ }
+ }
+ else
+ pTmp = pTmp->GetNext();
+ } while( m_pInsertRing.get() != pTmp );
+ }
+
+ do {
+ // coverity[deref_arg] - pTmp is valid here
+ if (IDocumentRedlineAccess::AppendResult::APPENDED ==
+ m_rDoc.getIDocumentRedlineAccess().AppendRedline(
+ new SwRangeRedline(aRedlnData, *pTmp), true) &&
+ m_rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ m_rDoc.GetIDocumentUndoRedo().AppendUndo(
+ std::make_unique<SwUndoCompDoc>( *pTmp, true ));
+ }
+ } while( m_pInsertRing.get() != ( pTmp = pTmp->GetNext()) );
+}
+
+typedef std::shared_ptr<CompareData> CompareDataPtr;
+typedef std::pair<CompareDataPtr, CompareDataPtr> CompareDataPtrPair;
+typedef std::vector<CompareDataPtrPair> Comparators;
+
+namespace
+{
+ Comparators buildComparators(SwDoc &rSrcDoc, SwDoc &rDestDoc)
+ {
+ Comparators aComparisons;
+ //compare main text
+ aComparisons.emplace_back(std::make_shared<CompareMainText>(rSrcDoc, true),
+ std::make_shared<CompareMainText>(rDestDoc, true));
+
+ //if we have the same number of frames then try to compare within them
+ const sw::SpzFrameFormats* pSrcFrameFormats = rSrcDoc.GetSpzFrameFormats();
+ const sw::SpzFrameFormats* pDestFrameFormats = rDestDoc.GetSpzFrameFormats();
+ if (pSrcFrameFormats->size() == pDestFrameFormats->size())
+ {
+ for(sw::FrameFormats<sw::SpzFrameFormat*>::size_type i = 0; i < pSrcFrameFormats->size(); ++i)
+ {
+ const sw::SpzFrameFormat& rSrcFormat = *(*pSrcFrameFormats)[i];
+ const sw::SpzFrameFormat& rDestFormat = *(*pDestFrameFormats)[i];
+ const SwNodeIndex* pSrcIdx = rSrcFormat.GetContent().GetContentIdx();
+ const SwNodeIndex* pDestIdx = rDestFormat.GetContent().GetContentIdx();
+ if (!pSrcIdx && !pDestIdx)
+ continue;
+ if (!pSrcIdx || !pDestIdx)
+ break;
+ const SwNode* pSrcNode = pSrcIdx->GetNode().EndOfSectionNode();
+ const SwNode* pDestNode = pDestIdx->GetNode().EndOfSectionNode();
+ if (!pSrcNode && !pDestNode)
+ continue;
+ if (!pSrcNode || !pDestNode)
+ break;
+ if (pSrcIdx->GetNodes()[pSrcIdx->GetIndex() + 1]->IsNoTextNode()
+ || pDestIdx->GetNodes()[pDestIdx->GetIndex() + 1]->IsNoTextNode())
+ {
+ continue; // tdf#125660 don't redline GrfNode/OLENode
+ }
+ aComparisons.emplace_back(std::make_shared<CompareFrameFormatText>(rSrcDoc, *pSrcIdx),
+ std::make_shared<CompareFrameFormatText>(rDestDoc, *pDestIdx));
+ }
+ }
+ return aComparisons;
+ }
+}
+
+// Returns (the difference count?) if something is different
+tools::Long SwDoc::CompareDoc( const SwDoc& rDoc )
+{
+ if( &rDoc == this )
+ return 0;
+
+ tools::Long nRet = 0;
+
+ // Get comparison options
+ CmpOptions.eCmpMode = SW_MOD()->GetCompareMode();
+ if( CmpOptions.eCmpMode == SwCompareMode::Auto )
+ {
+ if( getRsidRoot() == rDoc.getRsidRoot() )
+ {
+ CmpOptions.eCmpMode = SwCompareMode::ByChar;
+ CmpOptions.bUseRsid = true;
+ CmpOptions.nIgnoreLen = 2;
+ }
+ else
+ {
+ CmpOptions.eCmpMode = SwCompareMode::ByWord;
+ CmpOptions.bUseRsid = false;
+ CmpOptions.nIgnoreLen = 3;
+ }
+ }
+ else
+ {
+ CmpOptions.bUseRsid = getRsidRoot() == rDoc.getRsidRoot() && SW_MOD()->IsUseRsid();
+ CmpOptions.nIgnoreLen = SW_MOD()->IsIgnorePieces() ? SW_MOD()->GetPieceLen() : 0;
+ }
+
+ GetIDocumentUndoRedo().StartUndo(SwUndoId::EMPTY, nullptr);
+ bool bDocWasModified = getIDocumentState().IsModified();
+ SwDoc& rSrcDoc = const_cast<SwDoc&>(rDoc);
+ bool bSrcModified = rSrcDoc.getIDocumentState().IsModified();
+
+ RedlineFlags eSrcRedlMode = rSrcDoc.getIDocumentRedlineAccess().GetRedlineFlags();
+ rSrcDoc.getIDocumentRedlineAccess().SetRedlineFlags( RedlineFlags::ShowInsert );
+ getIDocumentRedlineAccess().SetRedlineFlags(RedlineFlags::On | RedlineFlags::ShowInsert);
+
+ Comparators aComparisons(buildComparators(rSrcDoc, *this));
+
+ for (auto& a : aComparisons)
+ {
+ CompareData& rD0 = *a.first;
+ CompareData& rD1 = *a.second;
+ rD1.CompareLines( rD0 );
+ nRet |= rD1.ShowDiffs( rD0 );
+ }
+
+ if( nRet )
+ {
+ getIDocumentRedlineAccess().SetRedlineFlags(RedlineFlags::On |
+ RedlineFlags::ShowInsert | RedlineFlags::ShowDelete);
+
+ for (auto& a : aComparisons)
+ {
+ CompareData& rD1 = *a.second;
+ rD1.SetRedlinesToDoc( !bDocWasModified );
+ }
+ getIDocumentState().SetModified();
+ }
+
+ rSrcDoc.getIDocumentRedlineAccess().SetRedlineFlags( eSrcRedlMode );
+ getIDocumentRedlineAccess().SetRedlineFlags(RedlineFlags::ShowInsert | RedlineFlags::ShowDelete);
+
+ if( !bSrcModified )
+ rSrcDoc.getIDocumentState().ResetModified();
+
+ GetIDocumentUndoRedo().EndUndo(SwUndoId::EMPTY, nullptr);
+
+ return nRet;
+}
+
+namespace
+{
+ struct SaveMergeRedline
+ {
+ const SwRangeRedline* pSrcRedl;
+ SwRangeRedline* pDestRedl;
+ SaveMergeRedline( const SwNode& rDstNd, const SwRangeRedline& rSrcRedl);
+ sal_uInt16 InsertRedline(SwPaM* pLastDestRedline);
+ };
+}
+
+SaveMergeRedline::SaveMergeRedline( const SwNode& rDstNd,
+ const SwRangeRedline& rSrcRedl)
+ : pSrcRedl( &rSrcRedl )
+{
+ SwPosition aPos( rDstNd );
+
+ const SwPosition* pStt = rSrcRedl.Start();
+ if( rDstNd.IsContentNode() )
+ aPos.SetContent( pStt->GetContentIndex() );
+ pDestRedl = new SwRangeRedline( rSrcRedl.GetRedlineData(), aPos );
+
+ if( RedlineType::Delete != pDestRedl->GetType() )
+ return;
+
+ // mark the area as deleted
+ const SwPosition* pEnd = rSrcRedl.End();
+
+ pDestRedl->SetMark();
+ pDestRedl->GetPoint()->Adjust( pEnd->GetNodeIndex() -
+ pStt->GetNodeIndex() );
+ if( pDestRedl->GetPointContentNode() )
+ pDestRedl->GetPoint()->SetContent( pEnd->GetContentIndex() );
+}
+
+sal_uInt16 SaveMergeRedline::InsertRedline(SwPaM* pLastDestRedline)
+{
+ sal_uInt16 nIns = 0;
+ SwDoc& rDoc = pDestRedl->GetDoc();
+
+ if( RedlineType::Insert == pDestRedl->GetType() )
+ {
+ // the part was inserted so copy it from the SourceDoc
+ ::sw::UndoGuard const undoGuard(rDoc.GetIDocumentUndoRedo());
+
+ SwNodeIndex aSaveNd( pDestRedl->GetPoint()->GetNode(), -1 );
+ const sal_Int32 nSaveCnt = pDestRedl->GetPoint()->GetContentIndex();
+
+ RedlineFlags eOld = rDoc.getIDocumentRedlineAccess().GetRedlineFlags();
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern(eOld | RedlineFlags::Ignore);
+
+ pSrcRedl->GetDoc().getIDocumentContentOperations().CopyRange(
+ *const_cast<SwPaM*>(static_cast<const SwPaM*>(pSrcRedl)),
+ *pDestRedl->GetPoint(), SwCopyFlags::CheckPosInFly);
+
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
+
+ pDestRedl->SetMark();
+ ++aSaveNd;
+ pDestRedl->GetMark()->Assign( aSaveNd, nSaveCnt );
+
+ if( pLastDestRedline && *pLastDestRedline->GetPoint() == *pDestRedl->GetPoint() )
+ *pLastDestRedline->GetPoint() = *pDestRedl->GetMark();
+ }
+ else
+ {
+ //JP 21.09.98: Bug 55909
+ // If there already is a deleted or inserted one at the same position, we have to split it!
+ SwPosition* pDStt = pDestRedl->GetMark(),
+ * pDEnd = pDestRedl->GetPoint();
+ SwRedlineTable::size_type n = 0;
+
+ // find the first redline for StartPos
+ if( !rDoc.getIDocumentRedlineAccess().GetRedline( *pDStt, &n ) && n )
+ --n;
+
+ const SwRedlineTable& rRedlineTable = rDoc.getIDocumentRedlineAccess().GetRedlineTable();
+ for( ; n < rRedlineTable.size(); ++n )
+ {
+ SwRangeRedline* pRedl = rRedlineTable[ n ];
+ SwPosition* pRStt = pRedl->Start(),
+ * pREnd = pRedl->End();
+ if( RedlineType::Delete == pRedl->GetType() ||
+ RedlineType::Insert == pRedl->GetType() )
+ {
+ SwComparePosition eCmpPos = ComparePosition( *pDStt, *pDEnd, *pRStt, *pREnd );
+ switch( eCmpPos )
+ {
+ case SwComparePosition::CollideStart:
+ case SwComparePosition::Behind:
+ break;
+
+ case SwComparePosition::Inside:
+ case SwComparePosition::Equal:
+ delete pDestRedl;
+ pDestRedl = nullptr;
+ [[fallthrough]];
+
+ case SwComparePosition::CollideEnd:
+ case SwComparePosition::Before:
+ n = rRedlineTable.size();
+ break;
+
+ case SwComparePosition::Outside:
+ assert(pDestRedl && "is this actually impossible");
+ if (pDestRedl)
+ {
+ SwRangeRedline* pCpyRedl = new SwRangeRedline(
+ pDestRedl->GetRedlineData(), *pDStt );
+ pCpyRedl->SetMark();
+ *pCpyRedl->GetPoint() = *pRStt;
+
+ std::unique_ptr<SwUndoCompDoc> pUndo;
+ if (rDoc.GetIDocumentUndoRedo().DoesUndo())
+ pUndo.reset(new SwUndoCompDoc( *pCpyRedl ));
+
+ // now modify doc: append redline, undo (and count)
+ rDoc.getIDocumentRedlineAccess().AppendRedline( pCpyRedl, true );
+ if( pUndo )
+ {
+ rDoc.GetIDocumentUndoRedo().AppendUndo(std::move(pUndo));
+ }
+ ++nIns;
+
+ *pDStt = *pREnd;
+
+ // we should start over now
+ n = SwRedlineTable::npos;
+ }
+ break;
+
+ case SwComparePosition::OverlapBefore:
+ *pDEnd = *pRStt;
+ break;
+
+ case SwComparePosition::OverlapBehind:
+ *pDStt = *pREnd;
+ break;
+ }
+ }
+ else if( *pDEnd <= *pRStt )
+ break;
+ }
+
+ }
+
+ if( pDestRedl )
+ {
+ std::unique_ptr<SwUndoCompDoc> pUndo;
+ if (rDoc.GetIDocumentUndoRedo().DoesUndo())
+ pUndo.reset(new SwUndoCompDoc( *pDestRedl ));
+
+ // now modify doc: append redline, undo (and count)
+ IDocumentRedlineAccess::AppendResult const result(
+ rDoc.getIDocumentRedlineAccess().AppendRedline(pDestRedl, true));
+ if( pUndo )
+ {
+ rDoc.GetIDocumentUndoRedo().AppendUndo( std::move(pUndo) );
+ }
+ ++nIns;
+
+ // if AppendRedline has deleted our redline, we may not keep a
+ // reference to it
+ if (IDocumentRedlineAccess::AppendResult::APPENDED != result)
+ pDestRedl = nullptr;
+ }
+ return nIns;
+}
+
+/// Merge two documents
+tools::Long SwDoc::MergeDoc( const SwDoc& rDoc )
+{
+ if( &rDoc == this )
+ return 0;
+
+ tools::Long nRet = 0;
+
+ GetIDocumentUndoRedo().StartUndo(SwUndoId::EMPTY, nullptr);
+
+ SwDoc& rSrcDoc = const_cast<SwDoc&>(rDoc);
+ bool bSrcModified = rSrcDoc.getIDocumentState().IsModified();
+
+ RedlineFlags eSrcRedlMode = rSrcDoc.getIDocumentRedlineAccess().GetRedlineFlags();
+ rSrcDoc.getIDocumentRedlineAccess().SetRedlineFlags( RedlineFlags::ShowDelete );
+ getIDocumentRedlineAccess().SetRedlineFlags( RedlineFlags::ShowDelete );
+
+ CompareMainText aD0(rSrcDoc, false);
+ CompareMainText aD1(*this, false);
+ aD1.CompareLines( aD0 );
+ if( !aD1.HasDiffs( aD0 ) )
+ {
+ // we want to get all redlines from the SourceDoc
+
+ // look for all insert redlines from the SourceDoc and determine their position in the DestDoc
+ std::vector<SaveMergeRedline> vRedlines;
+ const SwRedlineTable& rSrcRedlTable = rSrcDoc.getIDocumentRedlineAccess().GetRedlineTable();
+ SwNodeOffset nEndOfExtra = rSrcDoc.GetNodes().GetEndOfExtras().GetIndex();
+ SwNodeOffset nMyEndOfExtra = GetNodes().GetEndOfExtras().GetIndex();
+ for(const SwRangeRedline* pRedl : rSrcRedlTable)
+ {
+ SwNodeOffset nNd = pRedl->GetPoint()->GetNodeIndex();
+ RedlineType eType = pRedl->GetType();
+ if( nEndOfExtra < nNd &&
+ ( RedlineType::Insert == eType || RedlineType::Delete == eType ))
+ {
+ const SwNode* pDstNd = GetNodes()[
+ nMyEndOfExtra + nNd - nEndOfExtra ];
+
+ // Found the position.
+ // Then we also have to insert the redline to the line in the DestDoc.
+ vRedlines.emplace_back(*pDstNd, *pRedl);
+ }
+ }
+
+ if( !vRedlines.empty() )
+ {
+ // Carry over all into DestDoc
+ rSrcDoc.getIDocumentRedlineAccess().SetRedlineFlags(RedlineFlags::ShowInsert | RedlineFlags::ShowDelete);
+
+ getIDocumentRedlineAccess().SetRedlineFlags(
+ RedlineFlags::On |
+ RedlineFlags::ShowInsert |
+ RedlineFlags::ShowDelete);
+
+ SwPaM* pLastDestRedline(nullptr);
+ for(SaveMergeRedline& rRedline: vRedlines)
+ {
+ nRet += rRedline.InsertRedline(pLastDestRedline);
+ pLastDestRedline = rRedline.pDestRedl;
+ }
+ }
+ }
+
+ rSrcDoc.getIDocumentRedlineAccess().SetRedlineFlags( eSrcRedlMode );
+ if( !bSrcModified )
+ rSrcDoc.getIDocumentState().ResetModified();
+
+ getIDocumentRedlineAccess().SetRedlineFlags(RedlineFlags::ShowInsert | RedlineFlags::ShowDelete);
+
+ GetIDocumentUndoRedo().EndUndo(SwUndoId::EMPTY, nullptr);
+
+ return nRet;
+}
+
+LineArrayComparator::LineArrayComparator( const CompareData &rD1,
+ const CompareData &rD2, int nStt1,
+ int nEnd1, int nStt2, int nEnd2 )
+ : m_rData1( rD1 ), m_rData2( rD2 ), m_nFirst1( nStt1 ), m_nFirst2( nStt2 )
+{
+ m_nLen1 = nEnd1 - nStt1;
+ m_nLen2 = nEnd2 - nStt2;
+}
+
+bool LineArrayComparator::Compare( int nIdx1, int nIdx2 ) const
+{
+ if( nIdx1 < 0 || nIdx2 < 0 || nIdx1 >= m_nLen1 || nIdx2 >= m_nLen2 )
+ {
+ OSL_ENSURE( false, "Index out of range!" );
+ return false;
+ }
+
+ const SwTextNode *pTextNd1 = m_rData1.GetLine( m_nFirst1 + nIdx1 ).GetNode().GetTextNode();
+ const SwTextNode *pTextNd2 = m_rData2.GetLine( m_nFirst2 + nIdx2 ).GetNode().GetTextNode();
+
+ if( !pTextNd1 || !pTextNd2
+ || ( CmpOptions.bUseRsid && !pTextNd1->CompareParRsid( *pTextNd2 ) ) )
+ {
+ return false;
+ }
+
+ const sal_Int32 nPar1Len = pTextNd1->Len();
+ const sal_Int32 nPar2Len = pTextNd2->Len();
+
+ if( std::min( nPar1Len, nPar2Len ) * 3 < std::max( nPar1Len, nPar2Len ) )
+ {
+ return false;
+ }
+
+ sal_Int32 nBorderLen = ( nPar1Len + nPar2Len )/16;
+
+ if( nBorderLen < 3 )
+ {
+ nBorderLen = std::min<sal_Int32>( 3, std::min( nPar1Len, nPar2Len ) );
+ }
+
+ std::set<unsigned> aHashes;
+ unsigned nHash = 0;
+ unsigned nMul = 251;
+ unsigned nPow = 1;
+ sal_Int32 i;
+
+ for( i = 0; i < nBorderLen - 1; i++ )
+ {
+ nPow *= nMul;
+ }
+ for( i = 0; i < nBorderLen; i++ )
+ {
+ nHash = nHash*nMul + pTextNd1->GetText()[i];
+ }
+ aHashes.insert( nHash );
+ for( ; i < nPar1Len; i++ )
+ {
+ nHash = nHash - nPow*pTextNd1->GetText()[ i - nBorderLen ];
+ nHash = nHash*nMul + pTextNd1->GetText()[ i ];
+
+ aHashes.insert( nHash );
+ }
+
+ nHash = 0;
+ for( i = 0; i < nBorderLen; i++ )
+ {
+ nHash = nHash*nMul + pTextNd2->GetText()[ i ];
+ }
+
+ if( aHashes.find( nHash ) != aHashes.end() )
+ {
+ return true;
+ }
+
+ for( ; i < nPar2Len; i++ )
+ {
+ nHash = nHash - nPow*pTextNd2->GetText()[ i - nBorderLen ];
+ nHash = nHash*nMul + pTextNd2->GetText()[ i ];
+ if( aHashes.find( nHash ) != aHashes.end() )
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool CharArrayComparator::Compare( int nIdx1, int nIdx2 ) const
+{
+ if( nIdx1 < 0 || nIdx2 < 0 || nIdx1 >= GetLen1() || nIdx2 >= GetLen2() )
+ {
+ OSL_ENSURE( false, "Index out of range!" );
+ return false;
+ }
+
+ return ( !CmpOptions.bUseRsid
+ || m_pTextNode1->CompareRsid( *m_pTextNode2, nIdx1 + 1, nIdx2 + 1 ) )
+ && m_pTextNode1->GetText()[ nIdx1 ] == m_pTextNode2->GetText()[ nIdx2 ];
+}
+
+WordArrayComparator::WordArrayComparator( const SwTextNode *pNode1,
+ const SwTextNode *pNode2 )
+ : m_pTextNode1( pNode1 ), m_pTextNode2( pNode2 )
+{
+ m_pPos1.reset( new int[ m_pTextNode1->GetText().getLength() + 1 ] );
+ m_pPos2.reset( new int[ m_pTextNode2->GetText().getLength() + 1 ] );
+
+ CalcPositions( m_pPos1.get(), m_pTextNode1, m_nCount1 );
+ CalcPositions( m_pPos2.get(), m_pTextNode2, m_nCount2 );
+}
+
+bool WordArrayComparator::Compare( int nIdx1, int nIdx2 ) const
+{
+ int nLen = m_pPos1[ nIdx1 + 1 ] - m_pPos1[ nIdx1 ];
+ if( nLen != m_pPos2[ nIdx2 + 1 ] - m_pPos2[ nIdx2 ] )
+ {
+ return false;
+ }
+ for( int i = 0; i < nLen; i++)
+ {
+ if( m_pTextNode1->GetText()[ m_pPos1[ nIdx1 ] + i ]
+ != m_pTextNode2->GetText()[ m_pPos2[ nIdx2 ] + i ]
+ || ( CmpOptions.bUseRsid && !m_pTextNode1->CompareRsid( *m_pTextNode2,
+ m_pPos1[ nIdx1 ] + i, m_pPos2[ nIdx2 ] + i ) ) )
+ {
+ return false;
+ }
+ }
+ return true;
+}
+
+int WordArrayComparator::GetCharSequence( const int *pWordLcs1,
+ const int *pWordLcs2, int *pSubseq1, int *pSubseq2, int nLcsLen )
+{
+ int nLen = 0;
+ for( int i = 0; i < nLcsLen; i++ )
+ {
+ // Check for hash collisions
+ if( m_pPos1[ pWordLcs1[i] + 1 ] - m_pPos1[ pWordLcs1[i] ]
+ != m_pPos2[ pWordLcs2[i] + 1 ] - m_pPos2[ pWordLcs2[i] ] )
+ {
+ continue;
+ }
+ for( int j = 0; j < m_pPos1[pWordLcs1[i]+1] - m_pPos1[pWordLcs1[i]]; j++)
+ {
+ pSubseq1[ nLen ] = m_pPos1[ pWordLcs1[i] ] + j;
+ pSubseq2[ nLen ] = m_pPos2[ pWordLcs2[i] ] + j;
+
+ if( m_pTextNode1->GetText()[ m_pPos1[ pWordLcs1[i] ] + j ]
+ != m_pTextNode2->GetText()[ m_pPos2[ pWordLcs2[i] ] + j ] )
+ {
+ nLen -= j;
+ break;
+ }
+
+ nLen++;
+ }
+ }
+ return nLen;
+}
+
+void WordArrayComparator::CalcPositions( int *pPos, const SwTextNode *pTextNd,
+ int &nCnt )
+{
+ nCnt = -1;
+ for (int i = 0; i <= pTextNd->GetText().getLength(); ++i)
+ {
+ if (i == 0 || i == pTextNd->GetText().getLength()
+ || !rtl::isAsciiAlphanumeric( pTextNd->GetText()[ i - 1 ])
+ || !rtl::isAsciiAlphanumeric( pTextNd->GetText()[ i ]))
+ { // Begin new word
+ nCnt++;
+ pPos[ nCnt ] = i;
+ }
+ }
+}
+
+int CommonSubseq::FindLCS( int *pLcs1, int *pLcs2, int nStt1, int nEnd1,
+ int nStt2, int nEnd2 )
+{
+ int nLen1 = nEnd1 ? nEnd1 - nStt1 : m_rComparator.GetLen1();
+ int nLen2 = nEnd2 ? nEnd2 - nStt2 : m_rComparator.GetLen2();
+
+ assert( nLen1 >= 0 );
+ assert( nLen2 >= 0 );
+
+ std::unique_ptr<int*[]> pLcs( new int*[ nLen1 + 1 ] );
+ pLcs[ 0 ] = m_pData.get();
+
+ for( int i = 1; i < nLen1 + 1; i++ )
+ pLcs[ i ] = pLcs[ i - 1 ] + nLen2 + 1;
+
+ for( int i = 0; i <= nLen1; i++ )
+ pLcs[i][0] = 0;
+
+ for( int j = 0; j <= nLen2; j++ )
+ pLcs[0][j] = 0;
+
+ // Find lcs
+ for( int i = 1; i <= nLen1; i++ )
+ {
+ for( int j = 1; j <= nLen2; j++ )
+ {
+ if( m_rComparator.Compare( nStt1 + i - 1, nStt2 + j - 1 ) )
+ pLcs[i][j] = pLcs[i - 1][j - 1] + 1;
+ else
+ pLcs[i][j] = std::max( pLcs[i][j - 1], pLcs[i - 1][j] );
+ }
+ }
+
+ int nLcsLen = pLcs[ nLen1 ][ nLen2 ];
+
+ // Recover the lcs in the two sequences
+ if( pLcs1 && pLcs2 )
+ {
+ int nIdx1 = nLen1;
+ int nIdx2 = nLen2;
+ int nIdx = nLcsLen - 1;
+
+ while( nIdx1 > 0 && nIdx2 > 0 )
+ {
+ if( pLcs[ nIdx1 ][ nIdx2 ] == pLcs[ nIdx1 - 1 ][ nIdx2 ] )
+ nIdx1--;
+ else if( pLcs[ nIdx1 ][ nIdx2 ] == pLcs[ nIdx1 ][ nIdx2 - 1 ] )
+ nIdx2--;
+ else
+ {
+ nIdx1--;
+ nIdx2--;
+ pLcs1[ nIdx ] = nIdx1 + nStt1;
+ pLcs2[ nIdx ] = nIdx2 + nStt2;
+ nIdx--;
+ }
+ }
+ }
+
+ return nLcsLen;
+}
+
+int CommonSubseq::IgnoreIsolatedPieces( int *pLcs1, int *pLcs2, int nLen1,
+ int nLen2, int nLcsLen, int nPieceLen )
+{
+ if( !nLcsLen )
+ {
+ return 0;
+ }
+
+ int nNext = 0;
+
+ // Don't ignore text at the beginning of the paragraphs
+ if( pLcs1[ 0 ] == 0 && pLcs2[ 0 ] == 0 )
+ {
+ while( nNext < nLcsLen - 1 && pLcs1[ nNext ] + 1 == pLcs1[ nNext + 1 ]
+ && pLcs2[ nNext ] + 1 == pLcs2[ nNext + 1 ] )
+ {
+ nNext++;
+ }
+ nNext++;
+ }
+
+ int nCnt = 1;
+
+ for( int i = nNext; i < nLcsLen; i++ )
+ {
+ if( i != nLcsLen - 1 && pLcs1[ i ] + 1 == pLcs1[ i + 1 ]
+ && pLcs2[ i ] + 1 == pLcs2[ i + 1 ] )
+ {
+ nCnt++;
+ }
+ else
+ {
+ if( nCnt > nPieceLen
+ // Don't ignore text at the end of the paragraphs
+ || ( i == nLcsLen - 1
+ && pLcs1[i] == nLen1 - 1 && pLcs2[i] == nLen2 - 1 ))
+ {
+ for( int j = i + 1 - nCnt; j <= i; j++ )
+ {
+ pLcs2[ nNext ] = pLcs2[ j ];
+ pLcs1[ nNext ] = pLcs1[ j ];
+ nNext++;
+ }
+ }
+ nCnt = 1;
+ }
+ }
+
+ return nNext;
+}
+
+LgstCommonSubseq::LgstCommonSubseq( ArrayComparator &rComparator )
+ : CommonSubseq( rComparator, CUTOFF )
+{
+ m_pBuff1.reset( new int[ rComparator.GetLen2() + 1 ] );
+ m_pBuff2.reset( new int[ rComparator.GetLen2() + 1 ] );
+
+ m_pL1.reset( new int[ rComparator.GetLen2() + 1 ] );
+ m_pL2.reset( new int[ rComparator.GetLen2() + 1 ] );
+}
+
+void LgstCommonSubseq::FindL( int *pL, int nStt1, int nEnd1,
+ int nStt2, int nEnd2 )
+{
+ int nLen1 = nEnd1 ? nEnd1 - nStt1 : m_rComparator.GetLen1();
+ int nLen2 = nEnd2 ? nEnd2 - nStt2 : m_rComparator.GetLen2();
+
+ int *currL = m_pBuff1.get();
+ int *prevL = m_pBuff2.get();
+
+ // Avoid memory corruption
+ if( nLen2 > m_rComparator.GetLen2() )
+ {
+ assert( false );
+ return;
+ }
+
+ memset( m_pBuff1.get(), 0, sizeof( m_pBuff1[0] ) * ( nLen2 + 1 ) );
+ memset( m_pBuff2.get(), 0, sizeof( m_pBuff2[0] ) * ( nLen2 + 1 ) );
+
+ // Find lcs
+ for( int i = 1; i <= nLen1; i++ )
+ {
+ for( int j = 1; j <= nLen2; j++ )
+ {
+ if( m_rComparator.Compare( nStt1 + i - 1, nStt2 + j - 1 ) )
+ currL[j] = prevL[j - 1] + 1;
+ else
+ currL[j] = std::max( currL[j - 1], prevL[j] );
+ }
+ std::swap( currL, prevL );
+ }
+ memcpy( pL, prevL, ( nLen2 + 1 ) * sizeof( *prevL ) );
+}
+
+int LgstCommonSubseq::HirschbergLCS( int *pLcs1, int *pLcs2, int nStt1,
+ int nEnd1, int nStt2, int nEnd2 )
+{
+ static int nLen1;
+ static int nLen2;
+ nLen1 = nEnd1 - nStt1;
+ nLen2 = nEnd2 - nStt2;
+
+ if( ( nLen1 + 1 ) * ( nLen2 + 1 ) <= CUTOFF )
+ {
+ if( !nLen1 || !nLen2 )
+ {
+ return 0;
+ }
+ return FindLCS(pLcs1, pLcs2, nStt1, nEnd1, nStt2, nEnd2);
+ }
+
+ int nMid = nLen1/2;
+
+ FindL( m_pL1.get(), nStt1, nStt1 + nMid, nStt2, nEnd2 );
+ FindL( m_pL2.get(), nStt1 + nMid, nEnd1, nStt2, nEnd2 );
+
+ int nMaxPos = 0;
+ static int nMaxVal;
+ nMaxVal = -1;
+
+ static int i;
+ for( i = 0; i <= nLen2; i++ )
+ {
+ if( m_pL1[i] + ( m_pL2[nLen2] - m_pL2[i] ) > nMaxVal )
+ {
+ nMaxPos = i;
+ nMaxVal = m_pL1[i]+( m_pL2[nLen2] - m_pL2[i] );
+ }
+ }
+
+ int nRet = HirschbergLCS( pLcs1, pLcs2, nStt1, nStt1 + nMid,
+ nStt2, nStt2 + nMaxPos );
+ nRet += HirschbergLCS( pLcs1 + nRet, pLcs2 + nRet, nStt1 + nMid, nEnd1,
+ nStt2 + nMaxPos, nEnd2 );
+
+ return nRet;
+}
+
+int LgstCommonSubseq::Find( int *pSubseq1, int *pSubseq2 )
+{
+ int nStt = 0;
+ int nCutEnd = 0;
+ int nEnd1 = m_rComparator.GetLen1();
+ int nEnd2 = m_rComparator.GetLen2();
+
+ // Check for corresponding lines in the beginning of the sequences
+ while( nStt < nEnd1 && nStt < nEnd2 && m_rComparator.Compare( nStt, nStt ) )
+ {
+ pSubseq1[ nStt ] = nStt;
+ pSubseq2[ nStt ] = nStt;
+ nStt++;
+ }
+
+ pSubseq1 += nStt;
+ pSubseq2 += nStt;
+
+ // Check for corresponding lines in the end of the sequences
+ while( nStt < nEnd1 && nStt < nEnd2
+ && m_rComparator.Compare( nEnd1 - 1, nEnd2 - 1 ) )
+ {
+ nCutEnd++;
+ nEnd1--;
+ nEnd2--;
+ }
+
+ int nLen = HirschbergLCS( pSubseq1, pSubseq2, nStt, nEnd1, nStt, nEnd2 );
+
+ for( int i = 0; i < nCutEnd; i++ )
+ {
+ pSubseq1[ nLen + i ] = nEnd1 + i;
+ pSubseq2[ nLen + i ] = nEnd2 + i;
+ }
+
+ return nStt + nLen + nCutEnd;
+}
+
+int FastCommonSubseq::FindFastCS( int *pSeq1, int *pSeq2, int nStt1,
+ int nEnd1, int nStt2, int nEnd2 )
+{
+ int nCutBeg = 0;
+ int nCutEnd = 0;
+
+ // Check for corresponding lines in the beginning of the sequences
+ while( nStt1 < nEnd1 && nStt2 < nEnd2 && m_rComparator.Compare( nStt1, nStt2 ) )
+ {
+ pSeq1[ nCutBeg ] = nStt1++;
+ pSeq2[ nCutBeg ] = nStt2++;
+ nCutBeg++;
+ }
+
+ pSeq1 += nCutBeg;
+ pSeq2 += nCutBeg;
+
+ // Check for corresponding lines in the end of the sequences
+ while( nStt1 < nEnd1 && nStt2 < nEnd2
+ && m_rComparator.Compare( nEnd1 - 1, nEnd2 - 1 ) )
+ {
+ nCutEnd++;
+ nEnd1--;
+ nEnd2--;
+ }
+
+ int nLen1 = nEnd1 - nStt1;
+ int nLen2 = nEnd2 - nStt2;
+
+ // Return if a sequence is empty
+ if( nLen1 <= 0 || nLen2 <= 0 )
+ {
+ for( int i = 0; i < nCutEnd; i++ )
+ {
+ pSeq1[ i ] = nEnd1 + i;
+ pSeq2[ i ] = nEnd2 + i;
+ }
+ return nCutBeg + nCutEnd;
+ }
+
+ // Cut to LCS for small values
+ if( nLen1 < 3 || nLen2 < 3 || ( nLen1 + 1 ) * ( nLen2 + 1 ) <= CUTOFF )
+ {
+ int nLcsLen = FindLCS( pSeq1, pSeq2, nStt1, nEnd1, nStt2, nEnd2);
+
+ for( int i = 0; i < nCutEnd; i++ )
+ {
+ pSeq1[ nLcsLen + i ] = nEnd1 + i;
+ pSeq2[ nLcsLen + i ] = nEnd2 + i;
+ }
+ return nCutBeg + nLcsLen + nCutEnd;
+ }
+
+ int nMid1 = nLen1/2;
+ int nMid2 = nLen2/2;
+
+ int nRad;
+ int nPos1 = -1, nPos2 = -1;
+
+ // Find a point of correspondence in the middle of the sequences
+ for( nRad = 0; nRad*nRad < std::min( nMid1, nMid2 ); nRad++ )
+ {
+ // Search to the left and to the right of the middle of the first sequence
+ for( int i = nMid1 - nRad; i <= nMid1 + nRad; i++ )
+ {
+ if( m_rComparator.Compare( nStt1 + i, nStt2 + nMid2 - nRad ) )
+ {
+ nPos1 = nStt1 + i;
+ nPos2 = nStt2 + nMid2 - nRad;
+ break;
+ }
+ if( m_rComparator.Compare( nStt1 + i, nStt2 + nMid2 + nRad ) )
+ {
+ nPos1 = nStt1 + i;
+ nPos2 = nStt2 + nMid2 - nRad;
+ break;
+ }
+ }
+ // Search to the left and to the right of the middle of the second sequence
+ for( int i = nMid2 - nRad; i <= nMid2 + nRad; i++ )
+ {
+ if( m_rComparator.Compare( nStt2 + nMid2 - nRad, nStt2 + i ) )
+ {
+ nPos2 = nStt2 + i;
+ nPos1 = nStt1 + nMid1 - nRad;
+ break;
+ }
+ if( m_rComparator.Compare( nStt2 + nMid2 - nRad, nStt2 + i ) )
+ {
+ nPos2 = nStt2 + i;
+ nPos1 = nStt1 + nMid1 - nRad;
+ break;
+ }
+ }
+ }
+
+ // return if no point of correspondence found
+ if( nPos1 == -1 )
+ {
+ for( int i = 0; i < nCutEnd; i++ )
+ {
+ pSeq1[ i ] = nEnd1 + i;
+ pSeq2[ i ] = nEnd2 + i;
+ }
+ return nCutBeg + nCutEnd;
+ }
+
+ // Run the same on the sequences to the left of the correspondence point
+ int nLen = FindFastCS( pSeq1, pSeq2, nStt1, nPos1, nStt2, nPos2 );
+
+ pSeq1[ nLen ] = nPos1;
+ pSeq2[ nLen ] = nPos2;
+
+ // Run the same on the sequences to the right of the correspondence point
+ nLen += FindFastCS( pSeq1 + nLen + 1, pSeq2 + nLen + 1,
+ nPos1 + 1, nEnd1, nPos2 + 1, nEnd2 ) + 1;
+
+ for( int i = 0; i < nCutEnd; i++ )
+ {
+ pSeq1[ nLen + i ] = nEnd1 + i;
+ pSeq2[ nLen + i ] = nEnd2 + i;
+ }
+
+ return nLen + nCutBeg + nCutEnd;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/doccorr.cxx b/sw/source/core/doc/doccorr.cxx
new file mode 100644
index 0000000000..783e1aa234
--- /dev/null
+++ b/sw/source/core/doc/doccorr.cxx
@@ -0,0 +1,367 @@
+/* -*- 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 <doc.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <node.hxx>
+#include <editsh.hxx>
+#include <viscrs.hxx>
+#include <redline.hxx>
+#include <mvsave.hxx>
+#include <docary.hxx>
+#include <unocrsr.hxx>
+
+namespace
+{
+ /// find the relevant section in which the SwUnoCursor may wander.
+ /// returns NULL if no restrictions apply
+ const SwStartNode* lcl_FindUnoCursorSection( const SwNode& rNode )
+ {
+ const SwStartNode* pStartNode = rNode.IsStartNode() ? rNode.GetStartNode() : rNode.StartOfSectionNode();
+ while( ( pStartNode != nullptr ) &&
+ ( pStartNode->StartOfSectionNode() != pStartNode ) &&
+ // section node is only start node allowing overlapped delete
+ pStartNode->IsSectionNode() )
+ {
+ pStartNode = pStartNode->StartOfSectionNode();
+ }
+
+ return pStartNode;
+ }
+
+ bool lcl_PosCorrAbs(SwPosition & rPos,
+ const SwPosition& rStart,
+ const SwPosition& rEnd,
+ const SwPosition& rNewPos)
+ {
+ if ((rStart <= rPos) && (rPos <= rEnd))
+ {
+ rPos = rNewPos;
+ return true;
+ }
+ return false;
+ };
+
+ bool lcl_PaMCorrAbs(SwPaM & rPam,
+ const SwPosition& rStart,
+ const SwPosition& rEnd,
+ const SwPosition& rNewPos)
+ {
+ bool bRet = false;
+ bRet |= lcl_PosCorrAbs(rPam.GetBound(), rStart, rEnd, rNewPos);
+ bRet |= lcl_PosCorrAbs(rPam.GetBound(false), rStart, rEnd, rNewPos);
+ return bRet;
+ };
+
+ void lcl_PaMCorrRel1(SwPaM * pPam,
+ SwNode const * const pOldNode,
+ const SwPosition& rNewPos,
+ const sal_Int32 nCntIdx)
+ {
+ for(int nb = 0; nb < 2; ++nb)
+ {
+ SwPosition & rPos = pPam->GetBound(bool(nb));
+ if(&rPos.GetNode() == pOldNode)
+ {
+ rPos.Assign(rNewPos.GetNode(), SwNodeOffset(0),
+ nCntIdx + rPos.GetContentIndex());
+ }
+ }
+ }
+}
+
+void PaMCorrAbs( const SwPaM& rRange,
+ const SwPosition& rNewPos )
+{
+ SwPosition const aStart( *rRange.Start() );
+ SwPosition const aEnd( *rRange.End() );
+ SwPosition const aNewPos( rNewPos );
+ SwDoc& rDoc = aStart.GetNode().GetDoc();
+
+ if (SwCursorShell *const pShell = rDoc.GetEditShell())
+ {
+ for(const SwViewShell& rShell : pShell->GetRingContainer())
+ {
+ const SwCursorShell* pCursorShell = dynamic_cast<const SwCursorShell*>(&rShell);
+ if(!pCursorShell)
+ continue;
+ SwPaM *_pStackCursor = pCursorShell->GetStackCursor();
+ if( _pStackCursor )
+ for (;;)
+ {
+ lcl_PaMCorrAbs( *_pStackCursor, aStart, aEnd, aNewPos );
+ if( !_pStackCursor )
+ break;
+ _pStackCursor = _pStackCursor->GetNext();
+ if( _pStackCursor == pCursorShell->GetStackCursor() )
+ break;
+ }
+
+ for(SwPaM& rPaM : const_cast<SwShellCursor*>(pCursorShell->GetCursor_())->GetRingContainer())
+ {
+ lcl_PaMCorrAbs( rPaM, aStart, aEnd, aNewPos );
+ }
+
+ if( pCursorShell->IsTableMode() )
+ lcl_PaMCorrAbs( const_cast<SwPaM &>(*pCursorShell->GetTableCrs()), aStart, aEnd, aNewPos );
+ }
+ }
+
+ rDoc.cleanupUnoCursorTable();
+ for(const auto& pWeakUnoCursor : rDoc.mvUnoCursorTable)
+ {
+ auto pUnoCursor(pWeakUnoCursor.lock());
+ if(!pUnoCursor)
+ continue;
+
+ bool bChange = false; // has the UNO cursor been corrected?
+
+ // determine whether the UNO cursor will leave it's designated
+ // section
+ bool const bLeaveSection =
+ pUnoCursor->IsRemainInSection() &&
+ ( lcl_FindUnoCursorSection( aNewPos.GetNode() ) !=
+ lcl_FindUnoCursorSection(
+ pUnoCursor->GetPoint()->GetNode() ) );
+
+ for(SwPaM& rPaM : pUnoCursor->GetRingContainer())
+ {
+ bChange |= lcl_PaMCorrAbs( rPaM, aStart, aEnd, aNewPos );
+ }
+
+ SwUnoTableCursor *const pUnoTableCursor =
+ dynamic_cast<SwUnoTableCursor *>(pUnoCursor.get());
+ if( pUnoTableCursor )
+ {
+ for(SwPaM& rPaM : pUnoTableCursor->GetSelRing().GetRingContainer())
+ {
+ bChange |=
+ lcl_PaMCorrAbs( rPaM, aStart, aEnd, aNewPos );
+ }
+ }
+
+ // if a UNO cursor leaves its designated section, we must inform
+ // (and invalidate) said cursor
+ if (bChange && bLeaveSection)
+ {
+ // the UNO cursor has left its section. We need to notify it!
+ sw::UnoCursorHint aHint;
+ pUnoCursor->m_aNotifier.Broadcast(aHint);
+ }
+ }
+}
+
+void SwDoc::CorrAbs(const SwNode& rOldNode,
+ const SwPosition& rNewPos,
+ const sal_Int32 nOffset,
+ bool bMoveCursor)
+{
+ const SwContentNode *const pContentNode( rOldNode.GetContentNode() );
+ SwPaM const aPam(rOldNode, 0,
+ rOldNode, pContentNode ? pContentNode->Len() : 0);
+ SwPosition aNewPos(rNewPos);
+ aNewPos.AdjustContent(nOffset);
+
+ getIDocumentMarkAccess()->correctMarksAbsolute(rOldNode, rNewPos, nOffset);
+ // fix redlines
+ {
+ SwRedlineTable& rTable = getIDocumentRedlineAccess().GetRedlineTable();
+ for (SwRedlineTable::size_type n = 0; n < rTable.size(); )
+ {
+ // is on position ??
+ SwRangeRedline *const pRedline( rTable[ n ] );
+ bool const bChanged =
+ lcl_PaMCorrAbs(*pRedline, *aPam.Start(), *aPam.End(), aNewPos);
+ // clean up empty redlines: docredln.cxx asserts these as invalid
+ if (bChanged && (*pRedline->GetPoint() == *pRedline->GetMark())
+ && (pRedline->GetContentIdx() == nullptr))
+ {
+ rTable.DeleteAndDestroy(n);
+ }
+ else
+ {
+ ++n;
+ }
+ }
+
+ // To-Do - need to add here 'SwExtraRedlineTable' also ?
+ }
+
+ if(bMoveCursor)
+ {
+ ::PaMCorrAbs(aPam, aNewPos);
+ }
+}
+
+void SwDoc::CorrAbs(
+ const SwPaM& rRange,
+ const SwPosition& rNewPos,
+ bool bMoveCursor )
+{
+ const SwPosition& aStart(*rRange.Start());
+ const SwPosition& aEnd(*rRange.End());
+
+ DelBookmarks( aStart.GetNode(), aEnd.GetNode(), nullptr, aStart.GetContentIndex(), aEnd.GetContentIndex() );
+
+ if(bMoveCursor)
+ ::PaMCorrAbs(rRange, rNewPos);
+}
+
+void SwDoc::CorrAbs(
+ const SwNodeIndex& rStartNode,
+ const SwNodeIndex& rEndNode,
+ const SwPosition& rNewPos,
+ bool bMoveCursor )
+{
+ DelBookmarks( rStartNode.GetNode(), rEndNode.GetNode() );
+
+ if(bMoveCursor)
+ {
+ SwContentNode *const pContentNode( rEndNode.GetNode().GetContentNode() );
+ SwPaM const aPam(rStartNode, 0,
+ rEndNode, pContentNode ? pContentNode->Len() : 0);
+ ::PaMCorrAbs(aPam, rNewPos);
+ }
+}
+
+void PaMCorrRel( const SwNode &rOldNode,
+ const SwPosition &rNewPos,
+ const sal_Int32 nOffset )
+{
+ const SwNode* pOldNode = &rOldNode;
+ SwPosition aNewPos( rNewPos );
+ const SwDoc& rDoc = pOldNode->GetDoc();
+
+ const sal_Int32 nCntIdx = rNewPos.GetContentIndex() + nOffset;
+
+ if (SwCursorShell const* pShell = rDoc.GetEditShell())
+ {
+ for(const SwViewShell& rShell : pShell->GetRingContainer())
+ {
+ SwCursorShell* pCursorShell = const_cast<SwCursorShell*>(dynamic_cast<const SwCursorShell*>(&rShell));
+ if(!pCursorShell)
+ continue;
+ SwPaM *_pStackCursor = pCursorShell->GetStackCursor();
+ if( _pStackCursor )
+ for (;;)
+ {
+ lcl_PaMCorrRel1( _pStackCursor, pOldNode, aNewPos, nCntIdx );
+ if( !_pStackCursor )
+ break;
+ _pStackCursor = _pStackCursor->GetNext();
+ if( _pStackCursor == pCursorShell->GetStackCursor() )
+ break;
+ }
+
+ SwPaM* pStartPaM = pCursorShell->GetCursor_();
+ for(SwPaM& rPaM : pStartPaM->GetRingContainer())
+ {
+ lcl_PaMCorrRel1( &rPaM, pOldNode, aNewPos, nCntIdx);
+ }
+
+ if( pCursorShell->IsTableMode() )
+ lcl_PaMCorrRel1( pCursorShell->GetTableCrs(), pOldNode, aNewPos, nCntIdx );
+ }
+ }
+
+ rDoc.cleanupUnoCursorTable();
+ for(const auto& pWeakUnoCursor : rDoc.mvUnoCursorTable)
+ {
+ auto pUnoCursor(pWeakUnoCursor.lock());
+ if(!pUnoCursor)
+ continue;
+ for(SwPaM& rPaM : pUnoCursor->GetRingContainer())
+ {
+ lcl_PaMCorrRel1( &rPaM, pOldNode, aNewPos, nCntIdx );
+ }
+
+ SwUnoTableCursor* pUnoTableCursor =
+ dynamic_cast<SwUnoTableCursor*>(pUnoCursor.get());
+ if( pUnoTableCursor )
+ {
+ for(SwPaM& rPaM : pUnoTableCursor->GetSelRing().GetRingContainer())
+ {
+ lcl_PaMCorrRel1( &rPaM, pOldNode, aNewPos, nCntIdx );
+ }
+ }
+ }
+}
+
+void SwDoc::CorrRel(const SwNode& rOldNode,
+ const SwPosition& rNewPos,
+ const sal_Int32 nOffset,
+ bool bMoveCursor)
+{
+ getIDocumentMarkAccess()->correctMarksRelative(rOldNode, rNewPos, nOffset);
+
+ { // fix the Redlines
+ SwRedlineTable& rTable = getIDocumentRedlineAccess().GetRedlineTable();
+ SwPosition aNewPos(rNewPos);
+ for(SwRangeRedline* p : rTable)
+ {
+ // lies on the position ??
+ lcl_PaMCorrRel1( p, &rOldNode, aNewPos, aNewPos.GetContentIndex() + nOffset );
+ }
+
+ // To-Do - need to add here 'SwExtraRedlineTable' also ?
+ }
+
+ if(bMoveCursor)
+ ::PaMCorrRel(rOldNode, rNewPos, nOffset);
+}
+
+SwEditShell const * SwDoc::GetEditShell() const
+{
+ SwViewShell const *pCurrentView = getIDocumentLayoutAccess().GetCurrentViewShell();
+ // Layout and OLE shells should be available
+ if( pCurrentView )
+ {
+ for(const SwViewShell& rCurrentSh : pCurrentView->GetRingContainer())
+ {
+ // look for an EditShell (if it exists)
+ if( auto pEditShell = dynamic_cast<const SwEditShell *>(&rCurrentSh) )
+ {
+ return pEditShell;
+ }
+ }
+ }
+ return nullptr;
+}
+
+SwEditShell* SwDoc::GetEditShell()
+{
+ return const_cast<SwEditShell*>( const_cast<SwDoc const *>( this )->GetEditShell() );
+}
+
+::sw::IShellCursorSupplier * SwDoc::GetIShellCursorSupplier()
+{
+ return GetEditShell();
+}
+
+//bool foo()
+//{
+// bool b1 = true ? true : false;
+// bool b2 = (true ? true : false) ? true : false;
+// bool b3 = true ? (true ? true : false) : false;
+// bool b4 = true ? true : (true ? true : false);
+// return false;
+//}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/docdesc.cxx b/sw/source/core/doc/docdesc.cxx
new file mode 100644
index 0000000000..57e561e1f4
--- /dev/null
+++ b/sw/source/core/doc/docdesc.cxx
@@ -0,0 +1,1044 @@
+/* -*- 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 <cmdid.h>
+#include <init.hxx>
+#include <editeng/lrspitem.hxx>
+#include <editeng/ulspitem.hxx>
+#include <editeng/paperinf.hxx>
+#include <editeng/frmdiritem.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/dispatch.hxx>
+#include <tools/globname.hxx>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+#include <unotools/localedatawrapper.hxx>
+#include <fmtfsize.hxx>
+#include <fmthdft.hxx>
+#include <fmtcntnt.hxx>
+#include <ftninfo.hxx>
+#include <fesh.hxx>
+#include <ndole.hxx>
+#include <mdiexp.hxx>
+#include <doc.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <IDocumentFieldsAccess.hxx>
+#include <DocumentContentOperationsManager.hxx>
+#include <IDocumentState.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <rootfrm.hxx>
+#include <poolfmt.hxx>
+#include <docsh.hxx>
+#include <ftnidx.hxx>
+#include <fmtftn.hxx>
+#include <txtftn.hxx>
+#include <fldbas.hxx>
+#include <strings.hrc>
+#include <hints.hxx>
+#include <SwUndoPageDesc.hxx>
+#include <pagedeschint.hxx>
+#include <tgrditem.hxx>
+#include <unotools/configmgr.hxx>
+#include <unotools/syslocale.hxx>
+#include <svx/swframetypes.hxx>
+#include <o3tl/unit_conversion.hxx>
+
+#include <com/sun/star/embed/XEmbeddedObject.hpp>
+
+using namespace com::sun::star;
+
+static void lcl_DefaultPageFormat( sal_uInt16 nPoolFormatId,
+ SwFrameFormat &rFormat1,
+ SwFrameFormat &rFormat2,
+ SwFrameFormat &rFormat3,
+ SwFrameFormat &rFormat4)
+{
+ // --> #i41075# Printer on demand
+ // This function does not require a printer anymore.
+ // The default page size is obtained from the application
+ //locale
+
+ SwFormatFrameSize aFrameSize( SwFrameSize::Fixed );
+ const Size aPhysSize = SvxPaperInfo::GetDefaultPaperSize();
+ aFrameSize.SetSize( aPhysSize );
+
+ // Prepare for default margins.
+ // Margins have a default minimum size.
+ // If the printer forces a larger margins, that's ok too.
+ // The HTML page desc had A4 as page size always.
+ // This has been changed to take the page size from the printer.
+ // Unfortunately, the margins of the HTML page desc are smaller than
+ // the margins used here in general, so one extra case is required.
+ // In the long term, this needs to be changed to always keep the
+ // margins from the page desc.
+ sal_Int32 nMinTop, nMinBottom, nMinLeft, nMinRight;
+ if( RES_POOLPAGE_HTML == nPoolFormatId )
+ {
+ nMinRight = nMinTop = nMinBottom = o3tl::toTwips(1, o3tl::Length::cm);
+ nMinLeft = o3tl::toTwips(2, o3tl::Length::cm);
+ }
+ else if (!utl::ConfigManager::IsFuzzing() && MeasurementSystem::Metric == SvtSysLocale().GetLocaleData().getMeasurementSystemEnum() )
+ {
+ nMinTop = nMinBottom = nMinLeft = nMinRight = o3tl::toTwips(2, o3tl::Length::cm);
+ }
+ else
+ {
+ nMinTop = nMinBottom = o3tl::toTwips(1, o3tl::Length::in); // as in MS Word
+ nMinLeft = nMinRight = o3tl::toTwips(1.25, o3tl::Length::in);
+ }
+
+ // set margins
+ SvxLRSpaceItem aLR( RES_LR_SPACE );
+ SvxULSpaceItem aUL( RES_UL_SPACE );
+
+ aUL.SetUpper( o3tl::narrowing<sal_uInt16>(nMinTop) );
+ aUL.SetLower( o3tl::narrowing<sal_uInt16>(nMinBottom) );
+ aLR.SetRight( nMinRight );
+ aLR.SetLeft( nMinLeft );
+
+ rFormat1.SetFormatAttr( aFrameSize );
+ rFormat1.SetFormatAttr( aLR );
+ rFormat1.SetFormatAttr( aUL );
+
+ rFormat2.SetFormatAttr( aFrameSize );
+ rFormat2.SetFormatAttr( aLR );
+ rFormat2.SetFormatAttr( aUL );
+
+ rFormat3.SetFormatAttr( aFrameSize );
+ rFormat3.SetFormatAttr( aLR );
+ rFormat3.SetFormatAttr( aUL );
+
+ rFormat4.SetFormatAttr( aFrameSize );
+ rFormat4.SetFormatAttr( aLR );
+ rFormat4.SetFormatAttr( aUL );
+}
+
+static void lcl_DescSetAttr( const SwFrameFormat &rSource, SwFrameFormat &rDest,
+ const bool bPage = true )
+{
+ // We should actually use ItemSet's Intersect here, but that doesn't work
+ // correctly if we have different WhichRanges.
+
+ // Take over the attributes which are of interest.
+ sal_uInt16 const aIdArr[] = {
+ RES_FRM_SIZE, RES_UL_SPACE, // [83..86
+ RES_BACKGROUND, RES_SHADOW, // [99..101
+ RES_COL, RES_COL, // [103
+ RES_TEXTGRID, RES_TEXTGRID, // [109
+ RES_FRAMEDIR, RES_FRAMEDIR, // [114
+ RES_HEADER_FOOTER_EAT_SPACING, RES_HEADER_FOOTER_EAT_SPACING, // [115
+ RES_BACKGROUND_FULL_SIZE, RES_BACKGROUND_FULL_SIZE, // [131
+ RES_RTL_GUTTER, RES_RTL_GUTTER, // [132
+ RES_UNKNOWNATR_CONTAINER, RES_UNKNOWNATR_CONTAINER, // [143
+
+ // take over DrawingLayer FillStyles
+ XATTR_FILL_FIRST, XATTR_FILL_LAST, // [1014
+
+ 0};
+
+ const SfxPoolItem* pItem;
+ for( sal_uInt16 n = 0; aIdArr[ n ]; n += 2 )
+ {
+ for( sal_uInt16 nId = aIdArr[ n ]; nId <= aIdArr[ n+1]; ++nId )
+ {
+ // #i45539#
+ // bPage == true:
+ // All in aIdArr except from RES_HEADER_FOOTER_EAT_SPACING
+ // bPage == false:
+ // All in aIdArr except from RES_COL and RES_PAPER_BIN:
+ bool bExecuteId(true);
+
+ if(bPage)
+ {
+ // When Page
+ switch(nId)
+ {
+ // All in aIdArr except from RES_HEADER_FOOTER_EAT_SPACING
+ case RES_HEADER_FOOTER_EAT_SPACING:
+ // take out SvxBrushItem; it's the result of the fallback
+ // at SwFormat::GetItemState and not really in state SfxItemState::SET
+ case RES_BACKGROUND:
+ bExecuteId = false;
+ break;
+ default:
+ break;
+ }
+ }
+ else
+ {
+ // When not Page
+ switch(nId)
+ {
+ // When not Page: All in aIdArr except these:
+ case RES_COL:
+ case RES_PAPER_BIN:
+ case RES_BACKGROUND_FULL_SIZE:
+ case RES_RTL_GUTTER:
+ bExecuteId = false;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if(bExecuteId)
+ {
+ if (SfxItemState::SET == rSource.GetItemState(nId, false, &pItem))
+ {
+ rDest.SetFormatAttr(*pItem);
+ }
+ else
+ {
+ rDest.ResetFormatAttr(nId);
+ }
+ }
+ }
+ }
+
+ // Transmit pool and help IDs too
+ rDest.SetPoolFormatId( rSource.GetPoolFormatId() );
+ rDest.SetPoolHelpId( rSource.GetPoolHelpId() );
+ rDest.SetPoolHlpFileId( rSource.GetPoolHlpFileId() );
+}
+
+namespace
+{
+ SwFrameFormat& getFrameFormat(SwPageDesc &rDesc, bool bLeft, bool bFirst)
+ {
+ if (bFirst)
+ {
+ if (bLeft)
+ return rDesc.GetFirstLeft();
+ return rDesc.GetFirstMaster();
+ }
+ return rDesc.GetLeft();
+ }
+
+ const SwFrameFormat& getConstFrameFormat(const SwPageDesc &rDesc, bool bLeft, bool bFirst)
+ {
+ return getFrameFormat(const_cast<SwPageDesc&>(rDesc), bLeft, bFirst);
+ }
+}
+
+void SwDoc::CopyMasterHeader(const SwPageDesc &rChged, const SwFormatHeader &rHead, SwPageDesc &rDesc, bool bLeft, bool bFirst)
+{
+ assert(bLeft || bFirst);
+ SwFrameFormat& rDescFrameFormat = getFrameFormat(rDesc, bLeft, bFirst);
+ if (bFirst && bLeft)
+ {
+ // special case: always shared with something
+ rDescFrameFormat.SetFormatAttr( rChged.IsFirstShared()
+ ? rDesc.GetLeft().GetHeader()
+ : rDesc.GetFirstMaster().GetHeader());
+ }
+ else if ((bFirst ? rChged.IsFirstShared() : rChged.IsHeaderShared())
+ || !rHead.IsActive())
+ {
+ // Left or first shares the header with the Master.
+ rDescFrameFormat.SetFormatAttr( rDesc.GetMaster().GetHeader() );
+ }
+ else if ( rHead.IsActive() )
+ { // Left or first gets its own header if the Format doesn't already have one.
+ // If it already has one and it points to the same Section as the
+ // Right one, it needs to get an own Header.
+ // The content is evidently copied.
+ const SwFormatHeader &rFormatHead = rDescFrameFormat.GetHeader();
+ if ( !rFormatHead.IsActive() )
+ {
+ SwFormatHeader aHead( getIDocumentLayoutAccess().MakeLayoutFormat( RndStdIds::HEADERL, nullptr ) );
+ rDescFrameFormat.SetFormatAttr( aHead );
+ // take over additional attributes (margins, borders ...)
+ ::lcl_DescSetAttr( *rHead.GetHeaderFormat(), *aHead.GetHeaderFormat(), false);
+ }
+ else
+ {
+ const SwFormatContent &aCnt = rFormatHead.GetHeaderFormat()->GetContent();
+
+ if (!aCnt.GetContentIdx())
+ {
+ const SwFrameFormat& rChgedFrameFormat = getConstFrameFormat(rChged, bLeft, bFirst);
+ rDescFrameFormat.SetFormatAttr( rChgedFrameFormat.GetHeader() );
+ }
+ else
+ {
+ const SwFrameFormat *pRight = rHead.GetHeaderFormat();
+ if (!pRight)
+ return;
+ const SwFormatContent &aRCnt = pRight->GetContent();
+
+ if ((*aRCnt.GetContentIdx() == *aCnt.GetContentIdx()) ||
+ // The ContentIdx is _always_ different when called from
+ // SwDocStyleSheet::SetItemSet, because it deep-copies the
+ // PageDesc. So check if it was previously shared.
+ (bFirst ? rDesc.IsFirstShared() : rDesc.IsHeaderShared()))
+ {
+ SwFrameFormat *pFormat = new SwFrameFormat( GetAttrPool(),
+ bFirst ? "First header" : "Left header",
+ GetDfltFrameFormat() );
+ ::lcl_DescSetAttr( *pRight, *pFormat, false );
+ // The section which the right header attribute is pointing
+ // is copied, and the Index to the StartNode is set to
+ // the left or first header attribute.
+ SwStartNode* pSttNd = SwNodes::MakeEmptySection( GetNodes().GetEndOfAutotext(), SwHeaderStartNode );
+ SwNodeRange aRange( aRCnt.GetContentIdx()->GetNode(), SwNodeOffset(0),
+ *aRCnt.GetContentIdx()->GetNode().EndOfSectionNode() );
+ GetNodes().Copy_( aRange, *pSttNd->EndOfSectionNode(), false );
+ GetDocumentContentOperationsManager().CopyFlyInFlyImpl(aRange, nullptr, *pSttNd);
+ SwPaM const source(aRange.aStart, aRange.aEnd);
+ SwPosition dest(*pSttNd);
+ sw::CopyBookmarks(source, dest);
+ pFormat->SetFormatAttr( SwFormatContent( pSttNd ) );
+ rDescFrameFormat.SetFormatAttr( SwFormatHeader( pFormat ) );
+ }
+ else
+ ::lcl_DescSetAttr( *pRight,
+ *const_cast<SwFrameFormat*>(rFormatHead.GetHeaderFormat()), false );
+ }
+ }
+ }
+}
+
+void SwDoc::CopyMasterFooter(const SwPageDesc &rChged, const SwFormatFooter &rFoot, SwPageDesc &rDesc, bool bLeft, bool bFirst)
+{
+ assert(bLeft || bFirst);
+ SwFrameFormat& rDescFrameFormat = getFrameFormat(rDesc, bLeft, bFirst);
+ if (bFirst && bLeft)
+ {
+ // special case: always shared with something
+ rDescFrameFormat.SetFormatAttr( rChged.IsFirstShared()
+ ? rDesc.GetLeft().GetFooter()
+ : rDesc.GetFirstMaster().GetFooter());
+ }
+ else if ((bFirst ? rChged.IsFirstShared() : rChged.IsFooterShared())
+ || !rFoot.IsActive())
+ {
+ // Left or first shares the Header with the Master.
+ rDescFrameFormat.SetFormatAttr( rDesc.GetMaster().GetFooter() );
+ }
+ else if ( rFoot.IsActive() )
+ { // Left or first gets its own Footer if the Format does not already have one.
+ // If the Format already has a Footer and it points to the same section as the Right one,
+ // it needs to get an own one.
+ // The content is evidently copied.
+ const SwFormatFooter &rFormatFoot = rDescFrameFormat.GetFooter();
+ if ( !rFormatFoot.IsActive() )
+ {
+ SwFormatFooter aFoot( getIDocumentLayoutAccess().MakeLayoutFormat( RndStdIds::FOOTER, nullptr ) );
+ rDescFrameFormat.SetFormatAttr( aFoot );
+ // Take over additional attributes (margins, borders ...).
+ ::lcl_DescSetAttr( *rFoot.GetFooterFormat(), *aFoot.GetFooterFormat(), false);
+ }
+ else
+ {
+ const SwFormatContent &aLCnt = rFormatFoot.GetFooterFormat()->GetContent();
+ if( !aLCnt.GetContentIdx() )
+ {
+ const SwFrameFormat& rChgedFrameFormat = getConstFrameFormat(rChged, bLeft, bFirst);
+ rDescFrameFormat.SetFormatAttr( rChgedFrameFormat.GetFooter() );
+ }
+ else
+ {
+ const SwFrameFormat *pRight = rFoot.GetFooterFormat();
+ if (!pRight)
+ return;
+ const SwFormatContent &aRCnt = pRight->GetContent();
+
+ if ((*aRCnt.GetContentIdx() == *aLCnt.GetContentIdx()) ||
+ // The ContentIdx is _always_ different when called from
+ // SwDocStyleSheet::SetItemSet, because it deep-copies the
+ // PageDesc. So check if it was previously shared.
+ (bFirst ? rDesc.IsFirstShared() : rDesc.IsFooterShared()))
+ {
+ SwFrameFormat *pFormat = new SwFrameFormat( GetAttrPool(),
+ bFirst ? "First footer" : "Left footer",
+ GetDfltFrameFormat() );
+ ::lcl_DescSetAttr( *pRight, *pFormat, false );
+ // The section to which the right footer attribute is pointing
+ // is copied, and the Index to the StartNode is set to
+ // the left footer attribute.
+ SwStartNode* pSttNd = SwNodes::MakeEmptySection( GetNodes().GetEndOfAutotext(), SwFooterStartNode );
+ SwNodeRange aRange( aRCnt.GetContentIdx()->GetNode(), SwNodeOffset(0),
+ *aRCnt.GetContentIdx()->GetNode().EndOfSectionNode() );
+ GetNodes().Copy_( aRange, *pSttNd->EndOfSectionNode(), false );
+ GetDocumentContentOperationsManager().CopyFlyInFlyImpl(aRange, nullptr, *pSttNd);
+ SwPaM const source(aRange.aStart, aRange.aEnd);
+ SwPosition dest(*pSttNd);
+ sw::CopyBookmarks(source, dest);
+ pFormat->SetFormatAttr( SwFormatContent( pSttNd ) );
+ rDescFrameFormat.SetFormatAttr( SwFormatFooter( pFormat ) );
+ }
+ else
+ ::lcl_DescSetAttr( *pRight,
+ *const_cast<SwFrameFormat*>(rFormatFoot.GetFooterFormat()), false );
+ }
+ }
+ }
+}
+
+void SwDoc::ChgPageDesc( size_t i, const SwPageDesc &rChged )
+{
+ assert(i < m_PageDescs.size() && "PageDescs is out of range.");
+
+ SwPageDesc& rDesc = *m_PageDescs[i];
+ SwRootFrame* pTmpRoot = getIDocumentLayoutAccess().GetCurrentLayout();
+
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ // Stash header formats as needed.
+ const SwFormatHeader& rLeftHead = rChged.GetLeft().GetHeader();
+ const SwFormatHeader& rFirstMasterHead = rChged.GetFirstMaster().GetHeader();
+ const SwFormatHeader& rFirstLeftHead = rChged.GetFirstLeft().GetHeader();
+ const bool bStashLeftHead = !rDesc.IsHeaderShared() && rChged.IsHeaderShared();
+ const bool bStashFirstMasterHead = !rDesc.IsFirstShared() && rChged.IsFirstShared();
+ const bool bStashFirstLeftHead = (!rDesc.IsHeaderShared() && rChged.IsHeaderShared()) || (!rDesc.IsFirstShared() && rChged.IsFirstShared());
+ if (bStashLeftHead && rLeftHead.GetRegisteredIn() && !rDesc.HasStashedFormat(true, true, false))
+ rDesc.StashFrameFormat(rChged.GetLeft(), true, true, false);
+ if (bStashFirstMasterHead && rFirstMasterHead.GetRegisteredIn() && !rDesc.HasStashedFormat(true, false, true))
+ rDesc.StashFrameFormat(rChged.GetFirstMaster(), true, false, true);
+ if (bStashFirstLeftHead && rFirstLeftHead.GetRegisteredIn() && !rDesc.HasStashedFormat(true, true, true))
+ rDesc.StashFrameFormat(rChged.GetFirstLeft(), true, true, true);
+
+ // Stash footer formats as needed.
+ const SwFormatFooter& rLeftFoot = rChged.GetLeft().GetFooter();
+ const SwFormatFooter& rFirstMasterFoot = rChged.GetFirstMaster().GetFooter();
+ const SwFormatFooter& rFirstLeftFoot = rChged.GetFirstLeft().GetFooter();
+ const bool bStashLeftFoot = !rDesc.IsFooterShared() && rChged.IsFooterShared();
+ const bool bStashFirstMasterFoot = !rDesc.IsFirstShared() && rChged.IsFirstShared();
+ const bool bStashFirstLeftFoot = (!rDesc.IsFooterShared() && rChged.IsFooterShared()) || (!rDesc.IsFirstShared() && rChged.IsFirstShared());
+ if (bStashLeftFoot && rLeftFoot.GetRegisteredIn() && !rDesc.HasStashedFormat(false, true, false))
+ rDesc.StashFrameFormat(rChged.GetLeft(), false, true, false);
+ if (bStashFirstMasterFoot && rFirstMasterFoot.GetRegisteredIn() && !rDesc.HasStashedFormat(false, false, true))
+ rDesc.StashFrameFormat(rChged.GetFirstMaster(), false, false, true);
+ if (bStashFirstLeftFoot && rFirstLeftFoot.GetRegisteredIn() && !rDesc.HasStashedFormat(false, true, true))
+ rDesc.StashFrameFormat(rChged.GetFirstLeft(), false, true, true);
+
+ GetIDocumentUndoRedo().AppendUndo(std::make_unique<SwUndoPageDesc>(rDesc, rChged, this));
+ }
+ else
+ {
+ SwUndoId nBeingUndone(SwUndoId::EMPTY);
+ GetIDocumentUndoRedo().GetFirstRedoInfo(nullptr, &nBeingUndone);
+ if (SwUndoId::HEADER_FOOTER == nBeingUndone)
+ {
+ // The last format change is currently being undone. Remove header/footer and corresponding nodes.
+ auto rDescMasterHeaderFormat = rDesc.GetMaster().GetFormatAttr(RES_HEADER);
+ auto rDescLeftHeaderFormat = rDesc.GetLeft().GetFormatAttr(RES_HEADER);
+ auto rDescFirstLeftHeaderFormat = rDesc.GetFirstLeft().GetFormatAttr(RES_HEADER);
+ auto rDescMasterFooterFormat = rDesc.GetMaster().GetFormatAttr(RES_FOOTER);
+ auto rDescLeftFooterFormat = rDesc.GetLeft().GetFormatAttr(RES_FOOTER);
+ auto rDescFirstLeftFooterFormat = rDesc.GetFirstLeft().GetFormatAttr(RES_FOOTER);
+
+ auto rChgedMasterHeaderFormat = rChged.GetMaster().GetFormatAttr(RES_HEADER);
+ auto rChgedLeftHeaderFormat = rChged.GetLeft().GetFormatAttr(RES_HEADER);
+ auto rChgedFirstLeftHeaderFormat = rChged.GetFirstLeft().GetFormatAttr(RES_HEADER);
+ auto rChgedMasterFooterFormat = rChged.GetMaster().GetFormatAttr(RES_FOOTER);
+ auto rChgedLeftFooterFormat = rChged.GetLeft().GetFormatAttr(RES_FOOTER);
+ auto rChgedFirstLeftFooterFormat = rChged.GetFirstLeft().GetFormatAttr(RES_FOOTER);
+
+ rDesc.GetMaster().ResetFormatAttr(RES_HEADER);
+ rDesc.GetLeft().ResetFormatAttr(RES_HEADER);
+ rDesc.GetFirstLeft().ResetFormatAttr(RES_HEADER);
+ rDesc.GetMaster().ResetFormatAttr(RES_FOOTER);
+ rDesc.GetLeft().ResetFormatAttr(RES_FOOTER);
+ rDesc.GetFirstLeft().ResetFormatAttr(RES_FOOTER);
+
+ auto lDelHFFormat = [this](SwClient* pToRemove, SwFrameFormat* pFormat)
+ {
+ // Code taken from lcl_DelHFFormat
+ pFormat->Remove(pToRemove);
+ SwFormatContent& rCnt = const_cast<SwFormatContent&>(pFormat->GetContent());
+ if (rCnt.GetContentIdx())
+ {
+ SwNode* pNode = nullptr;
+ {
+ SwNodeIndex aIdx(*rCnt.GetContentIdx(), 0);
+ pNode = &aIdx.GetNode();
+ SwNodeOffset nEnd = pNode->EndOfSectionIndex();
+ while (aIdx < nEnd)
+ {
+ if (pNode->IsContentNode() &&
+ static_cast<SwContentNode*>(pNode)->HasWriterListeners())
+ {
+ SwCursorShell* pShell = SwIterator<SwCursorShell, SwContentNode>(*static_cast<SwContentNode*>(pNode)).First();
+ if (pShell)
+ {
+ pShell->ParkCursor(aIdx.GetNode());
+ aIdx = nEnd - 1;
+ }
+ }
+ ++aIdx;
+ pNode = &aIdx.GetNode();
+ }
+ }
+ rCnt.SetNewContentIdx(nullptr);
+
+ ::sw::UndoGuard const undoGuard(GetIDocumentUndoRedo());
+
+ assert(pNode);
+ getIDocumentContentOperations().DeleteSection(pNode);
+ }
+ delete pFormat;
+ };
+
+ if (rDescMasterHeaderFormat.GetHeaderFormat() && rDescMasterHeaderFormat != rChgedMasterHeaderFormat)
+ lDelHFFormat(&rDescMasterHeaderFormat, rDescMasterHeaderFormat.GetHeaderFormat());
+ else if (rDescLeftHeaderFormat.GetHeaderFormat() && rDescLeftHeaderFormat != rChgedLeftHeaderFormat)
+ lDelHFFormat(&rDescLeftHeaderFormat, rDescLeftHeaderFormat.GetHeaderFormat());
+ else if (rDescFirstLeftHeaderFormat.GetHeaderFormat() && rDescFirstLeftHeaderFormat != rChgedFirstLeftHeaderFormat)
+ lDelHFFormat(&rDescFirstLeftHeaderFormat, rDescFirstLeftHeaderFormat.GetHeaderFormat());
+
+ else if (rDescMasterFooterFormat.GetFooterFormat() && rDescMasterFooterFormat != rChgedMasterFooterFormat)
+ lDelHFFormat(&rDescMasterFooterFormat, rDescMasterFooterFormat.GetFooterFormat());
+ else if (rDescLeftFooterFormat.GetFooterFormat() && rDescLeftFooterFormat != rChgedLeftFooterFormat)
+ lDelHFFormat(&rDescLeftFooterFormat, rDescLeftFooterFormat.GetFooterFormat());
+ else if (rDescFirstLeftFooterFormat.GetFooterFormat() && rDescFirstLeftFooterFormat != rChgedFirstLeftFooterFormat)
+ lDelHFFormat(&rDescFirstLeftFooterFormat, rDescFirstLeftFooterFormat.GetFooterFormat());
+ }
+ }
+ ::sw::UndoGuard const undoGuard(GetIDocumentUndoRedo());
+
+ // Mirror at first if needed.
+ if ( rChged.GetUseOn() == UseOnPage::Mirror )
+ const_cast<SwPageDesc&>(rChged).Mirror();
+ else
+ {
+ // Or else transfer values from Master to Left
+ ::lcl_DescSetAttr(rChged.GetMaster(),
+ const_cast<SwPageDesc&>(rChged).GetLeft());
+ }
+ ::lcl_DescSetAttr(rChged.GetMaster(),
+ const_cast<SwPageDesc&>(rChged).GetFirstMaster());
+ ::lcl_DescSetAttr(rChged.GetLeft(),
+ const_cast<SwPageDesc&>(rChged).GetFirstLeft());
+
+ // Take over NumType.
+ if( rChged.GetNumType().GetNumberingType() != rDesc.GetNumType().GetNumberingType() )
+ {
+ rDesc.SetNumType( rChged.GetNumType() );
+ // Notify page number fields that NumFormat has changed
+ getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::PageNumber )->UpdateFields();
+ getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::RefPageGet )->UpdateFields();
+
+ // If the numbering scheme has changed we could have QuoVadis/ErgoSum texts
+ // that refer to a changed page, so we invalidate foot notes.
+ SwFootnoteIdxs& rFootnoteIdxs = GetFootnoteIdxs();
+ for( SwFootnoteIdxs::size_type nPos = 0; nPos < rFootnoteIdxs.size(); ++nPos )
+ {
+ SwTextFootnote *pTextFootnote = rFootnoteIdxs[ nPos ];
+ const SwFormatFootnote &rFootnote = pTextFootnote->GetFootnote();
+ pTextFootnote->SetNumber(rFootnote.GetNumber(), rFootnote.GetNumberRLHidden(), rFootnote.GetNumStr());
+ }
+ }
+
+ // Take over orientation
+ rDesc.SetLandscape( rChged.GetLandscape() );
+
+ // Synch header.
+ const SwFormatHeader& rMasterHead = rChged.GetMaster().GetHeader();
+ rDesc.GetMaster().SetFormatAttr( rMasterHead );
+ const bool bRestoreStashedLeftHead = rDesc.IsHeaderShared() && !rChged.IsHeaderShared();
+ const bool bRestoreStashedFirstMasterHead = rDesc.IsFirstShared() && !rChged.IsFirstShared();
+ const bool bRestoreStashedFirstLeftHead = (rDesc.IsHeaderShared() && !rChged.IsHeaderShared()) || (rDesc.IsFirstShared() && !rChged.IsFirstShared());
+ const SwFrameFormat* pStashedLeftFormat = bRestoreStashedLeftHead ? rChged.GetStashedFrameFormat(true, true, false) : nullptr;
+ const SwFrameFormat* pStashedFirstMasterFormat = bRestoreStashedFirstMasterHead ? rChged.GetStashedFrameFormat(true, false, true) : nullptr;
+ const SwFrameFormat* pStashedFirstLeftFormat = bRestoreStashedFirstLeftHead ? rChged.GetStashedFrameFormat(true, true, true) : nullptr;
+ CopyMasterHeader(rChged, pStashedLeftFormat ? pStashedLeftFormat->GetHeader() : rMasterHead, rDesc, true, false); // Copy left header
+ CopyMasterHeader(rChged, pStashedFirstMasterFormat ? pStashedFirstMasterFormat->GetHeader() : rMasterHead, rDesc, false, true); // Copy first master
+ CopyMasterHeader(rChged, pStashedFirstLeftFormat ? pStashedFirstLeftFormat->GetHeader() : rMasterHead, rDesc, true, true); // Copy first left
+
+ if (pStashedLeftFormat)
+ rDesc.RemoveStashedFormat(true, true, false);
+
+ if (pStashedFirstMasterFormat)
+ rDesc.RemoveStashedFormat(true, false, true);
+
+ if (pStashedFirstLeftFormat)
+ rDesc.RemoveStashedFormat(true, true, true);
+
+ rDesc.ChgHeaderShare( rChged.IsHeaderShared() );
+
+ // Synch Footer.
+ const SwFormatFooter& rMasterFoot = rChged.GetMaster().GetFooter();
+ rDesc.GetMaster().SetFormatAttr( rMasterFoot );
+ const bool bRestoreStashedLeftFoot = rDesc.IsFooterShared() && !rChged.IsFooterShared();
+ const bool bRestoreStashedFirstMasterFoot = rDesc.IsFirstShared() && !rChged.IsFirstShared();
+ const bool bRestoreStashedFirstLeftFoot = (rDesc.IsFooterShared() && !rChged.IsFooterShared()) || (rDesc.IsFirstShared() && !rChged.IsFirstShared());
+ const SwFrameFormat* pStashedLeftFoot = bRestoreStashedLeftFoot ? rChged.GetStashedFrameFormat(false, true, false) : nullptr;
+ const SwFrameFormat* pStashedFirstMasterFoot = bRestoreStashedFirstMasterFoot ? rChged.GetStashedFrameFormat(false, false, true) : nullptr;
+ const SwFrameFormat* pStashedFirstLeftFoot = bRestoreStashedFirstLeftFoot ? rChged.GetStashedFrameFormat(false, true, true) : nullptr;
+ CopyMasterFooter(rChged, pStashedLeftFoot ? pStashedLeftFoot->GetFooter() : rMasterFoot, rDesc, true, false); // Copy left footer
+ CopyMasterFooter(rChged, pStashedFirstMasterFoot ? pStashedFirstMasterFoot->GetFooter() : rMasterFoot, rDesc, false, true); // Copy first master
+ CopyMasterFooter(rChged, pStashedFirstLeftFoot ? pStashedFirstLeftFoot->GetFooter() : rMasterFoot, rDesc, true, true); // Copy first left
+
+ if (pStashedLeftFormat)
+ rDesc.RemoveStashedFormat(false, true, false);
+
+ if (pStashedFirstMasterFoot)
+ rDesc.RemoveStashedFormat(false, false, true);
+
+ if (pStashedFirstLeftFoot)
+ rDesc.RemoveStashedFormat(false, true, true);
+
+ rDesc.ChgFooterShare( rChged.IsFooterShared() );
+ // there is just one first shared flag for both header and footer?
+ rDesc.ChgFirstShare( rChged.IsFirstShared() );
+
+ if ( rDesc.GetName() != rChged.GetName() )
+ rDesc.SetName( rChged.GetName() );
+
+ // A RegisterChange is triggered, if necessary
+ rDesc.SetRegisterFormatColl( rChged.GetRegisterFormatColl() );
+
+ // If UseOn or the Follow change, the paragraphs need to know about it.
+ bool bUseOn = false;
+ bool bFollow = false;
+ if (rDesc.GetUseOn() != rChged.GetUseOn())
+ {
+ rDesc.SetUseOn( rChged.GetUseOn() );
+ bUseOn = true;
+ }
+ if (rDesc.GetFollow() != rChged.GetFollow())
+ {
+ if (rChged.GetFollow() == &rChged)
+ {
+ if (rDesc.GetFollow() != &rDesc)
+ {
+ rDesc.SetFollow( &rDesc );
+ bFollow = true;
+ }
+ }
+ else
+ {
+ rDesc.SetFollow( rChged.m_pFollow );
+ bFollow = true;
+ }
+ }
+
+ if ( (bUseOn || bFollow) && pTmpRoot)
+ // Inform layout!
+ {
+ for( auto aLayout : GetAllLayouts() )
+ aLayout->AllCheckPageDescs();
+ }
+
+ // Take over the page attributes.
+ ::lcl_DescSetAttr( rChged.GetMaster(), rDesc.GetMaster() );
+ ::lcl_DescSetAttr( rChged.GetLeft(), rDesc.GetLeft() );
+ ::lcl_DescSetAttr( rChged.GetFirstMaster(), rDesc.GetFirstMaster() );
+ ::lcl_DescSetAttr( rChged.GetFirstLeft(), rDesc.GetFirstLeft() );
+
+ // If the FootnoteInfo changes, the pages are triggered.
+ if( !(rDesc.GetFootnoteInfo() == rChged.GetFootnoteInfo()) )
+ {
+ rDesc.SetFootnoteInfo( rChged.GetFootnoteInfo() );
+ sw::PageFootnoteHint aHint;
+ rDesc.GetMaster().CallSwClientNotify(aHint);
+ rDesc.GetLeft().CallSwClientNotify(aHint);
+ rDesc.GetFirstMaster().CallSwClientNotify(aHint);
+ rDesc.GetFirstLeft().CallSwClientNotify(aHint);
+ }
+ getIDocumentState().SetModified();
+
+ SfxBindings* pBindings =
+ ( GetDocShell() && GetDocShell()->GetDispatcher() ) ? GetDocShell()->GetDispatcher()->GetBindings() : nullptr;
+ if ( pBindings )
+ {
+ pBindings->Invalidate( SID_ATTR_PAGE_COLUMN );
+ pBindings->Invalidate( SID_ATTR_PAGE );
+ pBindings->Invalidate( SID_ATTR_PAGE_SIZE );
+ pBindings->Invalidate( SID_ATTR_PAGE_ULSPACE );
+ pBindings->Invalidate( SID_ATTR_PAGE_LRSPACE );
+ }
+
+ //h/f of first-left page must not be unique but same as first master or left
+ assert((rDesc.IsFirstShared())
+ ? rDesc.GetFirstLeft().GetHeader().GetHeaderFormat() == rDesc.GetLeft().GetHeader().GetHeaderFormat()
+ : rDesc.GetFirstLeft().GetHeader().GetHeaderFormat() == rDesc.GetFirstMaster().GetHeader().GetHeaderFormat());
+ assert((rDesc.IsFirstShared())
+ ? rDesc.GetFirstLeft().GetFooter().GetFooterFormat() == rDesc.GetLeft().GetFooter().GetFooterFormat()
+ : rDesc.GetFirstLeft().GetFooter().GetFooterFormat() == rDesc.GetFirstMaster().GetFooter().GetFooterFormat());
+}
+
+/// All descriptors whose Follow point to the to-be-deleted have to be adapted.
+// #i7983#
+void SwDoc::PreDelPageDesc(SwPageDesc const * pDel)
+{
+ if (nullptr == pDel)
+ return;
+
+ // mba: test iteration as clients are removed while iteration
+ SwPageDescHint aHint( m_PageDescs[0] );
+ pDel->CallSwClientNotify( aHint );
+
+ bool bHasLayout = getIDocumentLayoutAccess().HasLayout();
+ if ( mpFootnoteInfo->DependsOn( pDel ) )
+ {
+ mpFootnoteInfo->ChgPageDesc( m_PageDescs[0] );
+ if ( bHasLayout )
+ {
+ for( auto aLayout : GetAllLayouts() )
+ aLayout->CheckFootnotePageDescs(false);
+ }
+ }
+ else if ( mpEndNoteInfo->DependsOn( pDel ) )
+ {
+ mpEndNoteInfo->ChgPageDesc( m_PageDescs[0] );
+ if ( bHasLayout )
+ {
+ for( auto aLayout : GetAllLayouts() )
+ aLayout->CheckFootnotePageDescs(true);
+ }
+ }
+
+ for (SwPageDesc* pPageDesc : m_PageDescs)
+ {
+ if (pPageDesc->GetFollow() == pDel)
+ {
+ pPageDesc->SetFollow(nullptr);
+ if( bHasLayout )
+ {
+ for( auto aLayout : GetAllLayouts() )
+ aLayout->AllCheckPageDescs();
+ }
+ }
+ }
+}
+
+void SwDoc::BroadcastStyleOperation(const OUString& rName, SfxStyleFamily eFamily,
+ SfxHintId nOp)
+{
+ if (mpDocShell)
+ {
+ SfxStyleSheetBasePool * pPool = mpDocShell->GetStyleSheetPool();
+
+ if (pPool)
+ {
+ SfxStyleSheetBase* pBase = pPool->Find(rName, eFamily);
+
+ if (pBase != nullptr)
+ pPool->Broadcast(SfxStyleSheetHint( nOp, *pBase ));
+ }
+ }
+}
+
+void SwDoc::DelPageDesc( size_t i, bool bBroadcast )
+{
+ OSL_ENSURE(i < m_PageDescs.size(), "PageDescs is out of range.");
+ OSL_ENSURE( i != 0, "You cannot delete the default Pagedesc.");
+ if ( i == 0 )
+ return;
+
+ SwPageDesc &rDel = *m_PageDescs[i];
+
+ if (bBroadcast)
+ BroadcastStyleOperation(rDel.GetName(), SfxStyleFamily::Page,
+ SfxHintId::StyleSheetErased);
+
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().AppendUndo(
+ std::make_unique<SwUndoPageDescDelete>(rDel, this));
+ }
+
+ PreDelPageDesc(&rDel); // #i7983#
+
+ m_PageDescs.erase(m_PageDescs.begin() + i);
+ getIDocumentState().SetModified();
+}
+
+SwPageDesc* SwDoc::MakePageDesc(const OUString &rName, const SwPageDesc *pCpy,
+ bool bRegardLanguage, bool bBroadcast)
+{
+ SwPageDesc *pNew;
+ if( pCpy )
+ {
+ pNew = new SwPageDesc( *pCpy );
+ pNew->SetName( rName );
+ if( rName != pCpy->GetName() )
+ {
+ pNew->SetPoolFormatId( USHRT_MAX );
+ pNew->SetPoolHelpId( USHRT_MAX );
+ pNew->SetPoolHlpFileId( UCHAR_MAX );
+ }
+ }
+ else
+ {
+ pNew = new SwPageDesc( rName, GetDfltFrameFormat(), this );
+ // Set the default page format.
+ lcl_DefaultPageFormat( USHRT_MAX, pNew->GetMaster(), pNew->GetLeft(), pNew->GetFirstMaster(), pNew->GetFirstLeft() );
+
+ SvxFrameDirection aFrameDirection = bRegardLanguage ?
+ GetDefaultFrameDirection(GetAppLanguage())
+ : SvxFrameDirection::Horizontal_LR_TB;
+
+ pNew->GetMaster().SetFormatAttr( SvxFrameDirectionItem(aFrameDirection, RES_FRAMEDIR) );
+ pNew->GetLeft().SetFormatAttr( SvxFrameDirectionItem(aFrameDirection, RES_FRAMEDIR) );
+ pNew->GetFirstMaster().SetFormatAttr( SvxFrameDirectionItem(aFrameDirection, RES_FRAMEDIR) );
+ pNew->GetFirstLeft().SetFormatAttr( SvxFrameDirectionItem(aFrameDirection, RES_FRAMEDIR) );
+ }
+
+ std::pair<SwPageDescs::const_iterator, bool> res = m_PageDescs.push_back( pNew );
+ SAL_WARN_IF(!res.second, "sw", "MakePageDesc called with existing name" );
+
+ if (bBroadcast)
+ BroadcastStyleOperation(rName, SfxStyleFamily::Page,
+ SfxHintId::StyleSheetCreated);
+
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().AppendUndo(std::make_unique<SwUndoPageDescCreate>(pNew, this));
+ }
+
+ getIDocumentState().SetModified();
+ return pNew;
+}
+
+void SwDoc::PrtOLENotify( bool bAll )
+{
+ SwFEShell *pShell = nullptr;
+ {
+ SwViewShell *pSh = getIDocumentLayoutAccess().GetCurrentViewShell();
+ if ( pSh )
+ {
+ for(SwViewShell& rShell : pSh->GetRingContainer())
+ {
+ if(auto pFEShell = dynamic_cast<SwFEShell*>( &rShell))
+ {
+ pShell = pFEShell;
+ break;
+ }
+ }
+ }
+ }
+ if ( !pShell )
+ {
+ // This doesn't make sense without a Shell and thus without a client, because
+ // the communication about size changes is implemented by these components.
+ // Because we don't have a Shell we remember this unfortunate situation
+ // in the document,
+ // which is made up for later on when creating the first Shell.
+ mbOLEPrtNotifyPending = true;
+ if ( bAll )
+ mbAllOLENotify = true;
+ }
+ else
+ {
+ if ( mbAllOLENotify )
+ bAll = true;
+
+ mbOLEPrtNotifyPending = mbAllOLENotify = false;
+
+ std::unique_ptr<SwOLENodes> pNodes = SwContentNode::CreateOLENodesArray( *GetDfltGrfFormatColl(), !bAll );
+ if ( pNodes )
+ {
+ ::StartProgress( STR_STATSTR_SWGPRTOLENOTIFY,
+ 0, pNodes->size(), GetDocShell());
+ getIDocumentLayoutAccess().GetCurrentLayout()->StartAllAction();
+
+ for( SwOLENodes::size_type i = 0; i < pNodes->size(); ++i )
+ {
+ ::SetProgressState( i, GetDocShell() );
+
+ SwOLENode* pOLENd = (*pNodes)[i];
+ pOLENd->SetOLESizeInvalid( false );
+
+ // At first load the Infos and see if it's not already in the exclude list.
+ SvGlobalName aName;
+
+ svt::EmbeddedObjectRef& xObj = pOLENd->GetOLEObj().GetObject();
+ if ( xObj.is() )
+ aName = SvGlobalName( xObj->getClassID() );
+ else // Not yet loaded
+ {
+ // TODO/LATER: retrieve ClassID of an unloaded object
+ // aName = ????
+ }
+
+ bool bFound = false;
+ for ( std::vector<SvGlobalName>::size_type j = 0;
+ j < pGlobalOLEExcludeList->size() && !bFound;
+ ++j )
+ {
+ bFound = (*pGlobalOLEExcludeList)[j] == aName;
+ }
+ if ( bFound )
+ continue;
+
+ // We don't know it, so the object has to be loaded.
+ // If it doesn't want to be informed
+ if ( xObj.is() )
+ {
+ pGlobalOLEExcludeList->push_back( aName );
+ }
+ }
+ pNodes.reset();
+ getIDocumentLayoutAccess().GetCurrentLayout()->EndAllAction();
+ ::EndProgress( GetDocShell() );
+ }
+ }
+}
+
+IMPL_LINK_NOARG( SwDoc, DoUpdateModifiedOLE, Timer *, void )
+{
+ SwFEShell* pSh = static_cast<SwFEShell*>(GetEditShell());
+ if (!pSh)
+ return;
+
+ mbOLEPrtNotifyPending = mbAllOLENotify = false;
+
+ std::unique_ptr<SwOLENodes> pNodes = SwContentNode::CreateOLENodesArray( *GetDfltGrfFormatColl(), true );
+ if( !pNodes )
+ return;
+
+ ::StartProgress( STR_STATSTR_SWGPRTOLENOTIFY,
+ 0, pNodes->size(), GetDocShell());
+ getIDocumentLayoutAccess().GetCurrentLayout()->StartAllAction();
+ SwUpdateAttr aHint(0,0,0);
+ for( SwOLENodes::size_type i = 0; i < pNodes->size(); ++i )
+ {
+ ::SetProgressState( i, GetDocShell() );
+
+ SwOLENode* pOLENd = (*pNodes)[i];
+ pOLENd->SetOLESizeInvalid( false );
+
+ // We don't know it, so the object has to be loaded.
+ // If it doesn't want to be informed
+ if( pOLENd->GetOLEObj().GetOleRef().is() ) // Broken?
+ {
+ pOLENd->UpdateAttr(aHint);
+ }
+ }
+ getIDocumentLayoutAccess().GetCurrentLayout()->EndAllAction();
+ ::EndProgress( GetDocShell() );
+}
+
+static SwPageDesc* lcl_FindPageDesc( const SwPageDescs *pPageDescs,
+ size_t *pPos, const OUString &rName )
+{
+ SwPageDesc* res = nullptr;
+ SwPageDescs::const_iterator it = pPageDescs->find( rName );
+ if( it != pPageDescs->end() )
+ {
+ res = *it;
+ if( pPos )
+ *pPos = std::distance( pPageDescs->begin(), it );
+ }
+ else if( pPos )
+ *pPos = SIZE_MAX;
+ return res;
+}
+
+SwPageDesc* SwDoc::FindPageDesc( const OUString & rName, size_t* pPos ) const
+{
+ return lcl_FindPageDesc( &m_PageDescs, pPos, rName );
+}
+
+bool SwDoc::ContainsPageDesc( const SwPageDesc *pDesc, size_t* pPos ) const
+{
+ if( pDesc == nullptr )
+ return false;
+ if( !m_PageDescs.contains( const_cast <SwPageDesc*>( pDesc ) ) ) {
+ if( pPos )
+ *pPos = SIZE_MAX;
+ return false;
+ }
+ if( ! pPos )
+ return true;
+
+ SwPageDesc* desc = lcl_FindPageDesc(
+ &m_PageDescs, pPos, pDesc->GetName() );
+ SAL_WARN_IF( desc != pDesc, "sw", "SwPageDescs container is broken!" );
+ return true;
+}
+
+void SwDoc::DelPageDesc( const OUString & rName, bool bBroadcast )
+{
+ size_t nI;
+
+ if (FindPageDesc(rName, &nI))
+ DelPageDesc(nI, bBroadcast);
+}
+
+void SwDoc::ChgPageDesc( const OUString & rName, const SwPageDesc & rDesc)
+{
+ size_t nI;
+
+ if (FindPageDesc(rName, &nI))
+ ChgPageDesc(nI, rDesc);
+}
+
+/*
+ * The HTML import cannot resist changing the page descriptions, I don't
+ * know why. This function is meant to check the page descriptors for invalid
+ * values.
+ */
+void SwDoc::CheckDefaultPageFormat()
+{
+ for ( size_t i = 0; i < GetPageDescCnt(); ++i )
+ {
+ SwPageDesc& rDesc = GetPageDesc( i );
+
+ SwFrameFormat& rMaster = rDesc.GetMaster();
+ SwFrameFormat& rLeft = rDesc.GetLeft();
+
+ const SwFormatFrameSize& rMasterSize = rMaster.GetFrameSize();
+ const SwFormatFrameSize& rLeftSize = rLeft.GetFrameSize();
+
+ const bool bSetSize = INVALID_TWIPS == rMasterSize.GetWidth() ||
+ INVALID_TWIPS == rMasterSize.GetHeight() ||
+ INVALID_TWIPS == rLeftSize.GetWidth() ||
+ INVALID_TWIPS == rLeftSize.GetHeight();
+
+ if ( bSetSize )
+ lcl_DefaultPageFormat( rDesc.GetPoolFormatId(), rDesc.GetMaster(), rDesc.GetLeft(), rDesc.GetFirstMaster(), rDesc.GetFirstLeft() );
+ }
+}
+
+void SwDoc::SetDefaultPageMode(bool bSquaredPageMode)
+{
+ if( !bSquaredPageMode == !IsSquaredPageMode() )
+ return;
+
+ const SwTextGridItem& rGrid = GetDefault( RES_TEXTGRID );
+ SwTextGridItem aNewGrid = rGrid;
+ aNewGrid.SetSquaredMode(bSquaredPageMode);
+ aNewGrid.Init();
+ SetDefault(aNewGrid);
+
+ for ( size_t i = 0; i < GetPageDescCnt(); ++i )
+ {
+ SwPageDesc& rDesc = GetPageDesc( i );
+
+ SwFrameFormat& rMaster = rDesc.GetMaster();
+ SwFrameFormat& rLeft = rDesc.GetLeft();
+
+ SwTextGridItem aGrid(rMaster.GetFormatAttr(RES_TEXTGRID));
+ aGrid.SwitchPaperMode( bSquaredPageMode );
+ rMaster.SetFormatAttr(aGrid);
+ rLeft.SetFormatAttr(aGrid);
+ }
+}
+
+bool SwDoc::IsSquaredPageMode() const
+{
+ const SwTextGridItem& rGrid = GetDefault( RES_TEXTGRID );
+ return rGrid.IsSquaredMode();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/docdraw.cxx b/sw/source/core/doc/docdraw.cxx
new file mode 100644
index 0000000000..aecbe2ac82
--- /dev/null
+++ b/sw/source/core/doc/docdraw.cxx
@@ -0,0 +1,699 @@
+/* -*- 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 <hintids.hxx>
+#include <editeng/flditem.hxx>
+#include <editeng/editeng.hxx>
+#include <editeng/colritem.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdogrp.hxx>
+#include <editeng/measfld.hxx>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+#include <fmtanchr.hxx>
+#include <charatr.hxx>
+#include <frmfmt.hxx>
+#include <charfmt.hxx>
+#include <viewimp.hxx>
+#include <doc.hxx>
+#include <docfunc.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <IDocumentDrawModelAccess.hxx>
+#include <IDocumentState.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <IDocumentStylePoolAccess.hxx>
+#include <poolfmt.hxx>
+#include <drawdoc.hxx>
+#include <UndoDraw.hxx>
+#include <swundo.hxx>
+#include <dcontact.hxx>
+#include <dview.hxx>
+#include <mvsave.hxx>
+#include <flyfrm.hxx>
+#include <dflyobj.hxx>
+#include <txtfrm.hxx>
+#include <editeng/frmdiritem.hxx>
+#include <fmtornt.hxx>
+#include <svx/svditer.hxx>
+
+#include <vector>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::linguistic2;
+
+/** local method to determine positioning and alignment attributes for a drawing
+ * object, which is newly connected to the layout.
+ *
+ * Used for a newly formed group object <SwDoc::GroupSelection(..)>
+ * and the members of a destroyed group <SwDoc::UnGroupSelection(..)>
+ */
+static void lcl_AdjustPositioningAttr( SwDrawFrameFormat* _pFrameFormat,
+ const SdrObject& _rSdrObj )
+{
+ const SwContact* pContact = GetUserCall( &_rSdrObj );
+ OSL_ENSURE( pContact, "<lcl_AdjustPositioningAttr(..)> - missing contact object." );
+
+ // determine position of new group object relative to its anchor frame position
+ SwTwips nHoriRelPos = 0;
+ SwTwips nVertRelPos = 0;
+ {
+ const SwFrame* pAnchorFrame = pContact->GetAnchoredObj( &_rSdrObj )->GetAnchorFrame();
+ OSL_ENSURE( !pAnchorFrame ||
+ !pAnchorFrame->IsTextFrame() ||
+ !static_cast<const SwTextFrame*>(pAnchorFrame)->IsFollow(),
+ "<lcl_AdjustPositioningAttr(..)> - anchor frame is a follow." );
+ bool bVert = false;
+ bool bR2L = false;
+ // #i45952# - use anchor position of anchor frame, if it exist.
+ Point aAnchorPos;
+ if ( pAnchorFrame )
+ {
+ // #i45952#
+ aAnchorPos = pAnchorFrame->GetFrameAnchorPos( ::HasWrap( &_rSdrObj ) );
+ bVert = pAnchorFrame->IsVertical();
+ bR2L = pAnchorFrame->IsRightToLeft();
+ }
+ else
+ {
+ // #i45952#
+ aAnchorPos = _rSdrObj.GetAnchorPos();
+ // If no anchor frame exist - e.g. because no layout exists - the
+ // default layout direction is taken.
+ const SvxFrameDirectionItem& rDirItem =
+ _pFrameFormat->GetAttrSet().GetPool()->GetDefaultItem( RES_FRAMEDIR );
+ switch ( rDirItem.GetValue() )
+ {
+ case SvxFrameDirection::Vertical_LR_TB:
+ {
+ // vertical from left-to-right
+ bVert = true;
+ bR2L = true;
+ OSL_FAIL( "<lcl_AdjustPositioningAttr(..)> - vertical from left-to-right not supported." );
+ }
+ break;
+ case SvxFrameDirection::Vertical_RL_TB:
+ {
+ // vertical from right-to-left
+ bVert = true;
+ bR2L = false;
+ }
+ break;
+ case SvxFrameDirection::Horizontal_RL_TB:
+ {
+ // horizontal from right-to-left
+ bVert = false;
+ bR2L = true;
+ }
+ break;
+ case SvxFrameDirection::Horizontal_LR_TB:
+ {
+ // horizontal from left-to-right
+ bVert = false;
+ bR2L = false;
+ }
+ break;
+ case SvxFrameDirection::Environment:
+ SAL_WARN("sw.core", "lcl_AdjustPositioningAttr(..) SvxFrameDirection::Environment not supported");
+ break;
+ default: break;
+ }
+
+ }
+ // use geometry of drawing object
+ const tools::Rectangle aObjRect = _rSdrObj.GetSnapRect();
+
+ if ( bVert )
+ {
+ if ( bR2L ) {
+ //SvxFrameDirection::Vertical_LR_TB
+ nHoriRelPos = aObjRect.Left() - aAnchorPos.getX();
+ nVertRelPos = aObjRect.Top() - aAnchorPos.getY();
+ } else {
+ //SvxFrameDirection::Vertical_RL_TB
+ nHoriRelPos = aObjRect.Top() - aAnchorPos.getY();
+ nVertRelPos = aAnchorPos.getX() - aObjRect.Right();
+ }
+ }
+ else if ( bR2L )
+ {
+ nHoriRelPos = aAnchorPos.getX() - aObjRect.Right();
+ nVertRelPos = aObjRect.Top() - aAnchorPos.getY();
+ }
+ else
+ {
+ nHoriRelPos = aObjRect.Left() - aAnchorPos.getX();
+ nVertRelPos = aObjRect.Top() - aAnchorPos.getY();
+ }
+ }
+
+ _pFrameFormat->SetFormatAttr( SwFormatHoriOrient( nHoriRelPos, text::HoriOrientation::NONE, text::RelOrientation::FRAME ) );
+ _pFrameFormat->SetFormatAttr( SwFormatVertOrient( nVertRelPos, text::VertOrientation::NONE, text::RelOrientation::FRAME ) );
+ // #i44334#, #i44681# - positioning attributes already set
+ _pFrameFormat->PosAttrSet();
+ // #i34750# - keep current object rectangle for drawing
+ // objects. The object rectangle is used on events from the drawing layer
+ // to adjust the positioning attributes - see <SwDrawContact::Changed_(..)>.
+ {
+ const SwAnchoredObject* pAnchoredObj = pContact->GetAnchoredObj( &_rSdrObj );
+ if ( auto pAnchoredDrawObj = dynamic_cast<const SwAnchoredDrawObject*>( pAnchoredObj) )
+ {
+ const tools::Rectangle aObjRect = _rSdrObj.GetSnapRect();
+ const_cast<SwAnchoredDrawObject*>(pAnchoredDrawObj)
+ ->SetLastObjRect( aObjRect );
+ }
+ }
+}
+
+SwDrawContact* SwDoc::GroupSelection( SdrView& rDrawView )
+{
+ // replace marked 'virtual' drawing objects by the corresponding 'master'
+ // drawing objects.
+ SwDrawView::ReplaceMarkedDrawVirtObjs( rDrawView );
+
+ const SdrMarkList &rMrkList = rDrawView.GetMarkedObjectList();
+ SdrObject *pObj = rMrkList.GetMark( 0 )->GetMarkedSdrObj();
+ bool bNoGroup = ( nullptr == pObj->getParentSdrObjectFromSdrObject() );
+ SwDrawContact* pNewContact = nullptr;
+ if( bNoGroup )
+ {
+ SwDrawFrameFormat *pFormat = nullptr;
+
+ // Revoke anchor attribute.
+ SwDrawContact *pMyContact = static_cast<SwDrawContact*>(GetUserCall(pObj));
+ const SwFormatAnchor aAnch( pMyContact->GetFormat()->GetAnchor() );
+
+ std::unique_ptr<SwUndoDrawGroup> pUndo;
+ if (GetIDocumentUndoRedo().DoesUndo())
+ pUndo.reset(new SwUndoDrawGroup( o3tl::narrowing<sal_uInt16>(rMrkList.GetMarkCount()), *this));
+
+ // #i53320#
+ bool bGroupMembersNotPositioned( false );
+ {
+ SwAnchoredDrawObject* pAnchoredDrawObj =
+ static_cast<SwAnchoredDrawObject*>(pMyContact->GetAnchoredObj( pObj ));
+ bGroupMembersNotPositioned = pAnchoredDrawObj->NotYetPositioned();
+ }
+
+ std::map<const SdrObject*, SwFrameFormat*> vSavedTextBoxes;
+ // Destroy ContactObjects and formats.
+ for( size_t i = 0; i < rMrkList.GetMarkCount(); ++i )
+ {
+ pObj = rMrkList.GetMark( i )->GetMarkedSdrObj();
+ SwDrawContact *pContact = static_cast<SwDrawContact*>(GetUserCall(pObj));
+
+ // #i53320#
+#if OSL_DEBUG_LEVEL > 0
+ SwAnchoredDrawObject* pAnchoredDrawObj =
+ static_cast<SwAnchoredDrawObject*>(pContact->GetAnchoredObj( pObj ));
+ OSL_ENSURE( bGroupMembersNotPositioned == pAnchoredDrawObj->NotYetPositioned(),
+ "<SwDoc::GroupSelection(..)> - group members have different positioning status!" );
+#endif
+ // Before the format will be killed, save its textbox for later use.
+ if (auto pShapeFormat = pContact->GetFormat())
+ if (auto pTextBoxNode = pShapeFormat->GetOtherTextBoxFormats())
+ for (const auto& rTextBoxElement : pTextBoxNode->GetAllTextBoxes())
+ vSavedTextBoxes.emplace(rTextBoxElement);
+
+ pFormat = static_cast<SwDrawFrameFormat*>(pContact->GetFormat());
+ // Deletes itself!
+ pContact->Changed(*pObj, SdrUserCallType::Delete, pObj->GetLastBoundRect() );
+ pObj->SetUserCall( nullptr );
+
+ if( pUndo )
+ pUndo->AddObj( i, pFormat, pObj );
+ else
+ DelFrameFormat( pFormat );
+
+ // #i45952# - re-introduce position normalization of group member
+ // objects, because its anchor position is cleared, when they are
+ // grouped.
+ Point aAnchorPos( pObj->GetAnchorPos() );
+ pObj->NbcSetAnchorPos( Point( 0, 0 ) );
+ pObj->NbcMove( Size( aAnchorPos.getX(), aAnchorPos.getY() ) );
+ }
+
+ pFormat = MakeDrawFrameFormat( GetUniqueDrawObjectName(),
+ GetDfltFrameFormat() );
+ pFormat->SetFormatAttr( aAnch );
+ // #i36010# - set layout direction of the position
+ pFormat->SetPositionLayoutDir(
+ text::PositionLayoutDir::PositionInLayoutDirOfAnchor );
+
+ // Add the saved textboxes to the new format.
+ auto pTextBoxNode = std::make_shared<SwTextBoxNode>(
+ SwTextBoxNode(static_cast<SwFrameFormat*>(pFormat)));
+ for (const auto& pTextBoxEntry : vSavedTextBoxes)
+ {
+ pTextBoxNode->AddTextBox(const_cast<SdrObject*>(pTextBoxEntry.first),
+ pTextBoxEntry.second);
+ pTextBoxEntry.second->SetOtherTextBoxFormats(pTextBoxNode);
+ }
+ pFormat->SetOtherTextBoxFormats(pTextBoxNode);
+ vSavedTextBoxes.clear();
+
+ rDrawView.GroupMarked();
+ OSL_ENSURE( rMrkList.GetMarkCount() == 1, "GroupMarked more or none groups." );
+
+ SdrObject* pNewGroupObj = rMrkList.GetMark( 0 )->GetMarkedSdrObj();
+ pNewGroupObj->SetName(pFormat->GetName());
+ pNewContact = new SwDrawContact( pFormat, pNewGroupObj );
+ // #i35635#
+ pNewContact->MoveObjToVisibleLayer( pNewGroupObj );
+ pNewContact->ConnectToLayout();
+ // #i53320# - No adjustment of the positioning and alignment
+ // attributes, if group members aren't positioned yet.
+ if ( !bGroupMembersNotPositioned )
+ {
+ // #i26791# - Adjust positioning and alignment attributes.
+ lcl_AdjustPositioningAttr( pFormat, *pNewGroupObj );
+ }
+
+ if( pUndo )
+ {
+ pUndo->SetGroupFormat( pFormat );
+ GetIDocumentUndoRedo().AppendUndo( std::move(pUndo) );
+ }
+ }
+ else
+ {
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().ClearRedo();
+ }
+
+ rDrawView.GroupMarked();
+ OSL_ENSURE( rMrkList.GetMarkCount() == 1, "GroupMarked more or none groups." );
+ }
+
+ return pNewContact;
+}
+
+static void lcl_CollectTextBoxesForSubGroupObj(SwFrameFormat* pTargetFormat, std::shared_ptr<SwTextBoxNode> pTextBoxNode,
+ SdrObject* pSourceObjs)
+{
+ if (auto pChildrenObjs = pSourceObjs->getChildrenOfSdrObject())
+ for (const rtl::Reference<SdrObject>& pSubObj : *pChildrenObjs)
+ lcl_CollectTextBoxesForSubGroupObj(pTargetFormat, pTextBoxNode, pSubObj.get());
+ else
+ {
+ if (auto pTextBox = pTextBoxNode->GetTextBox(pSourceObjs))
+ {
+ if (!pTargetFormat->GetOtherTextBoxFormats())
+ {
+ pTargetFormat->SetOtherTextBoxFormats(std::make_shared<SwTextBoxNode>(SwTextBoxNode(pTargetFormat)));
+ }
+ pTargetFormat->GetOtherTextBoxFormats()->AddTextBox(pSourceObjs, pTextBox);
+ pTextBox->SetOtherTextBoxFormats(pTargetFormat->GetOtherTextBoxFormats());
+ }
+ }
+}
+
+
+void SwDoc::UnGroupSelection( SdrView& rDrawView )
+{
+ bool const bUndo = GetIDocumentUndoRedo().DoesUndo();
+ if( bUndo )
+ {
+ GetIDocumentUndoRedo().ClearRedo();
+ }
+
+ // replace marked 'virtual' drawing objects by the corresponding 'master'
+ // drawing objects.
+ SwDrawView::ReplaceMarkedDrawVirtObjs( rDrawView );
+
+ const SdrMarkList &rMrkList = rDrawView.GetMarkedObjectList();
+ std::unique_ptr<std::vector< std::pair< SwDrawFrameFormat*, SdrObject* > >[]> pFormatsAndObjs;
+ const size_t nMarkCount( rMrkList.GetMarkCount() );
+ if ( nMarkCount )
+ {
+ pFormatsAndObjs.reset( new std::vector< std::pair< SwDrawFrameFormat*, SdrObject* > >[nMarkCount] );
+ SdrObject *pMyObj = rMrkList.GetMark( 0 )->GetMarkedSdrObj();
+ if( !pMyObj->getParentSdrObjectFromSdrObject() )
+ {
+ for ( size_t i = 0; i < nMarkCount; ++i )
+ {
+ SdrObject *pObj = rMrkList.GetMark( i )->GetMarkedSdrObj();
+ if ( auto pObjGroup = dynamic_cast<SdrObjGroup*>(pObj) )
+ {
+ SwDrawContact *pContact = static_cast<SwDrawContact*>(GetUserCall(pObj));
+
+ std::shared_ptr<SwTextBoxNode> pTextBoxNode;
+ if (auto pGroupFormat = pContact->GetFormat())
+ pTextBoxNode = pGroupFormat->GetOtherTextBoxFormats();
+
+ SwFormatAnchor aAnch( pContact->GetFormat()->GetAnchor() );
+ SdrObjList *pLst = pObjGroup->GetSubList();
+
+ SwUndoDrawUnGroup* pUndo = nullptr;
+ if( bUndo )
+ {
+ pUndo = new SwUndoDrawUnGroup( pObjGroup, *this );
+ GetIDocumentUndoRedo().AppendUndo(std::unique_ptr<SwUndo>(pUndo));
+ }
+
+ for ( size_t i2 = 0; i2 < pLst->GetObjCount(); ++i2 )
+ {
+ SdrObject* pSubObj = pLst->GetObj( i2 );
+ SwDrawFrameFormat *pFormat = MakeDrawFrameFormat( GetUniqueShapeName(),
+ GetDfltFrameFormat() );
+ pFormat->SetFormatAttr( aAnch );
+
+ if (pTextBoxNode)
+ {
+ if (!pObj->getChildrenOfSdrObject())
+ {
+ if (auto pTextBoxFormat = pTextBoxNode->GetTextBox(pSubObj))
+ {
+ auto pNewTextBoxNode =std::make_shared<SwTextBoxNode>(SwTextBoxNode(pFormat));
+ pNewTextBoxNode->AddTextBox(pSubObj, pTextBoxFormat);
+ pFormat->SetOtherTextBoxFormats(pNewTextBoxNode);
+ pTextBoxFormat->SetOtherTextBoxFormats(pNewTextBoxNode);
+ }
+ }
+ else
+ {
+ lcl_CollectTextBoxesForSubGroupObj(pFormat, pTextBoxNode, pSubObj);
+ }
+ }
+ // #i36010# - set layout direction of the position
+ pFormat->SetPositionLayoutDir(
+ text::PositionLayoutDir::PositionInLayoutDirOfAnchor );
+ if (pSubObj->GetName().isEmpty())
+ pSubObj->SetName(pFormat->GetName());
+ pFormatsAndObjs[i].emplace_back( pFormat, pSubObj );
+
+ if( bUndo )
+ pUndo->AddObj( o3tl::narrowing<sal_uInt16>(i2), pFormat );
+ }
+ }
+ }
+ }
+ }
+ rDrawView.UnGroupMarked();
+ // creation of <SwDrawContact> instances for the former group members and
+ // its connection to the Writer layout.
+ for ( size_t i = 0; i < nMarkCount; ++i )
+ {
+ SwUndoDrawUnGroupConnectToLayout* pUndo = nullptr;
+ if( bUndo )
+ {
+ pUndo = new SwUndoDrawUnGroupConnectToLayout(*this);
+ GetIDocumentUndoRedo().AppendUndo(std::unique_ptr<SwUndo>(pUndo));
+ }
+
+ while ( !pFormatsAndObjs[i].empty() )
+ {
+ SwDrawFrameFormat* pFormat( pFormatsAndObjs[i].back().first );
+ SdrObject* pObj( pFormatsAndObjs[i].back().second );
+ pFormatsAndObjs[i].pop_back();
+
+ SwDrawContact* pContact = new SwDrawContact( pFormat, pObj );
+ pContact->MoveObjToVisibleLayer( pObj );
+ pContact->ConnectToLayout();
+ lcl_AdjustPositioningAttr( pFormat, *pObj );
+
+ if ( bUndo )
+ {
+ pUndo->AddFormatAndObj( pFormat, pObj );
+ }
+ }
+ }
+}
+
+bool SwDoc::DeleteSelection( SwDrawView& rDrawView )
+{
+ bool bCallBase = false;
+ const SdrMarkList &rMrkList = rDrawView.GetMarkedObjectList();
+ if( rMrkList.GetMarkCount() )
+ {
+ GetIDocumentUndoRedo().StartUndo(SwUndoId::EMPTY, nullptr);
+ bool bDelMarked = true;
+
+ if( 1 == rMrkList.GetMarkCount() )
+ {
+ SdrObject *pObj = rMrkList.GetMark( 0 )->GetMarkedSdrObj();
+ if( auto pDrawObj = dynamic_cast<SwVirtFlyDrawObj*>( pObj) )
+ {
+ SwFlyFrameFormat* pFrameFormat = pDrawObj->GetFlyFrame()->GetFormat();
+ if( pFrameFormat )
+ {
+ getIDocumentLayoutAccess().DelLayoutFormat( pFrameFormat );
+ bDelMarked = false;
+ }
+ }
+ }
+
+ for( size_t i = 0; i < rMrkList.GetMarkCount(); ++i )
+ {
+ SdrObject *pObj = rMrkList.GetMark( i )->GetMarkedSdrObj();
+ if( dynamic_cast<const SwVirtFlyDrawObj*>( pObj) == nullptr )
+ {
+ SwDrawContact *pC = static_cast<SwDrawContact*>(GetUserCall(pObj));
+ SwDrawFrameFormat *pFrameFormat = static_cast<SwDrawFrameFormat*>(pC->GetFormat());
+ if( pFrameFormat &&
+ RndStdIds::FLY_AS_CHAR == pFrameFormat->GetAnchor().GetAnchorId() )
+ {
+ rDrawView.MarkObj( pObj, rDrawView.Imp().GetPageView(), true );
+ --i;
+ getIDocumentLayoutAccess().DelLayoutFormat( pFrameFormat );
+ }
+ }
+ }
+
+ if( rMrkList.GetMarkCount() && bDelMarked )
+ {
+ SdrObject *pObj = rMrkList.GetMark( 0 )->GetMarkedSdrObj();
+ if( !pObj->getParentSdrObjectFromSdrObject() )
+ {
+ std::unique_ptr<SwUndoDrawDelete> pUndo;
+ if (GetIDocumentUndoRedo().DoesUndo())
+ pUndo.reset(new SwUndoDrawDelete( o3tl::narrowing<sal_uInt16>(rMrkList.GetMarkCount()), *this ));
+
+ // Destroy ContactObjects, save formats.
+ for( size_t i = 0; i < rMrkList.GetMarkCount(); ++i )
+ {
+ const SdrMark& rMark = *rMrkList.GetMark( i );
+ pObj = rMark.GetMarkedSdrObj();
+ SwDrawContact *pContact = static_cast<SwDrawContact*>(pObj->GetUserCall());
+ if( pContact ) // of course not for grouped objects
+ {
+ SwDrawFrameFormat *pFormat = static_cast<SwDrawFrameFormat*>(pContact->GetFormat());
+ // before delete of selection is performed, marked
+ // <SwDrawVirtObj>-objects have to be replaced by its
+ // reference objects. Thus, assert, if a
+ // <SwDrawVirt>-object is found in the mark list.
+ if ( dynamic_cast<const SwDrawVirtObj*>( pObj) != nullptr )
+ {
+ OSL_FAIL( "<SwDrawVirtObj> is still marked for delete. application will crash!" );
+ }
+ // Deletes itself!
+ pContact->Changed(*pObj, SdrUserCallType::Delete, pObj->GetLastBoundRect() );
+ pObj->SetUserCall( nullptr );
+
+ if( pUndo )
+ pUndo->AddObj( pFormat, rMark );
+ else
+ DelFrameFormat( pFormat );
+ }
+ }
+
+ if( pUndo )
+ {
+ GetIDocumentUndoRedo().AppendUndo( std::move(pUndo) );
+ }
+ }
+ bCallBase = true;
+ }
+ getIDocumentState().SetModified();
+
+ GetIDocumentUndoRedo().EndUndo(SwUndoId::EMPTY, nullptr);
+ }
+
+ return bCallBase;
+}
+
+ZSortFly::ZSortFly(const SwFrameFormat* pFrameFormat, const SwFormatAnchor* pFlyAn, sal_uInt32 nArrOrdNum)
+ : m_pFormat(pFrameFormat)
+ , m_pAnchor(pFlyAn)
+ , m_nOrdNum(nArrOrdNum)
+{
+ SAL_WARN_IF(m_pFormat->Which() != RES_FLYFRMFMT && m_pFormat->Which() != RES_DRAWFRMFMT, "sw.core", "What kind of format is this?");
+ m_pFormat->CallSwClientNotify(sw::GetZOrderHint(m_nOrdNum));
+}
+
+/// In the Outliner, set a link to the method for field display in edit objects.
+void SwDoc::SetCalcFieldValueHdl(Outliner* pOutliner)
+{
+ pOutliner->SetCalcFieldValueHdl(LINK(this, SwDoc, CalcFieldValueHdl));
+}
+
+/// Recognise fields/URLs in the Outliner and set how they are displayed.
+IMPL_LINK(SwDoc, CalcFieldValueHdl, EditFieldInfo*, pInfo, void)
+{
+ if (!pInfo)
+ return;
+
+ const SvxFieldItem& rField = pInfo->GetField();
+ const SvxFieldData* pField = rField.GetField();
+
+ if (auto pDateField = dynamic_cast<const SvxDateField*>( pField))
+ {
+ // Date field
+ pInfo->SetRepresentation(
+ pDateField->GetFormatted(
+ *GetNumberFormatter(), LANGUAGE_SYSTEM) );
+ }
+ else if (auto pURLField = dynamic_cast<const SvxURLField*>( pField))
+ {
+ // URL field
+ switch ( pURLField->GetFormat() )
+ {
+ case SvxURLFormat::AppDefault: //!!! Can be set in App???
+ case SvxURLFormat::Repr:
+ pInfo->SetRepresentation(pURLField->GetRepresentation());
+ break;
+
+ case SvxURLFormat::Url:
+ pInfo->SetRepresentation(pURLField->GetURL());
+ break;
+ }
+
+ sal_uInt16 nChrFormat;
+
+ if (IsVisitedURL(pURLField->GetURL()))
+ nChrFormat = RES_POOLCHR_INET_VISIT;
+ else
+ nChrFormat = RES_POOLCHR_INET_NORMAL;
+
+ SwFormat *pFormat = getIDocumentStylePoolAccess().GetCharFormatFromPool(nChrFormat);
+
+ Color aColor(COL_LIGHTBLUE);
+ if (pFormat)
+ aColor = pFormat->GetColor().GetValue();
+
+ pInfo->SetTextColor(aColor);
+ }
+ else if (dynamic_cast<const SdrMeasureField*>( pField))
+ {
+ // Clear measure field
+ pInfo->SetFieldColor(std::optional<Color>());
+ }
+ else if ( auto pTimeField = dynamic_cast<const SvxExtTimeField*>( pField) )
+ {
+ // Time field
+ pInfo->SetRepresentation(
+ pTimeField->GetFormatted(*GetNumberFormatter(), LANGUAGE_SYSTEM) );
+ }
+ else
+ {
+ OSL_FAIL("unknown field command");
+ pInfo->SetRepresentation( OUString( '?' ) );
+ }
+}
+
+// #i62875#
+namespace docfunc
+{
+ bool ExistsDrawObjs( SwDoc& p_rDoc )
+ {
+ bool bExistsDrawObjs( false );
+
+ if ( p_rDoc.getIDocumentDrawModelAccess().GetDrawModel() &&
+ p_rDoc.getIDocumentDrawModelAccess().GetDrawModel()->GetPage( 0 ) )
+ {
+ const SdrPage& rSdrPage( *(p_rDoc.getIDocumentDrawModelAccess().GetDrawModel()->GetPage( 0 )) );
+
+ SdrObjListIter aIter( &rSdrPage, SdrIterMode::Flat );
+ while( aIter.IsMore() )
+ {
+ SdrObject* pObj( aIter.Next() );
+ if ( !dynamic_cast<SwVirtFlyDrawObj*>(pObj) &&
+ !dynamic_cast<SwFlyDrawObj*>(pObj) )
+ {
+ bExistsDrawObjs = true;
+ break;
+ }
+ }
+ }
+
+ return bExistsDrawObjs;
+ }
+
+ bool AllDrawObjsOnPage( SwDoc& p_rDoc )
+ {
+ bool bAllDrawObjsOnPage( true );
+
+ if ( p_rDoc.getIDocumentDrawModelAccess().GetDrawModel() &&
+ p_rDoc.getIDocumentDrawModelAccess().GetDrawModel()->GetPage( 0 ) )
+ {
+ const SdrPage& rSdrPage( *(p_rDoc.getIDocumentDrawModelAccess().GetDrawModel()->GetPage( 0 )) );
+
+ SdrObjListIter aIter( &rSdrPage, SdrIterMode::Flat );
+ while( aIter.IsMore() )
+ {
+ SdrObject* pObj( aIter.Next() );
+ if ( !dynamic_cast<SwVirtFlyDrawObj*>(pObj) &&
+ !dynamic_cast<SwFlyDrawObj*>(pObj) )
+ {
+ SwDrawContact* pDrawContact =
+ dynamic_cast<SwDrawContact*>(::GetUserCall( pObj ));
+ if ( pDrawContact )
+ {
+ SwAnchoredDrawObject* pAnchoredDrawObj =
+ dynamic_cast<SwAnchoredDrawObject*>(pDrawContact->GetAnchoredObj( pObj ));
+
+ // error handling
+ {
+ if ( !pAnchoredDrawObj )
+ {
+ OSL_FAIL( "<docfunc::AllDrawObjsOnPage() - missing anchored draw object" );
+ bAllDrawObjsOnPage = false;
+ break;
+ }
+ }
+
+ if ( pAnchoredDrawObj->NotYetPositioned() )
+ {
+ // The drawing object isn't yet layouted.
+ // Thus, it isn't known, if all drawing objects are on page.
+ bAllDrawObjsOnPage = false;
+ break;
+ }
+ else if ( pAnchoredDrawObj->IsOutsidePage() )
+ {
+ bAllDrawObjsOnPage = false;
+ break;
+ }
+ }
+ else
+ {
+ // contact object of drawing object doesn't exists.
+ // Thus, the drawing object isn't yet positioned.
+ // Thus, it isn't known, if all drawing objects are on page.
+ bAllDrawObjsOnPage = false;
+ break;
+ }
+ }
+ }
+ }
+
+ return bAllDrawObjsOnPage;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/docedt.cxx b/sw/source/core/doc/docedt.cxx
new file mode 100644
index 0000000000..c78d8e18b6
--- /dev/null
+++ b/sw/source/core/doc/docedt.cxx
@@ -0,0 +1,866 @@
+/* -*- 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 <fmtanchr.hxx>
+#include <fmtcntnt.hxx>
+#include <acorrect.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <docsh.hxx>
+#include <docary.hxx>
+#include <mdiexp.hxx>
+#include <mvsave.hxx>
+#include <redline.hxx>
+#include <rolbck.hxx>
+#include <rootfrm.hxx>
+#include <splargs.hxx>
+#include <swcrsr.hxx>
+#include <txtfrm.hxx>
+#include <unoflatpara.hxx>
+#include <SwGrammarMarkUp.hxx>
+#include <docedt.hxx>
+#include <frmfmt.hxx>
+#include <ndtxt.hxx>
+#include <undobj.hxx>
+#include <frameformats.hxx>
+
+#include <vector>
+#include <com/sun/star/linguistic2/XProofreadingIterator.hpp>
+#include <osl/diagnose.h>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::linguistic2;
+using namespace ::com::sun::star::i18n;
+
+
+void RestFlyInRange( SaveFlyArr & rArr, const SwPosition& rStartPos,
+ const SwNode* pInsertPos, bool const isForceToStartPos)
+{
+ SwPosition aPos(rStartPos);
+ for(const SaveFly & rSave : rArr)
+ {
+ // create new anchor
+ SwFrameFormat* pFormat = rSave.pFrameFormat;
+ SwFormatAnchor aAnchor( pFormat->GetAnchor() );
+
+ if (rSave.isAtInsertNode || isForceToStartPos)
+ {
+ if( pInsertPos != nullptr )
+ {
+ if (aAnchor.GetAnchorId() == RndStdIds::FLY_AT_PARA)
+ {
+ assert(pInsertPos->GetContentNode());
+ aPos.Assign( *pInsertPos->GetContentNode(),
+ rSave.nContentIndex);
+ }
+ else
+ {
+ assert(aAnchor.GetAnchorId() == RndStdIds::FLY_AT_CHAR);
+ aPos = rStartPos;
+ }
+ }
+ else
+ {
+ aPos.Assign(rStartPos.GetNode());
+ assert(aPos.GetNode().GetContentNode());
+ }
+ }
+ else
+ {
+ aPos.Assign(rStartPos.GetNodeIndex() + rSave.nNdDiff);
+ assert(aPos.GetNode().GetContentNode());
+ aPos.SetContent(
+ rSave.nNdDiff == SwNodeOffset(0)
+ ? rStartPos.GetContentIndex() + rSave.nContentIndex
+ : rSave.nContentIndex);
+ }
+
+ aAnchor.SetAnchor( &aPos );
+ pFormat->GetDoc()->GetSpzFrameFormats()->push_back(static_cast<sw::SpzFrameFormat*>(pFormat));
+ // SetFormatAttr should call Modify() and add it to the node
+ pFormat->SetFormatAttr( aAnchor );
+ SwContentNode* pCNd = aPos.GetNode().GetContentNode();
+ if (pCNd && pCNd->getLayoutFrame(pFormat->GetDoc()->getIDocumentLayoutAccess().GetCurrentLayout(), nullptr, nullptr))
+ pFormat->MakeFrames();
+ }
+ sw::CheckAnchoredFlyConsistency(rStartPos.GetNode().GetDoc());
+}
+
+void SaveFlyInRange( const SwNodeRange& rRg, SaveFlyArr& rArr )
+{
+ sw::SpzFrameFormats& rSpzs = *rRg.aStart.GetNode().GetDoc().GetSpzFrameFormats();
+ for(sw::FrameFormats<sw::SpzFrameFormat*>::size_type n = 0; n < rSpzs.size(); ++n )
+ {
+ auto pSpz = rSpzs[n];
+ SwFormatAnchor const*const pAnchor = &pSpz->GetAnchor();
+ SwNode const*const pAnchorNode = pAnchor->GetAnchorNode();
+ if (pAnchorNode &&
+ ((RndStdIds::FLY_AT_PARA == pAnchor->GetAnchorId()) ||
+ (RndStdIds::FLY_AT_CHAR == pAnchor->GetAnchorId())) &&
+ rRg.aStart <= *pAnchorNode && *pAnchorNode < rRg.aEnd.GetNode() )
+ {
+ SaveFly aSave( pAnchorNode->GetIndex() - rRg.aStart.GetIndex(),
+ (RndStdIds::FLY_AT_CHAR == pAnchor->GetAnchorId())
+ ? pAnchor->GetAnchorContentOffset()
+ : 0,
+ pSpz, false );
+ rArr.push_back( aSave );
+ pSpz->DelFrames();
+ // set a dummy anchor position to maintain anchoring invariants
+ SwFormatAnchor aAnchor( pSpz->GetAnchor() );
+ aAnchor.SetAnchor(nullptr);
+ pSpz->SetFormatAttr(aAnchor);
+ rSpzs.erase( rSpzs.begin() + n-- );
+ }
+ }
+ sw::CheckAnchoredFlyConsistency(rRg.aStart.GetNode().GetDoc());
+}
+
+void SaveFlyInRange( const SwPaM& rPam, const SwPosition& rInsPos,
+ SaveFlyArr& rArr, bool bMoveAllFlys, SwHistory *const pHistory)
+{
+ sw::SpzFrameFormats& rFormats = *rPam.GetPoint()->GetNode().GetDoc().GetSpzFrameFormats();
+ sw::SpzFrameFormat* pFormat;
+ const SwFormatAnchor* pAnchor;
+
+ const SwPosition* pPos = rPam.Start();
+ const SwNode& rSttNd = pPos->GetNode();
+
+ SwPosition atParaEnd(*rPam.End());
+ if (bMoveAllFlys)
+ {
+ assert(!rPam.End()->GetNode().IsTextNode() // can be table end-node
+ || rPam.End()->GetContentIndex() == rPam.End()->GetNode().GetTextNode()->Len());
+ atParaEnd.Adjust(SwNodeOffset(1));
+ }
+
+ for(sw::FrameFormats<sw::SpzFrameFormat*>::size_type n = 0; n < rFormats.size(); ++n )
+ {
+ pFormat = rFormats[n];
+ pAnchor = &pFormat->GetAnchor();
+ const SwPosition* pAPos = pAnchor->GetContentAnchor();
+ const SwNodeIndex* pContentIdx;
+ if (pAPos &&
+ ((RndStdIds::FLY_AT_PARA == pAnchor->GetAnchorId()) ||
+ (RndStdIds::FLY_AT_CHAR == pAnchor->GetAnchorId())) &&
+ // do not move if the InsPos is in the ContentArea of the Fly
+ ( nullptr == ( pContentIdx = pFormat->GetContent().GetContentIdx() ) ||
+ (*pContentIdx >= rInsPos.GetNode() ||
+ rInsPos.GetNode() >= *pContentIdx->GetNode().EndOfSectionNode())))
+ {
+ bool bInsPos = false;
+
+ if ( (RndStdIds::FLY_AT_CHAR == pAnchor->GetAnchorId()
+ && IsDestroyFrameAnchoredAtChar(*pAPos, *rPam.Start(), *rPam.End()))
+ || (RndStdIds::FLY_AT_PARA == pAnchor->GetAnchorId()
+ && IsSelectFrameAnchoredAtPara(*pAPos, *rPam.Start(), atParaEnd,
+ bMoveAllFlys
+ ? DelContentType::CheckNoCntnt|DelContentType::AllMask
+ : DelContentType::AllMask))
+ || (RndStdIds::FLY_AT_PARA == pAnchor->GetAnchorId()
+ && (bInsPos = (rInsPos.GetNode() == pAPos->GetNode())))
+ || (RndStdIds::FLY_AT_CHAR == pAnchor->GetAnchorId()
+ && (bInsPos = (rInsPos == *pAPos))))
+ {
+ if (pHistory)
+ {
+ pHistory->AddChangeFlyAnchor(*pFormat);
+ }
+ SaveFly aSave( pAPos->GetNodeIndex() - rSttNd.GetIndex(),
+ (RndStdIds::FLY_AT_CHAR == pAnchor->GetAnchorId())
+ ? (pAPos->GetNode() == rSttNd)
+ ? pAPos->GetContentIndex() - rPam.Start()->GetContentIndex()
+ : pAPos->GetContentIndex()
+ : 0,
+ pFormat, bInsPos );
+ rArr.push_back( aSave );
+ pFormat->DelFrames();
+ // set a dummy anchor position to maintain anchoring invariants
+ SwFormatAnchor aAnchor( pFormat->GetAnchor() );
+ aAnchor.SetAnchor(nullptr);
+ pFormat->SetFormatAttr(aAnchor);
+ rFormats.erase( rFormats.begin() + n-- );
+ }
+ }
+ }
+ sw::CheckAnchoredFlyConsistency(rPam.GetPoint()->GetNode().GetDoc());
+}
+
+/// Delete and move all Flys at the paragraph, that are within the selection.
+/// If there is a Fly at the SPoint, it is moved onto the Mark.
+void DelFlyInRange( SwNode& rMkNd,
+ SwNode& rPtNd,
+ std::optional<sal_Int32> oMkContentIdx, std::optional<sal_Int32> oPtContentIdx)
+{
+ assert(oMkContentIdx.has_value() == oPtContentIdx.has_value());
+ SwPosition const point(oPtContentIdx
+ ? SwPosition(rPtNd, rPtNd.GetContentNode(), *oPtContentIdx)
+ : SwPosition(rPtNd));
+ SwPosition const mark(oPtContentIdx
+ ? SwPosition(rMkNd, rMkNd.GetContentNode(), *oMkContentIdx)
+ : SwPosition(rMkNd));
+ SwPosition const& rStart = mark <= point ? mark : point;
+ SwPosition const& rEnd = mark <= point ? point : mark;
+
+ SwDoc& rDoc = rMkNd.GetDoc();
+ sw::SpzFrameFormats& rTable = *rDoc.GetSpzFrameFormats();
+ for ( auto i = rTable.size(); i; )
+ {
+ sw::SpzFrameFormat* pFormat = rTable[--i];
+ const SwFormatAnchor &rAnch = pFormat->GetAnchor();
+ SwPosition const*const pAPos = rAnch.GetContentAnchor();
+ if (pAPos &&
+ (((rAnch.GetAnchorId() == RndStdIds::FLY_AT_PARA)
+ && IsSelectFrameAnchoredAtPara(*pAPos, rStart, rEnd, oPtContentIdx
+ ? DelContentType::AllMask|DelContentType::WriterfilterHack
+ : DelContentType::AllMask|DelContentType::WriterfilterHack|DelContentType::CheckNoCntnt))
+ || ((rAnch.GetAnchorId() == RndStdIds::FLY_AT_CHAR)
+ && IsDestroyFrameAnchoredAtChar(*pAPos, rStart, rEnd, oPtContentIdx
+ ? DelContentType::AllMask|DelContentType::WriterfilterHack
+ : DelContentType::AllMask|DelContentType::WriterfilterHack|DelContentType::CheckNoCntnt))))
+ {
+ // If the Fly is deleted, all Flys in its content have to be deleted too.
+ const SwFormatContent &rContent = pFormat->GetContent();
+ // But only fly formats own their content, not draw formats.
+ if (rContent.GetContentIdx() && pFormat->Which() == RES_FLYFRMFMT)
+ {
+ DelFlyInRange( rContent.GetContentIdx()->GetNode(),
+ *rContent.GetContentIdx()->
+ GetNode().EndOfSectionNode() );
+ // Position could have been moved!
+ if (i > rTable.size())
+ i = rTable.size();
+ else if (i == rTable.size() || pFormat != rTable[i])
+ i = std::distance(rTable.begin(), rTable.find(pFormat));
+ }
+
+ rDoc.getIDocumentLayoutAccess().DelLayoutFormat( pFormat );
+
+ // DelLayoutFormat can also trigger the deletion of objects.
+ if (i > rTable.size())
+ i = rTable.size();
+ }
+ }
+}
+
+// #i59534: Redo of insertion of multiple text nodes runs into trouble
+// because of unnecessary expanded redlines
+// From now on this class saves the redline positions of all redlines which ends exact at the
+// insert position (node _and_ content index)
+SaveRedlEndPosForRestore::SaveRedlEndPosForRestore( const SwNode& rInsIdx, sal_Int32 nCnt )
+ : mnSaveContent( nCnt )
+{
+ const SwDoc& rDest = rInsIdx.GetDoc();
+ if( rDest.getIDocumentRedlineAccess().GetRedlineTable().empty() )
+ return;
+
+ SwRedlineTable::size_type nFndPos;
+ const SwPosition* pEnd;
+ SwPosition aSrcPos( rInsIdx, rInsIdx.GetContentNode(), nCnt );
+ rDest.getIDocumentRedlineAccess().GetRedline( aSrcPos, &nFndPos );
+ const SwRangeRedline* pRedl;
+ while( nFndPos--
+ && *( pEnd = ( pRedl = rDest.getIDocumentRedlineAccess().GetRedlineTable()[ nFndPos ] )->End() ) == aSrcPos
+ && *pRedl->Start() < aSrcPos )
+ {
+ if( !moSaveIndex )
+ {
+ moSaveIndex.emplace( rInsIdx, -1 );
+ }
+ mvSavArr.push_back( const_cast<SwPosition*>(pEnd) );
+ }
+}
+
+SaveRedlEndPosForRestore::~SaveRedlEndPosForRestore()
+{
+ moSaveIndex.reset();
+}
+
+void SaveRedlEndPosForRestore::Restore()
+{
+ if (mvSavArr.empty())
+ return;
+ ++(*moSaveIndex);
+ SwContentNode* pNode = moSaveIndex->GetNode().GetContentNode();
+ // If there's no content node at the remembered position, we will not restore the old position
+ // This may happen if a table (or section?) will be inserted.
+ if( pNode )
+ {
+ SwPosition aPos( *moSaveIndex, pNode, mnSaveContent );
+ for( auto n = mvSavArr.size(); n; )
+ *mvSavArr[ --n ] = aPos;
+ }
+}
+
+/// Convert list of ranges of whichIds to a corresponding list of whichIds
+static std::vector<sal_uInt16> lcl_RangesToVector(const WhichRangesContainer& pRanges)
+{
+ std::vector<sal_uInt16> aResult;
+
+ for(const WhichPair& rPair : pRanges)
+ {
+ for (sal_uInt16 j = rPair.first; j <= rPair.second; j++)
+ aResult.push_back(j);
+ }
+
+ return aResult;
+}
+
+void sw_GetJoinFlags( SwPaM& rPam, bool& rJoinText, bool& rJoinPrev )
+{
+ rJoinText = false;
+ rJoinPrev = false;
+ if( rPam.GetPoint()->GetNode() == rPam.GetMark()->GetNode() )
+ return;
+
+ auto [pStt, pEnd] = rPam.StartEnd(); // SwPosition*
+ SwTextNode *pSttNd = pStt->GetNode().GetTextNode();
+ if( !pSttNd )
+ return;
+
+ SwTextNode *pEndNd = pEnd->GetNode().GetTextNode();
+ rJoinText = nullptr != pEndNd;
+ if( !rJoinText )
+ return;
+
+ bool bExchange = pStt == rPam.GetPoint();
+ if( !pStt->GetContentIndex() &&
+ pEndNd->GetText().getLength() != pEnd->GetContentIndex())
+ bExchange = !bExchange;
+ if( bExchange )
+ rPam.Exchange();
+ rJoinPrev = rPam.GetPoint() == pStt;
+ OSL_ENSURE( !pStt->GetContentIndex() &&
+ pEndNd->GetText().getLength() != pEnd->GetContentIndex()
+ ? (rPam.GetPoint()->GetNode() < rPam.GetMark()->GetNode())
+ : (rPam.GetPoint()->GetNode() > rPam.GetMark()->GetNode()),
+ "sw_GetJoinFlags");
+}
+
+bool sw_JoinText( SwPaM& rPam, bool bJoinPrev )
+{
+ SwNodeIndex aIdx( rPam.GetPoint()->GetNode() );
+ SwTextNode *pTextNd = aIdx.GetNode().GetTextNode();
+ SwNodeIndex aOldIdx( aIdx );
+ SwTextNode *pOldTextNd = pTextNd;
+
+ if( pTextNd && pTextNd->CanJoinNext( &aIdx ) )
+ {
+ SwDoc& rDoc = rPam.GetDoc();
+ if( bJoinPrev )
+ {
+ // We do not need to handle xmlids in this case, because
+ // it is only invoked if one paragraph is/becomes completely empty
+ // (see sw_GetJoinFlags)
+ {
+ // If PageBreaks are deleted/set, it must not be added to the Undo history!
+ // Also, deleting the Node is not added to the Undo history!
+ ::sw::UndoGuard const undoGuard(rDoc.GetIDocumentUndoRedo());
+
+ /* PageBreaks, PageDesc, ColumnBreaks */
+ // If we need to change something about the logic to copy the PageBreaks,
+ // PageDesc, etc. we also have to change SwUndoDelete.
+ // There, we copy the AUTO PageBreak from the GetMarkNode!
+
+ /* The MarkNode */
+ pTextNd = aIdx.GetNode().GetTextNode();
+ if (pTextNd->HasSwAttrSet())
+ {
+ if( SfxItemState::SET == pTextNd->GetpSwAttrSet()->GetItemState( RES_BREAK, false) )
+ pTextNd->ResetAttr( RES_BREAK );
+ if( pTextNd->HasSwAttrSet() &&
+ SfxItemState::SET == pTextNd->GetpSwAttrSet()->GetItemState( RES_PAGEDESC, false ) )
+ pTextNd->ResetAttr( RES_PAGEDESC );
+ }
+
+ /* The PointNode */
+ if( pOldTextNd->HasSwAttrSet() )
+ {
+ const SfxPoolItem* pItem;
+ SfxItemSet aSet( rDoc.GetAttrPool(), aBreakSetRange );
+ const SfxItemSet* pSet = pOldTextNd->GetpSwAttrSet();
+ if( SfxItemState::SET == pSet->GetItemState( RES_BREAK,
+ false, &pItem ) )
+ aSet.Put( *pItem );
+ if( SfxItemState::SET == pSet->GetItemState( RES_PAGEDESC,
+ false, &pItem ) )
+ aSet.Put( *pItem );
+ if( aSet.Count() )
+ pTextNd->SetAttr( aSet );
+ }
+ pOldTextNd->FormatToTextAttr( pTextNd );
+
+ const std::shared_ptr< sw::mark::ContentIdxStore> pContentStore(sw::mark::ContentIdxStore::Create());
+ pContentStore->Save(rDoc, aOldIdx.GetIndex(), SAL_MAX_INT32);
+
+ SwContentIndex aAlphaIdx(pTextNd);
+ pOldTextNd->CutText( pTextNd, aAlphaIdx, SwContentIndex(pOldTextNd),
+ pOldTextNd->Len() );
+ SwPosition aAlphaPos( aIdx, aAlphaIdx );
+ rDoc.CorrRel( rPam.GetPoint()->GetNode(), aAlphaPos, 0, true );
+
+ // move all Bookmarks/TOXMarks
+ if( !pContentStore->Empty() )
+ pContentStore->Restore( rDoc, aIdx.GetIndex() );
+
+ // If the passed PaM is not in the Cursor ring,
+ // treat it separately (e.g. when it's being called from AutoFormat)
+ if( pOldTextNd == rPam.GetBound().GetContentNode() )
+ rPam.GetBound() = aAlphaPos;
+ if( pOldTextNd == rPam.GetBound( false ).GetContentNode() )
+ rPam.GetBound( false ) = aAlphaPos;
+ }
+ // delete the Node, at last!
+ SwNode::Merge const eOldMergeFlag(pOldTextNd->GetRedlineMergeFlag());
+ if (eOldMergeFlag == SwNode::Merge::First
+ && !pTextNd->IsCreateFrameWhenHidingRedlines())
+ {
+ sw::MoveDeletedPrevFrames(*pOldTextNd, *pTextNd);
+ }
+ rDoc.GetNodes().Delete( aOldIdx );
+ sw::CheckResetRedlineMergeFlag(*pTextNd,
+ eOldMergeFlag == SwNode::Merge::NonFirst
+ ? sw::Recreate::Predecessor
+ : sw::Recreate::No);
+ }
+ else
+ {
+ SwTextNode* pDelNd = aIdx.GetNode().GetTextNode();
+ if( pTextNd->Len() )
+ pDelNd->FormatToTextAttr( pTextNd );
+ else
+ {
+ /* This case was missed:
+
+ <something></something> <-- pTextNd
+ <other>ccc</other> <-- pDelNd
+
+ <something> and <other> are paragraph
+ attributes. The attribute <something> stayed if not
+ overwritten by an attribute in "ccc". Fixed by
+ first resetting all character attributes in first
+ paragraph (pTextNd).
+ */
+ std::vector<sal_uInt16> aShorts =
+ lcl_RangesToVector(aCharFormatSetRange);
+ pTextNd->ResetAttr(aShorts);
+
+ if( pDelNd->HasSwAttrSet() )
+ {
+ // only copy the character attributes
+ SfxItemSet aTmpSet( rDoc.GetAttrPool(), aCharFormatSetRange );
+ aTmpSet.Put( *pDelNd->GetpSwAttrSet() );
+ pTextNd->SetAttr( aTmpSet );
+ }
+ }
+
+ rDoc.CorrRel( aIdx.GetNode(), *rPam.GetPoint(), 0, true );
+ // #i100466# adjust given <rPam>, if it does not belong to the cursors
+ if ( pDelNd == rPam.GetBound().GetContentNode() )
+ {
+ rPam.GetBound().Assign( *pTextNd );
+ }
+ if( pDelNd == rPam.GetBound( false ).GetContentNode() )
+ {
+ rPam.GetBound( false ).Assign( *pTextNd );
+ }
+ pTextNd->JoinNext();
+ }
+ return true;
+ }
+ else return false;
+}
+
+static void lcl_syncGrammarError( SwTextNode &rTextNode, linguistic2::ProofreadingResult& rResult,
+ const ModelToViewHelper &rConversionMap )
+{
+ if( rTextNode.IsGrammarCheckDirty() )
+ return;
+ SwGrammarMarkUp* pWrong = rTextNode.GetGrammarCheck();
+ linguistic2::SingleProofreadingError* pArray = rResult.aErrors.getArray();
+ sal_uInt16 j = 0;
+ if( pWrong )
+ {
+ for( sal_Int32 i = 0; i < rResult.aErrors.getLength(); ++i )
+ {
+ const linguistic2::SingleProofreadingError &rError = rResult.aErrors[i];
+ const sal_Int32 nStart = rConversionMap.ConvertToModelPosition( rError.nErrorStart ).mnPos;
+ const sal_Int32 nEnd = rConversionMap.ConvertToModelPosition( rError.nErrorStart + rError.nErrorLength ).mnPos;
+ if( i != j )
+ pArray[j] = pArray[i];
+ if( pWrong->LookForEntry( nStart, nEnd ) )
+ ++j;
+ }
+ }
+ if( rResult.aErrors.getLength() > j )
+ rResult.aErrors.realloc( j );
+}
+
+uno::Any SwDoc::Spell( SwPaM& rPaM,
+ uno::Reference< XSpellChecker1 > const &xSpeller,
+ sal_uInt16* pPageCnt, sal_uInt16* pPageSt,
+ bool bGrammarCheck,
+ SwRootFrame const*const pLayout,
+ SwConversionArgs *pConvArgs ) const
+{
+ SwPosition* const pSttPos = rPaM.Start();
+ SwPosition* const pEndPos = rPaM.End();
+
+ std::unique_ptr<SwSpellArgs> pSpellArgs;
+ if (pConvArgs)
+ {
+ pConvArgs->SetStart(*pSttPos);
+ pConvArgs->SetEnd(*pEndPos);
+ }
+ else
+ pSpellArgs.reset(new SwSpellArgs( xSpeller, *pSttPos, *pEndPos, bGrammarCheck ));
+
+ SwNodeOffset nCurrNd = pSttPos->GetNodeIndex();
+ SwNodeOffset nEndNd = pEndPos->GetNodeIndex();
+
+ uno::Any aRet;
+ if( nCurrNd <= nEndNd )
+ {
+ SwContentFrame* pContentFrame;
+ bool bGoOn = true;
+ while( bGoOn )
+ {
+ SwNode* pNd = GetNodes()[ nCurrNd ];
+ switch( pNd->GetNodeType() )
+ {
+ case SwNodeType::Text:
+ if( nullptr != ( pContentFrame = pNd->GetTextNode()->getLayoutFrame( getIDocumentLayoutAccess().GetCurrentLayout() )) )
+ {
+ // skip protected and hidden Cells and Flys
+ if( pContentFrame->IsProtected() )
+ {
+ nCurrNd = pNd->EndOfSectionIndex();
+ }
+ else if( !static_cast<SwTextFrame*>(pContentFrame)->IsHiddenNow() )
+ {
+ if( pPageCnt && *pPageCnt && pPageSt )
+ {
+ sal_uInt16 nPageNr = pContentFrame->GetPhyPageNum();
+ if( !*pPageSt )
+ {
+ *pPageSt = nPageNr;
+ if( *pPageCnt < *pPageSt )
+ *pPageCnt = *pPageSt;
+ }
+ tools::Long nStat;
+ if( nPageNr >= *pPageSt )
+ nStat = nPageNr - *pPageSt + 1;
+ else
+ nStat = nPageNr + *pPageCnt - *pPageSt + 1;
+ ::SetProgressState( nStat, GetDocShell() );
+ }
+ //Spell() changes the pSpellArgs in case an error is found
+ sal_Int32 nBeginGrammarCheck = 0;
+ sal_Int32 nEndGrammarCheck = 0;
+ if( pSpellArgs && pSpellArgs->bIsGrammarCheck)
+ {
+ nBeginGrammarCheck = &pSpellArgs->pStartPos->GetNode() == pNd ? pSpellArgs->pStartPos->GetContentIndex() : 0;
+ // if grammar checking starts inside of a sentence the start position has to be adjusted
+ if( nBeginGrammarCheck )
+ {
+ SwContentIndex aStartIndex( pNd->GetTextNode(), nBeginGrammarCheck );
+ SwPosition aStart( *pNd, aStartIndex );
+ SwCursor aCursor(aStart, nullptr);
+ SwPosition aOrigPos = *aCursor.GetPoint();
+ aCursor.GoSentence( SwCursor::START_SENT );
+ if( aOrigPos != *aCursor.GetPoint() )
+ {
+ nBeginGrammarCheck = aCursor.GetPoint()->GetContentIndex();
+ }
+ }
+ nEndGrammarCheck = (&pSpellArgs->pEndPos->GetNode() == pNd)
+ ? pSpellArgs->pEndPos->GetContentIndex()
+ : pNd->GetTextNode()
+ ->GetText().getLength();
+ }
+
+ sal_Int32 nSpellErrorPosition = pNd->GetTextNode()->GetText().getLength();
+ if( (!pConvArgs && pNd->GetTextNode()->Spell( pSpellArgs.get() )) ||
+ ( pConvArgs && pNd->GetTextNode()->Convert( *pConvArgs )))
+ {
+ // Cancel and remember position
+ if( pSpellArgs )
+ nSpellErrorPosition = pSpellArgs->pStartPos->GetContentIndex() > pSpellArgs->pEndPos->GetContentIndex() ?
+ pSpellArgs->pEndPos->GetContentIndex() :
+ pSpellArgs->pStartPos->GetContentIndex();
+ if( nCurrNd != nEndNd )
+ {
+ pSttPos->Assign(nCurrNd, pSttPos->GetContentIndex());
+ pEndPos->Assign(nCurrNd, pEndPos->GetContentIndex());
+ nCurrNd = nEndNd;
+ }
+ }
+
+ if( pSpellArgs && pSpellArgs->bIsGrammarCheck )
+ {
+ uno::Reference< linguistic2::XProofreadingIterator > xGCIterator( GetGCIterator() );
+ if (xGCIterator.is())
+ {
+ uno::Reference< lang::XComponent > xDoc = GetDocShell()->GetBaseModel();
+ // Expand the string:
+ const ModelToViewHelper aConversionMap(*pNd->GetTextNode(), pLayout);
+ const OUString& aExpandText = aConversionMap.getViewText();
+
+ // get XFlatParagraph to use...
+ uno::Reference< text::XFlatParagraph > xFlatPara = new SwXFlatParagraph( *pNd->GetTextNode(), aExpandText, aConversionMap );
+
+ // get error position of cursor in XFlatParagraph
+ linguistic2::ProofreadingResult aResult;
+ bool bGrammarErrors;
+ do
+ {
+ aConversionMap.ConvertToViewPosition( nBeginGrammarCheck );
+ aResult = xGCIterator->checkSentenceAtPosition(
+ xDoc, xFlatPara, aExpandText, lang::Locale(), nBeginGrammarCheck, -1, -1 );
+
+ lcl_syncGrammarError( *pNd->GetTextNode(), aResult, aConversionMap );
+
+ // get suggestions to use for the specific error position
+ bGrammarErrors = aResult.aErrors.hasElements();
+ // if grammar checking doesn't have any progress then quit
+ if( aResult.nStartOfNextSentencePosition <= nBeginGrammarCheck )
+ break;
+ // prepare next iteration
+ nBeginGrammarCheck = aResult.nStartOfNextSentencePosition;
+ }
+ while( nSpellErrorPosition > aResult.nBehindEndOfSentencePosition && !bGrammarErrors && aResult.nBehindEndOfSentencePosition < nEndGrammarCheck );
+
+ if( bGrammarErrors && nSpellErrorPosition >= aResult.nBehindEndOfSentencePosition )
+ {
+ aRet <<= aResult;
+ //put the cursor to the current error
+ const linguistic2::SingleProofreadingError &rError = aResult.aErrors[0];
+ pSttPos->Assign(nCurrNd, pSttPos->GetContentIndex());
+ pEndPos->Assign(nCurrNd, pEndPos->GetContentIndex());
+ pSpellArgs->pStartPos->Assign(*pNd->GetTextNode(), aConversionMap.ConvertToModelPosition( rError.nErrorStart ).mnPos );
+ pSpellArgs->pEndPos->Assign(*pNd->GetTextNode(), aConversionMap.ConvertToModelPosition( rError.nErrorStart + rError.nErrorLength ).mnPos );
+ nCurrNd = nEndNd;
+ }
+ }
+ }
+ }
+ }
+ break;
+ case SwNodeType::Section:
+ if( static_cast<SwSectionNode*>(pNd)->GetSection().IsProtect() ||
+ static_cast<SwSectionNode*>(pNd)->GetSection().IsHidden() )
+ nCurrNd = pNd->EndOfSectionIndex();
+ break;
+ case SwNodeType::End:
+ {
+ break;
+ }
+ default: break;
+ }
+
+ bGoOn = nCurrNd < nEndNd;
+ ++nCurrNd;
+ }
+ }
+
+ if( !aRet.hasValue() )
+ {
+ if (pConvArgs)
+ aRet <<= pConvArgs->aConvText;
+ else
+ aRet <<= pSpellArgs->xSpellAlt;
+ }
+
+ return aRet;
+}
+
+namespace {
+
+class SwHyphArgs : public SwInterHyphInfo
+{
+ SwNodeIndex m_aNodeIdx;
+ const SwNode *m_pStart;
+ const SwNode *m_pEnd;
+ sal_uInt16 *m_pPageCnt;
+ sal_uInt16 *m_pPageSt;
+
+ sal_Int32 m_nPamStart;
+ sal_Int32 m_nPamLen;
+
+public:
+ SwHyphArgs( const SwPaM *pPam, const Point &rPoint,
+ sal_uInt16* pPageCount, sal_uInt16* pPageStart );
+ void SetPam( SwPaM *pPam ) const;
+ void SetNode( SwNode& rNew ) { m_aNodeIdx.Assign(rNew); }
+ inline void SetRange( const SwNode *pNew );
+ void NextNode() { ++m_aNodeIdx; }
+ sal_uInt16 *GetPageCnt() { return m_pPageCnt; }
+ sal_uInt16 *GetPageSt() { return m_pPageSt; }
+};
+
+}
+
+SwHyphArgs::SwHyphArgs( const SwPaM *pPam, const Point &rCursorPos,
+ sal_uInt16* pPageCount, sal_uInt16* pPageStart )
+ : SwInterHyphInfo( rCursorPos ), m_aNodeIdx(pPam->GetPoint()->GetNode()),
+ m_pPageCnt( pPageCount ), m_pPageSt( pPageStart )
+{
+ // The following constraints have to be met:
+ // 1) there is at least one Selection
+ // 2) SPoint() == Start()
+ OSL_ENSURE( pPam->HasMark(), "SwDoc::Hyphenate: blowing in the wind");
+ OSL_ENSURE( *pPam->GetPoint() <= *pPam->GetMark(),
+ "SwDoc::Hyphenate: New York, New York");
+
+ const SwPosition *pPoint = pPam->GetPoint();
+
+ // Set start
+ m_pStart = pPoint->GetNode().GetTextNode();
+ m_nPamStart = pPoint->GetContentIndex();
+
+ // Set End and Length
+ const SwPosition *pMark = pPam->GetMark();
+ m_pEnd = pMark->GetNode().GetTextNode();
+ m_nPamLen = pMark->GetContentIndex();
+ if( pPoint->GetNode() == pMark->GetNode() )
+ m_nPamLen = m_nPamLen - pPoint->GetContentIndex();
+}
+
+inline void SwHyphArgs::SetRange( const SwNode *pNew )
+{
+ m_nStart = m_pStart == pNew ? m_nPamStart : 0;
+ m_nEnd = m_pEnd == pNew ? m_nPamStart + m_nPamLen : SAL_MAX_INT32;
+}
+
+void SwHyphArgs::SetPam( SwPaM *pPam ) const
+{
+ pPam->GetPoint()->Assign( m_aNodeIdx, m_nWordStart );
+ pPam->GetMark()->Assign( m_aNodeIdx, m_nWordStart + m_nWordLen );
+}
+
+// Returns true if we can proceed.
+static bool lcl_HyphenateNode( SwNode* pNd, void* pArgs )
+{
+ // Hyphenate returns true if there is a hyphenation point and sets pPam
+ SwTextNode *pNode = pNd->GetTextNode();
+ SwHyphArgs *pHyphArgs = static_cast<SwHyphArgs*>(pArgs);
+ if( pNode )
+ {
+ // sw_redlinehide: this will be called once per node for merged nodes;
+ // the fully deleted ones won't have frames so are skipped.
+ SwContentFrame* pContentFrame = pNode->getLayoutFrame( pNode->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout() );
+ if( pContentFrame && !static_cast<SwTextFrame*>(pContentFrame)->IsHiddenNow() )
+ {
+ sal_uInt16 *pPageSt = pHyphArgs->GetPageSt();
+ sal_uInt16 *pPageCnt = pHyphArgs->GetPageCnt();
+ if( pPageCnt && *pPageCnt && pPageSt )
+ {
+ sal_uInt16 nPageNr = pContentFrame->GetPhyPageNum();
+ if( !*pPageSt )
+ {
+ *pPageSt = nPageNr;
+ if( *pPageCnt < *pPageSt )
+ *pPageCnt = *pPageSt;
+ }
+ tools::Long nStat = nPageNr >= *pPageSt ? nPageNr - *pPageSt + 1
+ : nPageNr + *pPageCnt - *pPageSt + 1;
+ ::SetProgressState( nStat, pNode->GetDoc().GetDocShell() );
+ }
+ pHyphArgs->SetRange( pNd );
+ if( pNode->Hyphenate( *pHyphArgs ) )
+ {
+ pHyphArgs->SetNode( *pNd );
+ return false;
+ }
+ }
+ }
+ pHyphArgs->NextNode();
+ return true;
+}
+
+uno::Reference< XHyphenatedWord > SwDoc::Hyphenate(
+ SwPaM *pPam, const Point &rCursorPos,
+ sal_uInt16* pPageCnt, sal_uInt16* pPageSt )
+{
+ OSL_ENSURE(this == &pPam->GetDoc(), "SwDoc::Hyphenate: strangers in the night");
+
+ if( *pPam->GetPoint() > *pPam->GetMark() )
+ pPam->Exchange();
+
+ SwHyphArgs aHyphArg( pPam, rCursorPos, pPageCnt, pPageSt );
+ SwNodeIndex aTmpIdx( pPam->GetMark()->GetNode(), 1 );
+ GetNodes().ForEach( pPam->GetPoint()->GetNode(), aTmpIdx.GetNode(),
+ lcl_HyphenateNode, &aHyphArg );
+ aHyphArg.SetPam( pPam );
+ return aHyphArg.GetHyphWord(); // will be set by lcl_HyphenateNode
+}
+
+// Save the current values to add them as automatic entries to AutoCorrect.
+void SwDoc::SetAutoCorrExceptWord( std::unique_ptr<SwAutoCorrExceptWord> pNew )
+{
+ mpACEWord = std::move(pNew);
+}
+
+void SwDoc::DeleteAutoCorrExceptWord()
+{
+ mpACEWord.reset();
+}
+
+void SwDoc::CountWords( const SwPaM& rPaM, SwDocStat& rStat )
+{
+ // This is a modified version of SwDoc::TransliterateText
+ auto [pStt, pEnd] = rPaM.StartEnd(); // SwPosition*
+
+ const SwNodeOffset nSttNd = pStt->GetNodeIndex();
+ const SwNodeOffset nEndNd = pEnd->GetNodeIndex();
+
+ const sal_Int32 nSttCnt = pStt->GetContentIndex();
+ const sal_Int32 nEndCnt = pEnd->GetContentIndex();
+
+ const SwTextNode* pTNd = pStt->GetNode().GetTextNode();
+ if( pStt == pEnd && pTNd ) // no region ?
+ {
+ // do nothing
+ return;
+ }
+
+ if( nSttNd != nEndNd )
+ {
+ SwNodeIndex aIdx( pStt->GetNode() );
+ if( nSttCnt )
+ {
+ ++aIdx;
+ if( pTNd )
+ pTNd->CountWords( rStat, nSttCnt, pTNd->GetText().getLength() );
+ }
+
+ for( ; aIdx.GetIndex() < nEndNd; ++aIdx )
+ if( nullptr != ( pTNd = aIdx.GetNode().GetTextNode() ))
+ pTNd->CountWords( rStat, 0, pTNd->GetText().getLength() );
+
+ if( nEndCnt && nullptr != ( pTNd = pEnd->GetNode().GetTextNode() ))
+ pTNd->CountWords( rStat, 0, nEndCnt );
+ }
+ else if( pTNd && nSttCnt < nEndCnt )
+ pTNd->CountWords( rStat, nSttCnt, nEndCnt );
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/docfld.cxx b/sw/source/core/doc/docfld.cxx
new file mode 100644
index 0000000000..c723ee162e
--- /dev/null
+++ b/sw/source/core/doc/docfld.cxx
@@ -0,0 +1,1230 @@
+/* -*- 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 <config_features.h>
+#include <config_fuzzers.h>
+
+#include <hintids.hxx>
+
+#include <comphelper/string.hxx>
+#include <osl/diagnose.h>
+#include <unotools/charclass.hxx>
+#ifndef UNX
+#include <unotools/transliterationwrapper.hxx>
+#endif
+#include <o3tl/string_view.hxx>
+#include <doc.hxx>
+#include <IDocumentFieldsAccess.hxx>
+#include <IDocumentMarkAccess.hxx>
+#include <IDocumentState.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <node2lay.hxx>
+#include <cntfrm.hxx>
+#include <pagefrm.hxx>
+#include <txtfrm.hxx>
+#include <notxtfrm.hxx>
+#include <pam.hxx>
+#include <ndtxt.hxx>
+#include <swtable.hxx>
+#include <calc.hxx>
+#include <txtfld.hxx>
+#include <fmtfld.hxx>
+#include <txttxmrk.hxx>
+#include <docfld.hxx>
+#include <docufld.hxx>
+#include <usrfld.hxx>
+#include <expfld.hxx>
+#include <dbfld.hxx>
+#include <reffld.hxx>
+#include <dbmgr.hxx>
+#include <section.hxx>
+#include <docary.hxx>
+#include <authfld.hxx>
+#include <txtinet.hxx>
+#include <fmtcntnt.hxx>
+#include <utility>
+
+using namespace ::com::sun::star::uno;
+
+// the StartIndex can be supplied optionally (e.g. if it was queried before - is a virtual
+// method otherwise!)
+SetGetExpField::SetGetExpField(
+ const SwNode& rNdIdx,
+ const SwTextField* pField,
+ std::optional<sal_Int32> oContentIdx,
+ sal_uInt16 const nPageNumber)
+ : m_nPageNumber(nPageNumber)
+{
+ m_eSetGetExpFieldType = TEXTFIELD;
+ m_CNTNT.pTextField = pField;
+ m_nNode = rNdIdx.GetIndex();
+ if( oContentIdx )
+ m_nContent = *oContentIdx;
+ else if( pField )
+ m_nContent = pField->GetStart();
+ else
+ m_nContent = 0;
+}
+
+SetGetExpField::SetGetExpField( const SwNode& rNdIdx,
+ const SwTextINetFormat& rINet )
+{
+ m_eSetGetExpFieldType = TEXTINET;
+ m_CNTNT.pTextINet = &rINet;
+ m_nNode = rNdIdx.GetIndex();
+ m_nContent = rINet.GetStart();
+}
+
+// Extension for Sections:
+// these always have content position 0xffffffff!
+// There is never a field on this, only up to COMPLETE_STRING possible
+SetGetExpField::SetGetExpField( const SwSectionNode& rSectNd,
+ const SwPosition* pPos,
+ sal_uInt16 const nPageNumber)
+ : m_nPageNumber(nPageNumber)
+{
+ m_eSetGetExpFieldType = SECTIONNODE;
+ m_CNTNT.pSection = &rSectNd.GetSection();
+
+ if( pPos )
+ {
+ m_nNode = pPos->GetNodeIndex();
+ m_nContent = pPos->GetContentIndex();
+ }
+ else
+ {
+ m_nNode = rSectNd.GetIndex();
+ m_nContent = 0;
+ }
+}
+
+SetGetExpField::SetGetExpField(::sw::mark::IBookmark const& rBookmark,
+ SwPosition const*const pPos,
+ sal_uInt16 const nPageNumber)
+ : m_nPageNumber(nPageNumber)
+{
+ m_eSetGetExpFieldType = BOOKMARK;
+ m_CNTNT.pBookmark = &rBookmark;
+
+ if (pPos)
+ {
+ m_nNode = pPos->GetNodeIndex();
+ m_nContent = pPos->GetContentIndex();
+ }
+ else
+ {
+ m_nNode = rBookmark.GetMarkStart().GetNodeIndex();
+ m_nContent = rBookmark.GetMarkStart().GetContentIndex();;
+ }
+}
+
+SetGetExpField::SetGetExpField( const SwTableBox& rTBox )
+{
+ m_eSetGetExpFieldType = TABLEBOX;
+ m_CNTNT.pTBox = &rTBox;
+
+ m_nNode = SwNodeOffset(0);
+ m_nContent = 0;
+ if( rTBox.GetSttNd() )
+ {
+ SwNodeIndex aIdx( *rTBox.GetSttNd() );
+ const SwContentNode* pNd = aIdx.GetNode().GetNodes().GoNext( &aIdx );
+ if( pNd )
+ m_nNode = pNd->GetIndex();
+ }
+}
+
+SetGetExpField::SetGetExpField( const SwNode& rNdIdx,
+ const SwTextTOXMark& rTOX )
+{
+ m_eSetGetExpFieldType = TEXTTOXMARK;
+ m_CNTNT.pTextTOX = &rTOX;
+ m_nNode = rNdIdx.GetIndex();
+ m_nContent = rTOX.GetStart();
+}
+
+SetGetExpField::SetGetExpField( const SwPosition& rPos )
+{
+ m_eSetGetExpFieldType = CRSRPOS;
+ m_CNTNT.pPos = &rPos;
+ m_nNode = rPos.GetNodeIndex();
+ m_nContent = rPos.GetContentIndex();
+}
+
+SetGetExpField::SetGetExpField( const SwFlyFrameFormat& rFlyFormat,
+ const SwPosition* pPos )
+{
+ m_eSetGetExpFieldType = FLYFRAME;
+ m_CNTNT.pFlyFormat = &rFlyFormat;
+ if( pPos )
+ {
+ m_nNode = pPos->GetNodeIndex();
+ m_nContent = pPos->GetContentIndex();
+ }
+ else
+ {
+ const SwFormatContent& rContent = rFlyFormat.GetContent();
+ m_nNode = rContent.GetContentIdx()->GetIndex() + 1;
+ m_nContent = 0;
+ }
+}
+
+void SetGetExpField::GetPosOfContent( SwPosition& rPos ) const
+{
+ const SwNode* pNd = GetNodeFromContent();
+ if( pNd )
+ pNd = pNd->GetContentNode();
+
+ if( pNd )
+ {
+ rPos.Assign( *static_cast<const SwContentNode*>(pNd), GetCntPosFromContent() );
+ }
+ else
+ {
+ rPos.Assign( m_nNode, m_nContent );
+ }
+}
+
+void SetGetExpField::SetBodyPos( const SwContentFrame& rFrame )
+{
+ if( !rFrame.IsInDocBody() )
+ {
+ SwNodeIndex aIdx( rFrame.IsTextFrame()
+ ? *static_cast<SwTextFrame const&>(rFrame).GetTextNodeFirst()
+ : *static_cast<SwNoTextFrame const&>(rFrame).GetNode() );
+ SwDoc& rDoc = aIdx.GetNodes().GetDoc();
+ SwPosition aPos( aIdx );
+ bool const bResult = ::GetBodyTextNode( rDoc, aPos, rFrame );
+ OSL_ENSURE(bResult, "Where is the field?");
+ m_nNode = aPos.GetNodeIndex();
+ // tdf#106663 - use the starting position of the frame
+ m_nContent = 0;
+ }
+}
+
+bool SetGetExpField::operator==( const SetGetExpField& rField ) const
+{
+ return m_nNode == rField.m_nNode
+ && m_nContent == rField.m_nContent
+ && ( !m_CNTNT.pTextField
+ || !rField.m_CNTNT.pTextField
+ || m_CNTNT.pTextField == rField.m_CNTNT.pTextField );
+}
+
+bool SetGetExpField::operator<( const SetGetExpField& rField ) const
+{
+ if (m_nPageNumber != rField.m_nPageNumber)
+ {
+ // sort "invalid" page nums of 0 after valid page nums of non 0
+ if (m_nPageNumber == 0 || rField.m_nPageNumber == 0)
+ return m_nPageNumber != 0;
+ return m_nPageNumber < rField.m_nPageNumber;
+ }
+ if( m_nNode < rField.m_nNode || ( m_nNode == rField.m_nNode && m_nContent < rField.m_nContent ))
+ return true;
+ else if( m_nNode != rField.m_nNode || m_nContent != rField.m_nContent )
+ return false;
+
+ const SwNode *pFirst = GetNodeFromContent(),
+ *pNext = rField.GetNodeFromContent();
+
+ // Position is the same: continue only if both field pointers are set!
+ if( !pFirst || !pNext )
+ return false;
+
+ // same Section?
+ if( pFirst->StartOfSectionNode() != pNext->StartOfSectionNode() )
+ {
+ // is one in the table?
+ const SwNode *pFirstStt, *pNextStt;
+ const SwTableNode* pTableNd = pFirst->FindTableNode();
+ if( pTableNd )
+ pFirstStt = pTableNd->StartOfSectionNode();
+ else
+ pFirstStt = pFirst->StartOfSectionNode();
+
+ pTableNd = pNext->FindTableNode();
+ if( pTableNd )
+ pNextStt = pTableNd->StartOfSectionNode();
+ else
+ pNextStt = pNext->StartOfSectionNode();
+
+ if( pFirstStt != pNextStt )
+ {
+ if( pFirst->IsTextNode() && pNext->IsTextNode() &&
+ ( pFirst->FindFlyStartNode() || pNext->FindFlyStartNode() ))
+ {
+ // FIXME: in NewFieldPortion(), SwGetExpField are expanded via
+ // DocumentFieldsManager::FieldsToExpand() calling
+ // std::upper_bound binary search function - the sort order
+ // depends on the fly positions in the layout, but the fly
+ // positions depend on the expansion of the SwGetExpField!
+ // This circular dep will cause trouble, it would be better to
+ // use only model positions (anchor), but then how to compare
+ // at-page anchored flys which don't have a model anchor?
+ return ::IsFrameBehind( *pNext->GetTextNode(), m_nContent, *pFirst->GetTextNode(), m_nContent );
+ }
+ return pFirstStt->GetIndex() < pNextStt->GetIndex();
+ }
+ }
+
+ // same Section: is the field in the same Node?
+ if( pFirst != pNext )
+ return pFirst->GetIndex() < pNext->GetIndex();
+
+ // same Node in the Section, check Position in the Node
+ return GetCntPosFromContent() < rField.GetCntPosFromContent();
+}
+
+const SwNode* SetGetExpField::GetNodeFromContent() const
+{
+ const SwNode* pRet = nullptr;
+ if( m_CNTNT.pTextField )
+ switch( m_eSetGetExpFieldType )
+ {
+ case TEXTFIELD:
+ pRet = &m_CNTNT.pTextField->GetTextNode();
+ break;
+
+ case TEXTINET:
+ pRet = &m_CNTNT.pTextINet->GetTextNode();
+ break;
+
+ case SECTIONNODE:
+ pRet = m_CNTNT.pSection->GetFormat()->GetSectionNode();
+ break;
+
+ case BOOKMARK:
+ pRet = &m_CNTNT.pBookmark->GetMarkStart().GetNode();
+ break;
+
+ case CRSRPOS:
+ pRet = &m_CNTNT.pPos->GetNode();
+ break;
+
+ case TEXTTOXMARK:
+ pRet = &m_CNTNT.pTextTOX->GetTextNode();
+ break;
+
+ case TABLEBOX:
+ if( m_CNTNT.pTBox->GetSttNd() )
+ {
+ SwNodeIndex aIdx( *m_CNTNT.pTBox->GetSttNd() );
+ pRet = aIdx.GetNode().GetNodes().GoNext( &aIdx );
+ }
+ break;
+
+ case FLYFRAME:
+ {
+ SwNodeIndex aIdx( *m_CNTNT.pFlyFormat->GetContent().GetContentIdx() );
+ pRet = aIdx.GetNode().GetNodes().GoNext( &aIdx );
+ }
+ break;
+ }
+ return pRet;
+}
+
+sal_Int32 SetGetExpField::GetCntPosFromContent() const
+{
+ sal_Int32 nRet = 0;
+ if( m_CNTNT.pTextField )
+ switch( m_eSetGetExpFieldType )
+ {
+ case TEXTFIELD:
+ nRet = m_CNTNT.pTextField->GetStart();
+ break;
+ case TEXTINET:
+ nRet = m_CNTNT.pTextINet->GetStart();
+ break;
+ case TEXTTOXMARK:
+ nRet = m_CNTNT.pTextTOX->GetStart();
+ break;
+ case BOOKMARK:
+ nRet = m_CNTNT.pBookmark->GetMarkStart().GetContentIndex();
+ break;
+ case CRSRPOS:
+ nRet = m_CNTNT.pPos->GetContentIndex();
+ break;
+ default:
+ break;
+ }
+ return nRet;
+}
+
+/// Look up the Name, if it is present, return its String, otherwise return an empty String
+OUString LookString( std::unordered_map<OUString, OUString> const & rTable, const OUString& rName )
+{
+ auto it = rTable.find( comphelper::string::strip(rName, ' ') );
+ if( it != rTable.end() )
+ return it->second;
+
+ return OUString();
+}
+
+SwDBData const & SwDoc::GetDBData()
+{
+#if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS
+ if(maDBData.sDataSource.isEmpty())
+ {
+ // Similar to: SwEditShell::IsAnyDatabaseFieldInDoc
+ for (const auto& pFieldType : *getIDocumentFieldsAccess().GetFieldTypes())
+ {
+ if (IsUsed(*pFieldType))
+ {
+ SwFieldIds nWhich = pFieldType->Which();
+ switch(nWhich)
+ {
+ case SwFieldIds::Database:
+ case SwFieldIds::DbNextSet:
+ case SwFieldIds::DbNumSet:
+ case SwFieldIds::DbSetNumber:
+ {
+ std::vector<SwFormatField*> vFields;
+ pFieldType->GatherFields(vFields);
+ if(vFields.size())
+ {
+ if(SwFieldIds::Database == nWhich)
+ maDBData = static_cast<SwDBFieldType*>(vFields.front()->GetField()->GetTyp())->GetDBData();
+ else
+ maDBData = static_cast<SwDBNameInfField*> (vFields.front()->GetField())->GetRealDBData();
+ }
+ }
+ break;
+ default: break;
+ }
+ }
+ }
+ }
+ if(maDBData.sDataSource.isEmpty())
+ maDBData = SwDBManager::GetAddressDBName();
+#endif
+ return maDBData;
+}
+
+void SwDoc::SetInitDBFields( bool b )
+{
+#if !HAVE_FEATURE_DBCONNECTIVITY || ENABLE_FUZZERS
+ (void) b;
+#else
+ GetDBManager()->SetInitDBFields( b );
+#endif
+}
+
+#if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS
+
+/// Get all databases that are used by fields
+static OUString lcl_DBDataToString(const SwDBData& rData)
+{
+ return rData.sDataSource + OUStringChar(DB_DELIM)
+ + rData.sCommand + OUStringChar(DB_DELIM)
+ + OUString::number(rData.nCommandType);
+}
+
+#endif
+
+void SwDoc::GetAllUsedDB( std::vector<OUString>& rDBNameList,
+ const std::vector<OUString>* pAllDBNames )
+{
+#if !HAVE_FEATURE_DBCONNECTIVITY || ENABLE_FUZZERS
+ (void) rDBNameList;
+ (void) pAllDBNames;
+#else
+ std::vector<OUString> aUsedDBNames;
+ std::vector<OUString> aAllDBNames;
+
+ if( !pAllDBNames )
+ {
+ GetAllDBNames( aAllDBNames );
+ pAllDBNames = &aAllDBNames;
+ }
+
+ SwSectionFormats& rArr = GetSections();
+ for (auto n = rArr.size(); n; )
+ {
+ SwSection* pSect = rArr[ --n ]->GetSection();
+
+ if( pSect )
+ {
+ AddUsedDBToList( rDBNameList, FindUsedDBs( *pAllDBNames,
+ pSect->GetCondition(), aUsedDBNames ) );
+ aUsedDBNames.clear();
+ }
+ }
+
+ for (sal_uInt16 const nWhichHint : { RES_TXTATR_FIELD, RES_TXTATR_INPUTFIELD })
+ {
+ for (const SfxPoolItem* pItem : GetAttrPool().GetItemSurrogates(nWhichHint))
+ {
+ const SwFormatField* pFormatField = static_cast<const SwFormatField*>(pItem);
+ const SwTextField* pTextField = pFormatField->GetTextField();
+ if (!pTextField || !pTextField->GetTextNode().GetNodes().IsDocNodes())
+ continue;
+
+ const SwField* pField = pFormatField->GetField();
+ switch (pField->GetTyp()->Which())
+ {
+ case SwFieldIds::Database:
+ AddUsedDBToList( rDBNameList,
+ lcl_DBDataToString(static_cast<const SwDBField*>(pField)->GetDBData() ));
+ break;
+
+ case SwFieldIds::DbSetNumber:
+ case SwFieldIds::DatabaseName:
+ AddUsedDBToList( rDBNameList,
+ lcl_DBDataToString(static_cast<const SwDBNameInfField*>(pField)->GetRealDBData() ));
+ break;
+
+ case SwFieldIds::DbNumSet:
+ case SwFieldIds::DbNextSet:
+ AddUsedDBToList( rDBNameList,
+ lcl_DBDataToString(static_cast<const SwDBNameInfField*>(pField)->GetRealDBData() ));
+ [[fallthrough]]; // JP: is that right like that?
+
+ case SwFieldIds::HiddenText:
+ case SwFieldIds::HiddenPara:
+ AddUsedDBToList(rDBNameList, FindUsedDBs( *pAllDBNames,
+ pField->GetPar1(), aUsedDBNames ));
+ aUsedDBNames.clear();
+ break;
+
+ case SwFieldIds::SetExp:
+ case SwFieldIds::GetExp:
+ case SwFieldIds::Table:
+ AddUsedDBToList(rDBNameList, FindUsedDBs( *pAllDBNames,
+ pField->GetFormula(), aUsedDBNames ));
+ aUsedDBNames.clear();
+ break;
+ default: break;
+ }
+ }
+ }
+#endif
+}
+
+void SwDoc::GetAllDBNames( std::vector<OUString>& rAllDBNames )
+{
+#if !HAVE_FEATURE_DBCONNECTIVITY || ENABLE_FUZZERS
+ (void) rAllDBNames;
+#else
+ SwDBManager* pMgr = GetDBManager();
+
+ const SwDSParams_t& rArr = pMgr->GetDSParamArray();
+ for (const auto& pParam : rArr)
+ {
+ rAllDBNames.emplace_back(pParam->sDataSource + OUStringChar(DB_DELIM) + pParam->sCommand);
+ }
+#endif
+}
+
+std::vector<OUString>& SwDoc::FindUsedDBs( const std::vector<OUString>& rAllDBNames,
+ const OUString& rFormula,
+ std::vector<OUString>& rUsedDBNames )
+{
+ const CharClass& rCC = GetAppCharClass();
+#ifndef UNX
+ const OUString sFormula(rCC.uppercase( rFormula ));
+#else
+ const OUString sFormula(rFormula);
+#endif
+
+ for (const auto &sItem : rAllDBNames)
+ {
+ sal_Int32 nPos = sFormula.indexOf( sItem );
+ if( nPos>=0 &&
+ sFormula[ nPos + sItem.getLength() ] == '.' &&
+ (!nPos || !rCC.isLetterNumeric( sFormula, nPos - 1 )))
+ {
+ // Look up table name
+ nPos += sItem.getLength() + 1;
+ const sal_Int32 nEndPos = sFormula.indexOf('.', nPos);
+ if( nEndPos>=0 )
+ {
+ rUsedDBNames.emplace_back(sItem + OUStringChar(DB_DELIM) + sFormula.subView( nPos, nEndPos - nPos ));
+ }
+ }
+ }
+ return rUsedDBNames;
+}
+
+void SwDoc::AddUsedDBToList( std::vector<OUString>& rDBNameList,
+ const std::vector<OUString>& rUsedDBNames )
+{
+ for ( const auto &sName : rUsedDBNames )
+ AddUsedDBToList( rDBNameList, sName );
+}
+
+void SwDoc::AddUsedDBToList( std::vector<OUString>& rDBNameList, const OUString& rDBName)
+{
+#if !HAVE_FEATURE_DBCONNECTIVITY || ENABLE_FUZZERS
+ (void) rDBNameList;
+ (void) rDBName;
+#else
+ if( rDBName.isEmpty() )
+ return;
+
+#ifdef UNX
+ for( const auto &sName : rDBNameList )
+ if( rDBName == o3tl::getToken(sName, 0, ';') )
+ return;
+#else
+ const ::utl::TransliterationWrapper& rSCmp = GetAppCmpStrIgnore();
+ for( const auto &sName : rDBNameList )
+ if( rSCmp.isEqual( rDBName, sName.getToken(0, ';') ) )
+ return;
+#endif
+
+ SwDBData aData;
+ sal_Int32 nIdx{ 0 };
+ aData.sDataSource = rDBName.getToken(0, DB_DELIM, nIdx);
+ aData.sCommand = rDBName.getToken(0, DB_DELIM, nIdx);
+ aData.nCommandType = -1;
+ GetDBManager()->CreateDSData(aData);
+ rDBNameList.push_back(rDBName);
+#endif
+}
+
+void SwDoc::ChangeDBFields( const std::vector<OUString>& rOldNames,
+ const OUString& rNewName )
+{
+#if !HAVE_FEATURE_DBCONNECTIVITY || ENABLE_FUZZERS
+ (void) rOldNames;
+ (void) rNewName;
+#else
+ SwDBData aNewDBData;
+ sal_Int32 nIdx{ 0 };
+ aNewDBData.sDataSource = rNewName.getToken(0, DB_DELIM, nIdx);
+ aNewDBData.sCommand = rNewName.getToken(0, DB_DELIM, nIdx);
+ aNewDBData.nCommandType = o3tl::toInt32(o3tl::getToken(rNewName, 0, DB_DELIM, nIdx));
+
+ SwSectionFormats& rArr = GetSections();
+ for (auto n = rArr.size(); n; )
+ {
+ SwSection* pSect = rArr[ --n ]->GetSection();
+
+ if( pSect )
+ {
+ pSect->SetCondition(ReplaceUsedDBs(rOldNames, rNewName, pSect->GetCondition()));
+ }
+ }
+
+ for (sal_uInt16 const nWhichHint : { RES_TXTATR_FIELD, RES_TXTATR_INPUTFIELD })
+ {
+ for (const SfxPoolItem* pItem : GetAttrPool().GetItemSurrogates(nWhichHint))
+ {
+ SwFormatField* pFormatField = const_cast<SwFormatField*>(static_cast<const SwFormatField*>(pItem));
+ SwTextField* pTextField = pFormatField->GetTextField();
+ if (!pTextField || !pTextField->GetTextNode().GetNodes().IsDocNodes())
+ continue;
+
+ SwField* pField = pFormatField->GetField();
+ bool bExpand = false;
+
+ switch( pField->GetTyp()->Which() )
+ {
+ case SwFieldIds::Database:
+#if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS
+ if (IsNameInArray(rOldNames, lcl_DBDataToString(static_cast<SwDBField*>(pField)->GetDBData())))
+ {
+ SwDBFieldType* pOldTyp = static_cast<SwDBFieldType*>(pField->GetTyp());
+
+ SwDBFieldType* pTyp = static_cast<SwDBFieldType*>(getIDocumentFieldsAccess().InsertFieldType(
+ SwDBFieldType(this, pOldTyp->GetColumnName(), aNewDBData)));
+
+ pFormatField->RegisterToFieldType( *pTyp );
+ pField->ChgTyp(pTyp);
+
+ static_cast<SwDBField*>(pField)->ClearInitialized();
+ static_cast<SwDBField*>(pField)->InitContent();
+
+ bExpand = true;
+ }
+#endif
+ break;
+
+ case SwFieldIds::DbSetNumber:
+ case SwFieldIds::DatabaseName:
+ if (IsNameInArray(rOldNames,
+ lcl_DBDataToString(static_cast<SwDBNameInfField*>(pField)->GetRealDBData())))
+ {
+ static_cast<SwDBNameInfField*>(pField)->SetDBData(aNewDBData);
+ bExpand = true;
+ }
+ break;
+
+ case SwFieldIds::DbNumSet:
+ case SwFieldIds::DbNextSet:
+ if (IsNameInArray(rOldNames,
+ lcl_DBDataToString(static_cast<SwDBNameInfField*>(pField)->GetRealDBData())))
+ {
+ static_cast<SwDBNameInfField*>(pField)->SetDBData(aNewDBData);
+ }
+ [[fallthrough]];
+ case SwFieldIds::HiddenText:
+ case SwFieldIds::HiddenPara:
+ pField->SetPar1( ReplaceUsedDBs(rOldNames, rNewName, pField->GetPar1()) );
+ bExpand = true;
+ break;
+
+ case SwFieldIds::SetExp:
+ case SwFieldIds::GetExp:
+ case SwFieldIds::Table:
+ pField->SetPar2( ReplaceUsedDBs(rOldNames, rNewName, pField->GetFormula()) );
+ bExpand = true;
+ break;
+ default: break;
+ }
+
+ if (bExpand)
+ pTextField->ExpandTextField( true );
+ }
+ }
+ getIDocumentState().SetModified();
+#endif
+}
+
+namespace
+{
+
+OUString lcl_CutOffDBCommandType(const OUString& rName)
+{
+ return rName.replaceFirst(OUStringChar(DB_DELIM), ".").getToken(0, DB_DELIM);
+}
+
+}
+
+OUString SwDoc::ReplaceUsedDBs( const std::vector<OUString>& rUsedDBNames,
+ const OUString& rNewName, const OUString& rFormula )
+{
+ const CharClass& rCC = GetAppCharClass();
+ const OUString sNewName( lcl_CutOffDBCommandType(rNewName) );
+ OUString sFormula(rFormula);
+
+ for(const auto & rUsedDBName : rUsedDBNames)
+ {
+ const OUString sDBName( lcl_CutOffDBCommandType(rUsedDBName) );
+
+ if (sDBName!=sNewName)
+ {
+ sal_Int32 nPos = 0;
+ for (;;)
+ {
+ nPos = sFormula.indexOf(sDBName, nPos);
+ if (nPos<0)
+ {
+ break;
+ }
+
+ if( sFormula[nPos + sDBName.getLength()] == '.' &&
+ (!nPos || !rCC.isLetterNumeric( sFormula, nPos - 1 )))
+ {
+ sFormula = sFormula.replaceAt(nPos, sDBName.getLength(), sNewName);
+ //prevent re-searching - this is useless and provokes
+ //endless loops when names containing each other and numbers are exchanged
+ //e.g.: old ?12345.12345 new: i12345.12345
+ nPos += sNewName.getLength();
+ }
+ }
+ }
+ }
+ return sFormula;
+}
+
+bool SwDoc::IsNameInArray( const std::vector<OUString>& rArr, const OUString& rName )
+{
+#ifdef UNX
+ for( const auto &sName : rArr )
+ if( rName == sName )
+ return true;
+#else
+ const ::utl::TransliterationWrapper& rSCmp = GetAppCmpStrIgnore();
+ for( const auto &sName : rArr )
+ if( rSCmp.isEqual( rName, sName ))
+ return true;
+#endif
+ return false;
+}
+
+void SwDoc::ChangeAuthorityData( const SwAuthEntry* pNewData )
+{
+ const SwFieldTypes::size_type nSize = getIDocumentFieldsAccess().GetFieldTypes()->size();
+
+ for( SwFieldTypes::size_type i = INIT_FLDTYPES; i < nSize; ++i )
+ {
+ SwFieldType* pFieldType = (*getIDocumentFieldsAccess().GetFieldTypes())[i].get();
+ if( SwFieldIds::TableOfAuthorities == pFieldType->Which() )
+ {
+ SwAuthorityFieldType* pAuthType = static_cast<SwAuthorityFieldType*>(pFieldType);
+ pAuthType->ChangeEntryContent(pNewData);
+ break;
+ }
+ }
+
+}
+
+void SwDocUpdateField::InsDelFieldInFieldLst( bool bIns, const SwTextField& rField )
+{
+ const SwFieldIds nWhich = rField.GetFormatField().GetField()->GetTyp()->Which();
+ switch( nWhich )
+ {
+ case SwFieldIds::Database:
+ case SwFieldIds::SetExp:
+ case SwFieldIds::HiddenPara:
+ case SwFieldIds::HiddenText:
+ case SwFieldIds::DbNumSet:
+ case SwFieldIds::DbNextSet:
+ case SwFieldIds::DbSetNumber:
+ case SwFieldIds::GetExp:
+ break; // these have to be added/removed!
+
+ default:
+ return;
+ }
+
+ SetFieldsDirty( true );
+ if (!m_pFieldSortList)
+ {
+ if( !bIns ) // if list is present and deleted
+ return; // don't do a thing
+ m_pFieldSortList.reset(new SetGetExpFields);
+ }
+
+ if( bIns ) // insert anew:
+ GetBodyNode( rField, nWhich );
+ else
+ {
+ // look up via the pTextField pointer. It is a sorted list, but it's sorted by node
+ // position. Until this is found, the search for the pointer is already done.
+ for (SetGetExpFields::size_type n = 0; n < m_pFieldSortList->size(); ++n)
+ {
+ if (&rField == (*m_pFieldSortList)[n]->GetPointer())
+ {
+ m_pFieldSortList->erase_at(n);
+ n--; // one field can occur multiple times
+ }
+ }
+ }
+}
+
+void SwDocUpdateField::MakeFieldList( SwDoc& rDoc, bool bAll, int eGetMode )
+{
+ if (!m_pFieldSortList || bAll
+ || ((eGetMode & m_nFieldListGetMode) != eGetMode)
+ || rDoc.GetNodes().Count() != m_nNodes)
+ {
+ MakeFieldList_( rDoc, eGetMode );
+ }
+}
+
+void SwDocUpdateField::MakeFieldList_( SwDoc& rDoc, int eGetMode )
+{
+ // new version: walk all fields of the attribute pool
+ m_pFieldSortList.reset(new SetGetExpFields);
+
+ // remember sections that were unhidden and need to be hidden again
+ std::vector<std::reference_wrapper<SwSection>> aUnhiddenSections;
+
+ // consider and unhide sections
+ // with hide condition, only in mode GETFLD_ALL (<eGetMode == GETFLD_ALL>)
+ // notes by OD:
+ // eGetMode == GETFLD_CALC in call from methods SwDoc::FieldsToCalc
+ // eGetMode == GETFLD_EXPAND in call from method SwDoc::FieldsToExpand
+ // eGetMode == GETFLD_ALL in call from method SwDoc::UpdateExpFields
+ // I figured out that hidden section only have to be shown,
+ // if fields have updated (call by SwDoc::UpdateExpFields) and thus
+ // the hide conditions of section have to be updated.
+ // For correct updating the hide condition of a section, its position
+ // have to be known in order to insert the hide condition as a new
+ // expression field into the sorted field list (<m_pFieldSortList>).
+ if ( eGetMode == GETFLD_ALL )
+ // Collect the sections first. Supply sections that are hidden by condition
+ // with frames so that the contained fields are sorted properly.
+ {
+ // In order for the frames to be created the right way, they have to be expanded
+ // from top to bottom
+ std::vector<SwNodeOffset> aTmpArr;
+ std::vector<SwNodeOffset>::size_type nArrStt = 0;
+ SwSectionFormats& rArr = rDoc.GetSections();
+ SwSectionNode* pSectNd = nullptr;
+ SwNodeOffset nSttContent = rDoc.GetNodes().GetEndOfExtras().GetIndex();
+
+ for (SwSectionFormats::size_type n = rArr.size(); n; )
+ {
+ SwSection* pSect = rArr[ --n ]->GetSection();
+ if( !pSect || !pSect->IsHidden() || pSect->GetCondition().isEmpty() )
+ continue;
+ pSectNd = pSect->GetFormat()->GetSectionNode();
+ if( pSectNd )
+ {
+ SwNodeOffset nIdx = pSectNd->GetIndex();
+ aTmpArr.push_back( nIdx );
+ if( nIdx < nSttContent )
+ ++nArrStt;
+ }
+ }
+ std::sort(aTmpArr.begin(), aTmpArr.end());
+
+ // Display all first so that we have frames. The BodyAnchor is defined by that.
+ // First the ContentArea, then the special areas!
+ for (std::vector<sal_uLong>::size_type n = nArrStt; n < aTmpArr.size(); ++n)
+ {
+ pSectNd = rDoc.GetNodes()[ aTmpArr[ n ] ]->GetSectionNode();
+ OSL_ENSURE( pSectNd, "Where is my SectionNode" );
+
+ auto& rSection = pSectNd->GetSection();
+ // unhide and remember the conditionally hidden sections
+ if (rSection.IsHidden() && !rSection.GetCondition().isEmpty() && rSection.IsCondHidden())
+ {
+ aUnhiddenSections.push_back(std::ref(rSection)); // remember to later hide again
+ rSection.SetCondHidden(false);
+ }
+ }
+ for (std::vector<sal_uLong>::size_type n = 0; n < nArrStt; ++n)
+ {
+ pSectNd = rDoc.GetNodes()[ aTmpArr[ n ] ]->GetSectionNode();
+ OSL_ENSURE( pSectNd, "Where is my SectionNode" );
+
+ auto& rSection = pSectNd->GetSection();
+ // unhide and remember the conditionally hidden sections
+ if (rSection.IsHidden() && !rSection.GetCondition().isEmpty() && rSection.IsCondHidden())
+ {
+ aUnhiddenSections.push_back(std::ref(rSection)); // remember to later hide again
+ rSection.SetCondHidden(false);
+ }
+ }
+
+ // add all to the list so that they are sorted
+ for (const auto &nId : aTmpArr)
+ {
+ SwSectionNode const& rSectionNode(*rDoc.GetNodes()[ nId ]->GetSectionNode());
+ GetBodyNodeGeneric(rSectionNode, rSectionNode);
+ }
+
+ // bookmarks with hide conditions, handle similar to sections
+ auto const& rIDMA(*rDoc.getIDocumentMarkAccess());
+ for (auto it = rIDMA.getBookmarksBegin(); it != rIDMA.getBookmarksEnd(); ++it)
+ {
+ auto const pBookmark(dynamic_cast<::sw::mark::IBookmark const*>(*it));
+ assert(pBookmark);
+ if (!pBookmark->GetHideCondition().isEmpty())
+ {
+ GetBodyNodeGeneric((*it)->GetMarkStart().GetNode(), *pBookmark);
+ }
+ }
+ }
+
+ static constexpr OUString sTrue(u"TRUE"_ustr);
+ static constexpr OUString sFalse(u"FALSE"_ustr);
+
+#if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS
+ bool bIsDBManager = nullptr != rDoc.GetDBManager();
+#endif
+
+ for (sal_uInt16 const nWhichHint : { RES_TXTATR_FIELD, RES_TXTATR_INPUTFIELD })
+ {
+ for (const SfxPoolItem* pItem : rDoc.GetAttrPool().GetItemSurrogates(nWhichHint))
+ {
+ const SwFormatField* pFormatField = static_cast<const SwFormatField*>(pItem);
+ const SwTextField* pTextField = pFormatField->GetTextField();
+ if (!pTextField || !pTextField->GetTextNode().GetNodes().IsDocNodes())
+ continue;
+
+ OUString sFormula;
+ const SwField* pField = pFormatField->GetField();
+ const SwFieldIds nWhich = pField->GetTyp()->Which();
+ switch (nWhich)
+ {
+ case SwFieldIds::DbSetNumber:
+ case SwFieldIds::GetExp:
+ if (GETFLD_ALL == eGetMode)
+ sFormula = sTrue;
+ break;
+
+ case SwFieldIds::Database:
+ if (GETFLD_EXPAND & eGetMode)
+ sFormula = sTrue;
+ break;
+
+ case SwFieldIds::SetExp:
+ if ((eGetMode != GETFLD_EXPAND) ||
+ (nsSwGetSetExpType::GSE_STRING & pField->GetSubType()))
+ {
+ sFormula = sTrue;
+ }
+ break;
+
+ case SwFieldIds::HiddenPara:
+ if (GETFLD_ALL == eGetMode)
+ {
+ sFormula = pField->GetPar1();
+ if (sFormula.isEmpty() || sFormula==sFalse)
+ const_cast<SwHiddenParaField*>(static_cast<const SwHiddenParaField*>(pField))->SetHidden( false );
+ else if (sFormula==sTrue)
+ const_cast<SwHiddenParaField*>(static_cast<const SwHiddenParaField*>(pField))->SetHidden( true );
+ else
+ break;
+
+ sFormula.clear();
+ // trigger formatting
+ const_cast<SwFormatField*>(pFormatField)->ForceUpdateTextNode();
+ }
+ break;
+
+ case SwFieldIds::HiddenText:
+ if (GETFLD_ALL == eGetMode)
+ {
+ sFormula = pField->GetPar1();
+ if (sFormula.isEmpty() || sFormula==sFalse)
+ const_cast<SwHiddenTextField*>(static_cast<const SwHiddenTextField*>(pField))->SetValue( true );
+ else if (sFormula==sTrue)
+ const_cast<SwHiddenTextField*>(static_cast<const SwHiddenTextField*>(pField))->SetValue( false );
+ else
+ break;
+
+ sFormula.clear();
+
+ // evaluate field
+ const_cast<SwHiddenTextField*>(static_cast<const SwHiddenTextField*>(pField))->Evaluate(rDoc);
+ // trigger formatting
+ const_cast<SwFormatField*>(pFormatField)->ForceUpdateTextNode();
+ }
+ break;
+
+#if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS
+ case SwFieldIds::DbNumSet:
+ {
+ SwDBData aDBData(const_cast<SwDBNumSetField*>(static_cast<const SwDBNumSetField*>(pField))->GetDBData(&rDoc));
+
+ if ( (bIsDBManager && rDoc.GetDBManager()->OpenDataSource(aDBData.sDataSource, aDBData.sCommand))
+ && (GETFLD_ALL == eGetMode
+ || (GETFLD_CALC & eGetMode
+ && static_cast<const SwDBNumSetField*>(pField)->IsCondValid()))
+ )
+ {
+ sFormula = pField->GetPar1();
+ }
+ }
+ break;
+ case SwFieldIds::DbNextSet:
+ {
+ SwDBData aDBData(const_cast<SwDBNextSetField*>(static_cast<const SwDBNextSetField*>(pField))->GetDBData(&rDoc));
+
+ if ( (bIsDBManager && rDoc.GetDBManager()->OpenDataSource(aDBData.sDataSource, aDBData.sCommand))
+ && (GETFLD_ALL == eGetMode
+ || (GETFLD_CALC & eGetMode
+ && static_cast<const SwDBNextSetField*>(pField)->IsCondValid()))
+ )
+ {
+ sFormula = pField->GetPar1();
+ }
+ }
+ break;
+#endif
+ default: break;
+ }
+
+ if (!sFormula.isEmpty())
+ {
+ GetBodyNode( *pTextField, nWhich );
+ }
+ }
+ }
+ m_nFieldListGetMode = eGetMode;
+ m_nNodes = rDoc.GetNodes().Count();
+
+ // return the conditional hidden value back to the previous value
+ for (auto& rSectionWrapper : aUnhiddenSections)
+ {
+ auto& rSection = rSectionWrapper.get();
+ rSection.SetCondHidden(true);
+ }
+}
+
+void SwDocUpdateField::GetBodyNode( const SwTextField& rTField, SwFieldIds nFieldWhich )
+{
+ const SwTextNode& rTextNd = rTField.GetTextNode();
+ const SwDoc& rDoc = rTextNd.GetDoc();
+
+ // always the first! (in tab headline, header-/footer)
+ Point aPt;
+ std::pair<Point, bool> const tmp(aPt, false);
+ // need pos to get the frame on the correct page
+ SwPosition const pos(rTextNd, rTField.GetStart());
+ const SwFrame* pFrame = rTextNd.getLayoutFrame(
+ rDoc.getIDocumentLayoutAccess().GetCurrentLayout(), &pos, &tmp);
+
+ std::unique_ptr<SetGetExpField> pNew;
+ bool bIsInBody = false;
+
+ if( !pFrame || pFrame->IsInDocBody() )
+ {
+ bIsInBody = rDoc.GetNodes().GetEndOfExtras().GetIndex() < rTextNd.GetIndex();
+
+ // We don't want to update fields in redlines, or those
+ // in frames whose anchor is in redline. However, we do want to update
+ // fields in hidden sections. So: In order to be updated, a field 1)
+ // must have a frame, or 2) it must be in the document body.
+ if (pFrame == nullptr && bIsInBody)
+ { // try harder to get a frame for the page number
+ pFrame = ::sw::FindNeighbourFrameForNode(rTextNd);
+ // possibly there is no layout at all, happens in mail merge
+ }
+ if( (pFrame != nullptr) || bIsInBody )
+ {
+ pNew.reset(new SetGetExpField(rTextNd, &rTField, std::nullopt,
+ pFrame ? pFrame->GetPhyPageNum() : 0));
+ }
+ }
+ else
+ {
+ // create index to determine the TextNode
+ SwPosition aPos( rDoc.GetNodes().GetEndOfPostIts() );
+ bool const bResult = GetBodyTextNode( rDoc, aPos, *pFrame );
+ OSL_ENSURE(bResult, "where is the Field");
+ pNew.reset(new SetGetExpField(aPos.GetNode(), &rTField, aPos.GetContentIndex(),
+ pFrame->GetPhyPageNum()));
+ }
+
+ // always set the BodyTextFlag in GetExp or DB fields
+ if( SwFieldIds::GetExp == nFieldWhich )
+ {
+ SwGetExpField* pGetField = const_cast<SwGetExpField*>(static_cast<const SwGetExpField*>(rTField.GetFormatField().GetField()));
+ pGetField->ChgBodyTextFlag( bIsInBody );
+ }
+#if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS
+ else if( SwFieldIds::Database == nFieldWhich )
+ {
+ SwDBField* pDBField = const_cast<SwDBField*>(static_cast<const SwDBField*>(rTField.GetFormatField().GetField()));
+ pDBField->ChgBodyTextFlag( bIsInBody );
+ }
+#endif
+ if( pNew != nullptr )
+ m_pFieldSortList->insert( std::move(pNew) );
+}
+
+template<typename T>
+void SwDocUpdateField::GetBodyNodeGeneric(SwNode const& rNode, T const& rCond)
+{
+ const SwDoc& rDoc = rNode.GetDoc();
+ std::unique_ptr<SetGetExpField> pNew;
+
+ if (rNode.GetIndex() < rDoc.GetNodes().GetEndOfExtras().GetIndex())
+ {
+ do { // middle check loop
+
+ // we need to get the anchor first
+ // create index to determine the TextNode
+ SwPosition aPos(rNode);
+ SwContentNode const*const pCNd = rNode.IsSectionNode()
+ ? rDoc.GetNodes().GoNext(&aPos.nNode) // to the next ContentNode
+ : rNode.GetContentNode();
+
+ if( !pCNd || !pCNd->IsTextNode() )
+ break;
+
+ // always the first! (in tab headline, header-/footer)
+ Point aPt;
+ std::pair<Point, bool> const tmp(aPt, false);
+ const SwContentFrame* pFrame = pCNd->getLayoutFrame(
+ rDoc.getIDocumentLayoutAccess().GetCurrentLayout(),
+ nullptr, &tmp);
+ if( !pFrame )
+ break;
+
+ bool const bResult = GetBodyTextNode( rDoc, aPos, *pFrame );
+ OSL_ENSURE(bResult, "where is the Field");
+ pNew.reset(new SetGetExpField(rCond, &aPos, pFrame->GetPhyPageNum()));
+
+ } while( false );
+ }
+
+ if( !pNew )
+ {
+ // try harder to get a frame for the page number
+ SwFrame const*const pFrame = ::sw::FindNeighbourFrameForNode(rNode);
+ pNew.reset(new SetGetExpField(rCond, nullptr, pFrame ? pFrame->GetPhyPageNum() : 0));
+ }
+
+ m_pFieldSortList->insert( std::move(pNew) );
+}
+
+void SwDocUpdateField::InsertFieldType( const SwFieldType& rType )
+{
+ OUString sFieldName;
+ switch( rType.Which() )
+ {
+ case SwFieldIds::User :
+ sFieldName = static_cast<const SwUserFieldType&>(rType).GetName();
+ break;
+ case SwFieldIds::SetExp:
+ sFieldName = static_cast<const SwSetExpFieldType&>(rType).GetName();
+ break;
+ default:
+ OSL_ENSURE( false, "No valid field type" );
+ }
+
+ if( sFieldName.isEmpty() )
+ return;
+
+ SetFieldsDirty( true );
+ // look up and remove from the hash table
+ sFieldName = GetAppCharClass().lowercase( sFieldName );
+
+ auto it = m_FieldTypeTable.find( sFieldName );
+ if( it == m_FieldTypeTable.end() )
+ m_FieldTypeTable.insert( { sFieldName, &rType } );
+}
+
+void SwDocUpdateField::RemoveFieldType( const SwFieldType& rType )
+{
+ OUString sFieldName;
+ switch( rType.Which() )
+ {
+ case SwFieldIds::User :
+ sFieldName = static_cast<const SwUserFieldType&>(rType).GetName();
+ break;
+ case SwFieldIds::SetExp:
+ sFieldName = static_cast<const SwSetExpFieldType&>(rType).GetName();
+ break;
+ default: break;
+ }
+
+ if( sFieldName.isEmpty() )
+ return;
+
+ SetFieldsDirty( true );
+ // look up and remove from the hash table
+ sFieldName = GetAppCharClass().lowercase( sFieldName );
+
+ GetFieldTypeTable().erase( sFieldName );
+}
+
+SwDocUpdateField::SwDocUpdateField(SwDoc& rDoc)
+ : m_nNodes(0)
+ , m_nFieldListGetMode(0)
+ , m_rDoc(rDoc)
+ , m_bInUpdateFields(false)
+ , m_bFieldsDirty(false)
+{
+}
+
+SwDocUpdateField::~SwDocUpdateField()
+{
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/docfly.cxx b/sw/source/core/doc/docfly.cxx
new file mode 100644
index 0000000000..c492212487
--- /dev/null
+++ b/sw/source/core/doc/docfly.cxx
@@ -0,0 +1,1165 @@
+/* -*- 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 <hintids.hxx>
+#include <svl/itemiter.hxx>
+#include <svx/svdobj.hxx>
+#include <svx/svdmark.hxx>
+#include <osl/diagnose.h>
+#include <fmtfsize.hxx>
+#include <fmtornt.hxx>
+#include <dcontact.hxx>
+#include <ndgrf.hxx>
+#include <doc.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <IDocumentDrawModelAccess.hxx>
+#include <IDocumentState.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <ndindex.hxx>
+#include <drawdoc.hxx>
+#include <fmtcntnt.hxx>
+#include <fmtanchr.hxx>
+#include <fmtflcnt.hxx>
+#include <txtfrm.hxx>
+#include <notxtfrm.hxx>
+#include <pagefrm.hxx>
+#include <rootfrm.hxx>
+#include <flyfrm.hxx>
+#include <textboxhelper.hxx>
+#include <txatbase.hxx>
+#include <frmfmt.hxx>
+#include <ndtxt.hxx>
+#include <pam.hxx>
+#include <swundo.hxx>
+#include <crstate.hxx>
+#include <UndoCore.hxx>
+#include <UndoAttribute.hxx>
+#include <fmtcnct.hxx>
+#include <dflyobj.hxx>
+#include <undoflystrattr.hxx>
+#include <calbck.hxx>
+#include <frameformats.hxx>
+#include <memory>
+#include <svx/xbtmpit.hxx>
+#include <svx/xflftrit.hxx>
+#include <svx/xlndsit.hxx>
+#include <svx/xlnstit.hxx>
+#include <svx/xlnedit.hxx>
+#include <svx/xflhtit.hxx>
+#include <formatflysplit.hxx>
+
+using namespace ::com::sun::star;
+
+size_t SwDoc::GetFlyCount( FlyCntType eType, bool bIgnoreTextBoxes ) const
+{
+ size_t nCount = 0;
+ const SwNodeIndex* pIdx;
+
+ for(sw::SpzFrameFormat* pFlyFormat: *GetSpzFrameFormats())
+ {
+ if (bIgnoreTextBoxes && SwTextBoxHelper::isTextBox(pFlyFormat, RES_FLYFRMFMT))
+ continue;
+
+ if( RES_FLYFRMFMT != pFlyFormat->Which() )
+ continue;
+ pIdx = pFlyFormat->GetContent().GetContentIdx();
+ if( pIdx && pIdx->GetNodes().IsDocNodes() )
+ {
+ const SwNode* pNd = GetNodes()[ pIdx->GetIndex() + 1 ];
+
+ switch( eType )
+ {
+ case FLYCNTTYPE_FRM:
+ if(!pNd->IsNoTextNode())
+ nCount++;
+ break;
+
+ case FLYCNTTYPE_GRF:
+ if( pNd->IsGrfNode() )
+ nCount++;
+ break;
+
+ case FLYCNTTYPE_OLE:
+ if(pNd->IsOLENode())
+ nCount++;
+ break;
+
+ default:
+ nCount++;
+ }
+ }
+ }
+ return nCount;
+}
+
+/// @attention If you change this, also update SwXFrameEnumeration in unocoll.
+SwFrameFormat* SwDoc::GetFlyNum( size_t nIdx, FlyCntType eType, bool bIgnoreTextBoxes )
+{
+ SwFrameFormat* pRetFormat = nullptr;
+ const SwNodeIndex* pIdx;
+ size_t nCount = 0;
+
+ for(sw::SpzFrameFormat* pFlyFormat: *GetSpzFrameFormats())
+ {
+ if (bIgnoreTextBoxes && SwTextBoxHelper::isTextBox(pFlyFormat, RES_FLYFRMFMT))
+ continue;
+
+ if( RES_FLYFRMFMT != pFlyFormat->Which() )
+ continue;
+ pIdx = pFlyFormat->GetContent().GetContentIdx();
+ if( pIdx && pIdx->GetNodes().IsDocNodes() )
+ {
+ const SwNode* pNd = GetNodes()[ pIdx->GetIndex() + 1 ];
+ switch( eType )
+ {
+ case FLYCNTTYPE_FRM:
+ if( !pNd->IsNoTextNode() && nIdx == nCount++)
+ pRetFormat = pFlyFormat;
+ break;
+ case FLYCNTTYPE_GRF:
+ if(pNd->IsGrfNode() && nIdx == nCount++ )
+ pRetFormat = pFlyFormat;
+ break;
+ case FLYCNTTYPE_OLE:
+ if(pNd->IsOLENode() && nIdx == nCount++)
+ pRetFormat = pFlyFormat;
+ break;
+ default:
+ if(nIdx == nCount++)
+ pRetFormat = pFlyFormat;
+ }
+ }
+ }
+ return pRetFormat;
+}
+
+std::vector<SwFrameFormat const*> SwDoc::GetFlyFrameFormats(
+ FlyCntType const eType, bool const bIgnoreTextBoxes)
+{
+ std::vector<SwFrameFormat const*> ret;
+ ret.reserve(GetSpzFrameFormats()->size());
+
+ for(sw::SpzFrameFormat* pFlyFormat: *GetSpzFrameFormats())
+ {
+ if (bIgnoreTextBoxes && SwTextBoxHelper::isTextBox(pFlyFormat, RES_FLYFRMFMT))
+ {
+ continue;
+ }
+
+ if (RES_FLYFRMFMT != pFlyFormat->Which())
+ {
+ continue;
+ }
+
+ SwNodeIndex const*const pIdx(pFlyFormat->GetContent().GetContentIdx());
+ if (pIdx && pIdx->GetNodes().IsDocNodes())
+ {
+ SwNode const*const pNd = GetNodes()[ pIdx->GetIndex() + 1 ];
+ switch (eType)
+ {
+ case FLYCNTTYPE_FRM:
+ if (!pNd->IsNoTextNode())
+ ret.push_back(pFlyFormat);
+ break;
+ case FLYCNTTYPE_GRF:
+ if (pNd->IsGrfNode())
+ ret.push_back(pFlyFormat);
+ break;
+ case FLYCNTTYPE_OLE:
+ if (pNd->IsOLENode())
+ ret.push_back(pFlyFormat);
+ break;
+ default:
+ ret.push_back(pFlyFormat);
+ }
+ }
+ }
+
+ return ret;
+}
+
+static Point lcl_FindAnchorLayPos( SwDoc& rDoc, const SwFormatAnchor& rAnch,
+ const SwFrameFormat* pFlyFormat )
+{
+ Point aRet;
+ if( rDoc.getIDocumentLayoutAccess().GetCurrentViewShell() )
+ switch( rAnch.GetAnchorId() )
+ {
+ case RndStdIds::FLY_AS_CHAR:
+ if( pFlyFormat && rAnch.GetAnchorNode() )
+ {
+ const SwFrame* pOld = static_cast<const SwFlyFrameFormat*>(pFlyFormat)->GetFrame( &aRet );
+ if( pOld )
+ aRet = pOld->getFrameArea().Pos();
+ }
+ break;
+
+ case RndStdIds::FLY_AT_PARA:
+ case RndStdIds::FLY_AT_CHAR: // LAYER_IMPL
+ if( rAnch.GetAnchorNode() )
+ {
+ const SwContentNode* pNd = rAnch.GetAnchorNode()->GetContentNode();
+ std::pair<Point, bool> const tmp(aRet, false);
+ const SwFrame* pOld = pNd ? pNd->getLayoutFrame(rDoc.getIDocumentLayoutAccess().GetCurrentLayout(), nullptr, &tmp) : nullptr;
+ if( pOld )
+ aRet = pOld->getFrameArea().Pos();
+ }
+ break;
+
+ case RndStdIds::FLY_AT_FLY: // LAYER_IMPL
+ if( rAnch.GetAnchorNode() )
+ {
+ const SwFlyFrameFormat* pFormat = static_cast<SwFlyFrameFormat*>(rAnch.GetAnchorNode()->
+ GetFlyFormat());
+ const SwFrame* pOld = pFormat ? pFormat->GetFrame( &aRet ) : nullptr;
+ if( pOld )
+ aRet = pOld->getFrameArea().Pos();
+ }
+ break;
+
+ case RndStdIds::FLY_AT_PAGE:
+ {
+ sal_uInt16 nPgNum = rAnch.GetPageNum();
+ const SwPageFrame *pPage = static_cast<SwPageFrame*>(rDoc.getIDocumentLayoutAccess().GetCurrentLayout()->Lower());
+ for( sal_uInt16 i = 1; (i <= nPgNum) && pPage; ++i,
+ pPage =static_cast<const SwPageFrame*>(pPage->GetNext()) )
+ if( i == nPgNum )
+ {
+ aRet = pPage->getFrameArea().Pos();
+ break;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ return aRet;
+}
+
+#define MAKEFRMS 0
+#define IGNOREANCHOR 1
+#define DONTMAKEFRMS 2
+
+sal_Int8 SwDoc::SetFlyFrameAnchor( SwFrameFormat& rFormat, SfxItemSet& rSet, bool bNewFrames )
+{
+ // Changing anchors is almost always allowed.
+ // Exception: Paragraph and character bound frames must not become
+ // page bound, if they are located in the header or footer.
+ const SwFormatAnchor &rOldAnch = rFormat.GetAnchor();
+ const RndStdIds nOld = rOldAnch.GetAnchorId();
+
+ SwFormatAnchor aNewAnch( rSet.Get( RES_ANCHOR ) );
+ RndStdIds nNew = aNewAnch.GetAnchorId();
+
+ // Is the new anchor valid?
+ if( !aNewAnch.GetAnchorNode() && (RndStdIds::FLY_AT_FLY == nNew ||
+ (RndStdIds::FLY_AT_PARA == nNew) || (RndStdIds::FLY_AS_CHAR == nNew) ||
+ (RndStdIds::FLY_AT_CHAR == nNew) ))
+ {
+ return IGNOREANCHOR;
+ }
+
+ if( nOld == nNew )
+ return DONTMAKEFRMS;
+
+ Point aOldAnchorPos( ::lcl_FindAnchorLayPos( *this, rOldAnch, &rFormat ));
+ Point aNewAnchorPos( ::lcl_FindAnchorLayPos( *this, aNewAnch, nullptr ));
+
+ // Destroy the old Frames.
+ // The Views are hidden implicitly, so hiding them another time would be
+ // kind of a show!
+ rFormat.DelFrames();
+
+ if ( RndStdIds::FLY_AS_CHAR == nOld )
+ {
+ // We need to handle InContents in a special way:
+ // The TextAttribute needs to be destroyed which, unfortunately, also
+ // destroys the format. To avoid that, we disconnect the format from
+ // the attribute.
+ SwNode *pAnchorNode = rOldAnch.GetAnchorNode();
+ SwTextNode *pTextNode = pAnchorNode->GetTextNode();
+ OSL_ENSURE( pTextNode->HasHints(), "Missing FlyInCnt-Hint." );
+ const sal_Int32 nIdx = rOldAnch.GetAnchorContentOffset();
+ SwTextAttr * const pHint =
+ pTextNode->GetTextAttrForCharAt( nIdx, RES_TXTATR_FLYCNT );
+ OSL_ENSURE( pHint && pHint->Which() == RES_TXTATR_FLYCNT,
+ "Missing FlyInCnt-Hint." );
+ OSL_ENSURE( pHint && pHint->GetFlyCnt().GetFrameFormat() == &rFormat,
+ "Wrong TextFlyCnt-Hint." );
+ if (pHint)
+ const_cast<SwFormatFlyCnt&>(pHint->GetFlyCnt()).SetFlyFormat();
+
+ // They are disconnected. We now have to destroy the attribute.
+ pTextNode->DeleteAttributes( RES_TXTATR_FLYCNT, nIdx, nIdx );
+ }
+
+ // We can finally set the attribute. It needs to be the first one!
+ // Undo depends on it!
+ rFormat.SetFormatAttr( aNewAnch );
+
+ // Correct the position
+ switch( nNew )
+ {
+ case RndStdIds::FLY_AS_CHAR:
+ // If no position attributes are received, we have to make sure
+ // that no forbidden automatic alignment is left.
+ {
+ SwNode *pAnchorNode = aNewAnch.GetAnchorNode();
+ SwTextNode *pNd = pAnchorNode->GetTextNode();
+ OSL_ENSURE( pNd, "Cursor does not point to TextNode." );
+
+ SwFormatFlyCnt aFormat( static_cast<SwFlyFrameFormat*>(&rFormat) );
+ pNd->InsertItem( aFormat, aNewAnch.GetAnchorContentOffset(), 0 );
+ }
+
+ if( SfxItemState::SET != rSet.GetItemState( RES_VERT_ORIENT, false ))
+ {
+ SwFormatVertOrient aOldV( rFormat.GetVertOrient() );
+ bool bSet = true;
+ switch( aOldV.GetVertOrient() )
+ {
+ case text::VertOrientation::LINE_TOP: aOldV.SetVertOrient( text::VertOrientation::TOP ); break;
+ case text::VertOrientation::LINE_CENTER: aOldV.SetVertOrient( text::VertOrientation::CENTER); break;
+ case text::VertOrientation::LINE_BOTTOM: aOldV.SetVertOrient( text::VertOrientation::BOTTOM); break;
+ case text::VertOrientation::NONE: aOldV.SetVertOrient( text::VertOrientation::CENTER); break;
+ default:
+ bSet = false;
+ }
+ if( bSet )
+ rSet.Put( aOldV );
+ }
+ break;
+
+ case RndStdIds::FLY_AT_PARA:
+ case RndStdIds::FLY_AT_CHAR: // LAYER_IMPL
+ case RndStdIds::FLY_AT_FLY: // LAYER_IMPL
+ case RndStdIds::FLY_AT_PAGE:
+ {
+ // If only the anchor type has changed (char -> para -> page) and the absolute position
+ // is unchanged even though there is a new relative orientation
+ // (likely because the old orientation was not valid for the new anchor type),
+ // then adjust the position to account for the moved anchor position.
+ const SwFormatHoriOrient* pHoriOrientItem = rSet.GetItemIfSet( RES_HORI_ORIENT, false );
+
+ SwFormatHoriOrient aOldH( rFormat.GetHoriOrient() );
+ bool bPutOldH(false);
+
+ if (text::HoriOrientation::NONE == aOldH.GetHoriOrient() && pHoriOrientItem
+ && text::HoriOrientation::NONE == pHoriOrientItem->GetHoriOrient()
+ && aOldH.GetPos() == pHoriOrientItem->GetPos())
+ {
+ SwTwips nPos = (RndStdIds::FLY_AS_CHAR == nOld) ? 0 : aOldH.GetPos();
+ nPos += aOldAnchorPos.getX() - aNewAnchorPos.getX();
+
+ assert(aOldH.GetRelationOrient() != pHoriOrientItem->GetRelationOrient());
+ aOldH.SetRelationOrient(pHoriOrientItem->GetRelationOrient());
+
+ aOldH.SetPos( nPos );
+ bPutOldH = true;
+ }
+ if (nNew == RndStdIds::FLY_AT_PAGE)
+ {
+ sal_Int16 nRelOrient(pHoriOrientItem
+ ? pHoriOrientItem->GetRelationOrient()
+ : aOldH.GetRelationOrient());
+ if (sw::GetAtPageRelOrientation(nRelOrient, false))
+ {
+ SAL_INFO("sw.ui", "fixing horizontal RelOrientation for at-page anchor");
+ aOldH.SetRelationOrient(nRelOrient);
+ bPutOldH = true;
+ }
+ }
+ if (bPutOldH)
+ {
+ rSet.Put( aOldH );
+ }
+
+ const SwFormatVertOrient* pVertOrientItem = rSet.GetItemIfSet( RES_VERT_ORIENT, false );
+ SwFormatVertOrient aOldV( rFormat.GetVertOrient() );
+
+ if (text::VertOrientation::NONE == aOldV.GetVertOrient() && pVertOrientItem
+ && text::VertOrientation::NONE == pVertOrientItem->GetVertOrient()
+ && aOldV.GetPos() == pVertOrientItem->GetPos())
+ {
+ SwTwips nPos = (RndStdIds::FLY_AS_CHAR == nOld) ? 0 : aOldV.GetPos();
+ nPos += aOldAnchorPos.getY() - aNewAnchorPos.getY();
+
+ assert(aOldV.GetRelationOrient() != pVertOrientItem->GetRelationOrient());
+ aOldV.SetRelationOrient(pVertOrientItem->GetRelationOrient());
+
+ aOldV.SetPos( nPos );
+ rSet.Put( aOldV );
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ if( bNewFrames )
+ rFormat.MakeFrames();
+
+ return MAKEFRMS;
+}
+
+static bool
+lcl_SetFlyFrameAttr(SwDoc & rDoc,
+ sal_Int8 (SwDoc::*pSetFlyFrameAnchor)(SwFrameFormat &, SfxItemSet &, bool),
+ SwFrameFormat & rFlyFormat, SfxItemSet & rSet)
+{
+ // #i32968# Inserting columns in the frame causes MakeFrameFormat to put two
+ // objects of type SwUndoFrameFormat on the undo stack. We don't want them.
+ ::sw::UndoGuard const undoGuard(rDoc.GetIDocumentUndoRedo());
+
+ // Is the anchor attribute included?
+ // If so, we pass it to a special method, which returns true
+ // if the Fly needs to be created anew, because we e.g change the FlyType.
+ sal_Int8 const nMakeFrames =
+ (SfxItemState::SET == rSet.GetItemState( RES_ANCHOR, false ))
+ ? (rDoc.*pSetFlyFrameAnchor)( rFlyFormat, rSet, false )
+ : DONTMAKEFRMS;
+
+ const SfxPoolItem* pItem;
+ SfxItemIter aIter( rSet );
+ SfxItemSet aTmpSet( rDoc.GetAttrPool(), aFrameFormatSetRange );
+ const SfxPoolItem* pItemIter = aIter.GetCurItem();
+ do {
+ switch(pItemIter->Which())
+ {
+ case RES_FILL_ORDER:
+ case RES_BREAK:
+ case RES_PAGEDESC:
+ case RES_CNTNT:
+ case RES_FOOTER:
+ OSL_FAIL( "Unknown Fly attribute." );
+ [[fallthrough]];
+ case RES_CHAIN:
+ rSet.ClearItem(pItemIter->Which());
+ break;
+ case RES_ANCHOR:
+ if( DONTMAKEFRMS != nMakeFrames )
+ break;
+ [[fallthrough]];
+ default:
+ if( !IsInvalidItem(pItemIter) && ( SfxItemState::SET !=
+ rFlyFormat.GetAttrSet().GetItemState(pItemIter->Which(), true, &pItem ) ||
+ *pItem != *pItemIter))
+ aTmpSet.Put(*pItemIter);
+ break;
+ }
+
+ pItemIter = aIter.NextItem();
+
+ } while (pItemIter && (0 != pItemIter->Which()));
+
+ if( aTmpSet.Count() )
+ rFlyFormat.SetFormatAttr( aTmpSet );
+
+ if( MAKEFRMS == nMakeFrames )
+ rFlyFormat.MakeFrames();
+
+ return aTmpSet.Count() || MAKEFRMS == nMakeFrames;
+}
+
+void SwDoc::CheckForUniqueItemForLineFillNameOrIndex(SfxItemSet& rSet)
+{
+ SwDrawModel* pDrawModel = getIDocumentDrawModelAccess().GetOrCreateDrawModel();
+ SfxItemIter aIter(rSet);
+
+ for (const SfxPoolItem* pItem = aIter.GetCurItem(); pItem; pItem = aIter.NextItem())
+ {
+ if (IsInvalidItem(pItem))
+ continue;
+ std::unique_ptr<SfxPoolItem> pResult;
+
+ switch(pItem->Which())
+ {
+ case XATTR_FILLBITMAP:
+ {
+ pResult = pItem->StaticWhichCast(XATTR_FILLBITMAP).checkForUniqueItem(pDrawModel);
+ break;
+ }
+ case XATTR_LINEDASH:
+ {
+ pResult = pItem->StaticWhichCast(XATTR_LINEDASH).checkForUniqueItem(pDrawModel);
+ break;
+ }
+ case XATTR_LINESTART:
+ {
+ pResult = pItem->StaticWhichCast(XATTR_LINESTART).checkForUniqueItem(pDrawModel);
+ break;
+ }
+ case XATTR_LINEEND:
+ {
+ pResult = pItem->StaticWhichCast(XATTR_LINEEND).checkForUniqueItem(pDrawModel);
+ break;
+ }
+ case XATTR_FILLGRADIENT:
+ {
+ pResult = pItem->StaticWhichCast(XATTR_FILLGRADIENT).checkForUniqueItem(pDrawModel);
+ break;
+ }
+ case XATTR_FILLFLOATTRANSPARENCE:
+ {
+ pResult = pItem->StaticWhichCast(XATTR_FILLFLOATTRANSPARENCE).checkForUniqueItem(pDrawModel);
+ break;
+ }
+ case XATTR_FILLHATCH:
+ {
+ pResult = pItem->StaticWhichCast(XATTR_FILLHATCH).checkForUniqueItem(pDrawModel);
+ break;
+ }
+ }
+
+ if(pResult)
+ {
+ rSet.Put(std::move(pResult));
+ }
+ }
+}
+
+bool SwDoc::SetFlyFrameAttr( SwFrameFormat& rFlyFormat, SfxItemSet& rSet )
+{
+ if( !rSet.Count() )
+ return false;
+
+ SwDocModifyAndUndoGuard guard(rFlyFormat);
+
+ bool const bRet = lcl_SetFlyFrameAttr(*this, &SwDoc::SetFlyFrameAnchor, rFlyFormat, rSet);
+
+ //SwTextBoxHelper::syncFlyFrameAttr(rFlyFormat, rSet);
+
+ return bRet;
+}
+
+// #i73249#
+void SwDoc::SetFlyFrameTitle( SwFlyFrameFormat& rFlyFrameFormat,
+ const OUString& sNewTitle )
+{
+ if ( rFlyFrameFormat.GetObjTitle() == sNewTitle )
+ {
+ return;
+ }
+
+ ::sw::DrawUndoGuard const drawUndoGuard(GetIDocumentUndoRedo());
+
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().AppendUndo( std::make_unique<SwUndoFlyStrAttr>( rFlyFrameFormat,
+ SwUndoId::FLYFRMFMT_TITLE,
+ rFlyFrameFormat.GetObjTitle(),
+ sNewTitle ) );
+ }
+
+ rFlyFrameFormat.SetObjTitle( sNewTitle, true );
+
+ getIDocumentState().SetModified();
+}
+
+void SwDoc::SetFlyFrameDescription( SwFlyFrameFormat& rFlyFrameFormat,
+ const OUString& sNewDescription )
+{
+ if ( rFlyFrameFormat.GetObjDescription() == sNewDescription )
+ {
+ return;
+ }
+
+ ::sw::DrawUndoGuard const drawUndoGuard(GetIDocumentUndoRedo());
+
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().AppendUndo( std::make_unique<SwUndoFlyStrAttr>( rFlyFrameFormat,
+ SwUndoId::FLYFRMFMT_DESCRIPTION,
+ rFlyFrameFormat.GetObjDescription(),
+ sNewDescription ) );
+ }
+
+ rFlyFrameFormat.SetObjDescription( sNewDescription, true );
+
+ getIDocumentState().SetModified();
+}
+
+void SwDoc::SetFlyFrameDecorative(SwFlyFrameFormat& rFlyFrameFormat,
+ bool const isDecorative)
+{
+ if (rFlyFrameFormat.GetAttrSet().Get(RES_DECORATIVE).GetValue() == isDecorative)
+ {
+ return;
+ }
+
+ ::sw::DrawUndoGuard const drawUndoGuard(GetIDocumentUndoRedo());
+
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().AppendUndo(
+ std::make_unique<SwUndoFlyDecorative>(rFlyFrameFormat, isDecorative));
+ }
+
+ rFlyFrameFormat.SetObjDecorative(isDecorative);
+
+ getIDocumentState().SetModified();
+}
+
+
+bool SwDoc::SetFrameFormatToFly( SwFrameFormat& rFormat, SwFrameFormat& rNewFormat,
+ SfxItemSet* pSet, bool bKeepOrient )
+{
+ bool bChgAnchor = false, bFrameSz = false;
+
+ const SwFormatFrameSize aFrameSz( rFormat.GetFrameSize() );
+
+ SwUndoSetFlyFormat* pUndo = nullptr;
+ bool const bUndo = GetIDocumentUndoRedo().DoesUndo();
+ if (bUndo)
+ {
+ pUndo = new SwUndoSetFlyFormat( rFormat, rNewFormat );
+ GetIDocumentUndoRedo().AppendUndo(std::unique_ptr<SwUndo>(pUndo));
+ }
+
+ // #i32968# Inserting columns in the section causes MakeFrameFormat to put
+ // 2 objects of type SwUndoFrameFormat on the undo stack. We don't want them.
+ ::sw::UndoGuard const undoGuard(GetIDocumentUndoRedo());
+
+ // Set the column first, or we'll have trouble with
+ //Set/Reset/Synch. and so on
+ if( SfxItemState::SET != rNewFormat.GetAttrSet().GetItemState( RES_COL ))
+ rFormat.ResetFormatAttr( RES_COL );
+
+ if( rFormat.DerivedFrom() != &rNewFormat )
+ {
+ rFormat.SetDerivedFrom( &rNewFormat );
+
+ // 1. If not automatic = ignore; else = dispose
+ // 2. Dispose of it!
+ if( SfxItemState::SET == rNewFormat.GetAttrSet().GetItemState( RES_FRM_SIZE, false ))
+ {
+ rFormat.ResetFormatAttr( RES_FRM_SIZE );
+ bFrameSz = true;
+ }
+
+ const SfxItemSet* pAsk = pSet;
+ if( !pAsk ) pAsk = &rNewFormat.GetAttrSet();
+ const SwFormatAnchor* pFormatAnchor = pAsk->GetItemIfSet( RES_ANCHOR, false );
+ if( pFormatAnchor
+ && pFormatAnchor->GetAnchorId() !=
+ rFormat.GetAnchor().GetAnchorId() )
+ {
+ if( pSet )
+ bChgAnchor = MAKEFRMS == SetFlyFrameAnchor( rFormat, *pSet, false );
+ else
+ {
+ // Needs to have the FlyFormat range, because we set attributes in it,
+ // in SetFlyFrameAnchor.
+ SfxItemSet aFlySet( *rNewFormat.GetAttrSet().GetPool(),
+ rNewFormat.GetAttrSet().GetRanges() );
+ aFlySet.Put( *pFormatAnchor );
+ bChgAnchor = MAKEFRMS == SetFlyFrameAnchor( rFormat, aFlySet, false);
+ }
+ }
+ }
+
+ // Only reset vertical and horizontal orientation, if we have automatic alignment
+ // set in the template. Otherwise use the old value.
+ // If we update the frame template the Fly should NOT lose its orientation (which
+ // is not being updated!).
+ // text::HoriOrientation::NONE and text::VertOrientation::NONE are allowed now
+ if (!bKeepOrient)
+ {
+ rFormat.ResetFormatAttr(RES_VERT_ORIENT);
+ rFormat.ResetFormatAttr(RES_HORI_ORIENT);
+ }
+
+ rFormat.ResetFormatAttr( RES_PRINT, RES_SURROUND );
+ rFormat.ResetFormatAttr( RES_LR_SPACE, RES_UL_SPACE );
+ rFormat.ResetFormatAttr( RES_BACKGROUND, RES_COL );
+ rFormat.ResetFormatAttr( RES_EDIT_IN_READONLY );
+
+ if( !bFrameSz )
+ rFormat.SetFormatAttr( aFrameSz );
+
+ if( bChgAnchor )
+ rFormat.MakeFrames();
+
+ if( pUndo )
+ pUndo->EndListeningAll();
+
+ getIDocumentState().SetModified();
+
+ return bChgAnchor;
+}
+
+void SwDoc::GetGrfNms( const SwFlyFrameFormat& rFormat, OUString* pGrfName,
+ OUString* pFltName )
+{
+ SwNodeIndex aIdx( *rFormat.GetContent().GetContentIdx(), 1 );
+ const SwGrfNode* pGrfNd = aIdx.GetNode().GetGrfNode();
+ if( pGrfNd && pGrfNd->IsLinkedFile() )
+ pGrfNd->GetFileFilterNms( pGrfName, pFltName );
+}
+
+bool SwDoc::ChgAnchor( const SdrMarkList& _rMrkList,
+ RndStdIds _eAnchorType,
+ const bool _bSameOnly,
+ const bool _bPosCorr )
+{
+ OSL_ENSURE( getIDocumentLayoutAccess().GetCurrentLayout(), "No layout!" );
+
+ if ( !_rMrkList.GetMarkCount() ||
+ _rMrkList.GetMark( 0 )->GetMarkedSdrObj()->getParentSdrObjectFromSdrObject() )
+ {
+ return false;
+ }
+
+ GetIDocumentUndoRedo().StartUndo( SwUndoId::INSATTR, nullptr );
+
+ bool bUnmark = false;
+ for ( size_t i = 0; i < _rMrkList.GetMarkCount(); ++i )
+ {
+ SdrObject* pObj = _rMrkList.GetMark( i )->GetMarkedSdrObj();
+ if ( dynamic_cast<const SwVirtFlyDrawObj*>( pObj) == nullptr )
+ {
+ SwDrawContact* pContact = static_cast<SwDrawContact*>(GetUserCall(pObj));
+
+ // consider, that drawing object has
+ // no user call. E.g.: a 'virtual' drawing object is disconnected by
+ // the anchor type change of the 'master' drawing object.
+ // Continue with next selected object and assert, if this isn't excepted.
+ if ( !pContact )
+ {
+#if OSL_DEBUG_LEVEL > 0
+ auto pSwDrawVirtObj = dynamic_cast<SwDrawVirtObj*>( pObj);
+ bool bNoUserCallExcepted = pSwDrawVirtObj && !pSwDrawVirtObj->IsConnected();
+ OSL_ENSURE( bNoUserCallExcepted, "SwDoc::ChgAnchor(..) - no contact at selected drawing object" );
+#endif
+ continue;
+ }
+
+ // #i26791#
+ const SwFrame* pOldAnchorFrame = pContact->GetAnchorFrame( pObj );
+ const SwFrame* pNewAnchorFrame = pOldAnchorFrame;
+
+ // #i54336#
+ // Instead of only keeping the index position for an as-character
+ // anchored object the complete <SwPosition> is kept, because the
+ // anchor index position could be moved, if the object again is
+ // anchored as character.
+ std::optional<SwPosition> oOldAsCharAnchorPos;
+ const RndStdIds eOldAnchorType = pContact->GetAnchorId();
+ if ( !_bSameOnly && eOldAnchorType == RndStdIds::FLY_AS_CHAR )
+ {
+ oOldAsCharAnchorPos.emplace(*pContact->GetAnchorFormat().GetContentAnchor());
+ }
+
+ if ( _bSameOnly )
+ _eAnchorType = eOldAnchorType;
+
+ SwFormatAnchor aNewAnch( _eAnchorType );
+ SwAnchoredObject *pAnchoredObj = pContact->GetAnchoredObj(pObj);
+ tools::Rectangle aObjRect(pAnchoredObj->GetObjRect().SVRect());
+ const Point aPt( aObjRect.TopLeft() );
+
+ switch ( _eAnchorType )
+ {
+ case RndStdIds::FLY_AT_PARA:
+ case RndStdIds::FLY_AT_CHAR:
+ {
+ const Point aNewPoint = ( pOldAnchorFrame->IsVertical() ||
+ pOldAnchorFrame->IsRightToLeft() )
+ ? aObjRect.TopRight()
+ : aPt;
+
+ // allow drawing objects in header/footer
+ pNewAnchorFrame = ::FindAnchor( pOldAnchorFrame, aNewPoint );
+ if ( pNewAnchorFrame->IsTextFrame() && static_cast<const SwTextFrame*>(pNewAnchorFrame)->IsFollow() )
+ {
+ pNewAnchorFrame = static_cast<const SwTextFrame*>(pNewAnchorFrame)->FindMaster();
+ }
+ if ( pNewAnchorFrame->IsProtected() )
+ {
+ pNewAnchorFrame = nullptr;
+ }
+ else
+ {
+ SwPosition aPos( pNewAnchorFrame->IsTextFrame()
+ ? *static_cast<SwTextFrame const*>(pNewAnchorFrame)->GetTextNodeForParaProps()
+ : *static_cast<SwNoTextFrame const*>(pNewAnchorFrame)->GetNode() );
+
+ aNewAnch.SetType( _eAnchorType );
+ aNewAnch.SetAnchor( &aPos );
+ }
+ }
+ break;
+
+ case RndStdIds::FLY_AT_FLY: // LAYER_IMPL
+ {
+ // Search the closest SwFlyFrame starting from the upper left corner.
+ SwFrame *pTextFrame;
+ {
+ SwCursorMoveState aState( CursorMoveState::SetOnlyText );
+ SwPosition aPos( GetNodes() );
+ Point aPoint( aPt );
+ aPoint.setX(aPoint.getX() - 1);
+ getIDocumentLayoutAccess().GetCurrentLayout()->GetModelPositionForViewPoint( &aPos, aPoint, &aState );
+ // consider that drawing objects can be in
+ // header/footer. Thus, <GetFrame()> by left-top-corner
+ std::pair<Point, bool> const tmp(aPt, false);
+ pTextFrame = aPos.GetNode().
+ GetContentNode()->getLayoutFrame(
+ getIDocumentLayoutAccess().GetCurrentLayout(),
+ nullptr, &tmp);
+ }
+ const SwFrame *pTmp = ::FindAnchor( pTextFrame, aPt );
+ pNewAnchorFrame = pTmp->FindFlyFrame();
+ if( pNewAnchorFrame && !pNewAnchorFrame->IsProtected() )
+ {
+ const SwFrameFormat *pTmpFormat = static_cast<const SwFlyFrame*>(pNewAnchorFrame)->GetFormat();
+ const SwFormatContent& rContent = pTmpFormat->GetContent();
+ SwPosition aPos( *rContent.GetContentIdx() );
+ aNewAnch.SetAnchor( &aPos );
+ break;
+ }
+
+ aNewAnch.SetType( RndStdIds::FLY_AT_PAGE );
+ [[fallthrough]];
+ }
+ case RndStdIds::FLY_AT_PAGE:
+ {
+ pNewAnchorFrame = getIDocumentLayoutAccess().GetCurrentLayout()->Lower();
+ while ( pNewAnchorFrame && !pNewAnchorFrame->getFrameArea().Contains( aPt ) )
+ pNewAnchorFrame = pNewAnchorFrame->GetNext();
+ if ( !pNewAnchorFrame )
+ continue;
+
+ aNewAnch.SetPageNum( static_cast<const SwPageFrame*>(pNewAnchorFrame)->GetPhyPageNum());
+ }
+ break;
+ case RndStdIds::FLY_AS_CHAR:
+ if( _bSameOnly ) // Change of position/size
+ {
+ if( !pOldAnchorFrame )
+ {
+ pContact->ConnectToLayout();
+ pOldAnchorFrame = pContact->GetAnchorFrame();
+ }
+ const_cast<SwTextFrame*>(static_cast<const SwTextFrame*>(pOldAnchorFrame))->Prepare();
+ }
+ else // Change of anchors
+ {
+ // allow drawing objects in header/footer
+ pNewAnchorFrame = ::FindAnchor( pOldAnchorFrame, aPt );
+ if( pNewAnchorFrame->IsProtected() )
+ {
+ pNewAnchorFrame = nullptr;
+ break;
+ }
+
+ bUnmark = ( 0 != i );
+ Point aPoint( aPt );
+ aPoint.setX(aPoint.getX() - 1); // Do not load in the DrawObj!
+ aNewAnch.SetType( RndStdIds::FLY_AS_CHAR );
+ assert(pNewAnchorFrame->IsTextFrame()); // because AS_CHAR
+ SwTextFrame const*const pFrame(
+ static_cast<SwTextFrame const*>(pNewAnchorFrame));
+ SwPosition aPos( *pFrame->GetTextNodeForParaProps() );
+ if ( pNewAnchorFrame->getFrameArea().Contains( aPoint ) )
+ {
+ // We need to find a TextNode, because only there we can anchor a
+ // content-bound DrawObject.
+ SwCursorMoveState aState( CursorMoveState::SetOnlyText );
+ getIDocumentLayoutAccess().GetCurrentLayout()->GetModelPositionForViewPoint( &aPos, aPoint, &aState );
+ }
+ else
+ {
+ if ( pNewAnchorFrame->getFrameArea().Bottom() < aPt.Y() )
+ {
+ aPos = pFrame->MapViewToModelPos(TextFrameIndex(0));
+ }
+ else
+ {
+ aPos = pFrame->MapViewToModelPos(
+ TextFrameIndex(pFrame->GetText().getLength()));
+ }
+ }
+ aNewAnch.SetAnchor( &aPos );
+ SetAttr( aNewAnch, *pContact->GetFormat() );
+ // #i26791# - adjust vertical positioning to 'center to
+ // baseline'
+ SetAttr( SwFormatVertOrient( 0, text::VertOrientation::CENTER, text::RelOrientation::FRAME ), *pContact->GetFormat() );
+ SwTextNode *pNd = aPos.GetNode().GetTextNode();
+ OSL_ENSURE( pNd, "Cursor not positioned at TextNode." );
+
+ SwFormatFlyCnt aFormat( pContact->GetFormat() );
+ pNd->InsertItem( aFormat, aPos.GetContentIndex(), 0 );
+
+ // Has a textbox attached to the format? Sync it as well!
+ if (pContact->GetFormat() && pContact->GetFormat()->GetOtherTextBoxFormats())
+ {
+ SwTextBoxHelper::synchronizeGroupTextBoxProperty(
+ SwTextBoxHelper::changeAnchor, pContact->GetFormat(), pObj);
+ }
+ }
+ break;
+ default:
+ OSL_ENSURE( false, "unexpected AnchorId." );
+ }
+
+ if ( (RndStdIds::FLY_AS_CHAR != _eAnchorType) &&
+ pNewAnchorFrame &&
+ ( !_bSameOnly || pNewAnchorFrame != pOldAnchorFrame ) )
+ {
+ // #i26791# - Direct object positioning no longer needed. Apply
+ // of attributes (method call <SetAttr(..)>) takes care of the
+ // invalidation of the object position.
+ if ( _bPosCorr )
+ {
+ // #i33313# - consider not connected 'virtual' drawing
+ // objects
+ auto pSwDrawVirtObj = dynamic_cast<SwDrawVirtObj*>( pObj);
+ if ( pSwDrawVirtObj && !pSwDrawVirtObj->IsConnected() )
+ {
+ SwRect aNewObjRect( aObjRect );
+ static_cast<SwAnchoredDrawObject*>(pContact->GetAnchoredObj( nullptr ))
+ ->AdjustPositioningAttr( pNewAnchorFrame,
+ &aNewObjRect );
+ }
+ else
+ {
+ static_cast<SwAnchoredDrawObject*>(pContact->GetAnchoredObj( pObj ))
+ ->AdjustPositioningAttr( pNewAnchorFrame );
+ }
+ }
+ if (aNewAnch.GetAnchorId() == RndStdIds::FLY_AT_PAGE)
+ {
+ SwFormatHoriOrient item(pContact->GetFormat()->GetHoriOrient());
+ sal_Int16 nRelOrient(item.GetRelationOrient());
+ if (sw::GetAtPageRelOrientation(nRelOrient, false))
+ {
+ SAL_INFO("sw.ui", "fixing horizontal RelOrientation for at-page anchor");
+ item.SetRelationOrient(nRelOrient);
+ SetAttr(item, *pContact->GetFormat());
+ }
+ }
+ // tdf#136385 set the anchor last - otherwise it messes up the
+ // position in SwDrawContact::Changed_() callback
+ SetAttr(aNewAnch, *pContact->GetFormat());
+ }
+
+ // we have changed the anchoring attributes, and those are used to
+ // order the object in its sorted list, so update its position
+ pAnchoredObj->UpdateObjInSortedList();
+
+ // #i54336#
+ if (oOldAsCharAnchorPos)
+ {
+ if ( pNewAnchorFrame)
+ {
+ // We need to handle InContents in a special way:
+ // The TextAttribute needs to be destroyed which, unfortunately, also
+ // destroys the format. To avoid that, we disconnect the format from
+ // the attribute.
+ const sal_Int32 nIndx( oOldAsCharAnchorPos->GetContentIndex() );
+ SwTextNode* pTextNode( oOldAsCharAnchorPos->GetNode().GetTextNode() );
+ assert(pTextNode && "<SwDoc::ChgAnchor(..)> - missing previous anchor text node for as-character anchored object");
+ SwTextAttr * const pHint =
+ pTextNode->GetTextAttrForCharAt( nIndx, RES_TXTATR_FLYCNT );
+ assert(pHint && "Missing FlyInCnt-Hint.");
+ const_cast<SwFormatFlyCnt&>(pHint->GetFlyCnt()).SetFlyFormat();
+
+ // They are disconnected. We now have to destroy the attribute.
+ pTextNode->DeleteAttributes( RES_TXTATR_FLYCNT, nIndx, nIndx );
+ }
+ }
+ }
+ }
+
+ GetIDocumentUndoRedo().EndUndo( SwUndoId::END, nullptr );
+ getIDocumentState().SetModified();
+
+ return bUnmark;
+}
+
+SwChainRet SwDoc::Chainable( const SwFrameFormat &rSource, const SwFrameFormat &rDest )
+{
+ // The Source must not yet have a Follow.
+ const SwFormatChain &rOldChain = rSource.GetChain();
+ if ( rOldChain.GetNext() )
+ return SwChainRet::SOURCE_CHAINED;
+
+ // Target must not be equal to Source and we also must not have a closed chain.
+ const SwFrameFormat *pFormat = &rDest;
+ do {
+ if( pFormat == &rSource )
+ return SwChainRet::SELF;
+ pFormat = pFormat->GetChain().GetNext();
+ } while ( pFormat );
+
+ // There must not be a chaining from outside to inside or the other way around.
+ if( rDest.IsLowerOf( rSource ) || rSource .IsLowerOf( rDest ) )
+ return SwChainRet::SELF;
+
+ // The Target must not yet have a Master.
+ const SwFormatChain &rChain = rDest.GetChain();
+ if( rChain.GetPrev() )
+ return SwChainRet::IS_IN_CHAIN;
+
+ // Split flys are incompatible with chaining.
+ const SwFormatFlySplit& rOldSplit = rSource.GetFlySplit();
+ if (rOldSplit.GetValue())
+ {
+ return SwChainRet::SOURCE_CHAINED;
+ }
+ const SwFormatFlySplit& rNewSplit = rDest.GetFlySplit();
+ if (rNewSplit.GetValue())
+ {
+ return SwChainRet::IS_IN_CHAIN;
+ }
+
+ // Target must be empty.
+ const SwNodeIndex* pCntIdx = rDest.GetContent().GetContentIdx();
+ if( !pCntIdx )
+ return SwChainRet::NOT_FOUND;
+
+ SwNodeIndex aNxtIdx( *pCntIdx, 1 );
+ const SwTextNode* pTextNd = aNxtIdx.GetNode().GetTextNode();
+ if( !pTextNd )
+ return SwChainRet::NOT_FOUND;
+
+ const SwNodeOffset nFlySttNd = pCntIdx->GetIndex();
+ if( SwNodeOffset(2) != ( pCntIdx->GetNode().EndOfSectionIndex() - nFlySttNd ) ||
+ pTextNd->GetText().getLength() )
+ {
+ return SwChainRet::NOT_EMPTY;
+ }
+
+ for(sw::SpzFrameFormat* pSpzFrameFm: *GetSpzFrameFormats())
+ {
+ const SwFormatAnchor& rAnchor = pSpzFrameFm->GetAnchor();
+ // #i20622# - to-frame anchored objects are allowed.
+ if ( (rAnchor.GetAnchorId() != RndStdIds::FLY_AT_PARA) &&
+ (rAnchor.GetAnchorId() != RndStdIds::FLY_AT_CHAR) )
+ continue;
+ if ( nullptr == rAnchor.GetAnchorNode() )
+ continue;
+ SwNodeOffset nTstSttNd = rAnchor.GetAnchorNode()->GetIndex();
+ if( nFlySttNd <= nTstSttNd && nTstSttNd < nFlySttNd + SwNodeOffset(2) )
+ {
+ return SwChainRet::NOT_EMPTY;
+ }
+ }
+
+ // We also need to consider the right area.
+ // Both Flys need to be located in the same area (Body, Header/Footer, Fly).
+ // If the Source is not the selected frame, it's enough to find a suitable
+ // one. e.g. if it's requested by the API.
+
+ // both in the same fly, header, footer or on the page?
+ const SwFormatAnchor &rSrcAnchor = rSource.GetAnchor(),
+ &rDstAnchor = rDest.GetAnchor();
+ SwNodeOffset nEndOfExtras = GetNodes().GetEndOfExtras().GetIndex();
+ bool bAllowed = false;
+ if ( RndStdIds::FLY_AT_PAGE == rSrcAnchor.GetAnchorId() )
+ {
+ if ( (RndStdIds::FLY_AT_PAGE == rDstAnchor.GetAnchorId()) ||
+ ( rDstAnchor.GetAnchorNode() &&
+ rDstAnchor.GetAnchorNode()->GetIndex() > nEndOfExtras ))
+ bAllowed = true;
+ }
+ else if( rSrcAnchor.GetAnchorNode() && rDstAnchor.GetAnchorNode() )
+ {
+ const SwNode &rSrcNd = *rSrcAnchor.GetAnchorNode(),
+ &rDstNd = *rDstAnchor.GetAnchorNode();
+ const SwStartNode* pSttNd = nullptr;
+ if( rSrcNd == rDstNd ||
+ ( !pSttNd &&
+ nullptr != ( pSttNd = rSrcNd.FindFlyStartNode() ) &&
+ pSttNd == rDstNd.FindFlyStartNode() ) ||
+ ( !pSttNd &&
+ nullptr != ( pSttNd = rSrcNd.FindFooterStartNode() ) &&
+ pSttNd == rDstNd.FindFooterStartNode() ) ||
+ ( !pSttNd &&
+ nullptr != ( pSttNd = rSrcNd.FindHeaderStartNode() ) &&
+ pSttNd == rDstNd.FindHeaderStartNode() ) ||
+ ( !pSttNd && rDstNd.GetIndex() > nEndOfExtras &&
+ rSrcNd.GetIndex() > nEndOfExtras ))
+ bAllowed = true;
+ }
+
+ return bAllowed ? SwChainRet::OK : SwChainRet::WRONG_AREA;
+}
+
+SwChainRet SwDoc::Chain( SwFrameFormat &rSource, const SwFrameFormat &rDest )
+{
+ SwChainRet nErr = Chainable( rSource, rDest );
+ if ( nErr == SwChainRet::OK )
+ {
+ GetIDocumentUndoRedo().StartUndo( SwUndoId::CHAINE, nullptr );
+
+ SwFlyFrameFormat& rDestFormat = const_cast<SwFlyFrameFormat&>(static_cast<const SwFlyFrameFormat&>(rDest));
+
+ // Attach Follow to the Master.
+ SwFormatChain aChain = rDestFormat.GetChain();
+ aChain.SetPrev( &static_cast<SwFlyFrameFormat&>(rSource) );
+ SetAttr( aChain, rDestFormat );
+
+ SfxItemSetFixed<RES_FRM_SIZE, RES_FRM_SIZE,
+ RES_CHAIN, RES_CHAIN> aSet( GetAttrPool() );
+
+ // Attach Follow to the Master.
+ aChain.SetPrev( &static_cast<SwFlyFrameFormat&>(rSource) );
+ SetAttr( aChain, rDestFormat );
+
+ // Attach Master to the Follow.
+ // Make sure that the Master has a fixed height.
+ aChain = rSource.GetChain();
+ aChain.SetNext( &rDestFormat );
+ aSet.Put( aChain );
+
+ SwFormatFrameSize aSize( rSource.GetFrameSize() );
+ if ( aSize.GetHeightSizeType() != SwFrameSize::Fixed )
+ {
+ SwFlyFrame *pFly = SwIterator<SwFlyFrame,SwFormat>( rSource ).First();
+ if ( pFly )
+ aSize.SetHeight( pFly->getFrameArea().Height() );
+ aSize.SetHeightSizeType( SwFrameSize::Fixed );
+ aSet.Put( aSize );
+ }
+ SetAttr( aSet, rSource );
+
+ GetIDocumentUndoRedo().EndUndo( SwUndoId::CHAINE, nullptr );
+ }
+ return nErr;
+}
+
+void SwDoc::Unchain( SwFrameFormat &rFormat )
+{
+ SwFormatChain aChain( rFormat.GetChain() );
+ if ( aChain.GetNext() )
+ {
+ GetIDocumentUndoRedo().StartUndo( SwUndoId::UNCHAIN, nullptr );
+ SwFrameFormat *pFollow = aChain.GetNext();
+ aChain.SetNext( nullptr );
+ SetAttr( aChain, rFormat );
+ aChain = pFollow->GetChain();
+ aChain.SetPrev( nullptr );
+ SetAttr( aChain, *pFollow );
+ GetIDocumentUndoRedo().EndUndo( SwUndoId::UNCHAIN, nullptr );
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/docfmt.cxx b/sw/source/core/doc/docfmt.cxx
new file mode 100644
index 0000000000..57c42c529e
--- /dev/null
+++ b/sw/source/core/doc/docfmt.cxx
@@ -0,0 +1,2097 @@
+/* -*- 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 <libxml/xmlwriter.h>
+#include <hintids.hxx>
+#include <svl/itemiter.hxx>
+#include <svl/numformat.hxx>
+#include <editeng/tstpitem.hxx>
+#include <editeng/lrspitem.hxx>
+#include <editeng/formatbreakitem.hxx>
+#include <editeng/rsiditem.hxx>
+#include <editeng/colritem.hxx>
+#include <officecfg/Office/Common.hxx>
+#include <osl/diagnose.h>
+#include <svl/zforlist.hxx>
+#include <comphelper/processfactory.hxx>
+#include <unotools/configmgr.hxx>
+#include <sal/log.hxx>
+#include <com/sun/star/i18n/WordType.hpp>
+#include <com/sun/star/i18n/XBreakIterator.hpp>
+#include <fmtpdsc.hxx>
+#include <fmthdft.hxx>
+#include <fmtcntnt.hxx>
+#include <doc.hxx>
+#include <docfunc.hxx>
+#include <drawdoc.hxx>
+#include <MarkManager.hxx>
+#include <IDocumentDrawModelAccess.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <DocumentContentOperationsManager.hxx>
+#include <DocumentSettingManager.hxx>
+#include <IDocumentFieldsAccess.hxx>
+#include <IDocumentState.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <IDocumentStylePoolAccess.hxx>
+#include <rootfrm.hxx>
+#include <txtfrm.hxx>
+#include <hints.hxx>
+#include <ndtxt.hxx>
+#include <pam.hxx>
+#include <UndoCore.hxx>
+#include <UndoAttribute.hxx>
+#include <UndoInsert.hxx>
+#include <pagedesc.hxx>
+#include <rolbck.hxx>
+#include <mvsave.hxx>
+#include <txatbase.hxx>
+#include <swtblfmt.hxx>
+#include <charfmt.hxx>
+#include <docary.hxx>
+#include <paratr.hxx>
+#include <redline.hxx>
+#include <reffld.hxx>
+#include <fmtinfmt.hxx>
+#include <breakit.hxx>
+#include <SwUndoFmt.hxx>
+#include <UndoManager.hxx>
+#include <swmodule.hxx>
+#include <modcfg.hxx>
+#include <frameformats.hxx>
+#include <textboxhelper.hxx>
+#include <textcontentcontrol.hxx>
+#include <memory>
+#include <algorithm>
+#include <functional>
+
+using namespace ::com::sun::star::i18n;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::uno;
+
+/*
+ * Internal functions
+ */
+
+static void SetTextFormatCollNext( SwTextFormatColl* pTextColl, const SwTextFormatColl* pDel )
+{
+ if ( &pTextColl->GetNextTextFormatColl() == pDel )
+ {
+ pTextColl->SetNextTextFormatColl( *pTextColl );
+ }
+}
+
+static bool lcl_RstAttr( SwNode* pNd, void* pArgs )
+{
+ const sw::DocumentContentOperationsManager::ParaRstFormat* pPara = static_cast<sw::DocumentContentOperationsManager::ParaRstFormat*>(pArgs);
+ SwContentNode* pNode = pNd->GetContentNode();
+ if (pPara && pPara->pLayout && pPara->pLayout->HasMergedParas()
+ && pNode && pNode->GetRedlineMergeFlag() == SwNode::Merge::Hidden)
+ {
+ return true;
+ }
+ if( pNode && pNode->HasSwAttrSet() )
+ {
+ const bool bLocked = pNode->IsModifyLocked();
+ pNode->LockModify();
+
+ SwDoc& rDoc = pNode->GetDoc();
+
+ // remove unused attribute RES_LR_SPACE
+ // add list attributes, except RES_PARATR_LIST_AUTOFMT
+ SfxItemSetFixed<
+ RES_PARATR_NUMRULE, RES_PARATR_NUMRULE,
+ RES_PARATR_LIST_BEGIN, RES_PARATR_LIST_AUTOFMT - 1,
+ RES_PAGEDESC, RES_BREAK,
+ RES_FRMATR_STYLE_NAME, RES_FRMATR_CONDITIONAL_STYLE_NAME> aSavedAttrsSet(rDoc.GetAttrPool());
+ const SfxItemSet* pAttrSetOfNode = pNode->GetpSwAttrSet();
+
+ std::vector<sal_uInt16> aClearWhichIds;
+ // restoring all paragraph list attributes
+ {
+ SfxItemSetFixed<RES_PARATR_LIST_BEGIN, RES_PARATR_LIST_AUTOFMT - 1> aListAttrSet( rDoc.GetAttrPool() );
+ aListAttrSet.Set(*pAttrSetOfNode);
+ if ( aListAttrSet.Count() )
+ {
+ aSavedAttrsSet.Put(aListAttrSet);
+ SfxItemIter aIter( aListAttrSet );
+ const SfxPoolItem* pItem = aIter.GetCurItem();
+ while( pItem )
+ {
+ aClearWhichIds.push_back( pItem->Which() );
+ pItem = aIter.NextItem();
+ }
+ }
+ }
+
+ if (auto pItem = pAttrSetOfNode->GetItemIfSet(RES_PARATR_NUMRULE, false);
+ pItem && !pItem->GetValue().isEmpty())
+ {
+ aSavedAttrsSet.Put(*pItem);
+ aClearWhichIds.push_back(RES_PARATR_NUMRULE);
+ }
+ if (auto pItem = pAttrSetOfNode->GetItemIfSet(RES_PAGEDESC, false);
+ pItem && pItem->GetPageDesc())
+ {
+ aSavedAttrsSet.Put(*pItem);
+ aClearWhichIds.push_back(RES_PAGEDESC);
+ }
+ if (auto pItem = pAttrSetOfNode->GetItemIfSet(RES_BREAK, false);
+ pItem && pItem->GetBreak() != SvxBreak::NONE)
+ {
+ aSavedAttrsSet.Put(*pItem);
+ aClearWhichIds.push_back(RES_BREAK);
+ }
+ if (auto pItem = pAttrSetOfNode->GetItemIfSet(RES_FRMATR_STYLE_NAME, false);
+ pItem && !pItem->GetValue().isEmpty())
+ {
+ aSavedAttrsSet.Put(*pItem);
+ aClearWhichIds.push_back(RES_FRMATR_STYLE_NAME);
+ }
+ if (auto pItem = pAttrSetOfNode->GetItemIfSet(RES_FRMATR_CONDITIONAL_STYLE_NAME, false);
+ pItem && !pItem->GetValue().isEmpty())
+ {
+ aSavedAttrsSet.Put(*pItem);
+ aClearWhichIds.push_back(RES_FRMATR_CONDITIONAL_STYLE_NAME);
+ }
+
+ // do not clear items directly from item set and only clear to be kept
+ // attributes, if no deletion item set is found.
+ const bool bKeepAttributes =
+ !pPara || !pPara->pDelSet || pPara->pDelSet->Count() == 0;
+ if ( bKeepAttributes )
+ {
+ pNode->ResetAttr( aClearWhichIds );
+ }
+
+ if( !bLocked )
+ pNode->UnlockModify();
+
+ if( pPara )
+ {
+ SwRegHistory aRegH( pNode, *pNode, pPara->pHistory );
+
+ if( pPara->pDelSet && pPara->pDelSet->Count() )
+ {
+ OSL_ENSURE( !bKeepAttributes,
+ "<lcl_RstAttr(..)> - certain attributes are kept, but not needed." );
+ SfxItemIter aIter( *pPara->pDelSet );
+ for (const SfxPoolItem* pItem = aIter.GetCurItem(); pItem; pItem = aIter.NextItem())
+ {
+ if ( ( pItem->Which() != RES_PAGEDESC &&
+ pItem->Which() != RES_BREAK &&
+ pItem->Which() != RES_FRMATR_STYLE_NAME &&
+ pItem->Which() != RES_FRMATR_CONDITIONAL_STYLE_NAME &&
+ pItem->Which() != RES_PARATR_NUMRULE ) ||
+ ( aSavedAttrsSet.GetItemState( pItem->Which(), false ) != SfxItemState::SET ) )
+ {
+ pNode->ResetAttr( pItem->Which() );
+ }
+ }
+ }
+ else if( pPara->bResetAll )
+ pNode->ResetAllAttr();
+ else
+ pNode->ResetAttr( RES_PARATR_BEGIN, POOLATTR_END - 1 );
+ }
+ else
+ pNode->ResetAllAttr();
+
+ // only restore saved attributes, if needed
+ if (bKeepAttributes && aSavedAttrsSet.Count())
+ {
+ pNode->LockModify();
+
+ pNode->SetAttr(aSavedAttrsSet);
+
+ if( !bLocked )
+ pNode->UnlockModify();
+ }
+ }
+ return true;
+}
+
+void SwDoc::RstTextAttrs(const SwPaM &rRg, bool bInclRefToxMark,
+ bool bExactRange, SwRootFrame const*const pLayout)
+{
+ SwHistory* pHst = nullptr;
+ SwDataChanged aTmp( rRg );
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ std::unique_ptr<SwUndoResetAttr> pUndo(new SwUndoResetAttr( rRg, RES_CHRFMT ));
+ pHst = &pUndo->GetHistory();
+ GetIDocumentUndoRedo().AppendUndo(std::move(pUndo));
+ }
+ auto [pStt, pEnd] = rRg.StartEnd(); // SwPosition*
+ sw::DocumentContentOperationsManager::ParaRstFormat aPara(
+ pStt, pEnd, pHst, nullptr, pLayout );
+ aPara.bInclRefToxMark = bInclRefToxMark;
+ aPara.bExactRange = bExactRange;
+ GetNodes().ForEach( pStt->GetNodeIndex(), pEnd->GetNodeIndex()+1,
+ sw::DocumentContentOperationsManager::lcl_RstTextAttr, &aPara );
+ getIDocumentState().SetModified();
+}
+
+void SwDoc::ResetAttrs( const SwPaM &rRg,
+ bool bTextAttr,
+ const o3tl::sorted_vector<sal_uInt16> &rAttrs,
+ const bool bSendDataChangedEvents,
+ SwRootFrame const*const pLayout)
+{
+ SwPaM* pPam = const_cast<SwPaM*>(&rRg);
+ std::optional<SwPaM> oExtraPaM;
+ if( !bTextAttr && !rAttrs.empty() && RES_TXTATR_END > *(rAttrs.begin()) )
+ bTextAttr = true;
+
+ if( !rRg.HasMark() )
+ {
+ SwTextNode* pTextNd = rRg.GetPoint()->GetNode().GetTextNode();
+ if( !pTextNd )
+ return ;
+
+ oExtraPaM.emplace( *rRg.GetPoint() );
+ pPam = &*oExtraPaM;
+
+ SwPosition& rSt = *pPam->GetPoint();
+ sal_Int32 nMkPos, nPtPos = rSt.GetContentIndex();
+
+ // Special case: if the Cursor is located within a URL attribute, we take over it's area
+ SwTextAttr const*const pURLAttr(
+ pTextNd->GetTextAttrAt(rSt.GetContentIndex(), RES_TXTATR_INETFMT));
+ if (pURLAttr && !pURLAttr->GetINetFormat().GetValue().isEmpty())
+ {
+ nMkPos = pURLAttr->GetStart();
+ nPtPos = *pURLAttr->End();
+ }
+ else
+ {
+ assert(g_pBreakIt && g_pBreakIt->GetBreakIter().is());
+ Boundary aBndry = g_pBreakIt->GetBreakIter()->getWordBoundary(
+ pTextNd->GetText(), nPtPos,
+ g_pBreakIt->GetLocale( pTextNd->GetLang( nPtPos ) ),
+ WordType::ANY_WORD /*ANYWORD_IGNOREWHITESPACES*/,
+ true);
+
+ if( aBndry.startPos < nPtPos && nPtPos < aBndry.endPos )
+ {
+ nMkPos = aBndry.startPos;
+ nPtPos = aBndry.endPos;
+ }
+ else
+ {
+ nPtPos = nMkPos = rSt.GetContentIndex();
+ if( bTextAttr )
+ pTextNd->DontExpandFormat( nPtPos );
+ }
+ }
+
+ rSt.SetContent(nMkPos);
+ pPam->SetMark();
+ pPam->GetPoint()->SetContent(nPtPos);
+ }
+
+ // #i96644#
+ std::optional< SwDataChanged > oDataChanged;
+ if ( bSendDataChangedEvents )
+ {
+ oDataChanged.emplace( *pPam );
+ }
+ SwHistory* pHst = nullptr;
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ std::unique_ptr<SwUndoResetAttr> pUndo(new SwUndoResetAttr( rRg,
+ bTextAttr ? sal_uInt16(RES_CONDTXTFMTCOLL) : sal_uInt16(RES_TXTFMTCOLL) ));
+ if( !rAttrs.empty() )
+ {
+ pUndo->SetAttrs( o3tl::sorted_vector(rAttrs) );
+ }
+ pHst = &pUndo->GetHistory();
+ GetIDocumentUndoRedo().AppendUndo(std::move(pUndo));
+ }
+
+ auto [pStt, pEnd] = pPam->StartEnd(); // SwPosition*
+ sw::DocumentContentOperationsManager::ParaRstFormat aPara(
+ pStt, pEnd, pHst, nullptr, pLayout);
+
+ // mst: not including META here; it seems attrs with CH_TXTATR are omitted
+ SfxItemSetFixed<RES_CHRATR_BEGIN, RES_CHRATR_END - 1,
+ RES_TXTATR_INETFMT, RES_TXTATR_UNKNOWN_CONTAINER,
+ RES_PARATR_BEGIN, RES_FRMATR_END - 1,
+ RES_UNKNOWNATR_BEGIN, RES_UNKNOWNATR_END - 1>
+ aDelSet(GetAttrPool());
+ for( auto it = rAttrs.rbegin(); it != rAttrs.rend(); ++it )
+ {
+ if( POOLATTR_END > *it )
+ aDelSet.Put( *GetDfltAttr( *it ));
+ }
+ if( aDelSet.Count() )
+ aPara.pDelSet = &aDelSet;
+
+ bool bAdd = true;
+ SwNodeIndex aTmpStt( pStt->GetNode() );
+ SwNodeIndex aTmpEnd( pEnd->GetNode() );
+ if( pStt->GetContentIndex() ) // just one part
+ {
+ // set up a later, and all CharFormatAttr -> TextFormatAttr
+ SwTextNode* pTNd = aTmpStt.GetNode().GetTextNode();
+ if( pTNd && pTNd->HasSwAttrSet() && pTNd->GetpSwAttrSet()->Count() )
+ {
+ if (pHst)
+ {
+ SwRegHistory history(pTNd, *pTNd, pHst);
+ pTNd->FormatToTextAttr(pTNd);
+ }
+ else
+ {
+ pTNd->FormatToTextAttr(pTNd);
+ }
+ }
+
+ ++aTmpStt;
+ }
+ if( pEnd->GetContentIndex() == pEnd->GetNode().GetContentNode()->Len() )
+ {
+ // set up a later, and all CharFormatAttr -> TextFormatAttr
+ ++aTmpEnd;
+ bAdd = false;
+ }
+ else if( pStt->GetNode() != pEnd->GetNode() || !pStt->GetContentIndex() )
+ {
+ SwTextNode* pTNd = aTmpEnd.GetNode().GetTextNode();
+ if( pTNd && pTNd->HasSwAttrSet() && pTNd->GetpSwAttrSet()->Count() )
+ {
+ if (pHst)
+ {
+ SwRegHistory history(pTNd, *pTNd, pHst);
+ pTNd->FormatToTextAttr(pTNd);
+ }
+ else
+ {
+ pTNd->FormatToTextAttr(pTNd);
+ }
+ }
+ }
+
+ if( aTmpStt < aTmpEnd )
+ GetNodes().ForEach( pStt->GetNode(), aTmpEnd.GetNode(), lcl_RstAttr, &aPara );
+ else if( !rRg.HasMark() )
+ {
+ aPara.bResetAll = false ;
+ ::lcl_RstAttr( &pStt->GetNode(), &aPara );
+ aPara.bResetAll = true ;
+ }
+
+ if( bTextAttr )
+ {
+ if( bAdd )
+ ++aTmpEnd;
+ GetNodes().ForEach( pStt->GetNode(), aTmpEnd.GetNode(), sw::DocumentContentOperationsManager::lcl_RstTextAttr, &aPara );
+ }
+
+ getIDocumentState().SetModified();
+
+ oDataChanged.reset(); //before delete pPam
+}
+
+/// Set the rsid of the next nLen symbols of rRg to the current session number
+void SwDoc::UpdateRsid( const SwPaM &rRg, const sal_Int32 nLen )
+{
+ if (!SW_MOD()->GetModuleConfig()->IsStoreRsid())
+ return;
+
+ SwTextNode *pTextNode = rRg.GetPoint()->GetNode().GetTextNode();
+ if (!pTextNode)
+ {
+ return;
+ }
+ const sal_Int32 nStart(rRg.GetPoint()->GetContentIndex() - nLen);
+ SvxRsidItem aRsid( mnRsid, RES_CHRATR_RSID );
+
+ SfxItemSetFixed<RES_CHRATR_RSID, RES_CHRATR_RSID> aSet(GetAttrPool());
+ aSet.Put(aRsid);
+ bool const bRet(pTextNode->SetAttr(aSet, nStart,
+ rRg.GetPoint()->GetContentIndex()));
+
+ if (bRet && GetIDocumentUndoRedo().DoesUndo())
+ {
+ SwUndo *const pLastUndo = GetUndoManager().GetLastUndo();
+ SwUndoInsert *const pUndoInsert(dynamic_cast<SwUndoInsert*>(pLastUndo));
+ // this function is called after Insert so expects to find SwUndoInsert
+ assert(pUndoInsert);
+ if (pUndoInsert)
+ {
+ pUndoInsert->SetWithRsid();
+ }
+ }
+}
+
+bool SwDoc::UpdateParRsid( SwTextNode *pTextNode, sal_uInt32 nVal )
+{
+ if (!SW_MOD()->GetModuleConfig()->IsStoreRsid())
+ return false;
+
+ if (!pTextNode)
+ {
+ return false;
+ }
+
+ SvxRsidItem aRsid( nVal ? nVal : mnRsid, RES_PARATR_RSID );
+ return pTextNode->SetAttr( aRsid );
+}
+
+/// Set the attribute according to the stated format.
+/// If Undo is enabled, the old values is added to the Undo history.
+void SwDoc::SetAttr( const SfxPoolItem& rAttr, SwFormat& rFormat )
+{
+ SfxItemSet aSet( GetAttrPool(), rAttr.Which(), rAttr.Which() );
+ aSet.Put( rAttr );
+ SetAttr( aSet, rFormat );
+}
+
+/// Set the attribute according to the stated format.
+/// If Undo is enabled, the old values is added to the Undo history.
+void SwDoc::SetAttr( const SfxItemSet& rSet, SwFormat& rFormat )
+{
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ SwUndoFormatAttrHelper aTmp( rFormat );
+ rFormat.SetFormatAttr( rSet );
+ if ( aTmp.GetUndo() )
+ {
+ GetIDocumentUndoRedo().AppendUndo( aTmp.ReleaseUndo() );
+ }
+ else
+ {
+ GetIDocumentUndoRedo().ClearRedo();
+ }
+ }
+ else
+ {
+ rFormat.SetFormatAttr( rSet );
+ }
+
+ // If the format is a shape, and it has a textbox, sync.
+ auto pShapeFormat = dynamic_cast<SwFrameFormat*>(&rFormat);
+ if (pShapeFormat && SwTextBoxHelper::isTextBox(pShapeFormat, RES_DRAWFRMFMT))
+ {
+ if (auto pObj = pShapeFormat->FindRealSdrObject())
+ {
+ SwTextBoxHelper::syncFlyFrameAttr(*pShapeFormat, rSet, pObj);
+ SwTextBoxHelper::changeAnchor(pShapeFormat, pObj);
+ }
+ }
+
+ getIDocumentState().SetModified();
+}
+
+void SwDoc::ResetAttrAtFormat( const std::vector<sal_uInt16>& rIds,
+ SwFormat& rChangedFormat )
+{
+ std::unique_ptr<SwUndo> pUndo;
+ if (GetIDocumentUndoRedo().DoesUndo())
+ pUndo.reset(new SwUndoFormatResetAttr( rChangedFormat, rIds ));
+
+ bool bAttrReset = false;
+ for (const auto& nWhichId : rIds)
+ bAttrReset = rChangedFormat.ResetFormatAttr(nWhichId) || bAttrReset;
+
+ if ( bAttrReset )
+ {
+ if ( pUndo )
+ {
+ GetIDocumentUndoRedo().AppendUndo( std::move(pUndo) );
+ }
+
+ getIDocumentState().SetModified();
+ }
+}
+
+static bool lcl_SetNewDefTabStops( SwTwips nOldWidth, SwTwips nNewWidth,
+ SvxTabStopItem& rChgTabStop )
+{
+ // Set the default values of all TabStops to the new value.
+ // Attention: we always work with the PoolAttribute here, so that
+ // we don't calculate the same value on the same TabStop (pooled!) for all sets.
+ // We send a FormatChg to modify.
+
+ sal_uInt16 nOldCnt = rChgTabStop.Count();
+ if( !nOldCnt || nOldWidth == nNewWidth )
+ return false;
+
+ // Find the default's beginning
+ sal_uInt16 n;
+ for( n = nOldCnt; n ; --n )
+ if( SvxTabAdjust::Default != rChgTabStop[n - 1].GetAdjustment() )
+ break;
+ ++n;
+ if( n < nOldCnt ) // delete the DefTabStops
+ rChgTabStop.Remove( n, nOldCnt - n );
+ return true;
+}
+
+/// Set the attribute as new default attribute in this document.
+/// If Undo is enabled, the old value is added to the Undo history.
+void SwDoc::SetDefault( const SfxPoolItem& rAttr )
+{
+ SfxItemSet aSet( GetAttrPool(), rAttr.Which(), rAttr.Which() );
+ aSet.Put( rAttr );
+ SetDefault( aSet );
+}
+
+void SwDoc::SetDefault( const SfxItemSet& rSet )
+{
+ if( !rSet.Count() )
+ return;
+
+ sw::BroadcastingModify aCallMod;
+ SwAttrSet aOld( GetAttrPool(), rSet.GetRanges() ),
+ aNew( GetAttrPool(), rSet.GetRanges() );
+ SfxItemIter aIter( rSet );
+ const SfxPoolItem* pItem = aIter.GetCurItem();
+ SfxItemPool* pSdrPool = GetAttrPool().GetSecondaryPool();
+ do
+ {
+ bool bCheckSdrDflt = false;
+ const sal_uInt16 nWhich = pItem->Which();
+ aOld.Put( GetAttrPool().GetDefaultItem( nWhich ) );
+ GetAttrPool().SetPoolDefaultItem( *pItem );
+ aNew.Put( GetAttrPool().GetDefaultItem( nWhich ) );
+
+ if (isCHRATR(nWhich) || isTXTATR(nWhich))
+ {
+ aCallMod.Add( mpDfltTextFormatColl.get() );
+ aCallMod.Add( mpDfltCharFormat.get() );
+ bCheckSdrDflt = nullptr != pSdrPool;
+ }
+ else if ( isPARATR(nWhich) ||
+ isPARATR_LIST(nWhich) )
+ {
+ aCallMod.Add( mpDfltTextFormatColl.get() );
+ bCheckSdrDflt = nullptr != pSdrPool;
+ }
+ else if (isGRFATR(nWhich))
+ {
+ aCallMod.Add( mpDfltGrfFormatColl.get() );
+ }
+ else if (isFRMATR(nWhich) || isDrawingLayerAttribute(nWhich) )
+ {
+ aCallMod.Add( mpDfltGrfFormatColl.get() );
+ aCallMod.Add( mpDfltTextFormatColl.get() );
+ aCallMod.Add( mpDfltFrameFormat.get() );
+ }
+ else if (isBOXATR(nWhich))
+ {
+ aCallMod.Add( mpDfltFrameFormat.get() );
+ }
+
+ // also copy the defaults
+ if( bCheckSdrDflt )
+ {
+ sal_uInt16 nSlotId = GetAttrPool().GetSlotId( nWhich );
+ if( 0 != nSlotId && nSlotId != nWhich )
+ {
+ sal_uInt16 nEdtWhich = pSdrPool->GetWhich( nSlotId );
+ if( 0 != nEdtWhich && nSlotId != nEdtWhich )
+ {
+ std::unique_ptr<SfxPoolItem> pCpy(pItem->Clone());
+ pCpy->SetWhich( nEdtWhich );
+ pSdrPool->SetPoolDefaultItem( *pCpy );
+ }
+ }
+ }
+
+ pItem = aIter.NextItem();
+ } while (pItem);
+
+ if( aNew.Count() && aCallMod.HasWriterListeners() )
+ {
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().AppendUndo( std::make_unique<SwUndoDefaultAttr>( aOld, *this ) );
+ }
+
+ const SvxTabStopItem* pTmpItem = aNew.GetItemIfSet( RES_PARATR_TABSTOP, false );
+ if( pTmpItem && pTmpItem->Count() )
+ {
+ // Set the default values of all TabStops to the new value.
+ // Attention: we always work with the PoolAttribute here, so that
+ // we don't calculate the same value on the same TabStop (pooled!) for all sets.
+ // We send a FormatChg to modify.
+ SwTwips nNewWidth = (*pTmpItem)[ 0 ].GetTabPos(),
+ nOldWidth = aOld.Get(RES_PARATR_TABSTOP)[ 0 ].GetTabPos();
+
+ bool bChg = false;
+ for (const SfxPoolItem* pItem2 : GetAttrPool().GetItemSurrogates(RES_PARATR_TABSTOP))
+ {
+ if(auto pTabStopItem = pItem2->DynamicWhichCast(RES_PARATR_TABSTOP))
+ bChg |= lcl_SetNewDefTabStops( nOldWidth, nNewWidth,
+ *const_cast<SvxTabStopItem*>(pTabStopItem) );
+ }
+
+ aNew.ClearItem( RES_PARATR_TABSTOP );
+ aOld.ClearItem( RES_PARATR_TABSTOP );
+ if( bChg )
+ {
+ SwFormatChg aChgFormat( mpDfltCharFormat.get() );
+ // notify the frames
+ aCallMod.CallSwClientNotify(sw::LegacyModifyHint( &aChgFormat, &aChgFormat ));
+ }
+ }
+ }
+
+ if( aNew.Count() && aCallMod.HasWriterListeners() )
+ {
+ SwAttrSetChg aChgOld( aOld, aOld );
+ SwAttrSetChg aChgNew( aNew, aNew );
+ aCallMod.CallSwClientNotify(sw::LegacyModifyHint( &aChgOld, &aChgNew )); // all changed are sent
+ }
+
+ // remove the default formats from the object again
+ SwIterator<SwClient, sw::BroadcastingModify> aClientIter(aCallMod);
+ for(SwClient* pClient = aClientIter.First(); pClient; pClient = aClientIter.Next())
+ aCallMod.Remove( pClient );
+
+ getIDocumentState().SetModified();
+}
+
+/// Get the default attribute in this document
+const SfxPoolItem& SwDoc::GetDefault( sal_uInt16 nFormatHint ) const
+{
+ return GetAttrPool().GetDefaultItem( nFormatHint );
+}
+
+/// Delete the formats
+void SwDoc::DelCharFormat(size_t nFormat, bool bBroadcast)
+{
+ SwCharFormat * pDel = (*mpCharFormatTable)[nFormat];
+
+ if (bBroadcast)
+ BroadcastStyleOperation(pDel->GetName(), SfxStyleFamily::Char,
+ SfxHintId::StyleSheetErased);
+
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().AppendUndo(
+ std::make_unique<SwUndoCharFormatDelete>(pDel, *this));
+ }
+
+ delete (*mpCharFormatTable)[nFormat];
+ mpCharFormatTable->erase(mpCharFormatTable->begin() + nFormat);
+
+ getIDocumentState().SetModified();
+}
+
+void SwDoc::DelCharFormat( SwCharFormat const *pFormat, bool bBroadcast )
+{
+ size_t nFormat = mpCharFormatTable->GetPos( pFormat );
+ OSL_ENSURE( SIZE_MAX != nFormat, "Format not found," );
+ DelCharFormat( nFormat, bBroadcast );
+}
+
+void SwDoc::DelFrameFormat( SwFrameFormat *pFormat, bool bBroadcast )
+{
+ if( dynamic_cast<const SwTableBoxFormat*>( pFormat) != nullptr || dynamic_cast<const SwTableLineFormat*>( pFormat) != nullptr )
+ {
+ OSL_ENSURE( false, "Format is not in the DocArray any more, "
+ "so it can be deleted with delete" );
+ delete pFormat;
+ }
+ else
+ {
+ // The format has to be in the one or the other, we'll see in which one.
+ if (mpFrameFormatTable->ContainsFormat(pFormat))
+ {
+ if (bBroadcast)
+ BroadcastStyleOperation(pFormat->GetName(),
+ SfxStyleFamily::Frame,
+ SfxHintId::StyleSheetErased);
+
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().AppendUndo(
+ std::make_unique<SwUndoFrameFormatDelete>(pFormat, *this));
+ }
+
+ mpFrameFormatTable->erase( pFormat );
+ delete pFormat;
+ }
+ else
+ {
+ auto pSpz = static_cast<sw::SpzFrameFormat*>(pFormat);
+ if(GetSpzFrameFormats()->ContainsFormat(pSpz))
+ {
+ GetSpzFrameFormats()->erase(pSpz);
+ delete pSpz;
+ }
+ else
+ SAL_WARN("sw", "FrameFormat not found.");
+ }
+ }
+}
+
+void SwDoc::DelTableFrameFormat( SwTableFormat *pFormat )
+{
+ auto it = mpTableFrameFormatTable->find( pFormat );
+ OSL_ENSURE( it != mpTableFrameFormatTable->end(), "Format not found," );
+ mpTableFrameFormatTable->erase( it );
+ delete pFormat;
+}
+
+SwFrameFormat* SwDoc::FindFrameFormatByName( const OUString& rName ) const
+{
+ return static_cast<SwFrameFormat*>(mpFrameFormatTable->FindFormatByName(rName));
+}
+
+/// Create the formats
+SwFlyFrameFormat *SwDoc::MakeFlyFrameFormat( const OUString &rFormatName,
+ SwFrameFormat *pDerivedFrom )
+{
+ SwFlyFrameFormat *pFormat = new SwFlyFrameFormat( GetAttrPool(), rFormatName, pDerivedFrom );
+ GetSpzFrameFormats()->push_back(pFormat);
+ getIDocumentState().SetModified();
+ return pFormat;
+}
+
+SwDrawFrameFormat *SwDoc::MakeDrawFrameFormat( const OUString &rFormatName,
+ SwFrameFormat *pDerivedFrom )
+{
+ SwDrawFrameFormat *pFormat = new SwDrawFrameFormat( GetAttrPool(), rFormatName, pDerivedFrom);
+ GetSpzFrameFormats()->push_back(pFormat);
+ getIDocumentState().SetModified();
+ return pFormat;
+}
+
+size_t SwDoc::GetTableFrameFormatCount(bool bUsed) const
+{
+ if (!bUsed)
+ return mpTableFrameFormatTable->size();
+ return std::count_if(mpTableFrameFormatTable->begin(), mpTableFrameFormatTable->end(),
+ std::mem_fn(&SwFormat::IsUsed));
+}
+
+SwTableFormat& SwDoc::GetTableFrameFormat(size_t nFormat, bool bUsed) const
+{
+ if (!bUsed)
+ return *const_cast<SwTableFormat*>((*mpTableFrameFormatTable)[nFormat]);
+ for(SwTableFormat* pFormat: *mpTableFrameFormatTable)
+ {
+ if(!pFormat->IsUsed())
+ continue;
+ if(nFormat)
+ --nFormat;
+ else
+ return *pFormat;
+ }
+ throw std::out_of_range("Format index out of range.");
+}
+
+SwTableFormat* SwDoc::MakeTableFrameFormat( const OUString &rFormatName,
+ SwFrameFormat *pDerivedFrom )
+{
+ SwTableFormat* pFormat = new SwTableFormat( GetAttrPool(), rFormatName, pDerivedFrom );
+ mpTableFrameFormatTable->push_back( pFormat );
+ getIDocumentState().SetModified();
+
+ return pFormat;
+}
+
+SwFrameFormat *SwDoc::MakeFrameFormat(const OUString &rFormatName,
+ SwFrameFormat *pDerivedFrom,
+ bool bBroadcast, bool bAuto)
+{
+ SwFrameFormat *pFormat = new SwFrameFormat( GetAttrPool(), rFormatName, pDerivedFrom );
+
+ pFormat->SetAuto(bAuto);
+ mpFrameFormatTable->push_back( pFormat );
+ getIDocumentState().SetModified();
+
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().AppendUndo(
+ std::make_unique<SwUndoFrameFormatCreate>(pFormat, pDerivedFrom, *this));
+ }
+
+ if (bBroadcast)
+ {
+ BroadcastStyleOperation(rFormatName, SfxStyleFamily::Frame,
+ SfxHintId::StyleSheetCreated);
+ }
+
+ return pFormat;
+}
+
+SwFormat *SwDoc::MakeFrameFormat_(const OUString &rFormatName,
+ SwFormat *pDerivedFrom,
+ bool bBroadcast, bool bAuto)
+{
+ SwFrameFormat *pFrameFormat = dynamic_cast<SwFrameFormat*>(pDerivedFrom);
+ pFrameFormat = MakeFrameFormat( rFormatName, pFrameFormat, bBroadcast, bAuto );
+ return pFrameFormat;
+}
+
+SwCharFormat *SwDoc::MakeCharFormat( const OUString &rFormatName,
+ SwCharFormat *pDerivedFrom,
+ bool bBroadcast )
+{
+ SwCharFormat *pFormat = new SwCharFormat( GetAttrPool(), rFormatName, pDerivedFrom );
+ mpCharFormatTable->insert( pFormat );
+ pFormat->SetAuto(false);
+ getIDocumentState().SetModified();
+
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().AppendUndo(
+ std::make_unique<SwUndoCharFormatCreate>(pFormat, pDerivedFrom, *this));
+ }
+
+ if (bBroadcast)
+ {
+ BroadcastStyleOperation(rFormatName, SfxStyleFamily::Char,
+ SfxHintId::StyleSheetCreated);
+ }
+
+ return pFormat;
+}
+
+SwFormat *SwDoc::MakeCharFormat_(const OUString &rFormatName,
+ SwFormat *pDerivedFrom,
+ bool bBroadcast, bool /*bAuto*/)
+{
+ SwCharFormat *pCharFormat = dynamic_cast<SwCharFormat*>(pDerivedFrom);
+ pCharFormat = MakeCharFormat( rFormatName, pCharFormat, bBroadcast );
+ return pCharFormat;
+}
+
+/// Create the FormatCollections
+SwTextFormatColl* SwDoc::MakeTextFormatColl( const OUString &rFormatName,
+ SwTextFormatColl *pDerivedFrom,
+ bool bBroadcast)
+{
+ SwTextFormatColl *pFormatColl = new SwTextFormatColl( GetAttrPool(), rFormatName,
+ pDerivedFrom );
+ mpTextFormatCollTable->push_back(pFormatColl);
+ pFormatColl->SetAuto(false);
+ getIDocumentState().SetModified();
+
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().AppendUndo(
+ std::make_unique<SwUndoTextFormatCollCreate>(pFormatColl, pDerivedFrom,
+ *this));
+ }
+
+ if (bBroadcast)
+ BroadcastStyleOperation(rFormatName, SfxStyleFamily::Para,
+ SfxHintId::StyleSheetCreated);
+
+ return pFormatColl;
+}
+
+SwFormat *SwDoc::MakeTextFormatColl_(const OUString &rFormatName,
+ SwFormat *pDerivedFrom,
+ bool bBroadcast, bool /*bAuto*/)
+{
+ SwTextFormatColl *pTextFormatColl = dynamic_cast<SwTextFormatColl*>(pDerivedFrom);
+ pTextFormatColl = MakeTextFormatColl( rFormatName, pTextFormatColl, bBroadcast );
+ return pTextFormatColl;
+}
+
+SwConditionTextFormatColl* SwDoc::MakeCondTextFormatColl( const OUString &rFormatName,
+ SwTextFormatColl *pDerivedFrom,
+ bool bBroadcast)
+{
+ SwConditionTextFormatColl*pFormatColl = new SwConditionTextFormatColl( GetAttrPool(),
+ rFormatName, pDerivedFrom );
+ mpTextFormatCollTable->push_back(pFormatColl);
+ pFormatColl->SetAuto(false);
+ getIDocumentState().SetModified();
+
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().AppendUndo(
+ std::make_unique<SwUndoCondTextFormatCollCreate>(pFormatColl, pDerivedFrom,
+ *this));
+ }
+
+ if (bBroadcast)
+ BroadcastStyleOperation(rFormatName, SfxStyleFamily::Para,
+ SfxHintId::StyleSheetCreated);
+
+ return pFormatColl;
+}
+
+// GRF
+SwGrfFormatColl* SwDoc::MakeGrfFormatColl( const OUString &rFormatName,
+ SwGrfFormatColl *pDerivedFrom )
+{
+ SwGrfFormatColl *pFormatColl = new SwGrfFormatColl( GetAttrPool(), rFormatName,
+ pDerivedFrom );
+ mpGrfFormatCollTable->push_back( pFormatColl );
+ pFormatColl->SetAuto(false);
+ getIDocumentState().SetModified();
+ return pFormatColl;
+}
+
+void SwDoc::DelTextFormatColl(size_t nFormatColl, bool bBroadcast)
+{
+ OSL_ENSURE( nFormatColl, "Remove of Coll 0." );
+
+ // Who has the to-be-deleted as their Next?
+ SwTextFormatColl *pDel = (*mpTextFormatCollTable)[nFormatColl];
+ if( mpDfltTextFormatColl.get() == pDel )
+ return; // never delete default!
+
+ if (bBroadcast)
+ BroadcastStyleOperation(pDel->GetName(), SfxStyleFamily::Para,
+ SfxHintId::StyleSheetErased);
+
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ std::unique_ptr<SwUndoTextFormatCollDelete> pUndo;
+ if (RES_CONDTXTFMTCOLL == pDel->Which())
+ {
+ pUndo.reset(new SwUndoCondTextFormatCollDelete(pDel, *this));
+ }
+ else
+ {
+ pUndo.reset(new SwUndoTextFormatCollDelete(pDel, *this));
+ }
+
+ GetIDocumentUndoRedo().AppendUndo(std::move(pUndo));
+ }
+
+ // Remove the FormatColl
+ mpTextFormatCollTable->erase(mpTextFormatCollTable->begin() + nFormatColl);
+ // Correct next
+ for( SwTextFormatColls::const_iterator it = mpTextFormatCollTable->begin() + 1; it != mpTextFormatCollTable->end(); ++it )
+ SetTextFormatCollNext( *it, pDel );
+ delete pDel;
+ getIDocumentState().SetModified();
+}
+
+void SwDoc::DelTextFormatColl( SwTextFormatColl const *pColl, bool bBroadcast )
+{
+ size_t nFormat = mpTextFormatCollTable->GetPos( pColl );
+ OSL_ENSURE( SIZE_MAX != nFormat, "Collection not found," );
+ DelTextFormatColl( nFormat, bBroadcast );
+}
+
+static bool lcl_SetTextFormatColl( SwNode* pNode, void* pArgs )
+{
+ SwContentNode* pCNd = pNode->GetTextNode();
+
+ if( pCNd == nullptr)
+ return true;
+
+ sw::DocumentContentOperationsManager::ParaRstFormat* pPara = static_cast<sw::DocumentContentOperationsManager::ParaRstFormat*>(pArgs);
+
+ if (pPara->pLayout && pPara->pLayout->HasMergedParas())
+ {
+ if (pCNd->GetRedlineMergeFlag() == SwNode::Merge::Hidden)
+ {
+ return true;
+ }
+ if (pCNd->IsTextNode())
+ {
+ pCNd = sw::GetParaPropsNode(*pPara->pLayout, *pCNd);
+ }
+ }
+
+ SwTextFormatColl* pFormat = pPara->pFormatColl;
+ if ( pPara->bReset )
+ {
+ lcl_RstAttr(pCNd, pPara);
+
+ // #i62675# check, if paragraph style has changed
+ if ( pPara->bResetListAttrs &&
+ pFormat != pCNd->GetFormatColl() &&
+ pFormat->GetItemState( RES_PARATR_NUMRULE ) == SfxItemState::SET )
+ {
+ // Check, if the list style of the paragraph will change.
+ bool bChangeOfListStyleAtParagraph( true );
+ SwTextNode& rTNd(*pCNd->GetTextNode());
+ {
+ SwNumRule* pNumRuleAtParagraph(rTNd.GetNumRule());
+ if ( pNumRuleAtParagraph )
+ {
+ const SwNumRuleItem& rNumRuleItemAtParagraphStyle =
+ pFormat->GetNumRule();
+ if ( rNumRuleItemAtParagraphStyle.GetValue() ==
+ pNumRuleAtParagraph->GetName() )
+ {
+ bChangeOfListStyleAtParagraph = false;
+ }
+ }
+ }
+
+ if ( bChangeOfListStyleAtParagraph )
+ {
+ std::unique_ptr< SwRegHistory > pRegH;
+ if ( pPara->pHistory )
+ {
+ pRegH.reset(new SwRegHistory(&rTNd, rTNd, pPara->pHistory));
+ }
+
+ pCNd->ResetAttr( RES_PARATR_NUMRULE );
+
+ // reset all list attributes
+ pCNd->ResetAttr( RES_PARATR_LIST_LEVEL );
+ pCNd->ResetAttr( RES_PARATR_LIST_ISRESTART );
+ pCNd->ResetAttr( RES_PARATR_LIST_RESTARTVALUE );
+ pCNd->ResetAttr( RES_PARATR_LIST_ISCOUNTED );
+ pCNd->ResetAttr( RES_PARATR_LIST_ID );
+ }
+ else
+ {
+ // The List Level must be applied as direct formatting. The spec says:
+ // 19.495 The style:list-level attribute specifies the list level value
+ // of a list style that may be applied to any paragraph style.
+ // It does not directly specify the paragraph's list level value,
+ // but consumers can change the paragraph's list level value to the specified value
+ // when the paragraph style is applied.
+ pCNd->SetAttr(pFormat->GetFormatAttr(RES_PARATR_LIST_LEVEL));
+ }
+ }
+ }
+
+ // add to History so that old data is saved, if necessary
+ if( pPara->pHistory )
+ pPara->pHistory->AddColl(pCNd->GetFormatColl(), pCNd->GetIndex(),
+ SwNodeType::Text );
+
+ pCNd->ChgFormatColl( pFormat );
+
+ pPara->nWhich++;
+
+ return true;
+}
+
+bool SwDoc::SetTextFormatColl(const SwPaM &rRg,
+ SwTextFormatColl *pFormat,
+ const bool bReset,
+ const bool bResetListAttrs,
+ SwRootFrame const*const pLayout)
+{
+ SwDataChanged aTmp( rRg );
+ auto [pStt, pEnd] = rRg.StartEnd(); // SwPosition*
+ SwHistory* pHst = nullptr;
+ bool bRet = true;
+
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ std::unique_ptr<SwUndoFormatColl> pUndo(new SwUndoFormatColl( rRg, pFormat,
+ bReset,
+ bResetListAttrs ));
+ pHst = pUndo->GetHistory();
+ GetIDocumentUndoRedo().AppendUndo(std::move(pUndo));
+ }
+
+ sw::DocumentContentOperationsManager::ParaRstFormat aPara(
+ pStt, pEnd, pHst, nullptr, pLayout);
+ aPara.pFormatColl = pFormat;
+ aPara.bReset = bReset;
+ // #i62675#
+ aPara.bResetListAttrs = bResetListAttrs;
+
+ GetNodes().ForEach( pStt->GetNodeIndex(), pEnd->GetNodeIndex()+1,
+ lcl_SetTextFormatColl, &aPara );
+ if( !aPara.nWhich )
+ bRet = false; // didn't find a valid Node
+
+ if (bRet)
+ {
+ getIDocumentState().SetModified();
+ }
+
+ return bRet;
+}
+
+/// Copy the formats to itself
+SwFormat* SwDoc::CopyFormat( const SwFormat& rFormat,
+ const SwFormatsBase& rFormatArr,
+ FNCopyFormat fnCopyFormat, const SwFormat& rDfltFormat )
+{
+ // It's no autoformat, default format or collection format,
+ // then search for it.
+ if( !rFormat.IsAuto() || !rFormat.GetRegisteredIn() )
+ for( size_t n = 0; n < rFormatArr.GetFormatCount(); ++n )
+ {
+ // Does the Doc already contain the template?
+ if( rFormatArr.GetFormat(n)->GetName()==rFormat.GetName() )
+ return rFormatArr.GetFormat(n);
+ }
+
+ // Search for the "parent" first
+ SwFormat* pParent = const_cast<SwFormat*>(&rDfltFormat);
+ if( rFormat.DerivedFrom() && pParent != rFormat.DerivedFrom() )
+ pParent = CopyFormat( *rFormat.DerivedFrom(), rFormatArr,
+ fnCopyFormat, rDfltFormat );
+
+ // Create the format and copy the attributes
+ // #i40550#
+ SwFormat* pNewFormat = (this->*fnCopyFormat)( rFormat.GetName(), pParent, false, true );
+ pNewFormat->SetAuto( rFormat.IsAuto() );
+ pNewFormat->CopyAttrs( rFormat ); // copy the attributes
+
+ pNewFormat->SetPoolFormatId( rFormat.GetPoolFormatId() );
+ pNewFormat->SetPoolHelpId( rFormat.GetPoolHelpId() );
+
+ // Always set the HelpFile Id to default!
+ pNewFormat->SetPoolHlpFileId( UCHAR_MAX );
+
+ return pNewFormat;
+}
+
+/// copy the frame format
+SwFrameFormat* SwDoc::CopyFrameFormat( const SwFrameFormat& rFormat )
+{
+ return static_cast<SwFrameFormat*>(CopyFormat( rFormat, *GetFrameFormats(), &SwDoc::MakeFrameFormat_,
+ *GetDfltFrameFormat() ));
+}
+
+/// copy the char format
+SwCharFormat* SwDoc::CopyCharFormat( const SwCharFormat& rFormat )
+{
+ return static_cast<SwCharFormat*>(CopyFormat( rFormat, *GetCharFormats(),
+ &SwDoc::MakeCharFormat_,
+ *GetDfltCharFormat() ));
+}
+
+/// copy TextNodes
+SwTextFormatColl* SwDoc::CopyTextColl( const SwTextFormatColl& rColl )
+{
+ SwTextFormatColl* pNewColl = FindTextFormatCollByName( rColl.GetName() );
+ if( pNewColl )
+ return pNewColl;
+
+ // search for the "parent" first
+ SwTextFormatColl* pParent = mpDfltTextFormatColl.get();
+ if( pParent != rColl.DerivedFrom() )
+ pParent = CopyTextColl( *static_cast<SwTextFormatColl*>(rColl.DerivedFrom()) );
+
+ if( RES_CONDTXTFMTCOLL == rColl.Which() )
+ {
+ pNewColl = new SwConditionTextFormatColl( GetAttrPool(), rColl.GetName(),
+ pParent);
+ mpTextFormatCollTable->push_back( pNewColl );
+ pNewColl->SetAuto(false);
+ getIDocumentState().SetModified();
+
+ // copy the conditions
+ static_cast<SwConditionTextFormatColl*>(pNewColl)->SetConditions(
+ static_cast<const SwConditionTextFormatColl&>(rColl).GetCondColls() );
+ }
+ else
+ pNewColl = MakeTextFormatColl( rColl.GetName(), pParent );
+
+ // copy the auto formats or the attributes
+ pNewColl->CopyAttrs( rColl );
+
+ if(rColl.IsAssignedToListLevelOfOutlineStyle())
+ pNewColl->AssignToListLevelOfOutlineStyle(rColl.GetAssignedOutlineStyleLevel());
+ pNewColl->SetPoolFormatId( rColl.GetPoolFormatId() );
+ pNewColl->SetPoolHelpId( rColl.GetPoolHelpId() );
+
+ // Always set the HelpFile Id to default!
+ pNewColl->SetPoolHlpFileId( UCHAR_MAX );
+
+ if( &rColl.GetNextTextFormatColl() != &rColl )
+ pNewColl->SetNextTextFormatColl( *CopyTextColl( rColl.GetNextTextFormatColl() ));
+
+ // create the NumRule if necessary
+ if( this != rColl.GetDoc() )
+ {
+ const SwNumRuleItem* pItem = pNewColl->GetItemIfSet( RES_PARATR_NUMRULE,
+ false );
+ if( pItem )
+ {
+ const OUString& rName = pItem->GetValue();
+ if( !rName.isEmpty() )
+ {
+ const SwNumRule* pRule = rColl.GetDoc()->FindNumRulePtr( rName );
+ if( pRule && !pRule->IsAutoRule() )
+ {
+ SwNumRule* pDestRule = FindNumRulePtr( rName );
+ if( pDestRule )
+ pDestRule->SetInvalidRule( true );
+ else
+ MakeNumRule( rName, pRule );
+ }
+ }
+ }
+ }
+ return pNewColl;
+}
+
+/// copy the graphic nodes
+SwGrfFormatColl* SwDoc::CopyGrfColl( const SwGrfFormatColl& rColl )
+{
+ SwGrfFormatColl* pNewColl = mpGrfFormatCollTable->FindFormatByName( rColl.GetName() );
+ if( pNewColl )
+ return pNewColl;
+
+ // Search for the "parent" first
+ SwGrfFormatColl* pParent = mpDfltGrfFormatColl.get();
+ if( pParent != rColl.DerivedFrom() )
+ pParent = CopyGrfColl( *static_cast<SwGrfFormatColl*>(rColl.DerivedFrom()) );
+
+ // if not, copy them
+ pNewColl = MakeGrfFormatColl( rColl.GetName(), pParent );
+
+ // copy the attributes
+ pNewColl->CopyAttrs( rColl );
+
+ pNewColl->SetPoolFormatId( rColl.GetPoolFormatId() );
+ pNewColl->SetPoolHelpId( rColl.GetPoolHelpId() );
+
+ // Always set the HelpFile Id to default!
+ pNewColl->SetPoolHlpFileId( UCHAR_MAX );
+
+ return pNewColl;
+}
+
+void SwDoc::CopyFormatArr( const SwFormatsBase& rSourceArr,
+ SwFormatsBase const & rDestArr,
+ FNCopyFormat fnCopyFormat,
+ SwFormat& rDfltFormat )
+{
+ SwFormat* pSrc, *pDest;
+
+ // 1st step: Create all formats (skip the 0th - it's the default one)
+ for( size_t nSrc = rSourceArr.GetFormatCount(); nSrc > 1; )
+ {
+ pSrc = rSourceArr.GetFormat( --nSrc );
+ if( pSrc->IsDefault() || pSrc->IsAuto() )
+ continue;
+
+ if( nullptr == rDestArr.FindFormatByName( pSrc->GetName() ) )
+ {
+ if( RES_CONDTXTFMTCOLL == pSrc->Which() )
+ MakeCondTextFormatColl( pSrc->GetName(), static_cast<SwTextFormatColl*>(&rDfltFormat) );
+ else
+ // #i40550#
+ (this->*fnCopyFormat)( pSrc->GetName(), &rDfltFormat, false, true );
+ }
+ }
+
+ // 2nd step: Copy all attributes, set the right parents
+ for( size_t nSrc = rSourceArr.GetFormatCount(); nSrc > 1; )
+ {
+ pSrc = rSourceArr.GetFormat( --nSrc );
+ if( pSrc->IsDefault() || pSrc->IsAuto() )
+ continue;
+
+ pDest = rDestArr.FindFormatByName( pSrc->GetName() );
+ pDest->SetAuto(false);
+ pDest->DelDiffs( *pSrc );
+
+ // #i94285#: existing <SwFormatPageDesc> instance, before copying attributes
+ const SwFormatPageDesc* pItem;
+ if( &GetAttrPool() != pSrc->GetAttrSet().GetPool()
+ && (pItem = pSrc->GetAttrSet().GetItemIfSet( RES_PAGEDESC, false ))
+ && pItem->GetPageDesc() )
+ {
+ SwFormatPageDesc aPageDesc( *pItem );
+ const OUString& rNm = aPageDesc.GetPageDesc()->GetName();
+ SwPageDesc* pPageDesc = FindPageDesc( rNm );
+ if( !pPageDesc )
+ {
+ pPageDesc = MakePageDesc(rNm);
+ }
+ aPageDesc.RegisterToPageDesc( *pPageDesc );
+ SwAttrSet aTmpAttrSet( pSrc->GetAttrSet() );
+ aTmpAttrSet.Put( aPageDesc );
+ pDest->SetFormatAttr( aTmpAttrSet );
+ }
+ else
+ {
+ pDest->SetFormatAttr( pSrc->GetAttrSet() );
+ }
+
+ pDest->SetPoolFormatId( pSrc->GetPoolFormatId() );
+ pDest->SetPoolHelpId( pSrc->GetPoolHelpId() );
+
+ // Always set the HelpFile Id to default!
+ pDest->SetPoolHlpFileId( UCHAR_MAX );
+
+ if( pSrc->DerivedFrom() )
+ pDest->SetDerivedFrom( rDestArr.FindFormatByName(
+ pSrc->DerivedFrom()->GetName() ) );
+ if( RES_TXTFMTCOLL == pSrc->Which() ||
+ RES_CONDTXTFMTCOLL == pSrc->Which() )
+ {
+ SwTextFormatColl* pSrcColl = static_cast<SwTextFormatColl*>(pSrc),
+ * pDstColl = static_cast<SwTextFormatColl*>(pDest);
+ if( &pSrcColl->GetNextTextFormatColl() != pSrcColl )
+ pDstColl->SetNextTextFormatColl(
+ *static_cast<SwTextFormatColl*>(rDestArr.FindFormatByName( pSrcColl->GetNextTextFormatColl().GetName() )) );
+
+ if(pSrcColl->IsAssignedToListLevelOfOutlineStyle())
+ pDstColl->AssignToListLevelOfOutlineStyle(pSrcColl->GetAssignedOutlineStyleLevel());
+
+ if( RES_CONDTXTFMTCOLL == pSrc->Which() )
+ {
+ if (pDstColl->Which() != RES_CONDTXTFMTCOLL)
+ {
+ // Target already had a style with a matching name, but it's not a conditional
+ // style, then don't copy the conditions.
+ continue;
+ }
+
+ // Copy the conditions, but delete the old ones first!
+ static_cast<SwConditionTextFormatColl*>(pDstColl)->SetConditions(
+ static_cast<SwConditionTextFormatColl*>(pSrc)->GetCondColls() );
+ }
+ }
+ }
+}
+
+void SwDoc::CopyPageDescHeaderFooterImpl( bool bCpyHeader,
+ const SwFrameFormat& rSrcFormat, SwFrameFormat& rDestFormat )
+{
+ // Treat the header and footer attributes in the right way:
+ // Copy content nodes across documents!
+ sal_uInt16 nAttr = bCpyHeader ? sal_uInt16(RES_HEADER) : sal_uInt16(RES_FOOTER);
+ const SfxPoolItem* pItem;
+ if( SfxItemState::SET != rSrcFormat.GetAttrSet().GetItemState( nAttr, false, &pItem ))
+ return ;
+
+ // The header only contains the reference to the format from the other document!
+ std::unique_ptr<SfxPoolItem> pNewItem(pItem->Clone());
+
+ SwFrameFormat* pOldFormat;
+ if( bCpyHeader )
+ pOldFormat = pNewItem->StaticWhichCast(RES_HEADER).GetHeaderFormat();
+ else
+ pOldFormat = pNewItem->StaticWhichCast(RES_FOOTER).GetFooterFormat();
+
+ if( !pOldFormat )
+ return;
+
+ SwFrameFormat* pNewFormat = new SwFrameFormat( GetAttrPool(), "CpyDesc",
+ GetDfltFrameFormat() );
+ pNewFormat->CopyAttrs( *pOldFormat );
+
+ if( const SwFormatContent* pContent = pNewFormat->GetAttrSet().GetItemIfSet(
+ RES_CNTNT, false ) )
+ {
+ if( pContent->GetContentIdx() )
+ {
+ const SwNodes& rSrcNds = rSrcFormat.GetDoc()->GetNodes();
+ SwStartNode* pSttNd = SwNodes::MakeEmptySection( GetNodes().GetEndOfAutotext(),
+ bCpyHeader
+ ? SwHeaderStartNode
+ : SwFooterStartNode );
+ const SwNode& rCSttNd = pContent->GetContentIdx()->GetNode();
+ SwNodeRange aRg( rCSttNd, SwNodeOffset(0), *rCSttNd.EndOfSectionNode() );
+ rSrcNds.Copy_( aRg, *pSttNd->EndOfSectionNode() );
+ rSrcFormat.GetDoc()->GetDocumentContentOperationsManager().CopyFlyInFlyImpl(aRg, nullptr, *pSttNd);
+ // TODO: investigate calling CopyWithFlyInFly?
+ SwPaM const source(aRg.aStart, aRg.aEnd);
+ SwPosition dest(*pSttNd);
+ sw::CopyBookmarks(source, dest);
+ pNewFormat->SetFormatAttr( SwFormatContent( pSttNd ));
+ }
+ else
+ pNewFormat->ResetFormatAttr( RES_CNTNT );
+ }
+ if( bCpyHeader )
+ pNewItem->StaticWhichCast(RES_HEADER).RegisterToFormat(*pNewFormat);
+ else
+ pNewItem->StaticWhichCast(RES_FOOTER).RegisterToFormat(*pNewFormat);
+ rDestFormat.SetFormatAttr( *pNewItem );
+}
+
+void SwDoc::CopyPageDesc( const SwPageDesc& rSrcDesc, SwPageDesc& rDstDesc,
+ bool bCopyPoolIds )
+{
+ bool bNotifyLayout = false;
+ SwRootFrame* pTmpRoot = getIDocumentLayoutAccess().GetCurrentLayout();
+
+ rDstDesc.SetLandscape( rSrcDesc.GetLandscape() );
+ rDstDesc.SetNumType( rSrcDesc.GetNumType() );
+ if( rDstDesc.ReadUseOn() != rSrcDesc.ReadUseOn() )
+ {
+ rDstDesc.WriteUseOn( rSrcDesc.ReadUseOn() );
+ bNotifyLayout = true;
+ }
+
+ if( bCopyPoolIds )
+ {
+ rDstDesc.SetPoolFormatId( rSrcDesc.GetPoolFormatId() );
+ rDstDesc.SetPoolHelpId( rSrcDesc.GetPoolHelpId() );
+ // Always set the HelpFile Id to default!
+ rDstDesc.SetPoolHlpFileId( UCHAR_MAX );
+ }
+
+ if( rSrcDesc.GetFollow() != &rSrcDesc )
+ {
+ const SwPageDesc* pSrcFollow = rSrcDesc.GetFollow();
+ SwPageDesc* pFollow = FindPageDesc( pSrcFollow->GetName() );
+ if( !pFollow )
+ {
+ // copy
+ pFollow = MakePageDesc( pSrcFollow->GetName() );
+ CopyPageDesc( *pSrcFollow, *pFollow );
+ }
+ rDstDesc.SetFollow( pFollow );
+ bNotifyLayout = true;
+ }
+
+ // the header and footer attributes are copied separately
+ // the content sections have to be copied in their entirety
+ {
+ SfxItemSet aAttrSet( rSrcDesc.GetMaster().GetAttrSet() );
+ aAttrSet.ClearItem( RES_HEADER );
+ aAttrSet.ClearItem( RES_FOOTER );
+
+ rDstDesc.GetMaster().DelDiffs( aAttrSet );
+ rDstDesc.GetMaster().SetFormatAttr( aAttrSet );
+
+ aAttrSet.ClearItem();
+ aAttrSet.Put( rSrcDesc.GetLeft().GetAttrSet() );
+ aAttrSet.ClearItem( RES_HEADER );
+ aAttrSet.ClearItem( RES_FOOTER );
+
+ rDstDesc.GetLeft().DelDiffs( aAttrSet );
+ rDstDesc.GetLeft().SetFormatAttr( aAttrSet );
+
+ aAttrSet.ClearItem();
+ aAttrSet.Put( rSrcDesc.GetFirstMaster().GetAttrSet() );
+ aAttrSet.ClearItem( RES_HEADER );
+ aAttrSet.ClearItem( RES_FOOTER );
+
+ rDstDesc.GetFirstMaster().DelDiffs( aAttrSet );
+ rDstDesc.GetFirstMaster().SetFormatAttr( aAttrSet );
+
+ aAttrSet.ClearItem();
+ aAttrSet.Put( rSrcDesc.GetFirstLeft().GetAttrSet() );
+ aAttrSet.ClearItem( RES_HEADER );
+ aAttrSet.ClearItem( RES_FOOTER );
+
+ rDstDesc.GetFirstLeft().DelDiffs( aAttrSet );
+ rDstDesc.GetFirstLeft().SetFormatAttr( aAttrSet );
+ }
+
+ CopyHeader( rSrcDesc.GetMaster(), rDstDesc.GetMaster() );
+ CopyFooter( rSrcDesc.GetMaster(), rDstDesc.GetMaster() );
+ if( !rDstDesc.IsHeaderShared() )
+ CopyHeader( rSrcDesc.GetLeft(), rDstDesc.GetLeft() );
+ else
+ rDstDesc.GetLeft().SetFormatAttr( rDstDesc.GetMaster().GetHeader() );
+ if( !rDstDesc.IsFirstShared() )
+ {
+ CopyHeader( rSrcDesc.GetFirstMaster(), rDstDesc.GetFirstMaster() );
+ rDstDesc.GetFirstLeft().SetFormatAttr(rDstDesc.GetFirstMaster().GetHeader());
+ }
+ else
+ {
+ rDstDesc.GetFirstMaster().SetFormatAttr( rDstDesc.GetMaster().GetHeader() );
+ rDstDesc.GetFirstLeft().SetFormatAttr(rDstDesc.GetLeft().GetHeader());
+ }
+
+ if( !rDstDesc.IsFooterShared() )
+ CopyFooter( rSrcDesc.GetLeft(), rDstDesc.GetLeft() );
+ else
+ rDstDesc.GetLeft().SetFormatAttr( rDstDesc.GetMaster().GetFooter() );
+ if( !rDstDesc.IsFirstShared() )
+ {
+ CopyFooter( rSrcDesc.GetFirstMaster(), rDstDesc.GetFirstMaster() );
+ rDstDesc.GetFirstLeft().SetFormatAttr(rDstDesc.GetFirstMaster().GetFooter());
+ }
+ else
+ {
+ rDstDesc.GetFirstMaster().SetFormatAttr( rDstDesc.GetMaster().GetFooter() );
+ rDstDesc.GetFirstLeft().SetFormatAttr(rDstDesc.GetLeft().GetFooter());
+ }
+
+ if( bNotifyLayout && pTmpRoot )
+ {
+ for( auto aLayout : GetAllLayouts() )
+ aLayout->AllCheckPageDescs();
+ }
+
+ // If foot notes change the pages have to be triggered
+ if( !(rDstDesc.GetFootnoteInfo() == rSrcDesc.GetFootnoteInfo()) )
+ {
+ sw::PageFootnoteHint aHint;
+ rDstDesc.SetFootnoteInfo( rSrcDesc.GetFootnoteInfo() );
+ rDstDesc.GetMaster().CallSwClientNotify(aHint);
+ rDstDesc.GetLeft().CallSwClientNotify(aHint);
+ rDstDesc.GetFirstMaster().CallSwClientNotify(aHint);
+ rDstDesc.GetFirstLeft().CallSwClientNotify(aHint);
+ }
+
+ // Copy the stashed formats as well between the page descriptors...
+ for (bool bFirst : { true, false })
+ {
+ for (bool bLeft : { true, false })
+ {
+ for (bool bHeader : { true, false })
+ {
+ if (!bLeft && !bFirst)
+ continue;
+
+ // Copy format only if it exists
+ if (auto pStashedFormatSrc = rSrcDesc.GetStashedFrameFormat(bHeader, bLeft, bFirst))
+ {
+ if (pStashedFormatSrc->GetDoc() != this)
+ {
+ SwFrameFormat* pNewFormat = new SwFrameFormat(GetAttrPool(), "CopyDesc", GetDfltFrameFormat());
+
+ SfxItemSet aAttrSet(pStashedFormatSrc->GetAttrSet());
+ aAttrSet.ClearItem(RES_HEADER);
+ aAttrSet.ClearItem(RES_FOOTER);
+
+ pNewFormat->DelDiffs( aAttrSet );
+ pNewFormat->SetFormatAttr( aAttrSet );
+
+ if (bHeader)
+ CopyHeader(*pStashedFormatSrc, *pNewFormat);
+ else
+ CopyFooter(*pStashedFormatSrc, *pNewFormat);
+
+ rDstDesc.StashFrameFormat(*pNewFormat, bHeader, bLeft, bFirst);
+ }
+ else
+ {
+ rDstDesc.StashFrameFormat(*pStashedFormatSrc, bHeader, bLeft, bFirst);
+ }
+ }
+ }
+ }
+ }
+}
+
+void SwDoc::ReplaceStyles( const SwDoc& rSource, bool bIncludePageStyles )
+{
+ ::sw::UndoGuard const undoGuard(GetIDocumentUndoRedo());
+
+ CopyFormatArr( *rSource.mpCharFormatTable, *mpCharFormatTable,
+ &SwDoc::MakeCharFormat_, *mpDfltCharFormat );
+ CopyFormatArr( *rSource.mpFrameFormatTable, *mpFrameFormatTable,
+ &SwDoc::MakeFrameFormat_, *mpDfltFrameFormat );
+ CopyFormatArr( *rSource.mpTextFormatCollTable, *mpTextFormatCollTable,
+ &SwDoc::MakeTextFormatColl_, *mpDfltTextFormatColl );
+
+ //To-Do:
+ // a) in sd rtf import (View::InsertData) don't use
+ // a super-fragile test for mere presence of \trowd to
+ // indicate import of rtf into a table
+ // b) then drop use of bIncludePageStyles
+ if (bIncludePageStyles)
+ {
+ // and now the page templates
+ SwPageDescs::size_type nCnt = rSource.m_PageDescs.size();
+ if( nCnt )
+ {
+ // a different Doc -> Number formatter needs to be merged
+ SwTableNumFormatMerge aTNFM( rSource, *this );
+
+ // 1st step: Create all formats (skip the 0th - it's the default!)
+ while( nCnt )
+ {
+ const SwPageDesc &rSrc = *rSource.m_PageDescs[ --nCnt ];
+ if( nullptr == FindPageDesc( rSrc.GetName() ) )
+ MakePageDesc( rSrc.GetName() );
+ }
+
+ // 2nd step: Copy all attributes, set the right parents
+ for (SwPageDescs::size_type i = rSource.m_PageDescs.size(); i; )
+ {
+ const SwPageDesc &rSrc = *rSource.m_PageDescs[ --i ];
+ SwPageDesc* pDesc = FindPageDesc( rSrc.GetName() );
+ CopyPageDesc( rSrc, *pDesc);
+ }
+ }
+ }
+
+ // then there are the numbering templates
+ const SwNumRuleTable::size_type nCnt = rSource.GetNumRuleTable().size();
+ if( nCnt )
+ {
+ const SwNumRuleTable& rArr = rSource.GetNumRuleTable();
+ for( SwNumRuleTable::size_type n = 0; n < nCnt; ++n )
+ {
+ const SwNumRule& rR = *rArr[ n ];
+ SwNumRule* pNew = FindNumRulePtr( rR.GetName());
+ if( pNew )
+ pNew->CopyNumRule(*this, rR);
+ else
+ {
+ if( !rR.IsAutoRule() )
+ MakeNumRule( rR.GetName(), &rR );
+ else
+ {
+ // as we reset all styles, there shouldn't be any unknown
+ // automatic SwNumRules, because all should have been
+ // created by the style copying!
+ // So just warn and ignore.
+ SAL_WARN( "sw.core", "Found unknown auto SwNumRule during reset!" );
+ }
+ }
+ }
+ }
+
+ if (undoGuard.UndoWasEnabled())
+ {
+ // nodes array was modified!
+ GetIDocumentUndoRedo().DelAllUndoObj();
+ }
+
+ getIDocumentState().SetModified();
+}
+
+void SwDoc::MoveLeftMargin(const SwPaM& rPam, bool bRight, bool bModulus,
+ SwRootFrame const*const pLayout)
+{
+ SwHistory* pHistory = nullptr;
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ std::unique_ptr<SwUndoMoveLeftMargin> pUndo(new SwUndoMoveLeftMargin( rPam, bRight,
+ bModulus ));
+ pHistory = &pUndo->GetHistory();
+ GetIDocumentUndoRedo().AppendUndo( std::move(pUndo) );
+ }
+
+ const SvxTabStopItem& rTabItem = GetDefault( RES_PARATR_TABSTOP );
+ const sal_Int32 nDefDist = rTabItem.Count() ? rTabItem[0].GetTabPos() : 1134;
+ const SwPosition &rStt = *rPam.Start(), &rEnd = *rPam.End();
+ SwNodeIndex aIdx( rStt.GetNode() );
+ while( aIdx <= rEnd.GetNode() )
+ {
+ SwTextNode* pTNd = aIdx.GetNode().GetTextNode();
+ if( pTNd )
+ {
+ pTNd = sw::GetParaPropsNode(*pLayout, aIdx.GetNode());
+ SvxFirstLineIndentItem firstLine(pTNd->SwContentNode::GetAttr(RES_MARGIN_FIRSTLINE));
+ SvxTextLeftMarginItem leftMargin(pTNd->SwContentNode::GetAttr(RES_MARGIN_TEXTLEFT));
+
+ // #i93873# See also lcl_MergeListLevelIndentAsLRSpaceItem in thints.cxx
+ ::sw::ListLevelIndents const indents(pTNd->AreListLevelIndentsApplicable());
+ if (indents != ::sw::ListLevelIndents::No)
+ {
+ const SwNumRule* pRule = pTNd->GetNumRule();
+ if ( pRule )
+ {
+ const int nListLevel = pTNd->GetActualListLevel();
+ if ( nListLevel >= 0 )
+ {
+ const SwNumFormat& rFormat = pRule->Get(o3tl::narrowing<sal_uInt16>(nListLevel));
+ if ( rFormat.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_ALIGNMENT )
+ {
+ if (indents & ::sw::ListLevelIndents::LeftMargin)
+ {
+ leftMargin.SetTextLeft(rFormat.GetIndentAt());
+ }
+ if (indents & ::sw::ListLevelIndents::FirstLine)
+ {
+ firstLine.SetTextFirstLineOffset(static_cast<short>(rFormat.GetFirstLineIndent()));
+ }
+ }
+ }
+ }
+ }
+
+ tools::Long nNext = leftMargin.GetTextLeft();
+ if( bModulus )
+ nNext = ( nNext / nDefDist ) * nDefDist;
+
+ if( bRight )
+ nNext += nDefDist;
+ else
+ if(nNext >0) // fdo#75936 set limit for decreasing indent
+ nNext -= nDefDist;
+
+ leftMargin.SetTextLeft( nNext );
+
+ SwRegHistory aRegH( pTNd, *pTNd, pHistory );
+ pTNd->SetAttr(firstLine);
+ pTNd->SetAttr(leftMargin);
+ aIdx = *sw::GetFirstAndLastNode(*pLayout, aIdx.GetNode()).second;
+ }
+ ++aIdx;
+ }
+ getIDocumentState().SetModified();
+}
+
+bool SwDoc::DontExpandFormat( const SwPosition& rPos, bool bFlag )
+{
+ bool bRet = false;
+ SwTextNode* pTextNd = rPos.GetNode().GetTextNode();
+ if( pTextNd )
+ {
+ bRet = pTextNd->DontExpandFormat( rPos.GetContentIndex(), bFlag );
+ if( bRet && GetIDocumentUndoRedo().DoesUndo() )
+ {
+ GetIDocumentUndoRedo().AppendUndo( std::make_unique<SwUndoDontExpandFormat>(rPos) );
+ }
+ }
+ return bRet;
+}
+
+SwTableBoxFormat* SwDoc::MakeTableBoxFormat()
+{
+ SwTableBoxFormat* pFormat = new SwTableBoxFormat( GetAttrPool(), mpDfltFrameFormat.get() );
+ pFormat->SetFormatName("TableBox" + OUString::number(reinterpret_cast<sal_IntPtr>(pFormat)));
+ getIDocumentState().SetModified();
+ return pFormat;
+}
+
+SwTableLineFormat* SwDoc::MakeTableLineFormat()
+{
+ SwTableLineFormat* pFormat = new SwTableLineFormat( GetAttrPool(), mpDfltFrameFormat.get() );
+ pFormat->SetFormatName("TableLine" + OUString::number(reinterpret_cast<sal_IntPtr>(pFormat)));
+ getIDocumentState().SetModified();
+ return pFormat;
+}
+
+void SwDoc::EnsureNumberFormatter()
+{
+ if (mpNumberFormatter == nullptr)
+ {
+ LanguageType eLang = LANGUAGE_SYSTEM;
+ mpNumberFormatter = new SvNumberFormatter(comphelper::getProcessComponentContext(), eLang);
+ mpNumberFormatter->SetEvalDateFormat( NF_EVALDATEFORMAT_FORMAT_INTL );
+ if (!utl::ConfigManager::IsFuzzing())
+ mpNumberFormatter->SetYear2000(
+ officecfg::Office::Common::DateFormat::TwoDigitYear::get());
+ };
+}
+
+SwTableNumFormatMerge::SwTableNumFormatMerge( const SwDoc& rSrc, SwDoc& rDest )
+ : pNFormat( nullptr )
+{
+ // a different Doc -> Number formatter needs to be merged
+ if( &rSrc != &rDest )
+ {
+ SvNumberFormatter* pN = const_cast<SwDoc&>(rSrc).GetNumberFormatter( false );
+ if( pN )
+ {
+ pNFormat = rDest.GetNumberFormatter();
+ pNFormat->MergeFormatter( *pN );
+ }
+ }
+
+ if( &rSrc != &rDest )
+ static_cast<SwGetRefFieldType*>(rSrc.getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::GetRef ))->
+ MergeWithOtherDoc( rDest );
+}
+
+SwTableNumFormatMerge::~SwTableNumFormatMerge()
+{
+ if( pNFormat )
+ pNFormat->ClearMergeTable();
+}
+
+void SwDoc::SetTextFormatCollByAutoFormat( const SwPosition& rPos, sal_uInt16 nPoolId,
+ const SfxItemSet* pSet )
+{
+ SwPaM aPam( rPos );
+ SwTextNode* pTNd = rPos.GetNode().GetTextNode();
+ assert(pTNd);
+
+ if (mbIsAutoFormatRedline)
+ {
+ // create the redline object
+ const SwTextFormatColl& rColl = *pTNd->GetTextColl();
+ SwRangeRedline* pRedl = new SwRangeRedline( RedlineType::FmtColl, aPam );
+ pRedl->SetMark();
+
+ // Only those items that are not set by the Set again in the Node
+ // are of interest. Thus, we take the difference.
+ SwRedlineExtraData_FormatColl aExtraData( rColl.GetName(),
+ rColl.GetPoolFormatId() );
+ if( pSet && pTNd->HasSwAttrSet() )
+ {
+ SfxItemSet aTmp( *pTNd->GetpSwAttrSet() );
+ aTmp.Differentiate( *pSet );
+ // we handle the adjust item separately
+ const SfxPoolItem* pItem;
+ if( SfxItemState::SET == pTNd->GetpSwAttrSet()->GetItemState(
+ RES_PARATR_ADJUST, false, &pItem ))
+ aTmp.Put( *pItem );
+ aExtraData.SetItemSet( aTmp );
+ }
+ pRedl->SetExtraData( &aExtraData );
+
+ //TODO: Undo is still missing!
+ getIDocumentRedlineAccess().AppendRedline( pRedl, true );
+ }
+
+ SetTextFormatColl( aPam, getIDocumentStylePoolAccess().GetTextCollFromPool( nPoolId ) );
+
+ if (pSet && pSet->Count())
+ {
+ aPam.SetMark();
+ aPam.GetMark()->SetContent(pTNd->GetText().getLength());
+ // sw_redlinehide: don't need layout currently because the only caller
+ // passes in the properties node
+ assert(static_cast<SwTextFrame const*>(pTNd->getLayoutFrame(nullptr))->GetTextNodeForParaProps() == pTNd);
+ getIDocumentContentOperations().InsertItemSet( aPam, *pSet );
+ }
+}
+
+void SwDoc::SetFormatItemByAutoFormat( const SwPaM& rPam, const SfxItemSet& rSet )
+{
+ SwTextNode* pTNd = rPam.GetPoint()->GetNode().GetTextNode();
+ assert(pTNd);
+
+ RedlineFlags eOld = getIDocumentRedlineAccess().GetRedlineFlags();
+
+ if (mbIsAutoFormatRedline)
+ {
+ // create the redline object
+ SwRangeRedline* pRedl = new SwRangeRedline( RedlineType::Format, rPam );
+ if( !pRedl->HasMark() )
+ pRedl->SetMark();
+
+ // Only those items that are not set by the Set again in the Node
+ // are of interest. Thus, we take the difference.
+ SwRedlineExtraData_Format aExtraData( rSet );
+
+ pRedl->SetExtraData( &aExtraData );
+
+ //TODO: Undo is still missing!
+ getIDocumentRedlineAccess().AppendRedline( pRedl, true );
+
+ getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld | RedlineFlags::Ignore );
+ }
+
+ const sal_Int32 nEnd(rPam.End()->GetContentIndex());
+ std::vector<WhichPair> whichIds;
+ SfxItemIter iter(rSet);
+ for (SfxPoolItem const* pItem = iter.GetCurItem(); pItem; pItem = iter.NextItem())
+ {
+ whichIds.push_back({pItem->Which(), pItem->Which()});
+ }
+ SfxItemSet currentSet(GetAttrPool(), WhichRangesContainer(whichIds.data(), whichIds.size()));
+ pTNd->GetParaAttr(currentSet, nEnd, nEnd);
+ for (const WhichPair& rPair : whichIds)
+ { // yuk - want to explicitly set the pool defaults too :-/
+ currentSet.Put(currentSet.Get(rPair.first));
+ }
+
+ getIDocumentContentOperations().InsertItemSet( rPam, rSet, SetAttrMode::DONTEXPAND );
+
+ // fdo#62536: DONTEXPAND does not work when there is already an AUTOFMT
+ // here, so insert the old attributes as an empty hint to stop expand
+ SwPaM endPam(*pTNd, nEnd);
+ endPam.SetMark();
+ getIDocumentContentOperations().InsertItemSet(endPam, currentSet);
+
+ getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
+}
+
+void SwDoc::ChgFormat(SwFormat & rFormat, const SfxItemSet & rSet)
+{
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ // copying <rSet> to <aSet>
+ SfxItemSet aSet(rSet);
+ // remove from <aSet> all items, which are already set at the format
+ aSet.Differentiate(rFormat.GetAttrSet());
+ // <aSet> contains now all *new* items for the format
+
+ // copying current format item set to <aOldSet>
+ SfxItemSet aOldSet(rFormat.GetAttrSet());
+ // insert new items into <aOldSet>
+ aOldSet.Put(aSet);
+ // invalidate all new items in <aOldSet> in order to clear these items,
+ // if the undo action is triggered.
+ {
+ SfxItemIter aIter(aSet);
+
+ for (const SfxPoolItem* pItem = aIter.GetCurItem(); pItem; pItem = aIter.NextItem())
+ {
+ aOldSet.InvalidateItem(pItem->Which());
+ }
+ }
+
+ GetIDocumentUndoRedo().AppendUndo(
+ std::make_unique<SwUndoFormatAttr>(std::move(aOldSet), rFormat, /*bSaveDrawPt*/true));
+ }
+
+ rFormat.SetFormatAttr(rSet);
+}
+
+void SwDoc::RenameFormat(SwFormat & rFormat, const OUString & sNewName,
+ bool bBroadcast)
+{
+ SfxStyleFamily eFamily = SfxStyleFamily::All;
+
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ std::unique_ptr<SwUndo> pUndo;
+
+ switch (rFormat.Which())
+ {
+ case RES_CHRFMT:
+ pUndo.reset(new SwUndoRenameCharFormat(rFormat.GetName(), sNewName, *this));
+ eFamily = SfxStyleFamily::Char;
+ break;
+ case RES_TXTFMTCOLL:
+ pUndo.reset(new SwUndoRenameFormatColl(rFormat.GetName(), sNewName, *this));
+ eFamily = SfxStyleFamily::Para;
+ break;
+ case RES_FRMFMT:
+ pUndo.reset(new SwUndoRenameFrameFormat(rFormat.GetName(), sNewName, *this));
+ eFamily = SfxStyleFamily::Frame;
+ break;
+
+ default:
+ break;
+ }
+
+ if (pUndo)
+ {
+ GetIDocumentUndoRedo().AppendUndo(std::move(pUndo));
+ }
+ }
+
+ // name change means the o3tl::sorted_array is not property sorted
+ if (rFormat.Which() == RES_CHRFMT)
+ mpCharFormatTable->SetFormatNameAndReindex(static_cast<SwCharFormat*>(&rFormat), sNewName);
+ else
+ rFormat.SetFormatName(sNewName);
+
+ if (bBroadcast)
+ BroadcastStyleOperation(sNewName, eFamily, SfxHintId::StyleSheetModified);
+}
+
+void SwDoc::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ bool bOwns = false;
+ if (!pWriter)
+ {
+ pWriter = xmlNewTextWriterFilename("nodes.xml", 0);
+ xmlTextWriterSetIndent(pWriter,1);
+ (void)xmlTextWriterSetIndentString(pWriter, BAD_CAST(" "));
+ (void)xmlTextWriterStartDocument(pWriter, nullptr, nullptr, nullptr);
+ bOwns = true;
+ }
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwDoc"));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
+
+ m_pNodes->dumpAsXml(pWriter);
+ m_PageDescs.dumpAsXml(pWriter);
+ maDBData.dumpAsXml(pWriter);
+ mpMarkManager->dumpAsXml(pWriter);
+ m_pContentControlManager->dumpAsXml(pWriter);
+ m_pUndoManager->dumpAsXml(pWriter);
+ m_pDocumentSettingManager->dumpAsXml(pWriter);
+ getIDocumentFieldsAccess().GetFieldTypes()->dumpAsXml(pWriter);
+ mpTextFormatCollTable->dumpAsXml(pWriter);
+ mpCharFormatTable->dumpAsXml(pWriter);
+ mpFrameFormatTable->dumpAsXml(pWriter, "frmFormatTable");
+ mpSpzFrameFormatTable->dumpAsXml(pWriter, "spzFrameFormatTable");
+ mpSectionFormatTable->dumpAsXml(pWriter);
+ mpTableFrameFormatTable->dumpAsXml(pWriter, "tableFrameFormatTable");
+ mpNumRuleTable->dumpAsXml(pWriter);
+ getIDocumentRedlineAccess().GetRedlineTable().dumpAsXml(pWriter);
+ getIDocumentRedlineAccess().GetExtraRedlineTable().dumpAsXml(pWriter);
+ if (const SdrModel* pModel = getIDocumentDrawModelAccess().GetDrawModel())
+ pModel->dumpAsXml(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbModified"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"), BAD_CAST(OString::boolean(getIDocumentState().IsModified()).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterEndElement(pWriter);
+ if (bOwns)
+ {
+ (void)xmlTextWriterEndDocument(pWriter);
+ xmlFreeTextWriter(pWriter);
+ }
+}
+
+void SwDBData::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwDBData"));
+
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("sDataSource"), BAD_CAST(sDataSource.toUtf8().getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("sCommand"), BAD_CAST(sCommand.toUtf8().getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("nCommandType"), BAD_CAST(OString::number(nCommandType).getStr()));
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+std::set<Color> SwDoc::GetDocColors()
+{
+ std::set<Color> aDocColors;
+ SwAttrPool& rPool = GetAttrPool();
+ const sal_uInt16 pAttribs[] = {RES_CHRATR_COLOR, RES_CHRATR_HIGHLIGHT, RES_BACKGROUND};
+ for (sal_uInt16 nAttrib : pAttribs)
+ {
+ for (const SfxPoolItem* pItem : rPool.GetItemSurrogates(nAttrib))
+ {
+ auto pColorItem = static_cast<const SvxColorItem*>(pItem);
+ Color aColor( pColorItem->GetValue() );
+ if (COL_AUTO != aColor)
+ aDocColors.insert(aColor);
+ }
+ }
+ return aDocColors;
+}
+
+// #i69627#
+namespace docfunc
+{
+ bool HasOutlineStyleToBeWrittenAsNormalListStyle( SwDoc& rDoc )
+ {
+ // If a parent paragraph style of one of the paragraph styles, which
+ // are assigned to the list levels of the outline style, has a list style
+ // set or inherits a list style from its parent style, the outline style
+ // has to be written as a normal list style to the OpenDocument file
+ // format or the OpenOffice.org file format.
+ bool bRet( false );
+
+ const SwTextFormatColls* pTextFormatColls( rDoc.GetTextFormatColls() );
+ if ( pTextFormatColls )
+ {
+ for ( auto pTextFormatColl : *pTextFormatColls )
+ {
+ if ( pTextFormatColl->IsDefault() ||
+ ! pTextFormatColl->IsAssignedToListLevelOfOutlineStyle() )
+ {
+ continue;
+ }
+
+ const SwTextFormatColl* pParentTextFormatColl =
+ dynamic_cast<const SwTextFormatColl*>( pTextFormatColl->DerivedFrom());
+ if ( !pParentTextFormatColl )
+ continue;
+
+ if ( SfxItemState::SET == pParentTextFormatColl->GetItemState( RES_PARATR_NUMRULE ) )
+ {
+ // #i106218# consider that the outline style is set
+ const SwNumRuleItem& rDirectItem = pParentTextFormatColl->GetNumRule();
+ if ( rDirectItem.GetValue() != rDoc.GetOutlineNumRule()->GetName() )
+ {
+ bRet = true;
+ break;
+ }
+ }
+ }
+
+ }
+ return bRet;
+ }
+}
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/docftn.cxx b/sw/source/core/doc/docftn.cxx
new file mode 100644
index 0000000000..16f6694c94
--- /dev/null
+++ b/sw/source/core/doc/docftn.cxx
@@ -0,0 +1,547 @@
+/* -*- 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 <ftnidx.hxx>
+#include <rootfrm.hxx>
+#include <txtftn.hxx>
+#include <fmtftn.hxx>
+#include <pam.hxx>
+#include <pagedesc.hxx>
+#include <charfmt.hxx>
+#include <UndoAttribute.hxx>
+#include <rolbck.hxx>
+#include <doc.hxx>
+#include <IDocumentFieldsAccess.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <IDocumentState.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <IDocumentStylePoolAccess.hxx>
+#include <ndtxt.hxx>
+#include <poolfmt.hxx>
+#include <ftninfo.hxx>
+
+SwEndNoteInfo& SwEndNoteInfo::operator=(const SwEndNoteInfo& rInfo)
+{
+ m_pTextFormatColl = rInfo.m_pTextFormatColl;
+ m_pPageDesc = rInfo.m_pPageDesc;
+ m_pCharFormat = rInfo.m_pCharFormat;
+ m_pAnchorFormat = rInfo.m_pAnchorFormat;
+ m_aDepends.EndListeningAll();
+ m_aDepends.StartListening(m_pTextFormatColl);
+ m_aDepends.StartListening(m_pPageDesc);
+ m_aDepends.StartListening(m_pCharFormat);
+ m_aDepends.StartListening(m_pAnchorFormat);
+
+ m_aFormat = rInfo.m_aFormat;
+ m_nFootnoteOffset = rInfo.m_nFootnoteOffset;
+ m_bEndNote = rInfo.m_bEndNote;
+ m_sPrefix = rInfo.m_sPrefix;
+ m_sSuffix = rInfo.m_sSuffix;
+ return *this;
+}
+
+bool SwEndNoteInfo::operator==( const SwEndNoteInfo& rInfo ) const
+{
+ return
+ m_pTextFormatColl == rInfo.m_pTextFormatColl &&
+ m_pPageDesc == rInfo.m_pPageDesc &&
+ m_pCharFormat == rInfo.m_pCharFormat &&
+ m_pAnchorFormat == rInfo.m_pAnchorFormat &&
+ m_aFormat.GetNumberingType() == rInfo.m_aFormat.GetNumberingType() &&
+ m_nFootnoteOffset == rInfo.m_nFootnoteOffset &&
+ m_bEndNote == rInfo.m_bEndNote &&
+ m_sPrefix == rInfo.m_sPrefix &&
+ m_sSuffix == rInfo.m_sSuffix;
+}
+
+SwEndNoteInfo::SwEndNoteInfo(const SwEndNoteInfo& rInfo) :
+ SwClient(nullptr),
+ m_aDepends(*this),
+ m_pTextFormatColl(rInfo.m_pTextFormatColl),
+ m_pPageDesc(rInfo.m_pPageDesc),
+ m_pCharFormat(rInfo.m_pCharFormat),
+ m_pAnchorFormat(rInfo.m_pAnchorFormat),
+ m_sPrefix( rInfo.m_sPrefix ),
+ m_sSuffix( rInfo.m_sSuffix ),
+ m_bEndNote( true ),
+ m_aFormat( rInfo.m_aFormat ),
+ m_nFootnoteOffset( rInfo.m_nFootnoteOffset )
+{
+ m_aDepends.StartListening(m_pTextFormatColl);
+ m_aDepends.StartListening(m_pPageDesc);
+ m_aDepends.StartListening(m_pCharFormat);
+ m_aDepends.StartListening(m_pAnchorFormat);
+}
+
+SwEndNoteInfo::SwEndNoteInfo() :
+ SwClient(nullptr),
+ m_aDepends(*this),
+ m_pTextFormatColl(nullptr),
+ m_pPageDesc(nullptr),
+ m_pCharFormat(nullptr),
+ m_pAnchorFormat(nullptr),
+ m_bEndNote( true ),
+ m_nFootnoteOffset( 0 )
+{
+ m_aFormat.SetNumberingType(SVX_NUM_ROMAN_LOWER);
+}
+
+SwPageDesc* SwEndNoteInfo::GetPageDesc(SwDoc& rDoc) const
+{
+ if(!m_pPageDesc)
+ {
+ m_pPageDesc = rDoc.getIDocumentStylePoolAccess().GetPageDescFromPool( o3tl::narrowing<sal_uInt16>(
+ m_bEndNote ? RES_POOLPAGE_ENDNOTE : RES_POOLPAGE_FOOTNOTE ) );
+ m_aDepends.StartListening(m_pPageDesc);
+ }
+ return m_pPageDesc;
+}
+
+bool SwEndNoteInfo::KnowsPageDesc() const
+{
+ return m_pPageDesc != nullptr;
+}
+
+bool SwEndNoteInfo::DependsOn(const SwPageDesc* pDesc) const
+{
+ return m_pPageDesc == pDesc;
+}
+
+void SwEndNoteInfo::ChgPageDesc(SwPageDesc* pDesc)
+{
+ m_aDepends.EndListening(m_pPageDesc);
+ m_pPageDesc = pDesc;
+ m_aDepends.StartListening(m_pPageDesc);
+}
+
+void SwEndNoteInfo::SetFootnoteTextColl(SwTextFormatColl& rFormat)
+{
+ m_aDepends.EndListening(m_pTextFormatColl);
+ m_pTextFormatColl = &rFormat;
+ m_aDepends.StartListening(m_pTextFormatColl);
+}
+
+SwCharFormat* SwEndNoteInfo::GetCharFormat(SwDoc& rDoc) const
+{
+ auto pCharFormatFromDoc = rDoc.getIDocumentStylePoolAccess().GetCharFormatFromPool( o3tl::narrowing<sal_uInt16>(
+ m_bEndNote ? RES_POOLCHR_ENDNOTE : RES_POOLCHR_FOOTNOTE ) );
+ if (m_pCharFormat != pCharFormatFromDoc)
+ {
+ m_aDepends.EndListening(m_pCharFormat);
+ m_aDepends.StartListening(pCharFormatFromDoc);
+ m_pCharFormat = pCharFormatFromDoc;
+ }
+ return m_pCharFormat;
+}
+
+namespace
+{
+ void lcl_ResetPoolIdForDocAndSync(const sal_uInt16 nId, SwCharFormat* pFormat, const SwEndNoteInfo& rInfo)
+ {
+ auto pDoc = pFormat->GetDoc();
+ if(!pDoc)
+ return;
+ for(auto pDocFormat : *pDoc->GetCharFormats())
+ {
+ if(pDocFormat == pFormat)
+ pDocFormat->SetPoolFormatId(nId);
+ else if(pDocFormat->GetPoolFormatId() == nId)
+ pDocFormat->SetPoolFormatId(0);
+ }
+ rInfo.GetCharFormat(*pDoc);
+ rInfo.GetAnchorCharFormat(*pDoc);
+ }
+}
+
+void SwEndNoteInfo::SetCharFormat(SwCharFormat* pFormat)
+{
+ lcl_ResetPoolIdForDocAndSync(
+ o3tl::narrowing<sal_uInt16>(m_bEndNote
+ ? RES_POOLCHR_ENDNOTE
+ : RES_POOLCHR_FOOTNOTE),
+ pFormat,
+ *this);
+}
+
+SwCharFormat* SwEndNoteInfo::GetAnchorCharFormat(SwDoc& rDoc) const
+{
+ auto pAnchorFormatFromDoc = rDoc.getIDocumentStylePoolAccess().GetCharFormatFromPool( o3tl::narrowing<sal_uInt16>(
+ m_bEndNote ? RES_POOLCHR_ENDNOTE_ANCHOR : RES_POOLCHR_FOOTNOTE_ANCHOR ) );
+ if(m_pAnchorFormat != pAnchorFormatFromDoc)
+ {
+ m_aDepends.EndListening(m_pAnchorFormat);
+ m_aDepends.StartListening(pAnchorFormatFromDoc);
+ m_pAnchorFormat = pAnchorFormatFromDoc;
+ }
+ return m_pAnchorFormat;
+}
+
+void SwEndNoteInfo::SetAnchorCharFormat(SwCharFormat* pFormat)
+{
+ lcl_ResetPoolIdForDocAndSync(
+ o3tl::narrowing<sal_uInt16>(m_bEndNote
+ ? RES_POOLCHR_ENDNOTE_ANCHOR
+ : RES_POOLCHR_FOOTNOTE_ANCHOR),
+ pFormat,
+ *this);
+}
+
+SwCharFormat* SwEndNoteInfo::GetCurrentCharFormat(const bool bAnchor) const
+{
+ return bAnchor
+ ? m_pAnchorFormat
+ : m_pCharFormat;
+}
+
+void SwEndNoteInfo::UpdateFormatOrAttr()
+{
+ auto pFormat = GetCurrentCharFormat(m_pCharFormat == nullptr);
+ if (!pFormat || !m_aDepends.IsListeningTo(pFormat) || pFormat->IsFormatInDTOR())
+ return;
+ SwDoc* pDoc = pFormat->GetDoc();
+ SwFootnoteIdxs& rFootnoteIdxs = pDoc->GetFootnoteIdxs();
+ for(auto pTextFootnote : rFootnoteIdxs)
+ {
+ const SwFormatFootnote &rFootnote = pTextFootnote->GetFootnote();
+ if(rFootnote.IsEndNote() == m_bEndNote)
+ pTextFootnote->SetNumber(rFootnote.GetNumber(), rFootnote.GetNumberRLHidden(), rFootnote.GetNumStr());
+ }
+}
+
+
+void SwEndNoteInfo::SwClientNotify( const SwModify& rModify, const SfxHint& rHint)
+{
+ if (rHint.GetId() == SfxHintId::SwLegacyModify)
+ {
+ auto pLegacyHint = static_cast<const sw::LegacyModifyHint*>(&rHint);
+ switch(pLegacyHint->GetWhich())
+ {
+ case RES_ATTRSET_CHG:
+ case RES_FMT_CHG:
+ UpdateFormatOrAttr();
+ break;
+ default:
+ CheckRegistration( pLegacyHint->m_pOld );
+ }
+ }
+ else if (auto pModifyChangedHint = dynamic_cast<const sw::ModifyChangedHint*>(&rHint))
+ {
+ auto pNew = const_cast<sw::BroadcastingModify*>(static_cast<const sw::BroadcastingModify*>(pModifyChangedHint->m_pNew));
+ if(m_pAnchorFormat == &rModify)
+ m_pAnchorFormat = static_cast<SwCharFormat*>(pNew);
+ else if(m_pCharFormat == &rModify)
+ m_pCharFormat = static_cast<SwCharFormat*>(pNew);
+ else if(m_pPageDesc == &rModify)
+ m_pPageDesc = static_cast<SwPageDesc*>(pNew);
+ else if(m_pTextFormatColl == &rModify)
+ m_pTextFormatColl = static_cast<SwTextFormatColl*>(pNew);
+ }
+}
+
+SwFootnoteInfo& SwFootnoteInfo::operator=(const SwFootnoteInfo& rInfo)
+{
+ SwEndNoteInfo::operator=(rInfo);
+ m_aQuoVadis = rInfo.m_aQuoVadis;
+ m_aErgoSum = rInfo.m_aErgoSum;
+ m_ePos = rInfo.m_ePos;
+ m_eNum = rInfo.m_eNum;
+ return *this;
+}
+
+bool SwFootnoteInfo::operator==( const SwFootnoteInfo& rInfo ) const
+{
+ return m_ePos == rInfo.m_ePos &&
+ m_eNum == rInfo.m_eNum &&
+ SwEndNoteInfo::operator==(rInfo) &&
+ m_aQuoVadis == rInfo.m_aQuoVadis &&
+ m_aErgoSum == rInfo.m_aErgoSum;
+}
+
+SwFootnoteInfo::SwFootnoteInfo(const SwFootnoteInfo& rInfo) :
+ SwEndNoteInfo( rInfo ),
+ m_aQuoVadis( rInfo.m_aQuoVadis ),
+ m_aErgoSum( rInfo.m_aErgoSum ),
+ m_ePos( rInfo.m_ePos ),
+ m_eNum( rInfo.m_eNum )
+{
+ m_bEndNote = false;
+}
+
+SwFootnoteInfo::SwFootnoteInfo() :
+ m_ePos( FTNPOS_PAGE ),
+ m_eNum( FTNNUM_DOC )
+{
+ m_aFormat.SetNumberingType(SVX_NUM_ARABIC);
+ m_bEndNote = false;
+}
+
+void SwDoc::SetFootnoteInfo(const SwFootnoteInfo& rInfo)
+{
+ SwRootFrame* pTmpRoot = getIDocumentLayoutAccess().GetCurrentLayout();
+ if( GetFootnoteInfo() == rInfo )
+ return;
+
+ const SwFootnoteInfo &rOld = GetFootnoteInfo();
+
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().AppendUndo( std::make_unique<SwUndoFootNoteInfo>(rOld, *this) );
+ }
+
+ bool bFootnotePos = rInfo.m_ePos != rOld.m_ePos;
+ bool bFootnoteDesc = rOld.m_ePos == FTNPOS_CHAPTER &&
+ rInfo.GetPageDesc( *this ) != rOld.GetPageDesc( *this );
+ bool bExtra = rInfo.m_aQuoVadis != rOld.m_aQuoVadis ||
+ rInfo.m_aErgoSum != rOld.m_aErgoSum ||
+ rInfo.m_aFormat.GetNumberingType() != rOld.m_aFormat.GetNumberingType() ||
+ rInfo.GetPrefix() != rOld.GetPrefix() ||
+ rInfo.GetSuffix() != rOld.GetSuffix();
+ SwCharFormat *pOldChrFormat = rOld.GetCharFormat( *this ),
+ *pNewChrFormat = rInfo.GetCharFormat( *this );
+ bool bFootnoteChrFormats = pOldChrFormat != pNewChrFormat;
+
+ *mpFootnoteInfo = rInfo;
+
+ if (pTmpRoot)
+ {
+ o3tl::sorted_vector<SwRootFrame*> aAllLayouts = GetAllLayouts();
+ if ( bFootnotePos )
+ for( auto aLayout : aAllLayouts )
+ aLayout->AllRemoveFootnotes();
+ else
+ {
+ for( auto aLayout : aAllLayouts )
+ aLayout->UpdateFootnoteNums();
+ if ( bFootnoteDesc )
+ for( auto aLayout : aAllLayouts )
+ aLayout->CheckFootnotePageDescs(false);
+ if ( bExtra )
+ {
+ // For messages regarding ErgoSum etc. we save the extra code and use the
+ // available methods.
+ SwFootnoteIdxs& rFootnoteIdxs = GetFootnoteIdxs();
+ for( size_t nPos = 0; nPos < rFootnoteIdxs.size(); ++nPos )
+ {
+ SwTextFootnote *pTextFootnote = rFootnoteIdxs[ nPos ];
+ const SwFormatFootnote &rFootnote = pTextFootnote->GetFootnote();
+ if ( !rFootnote.IsEndNote() )
+ pTextFootnote->SetNumber(rFootnote.GetNumber(), rFootnote.GetNumberRLHidden(), rFootnote.GetNumStr());
+ }
+ }
+ }
+ }
+ if( FTNNUM_PAGE != rInfo.m_eNum )
+ GetFootnoteIdxs().UpdateAllFootnote();
+ else if( bFootnoteChrFormats )
+ {
+ mpFootnoteInfo->UpdateFormatOrAttr();
+ }
+
+ // #i81002# no update during loading
+ if ( !IsInReading() )
+ {
+ getIDocumentFieldsAccess().UpdateRefFields();
+ }
+ getIDocumentState().SetModified();
+
+}
+
+void SwDoc::SetEndNoteInfo(const SwEndNoteInfo& rInfo)
+{
+ SwRootFrame* pTmpRoot = getIDocumentLayoutAccess().GetCurrentLayout();
+ if( GetEndNoteInfo() == rInfo )
+ return;
+
+ if(GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().AppendUndo(
+ std::make_unique<SwUndoEndNoteInfo>( GetEndNoteInfo(), *this ) );
+ }
+
+ bool bNumChg = rInfo.m_nFootnoteOffset != GetEndNoteInfo().m_nFootnoteOffset;
+ // this seems to be an optimization: UpdateAllFootnote() is only called
+ // if the offset changes; if the offset is the same,
+ // but type/prefix/suffix changes, just set new numbers.
+ bool const bExtra = !bNumChg &&
+ ( (rInfo.m_aFormat.GetNumberingType() !=
+ GetEndNoteInfo().m_aFormat.GetNumberingType())
+ || (rInfo.GetPrefix() != GetEndNoteInfo().GetPrefix())
+ || (rInfo.GetSuffix() != GetEndNoteInfo().GetSuffix())
+ );
+ bool bFootnoteDesc = rInfo.GetPageDesc( *this ) !=
+ GetEndNoteInfo().GetPageDesc( *this );
+ SwCharFormat *pOldChrFormat = GetEndNoteInfo().GetCharFormat( *this ),
+ *pNewChrFormat = rInfo.GetCharFormat( *this );
+ bool bFootnoteChrFormats = pOldChrFormat != pNewChrFormat;
+
+ *mpEndNoteInfo = rInfo;
+
+ if ( pTmpRoot )
+ {
+ if ( bFootnoteDesc )
+ {
+ for( auto aLayout : GetAllLayouts() )
+ aLayout->CheckFootnotePageDescs(true);
+ }
+ if ( bExtra )
+ {
+ // For messages regarding ErgoSum etc. we save the extra code and use the
+ // available methods.
+ SwFootnoteIdxs& rFootnoteIdxs = GetFootnoteIdxs();
+ for( size_t nPos = 0; nPos < rFootnoteIdxs.size(); ++nPos )
+ {
+ SwTextFootnote *pTextFootnote = rFootnoteIdxs[ nPos ];
+ const SwFormatFootnote &rFootnote = pTextFootnote->GetFootnote();
+ if ( rFootnote.IsEndNote() )
+ pTextFootnote->SetNumber(rFootnote.GetNumber(), rFootnote.GetNumberRLHidden(), rFootnote.GetNumStr());
+ }
+ }
+ }
+ if( bNumChg )
+ GetFootnoteIdxs().UpdateAllFootnote();
+ else if( bFootnoteChrFormats )
+ {
+ mpEndNoteInfo->UpdateFormatOrAttr();
+ }
+
+ // #i81002# no update during loading
+ if ( !IsInReading() )
+ {
+ getIDocumentFieldsAccess().UpdateRefFields();
+ }
+ getIDocumentState().SetModified();
+
+}
+
+bool SwDoc::SetCurFootnote( const SwPaM& rPam, const OUString& rNumStr,
+ bool bIsEndNote)
+{
+ SwFootnoteIdxs& rFootnoteArr = GetFootnoteIdxs();
+ SwRootFrame* pTmpRoot = getIDocumentLayoutAccess().GetCurrentLayout();
+
+ auto [pStt, pEnd] = rPam.StartEnd(); // SwPosition*
+ const SwNodeOffset nSttNd = pStt->GetNodeIndex();
+ const sal_Int32 nSttCnt = pStt->GetContentIndex();
+ const SwNodeOffset nEndNd = pEnd->GetNodeIndex();
+ const sal_Int32 nEndCnt = pEnd->GetContentIndex();
+
+ size_t nPos = 0;
+ rFootnoteArr.SeekEntry( pStt->GetNode(), &nPos );
+
+ std::unique_ptr<SwUndoChangeFootNote> pUndo;
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().ClearRedo(); // AppendUndo far below, so leave it
+ pUndo.reset(new SwUndoChangeFootNote( rPam, rNumStr, bIsEndNote ));
+ }
+
+ bool bChg = false;
+ bool bTypeChgd = false;
+ const size_t nPosSave = nPos;
+ while( nPos < rFootnoteArr.size() )
+ {
+ SwTextFootnote* pTextFootnote = rFootnoteArr[ nPos++ ];
+ SwNodeOffset nIdx = SwTextFootnote_GetIndex(pTextFootnote);
+ if( nIdx >= nEndNd &&
+ ( nIdx != nEndNd || nEndCnt < pTextFootnote->GetStart() ) )
+ continue;
+ if( nIdx > nSttNd || ( nIdx == nSttNd &&
+ nSttCnt <= pTextFootnote->GetStart() ) )
+ {
+ const SwFormatFootnote& rFootnote = pTextFootnote->GetFootnote();
+ if( rFootnote.GetNumStr() != rNumStr ||
+ rFootnote.IsEndNote() != bIsEndNote )
+ {
+ bChg = true;
+ if ( pUndo )
+ {
+ pUndo->GetHistory().AddFootnote(*pTextFootnote);
+ }
+
+ pTextFootnote->SetNumber(rFootnote.GetNumber(), rFootnote.GetNumberRLHidden(), rNumStr);
+ if( rFootnote.IsEndNote() != bIsEndNote )
+ {
+ const_cast<SwFormatFootnote&>(rFootnote).SetEndNote( bIsEndNote );
+ bTypeChgd = true;
+ pTextFootnote->CheckCondColl();
+ //#i11339# dispose UNO wrapper when a footnote is changed to an endnote or vice versa
+ const_cast<SwFormatFootnote&>(rFootnote).InvalidateFootnote();
+ }
+ }
+ }
+ }
+
+ nPos = nPosSave; // There are more in the front!
+ while( nPos )
+ {
+ SwTextFootnote* pTextFootnote = rFootnoteArr[ --nPos ];
+ SwNodeOffset nIdx = SwTextFootnote_GetIndex(pTextFootnote);
+ if( nIdx <= nSttNd &&
+ ( nIdx != nSttNd || nSttCnt > pTextFootnote->GetStart() ) )
+ continue;
+ if( nIdx < nEndNd || ( nIdx == nEndNd &&
+ nEndCnt >= pTextFootnote->GetStart() ) )
+ {
+ const SwFormatFootnote& rFootnote = pTextFootnote->GetFootnote();
+ if( rFootnote.GetNumStr() != rNumStr ||
+ rFootnote.IsEndNote() != bIsEndNote )
+ {
+ bChg = true;
+ if ( pUndo )
+ {
+ pUndo->GetHistory().AddFootnote(*pTextFootnote);
+ }
+
+ pTextFootnote->SetNumber(rFootnote.GetNumber(), rFootnote.GetNumberRLHidden(), rNumStr);
+ if( rFootnote.IsEndNote() != bIsEndNote )
+ {
+ const_cast<SwFormatFootnote&>(rFootnote).SetEndNote( bIsEndNote );
+ bTypeChgd = true;
+ pTextFootnote->CheckCondColl();
+ }
+ }
+ }
+ }
+
+ // Who needs to be triggered?
+ if( bChg )
+ {
+ if( pUndo )
+ {
+ GetIDocumentUndoRedo().AppendUndo(std::move(pUndo));
+ }
+
+ if ( bTypeChgd )
+ rFootnoteArr.UpdateAllFootnote();
+ if( FTNNUM_PAGE != GetFootnoteInfo().m_eNum )
+ {
+ if ( !bTypeChgd )
+ rFootnoteArr.UpdateAllFootnote();
+ }
+ else if( pTmpRoot )
+ {
+ for( auto aLayout : GetAllLayouts() )
+ aLayout->UpdateFootnoteNums();
+ }
+ getIDocumentState().SetModified();
+ }
+ return bChg;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/docglbl.cxx b/sw/source/core/doc/docglbl.cxx
new file mode 100644
index 0000000000..56f78b66cb
--- /dev/null
+++ b/sw/source/core/doc/docglbl.cxx
@@ -0,0 +1,515 @@
+/* -*- 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 <osl/diagnose.h>
+#include <unotools/tempfile.hxx>
+#include <svl/stritem.hxx>
+#include <svl/eitem.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/docfilt.hxx>
+#include <sfx2/fcontnr.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/request.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <sfx2/viewfrm.hxx>
+#include <tools/datetime.hxx>
+#include <fmtinfmt.hxx>
+#include <fmtanchr.hxx>
+#include <doc.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <DocumentSettingManager.hxx>
+#include <DocumentContentOperationsManager.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <docary.hxx>
+#include <pam.hxx>
+#include <ndtxt.hxx>
+#include <docsh.hxx>
+#include <section.hxx>
+#include <calbck.hxx>
+#include <iodetect.hxx>
+#include <frameformats.hxx>
+#include <memory>
+#include <com/sun/star/uno/Reference.h>
+#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
+#include <com/sun/star/document/XDocumentProperties.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+
+using namespace ::com::sun::star;
+
+namespace {
+
+enum SwSplitDocType
+{
+ SPLITDOC_TO_GLOBALDOC,
+ SPLITDOC_TO_HTML
+};
+
+}
+
+bool SwDoc::GenerateGlobalDoc( const OUString& rPath,
+ const SwTextFormatColl* pSplitColl )
+{
+ return SplitDoc( SPLITDOC_TO_GLOBALDOC, rPath, false, pSplitColl );
+}
+
+bool SwDoc::GenerateGlobalDoc( const OUString& rPath, int nOutlineLevel )
+{
+ return SplitDoc( SPLITDOC_TO_GLOBALDOC, rPath, true, nullptr, nOutlineLevel );
+}
+
+bool SwDoc::GenerateHTMLDoc( const OUString& rPath, int nOutlineLevel )
+{
+ return SplitDoc( SPLITDOC_TO_HTML, rPath, true, nullptr, nOutlineLevel );
+}
+
+bool SwDoc::GenerateHTMLDoc( const OUString& rPath,
+ const SwTextFormatColl* pSplitColl )
+{
+ return SplitDoc( SPLITDOC_TO_HTML, rPath, false, pSplitColl );
+}
+
+// two helpers for outline mode
+static SwNode* GetStartNode( SwOutlineNodes const * pOutlNds, int nOutlineLevel, SwOutlineNodes::size_type* nOutl )
+{
+ for( ; *nOutl < pOutlNds->size(); ++(*nOutl) )
+ {
+ SwNode* pNd = (*pOutlNds)[ *nOutl ];
+ if( pNd->GetTextNode()->GetAttrOutlineLevel() == nOutlineLevel && !pNd->FindTableNode() )
+ {
+ return pNd;
+ }
+ }
+
+ return nullptr;
+}
+
+static SwNode* GetEndNode( SwOutlineNodes const * pOutlNds, int nOutlineLevel, SwOutlineNodes::size_type* nOutl )
+{
+ SwNode* pNd;
+
+ for( ++(*nOutl); (*nOutl) < pOutlNds->size(); ++(*nOutl) )
+ {
+ pNd = (*pOutlNds)[ *nOutl ];
+
+ const int nLevel = pNd->GetTextNode()->GetAttrOutlineLevel();
+
+ if( ( 0 < nLevel && nLevel <= nOutlineLevel ) &&
+ !pNd->FindTableNode() )
+ {
+ return pNd;
+ }
+ }
+ return nullptr;
+}
+
+// two helpers for collection mode
+static SwNode* GetStartNode( const SwOutlineNodes* pOutlNds, const SwTextFormatColl* pSplitColl, SwOutlineNodes::size_type* nOutl )
+{
+ for( ; *nOutl < pOutlNds->size(); ++(*nOutl) )
+ {
+ SwNode* pNd = (*pOutlNds)[ *nOutl ];
+ if( pNd->GetTextNode()->GetTextColl() == pSplitColl &&
+ !pNd->FindTableNode() )
+ {
+ return pNd;
+ }
+ }
+ return nullptr;
+}
+
+static SwNode* GetEndNode( const SwOutlineNodes* pOutlNds, const SwTextFormatColl* pSplitColl, SwOutlineNodes::size_type* nOutl )
+{
+ SwNode* pNd;
+
+ for( ++(*nOutl); *nOutl < pOutlNds->size(); ++(*nOutl) )
+ {
+ pNd = (*pOutlNds)[ *nOutl ];
+ SwTextFormatColl* pTColl = pNd->GetTextNode()->GetTextColl();
+
+ if( ( pTColl == pSplitColl ||
+ ( pSplitColl->GetAttrOutlineLevel() > 0 &&
+ pTColl->GetAttrOutlineLevel() > 0 &&
+ pTColl->GetAttrOutlineLevel() <
+ pSplitColl->GetAttrOutlineLevel() )) &&
+ !pNd->FindTableNode() )
+ {
+ return pNd;
+ }
+ }
+ return nullptr;
+}
+
+bool SwDoc::SplitDoc( sal_uInt16 eDocType, const OUString& rPath, bool bOutline, const SwTextFormatColl* pSplitColl, int nOutlineLevel )
+{
+ // Iterate over all the template's Nodes, creating an own
+ // document for every single one and replace linked sections (GlobalDoc) for links (HTML).
+ // Finally, we save this document as a GlobalDoc/HTMLDoc.
+ if( !mpDocShell || !mpDocShell->GetMedium() ||
+ ( SPLITDOC_TO_GLOBALDOC == eDocType && GetDocumentSettingManager().get(DocumentSettingId::GLOBAL_DOCUMENT) ) )
+ return false;
+
+ SwOutlineNodes::size_type nOutl = 0;
+ SwOutlineNodes* pOutlNds = const_cast<SwOutlineNodes*>(&GetNodes().GetOutLineNds());
+ std::unique_ptr<SwOutlineNodes> xTmpOutlNds;
+ SwNode* pStartNd;
+
+ if ( !bOutline) {
+ if( pSplitColl )
+ {
+ // If it isn't an OutlineNumbering, then use an own array and collect the Nodes.
+ if( pSplitColl->GetAttrOutlineLevel() == 0 )
+ {
+ xTmpOutlNds.reset(new SwOutlineNodes);
+ pOutlNds = xTmpOutlNds.get();
+ SwIterator<SwTextNode,SwFormatColl> aIter( *pSplitColl );
+ for( SwTextNode* pTNd = aIter.First(); pTNd; pTNd = aIter.Next() )
+ if( pTNd->GetNodes().IsDocNodes() )
+ pOutlNds->insert( pTNd );
+
+ if( pOutlNds->empty() )
+ return false;
+ }
+ }
+ else
+ {
+ // Look for the 1st level OutlineTemplate
+ const SwTextFormatColls& rFormatColls =*GetTextFormatColls();
+ for( SwTextFormatColls::size_type n = rFormatColls.size(); n; )
+ if ( rFormatColls[ --n ]->GetAttrOutlineLevel() == 1 )
+ {
+ pSplitColl = rFormatColls[ n ];
+ break;
+ }
+
+ if( !pSplitColl )
+ return false;
+ }
+ }
+
+ std::shared_ptr<const SfxFilter> pFilter;
+ switch( eDocType )
+ {
+ case SPLITDOC_TO_HTML:
+ pFilter = SwIoSystem::GetFilterOfFormat(u"HTML");
+ break;
+
+ default:
+ pFilter = SwIoSystem::GetFilterOfFormat(FILTER_XML);
+ eDocType = SPLITDOC_TO_GLOBALDOC;
+ break;
+ }
+
+ if( !pFilter )
+ return false;
+
+ // Deactivate Undo/Redline in any case
+ GetIDocumentUndoRedo().DoUndo(false);
+ getIDocumentRedlineAccess().SetRedlineFlags_intern( getIDocumentRedlineAccess().GetRedlineFlags() & ~RedlineFlags::On );
+
+ OUString sExt = pFilter->GetSuffixes().getToken(0, ',');
+ if( sExt.isEmpty() )
+ {
+ sExt = ".sxw";
+ }
+ else
+ {
+ if( '.' != sExt[ 0 ] )
+ {
+ sExt = "." + sExt;
+ }
+ }
+
+ INetURLObject aEntry(rPath);
+ OUString sLeading(aEntry.GetBase());
+ aEntry.removeSegment();
+ OUString sPath = aEntry.GetMainURL( INetURLObject::DecodeMechanism::NONE );
+ utl::TempFileNamed aTemp(sLeading, true, sExt, &sPath);
+ aTemp.EnableKillingFile();
+
+ DateTime aTmplDate( DateTime::SYSTEM );
+ {
+ tools::Time a2Min( 0 ); a2Min.SetMin( 2 );
+ aTmplDate += a2Min;
+ }
+
+ // Skip all invalid ones
+ while( nOutl < pOutlNds->size() &&
+ (*pOutlNds)[ nOutl ]->GetIndex() < GetNodes().GetEndOfExtras().GetIndex() )
+ ++nOutl;
+
+ do {
+ if( bOutline )
+ pStartNd = GetStartNode( pOutlNds, nOutlineLevel, &nOutl );
+ else
+ pStartNd = GetStartNode( pOutlNds, pSplitColl, &nOutl );
+
+ if( pStartNd )
+ {
+ SwNode* pEndNd;
+ if( bOutline )
+ pEndNd = GetEndNode( pOutlNds, nOutlineLevel, &nOutl );
+ else
+ pEndNd = GetEndNode( pOutlNds, pSplitColl, &nOutl );
+ SwNodeIndex aEndIdx( pEndNd ? *pEndNd
+ : GetNodes().GetEndOfContent() );
+
+ // Write out the Nodes completely
+ OUString sFileName;
+ if( pStartNd->GetIndex() + 1 < aEndIdx.GetIndex() )
+ {
+ SfxObjectShellLock xDocSh( new SwDocShell( SfxObjectCreateMode::INTERNAL ));
+ if( xDocSh->DoInitNew() )
+ {
+ SwDoc* pDoc = static_cast<SwDocShell*>(&xDocSh)->GetDoc();
+
+ uno::Reference<document::XDocumentPropertiesSupplier> xDPS(
+ static_cast<SwDocShell*>(&xDocSh)->GetModel(),
+ uno::UNO_QUERY_THROW);
+ uno::Reference<document::XDocumentProperties> xDocProps(
+ xDPS->getDocumentProperties());
+ OSL_ENSURE(xDocProps.is(), "Doc has no DocumentProperties");
+ // the GlobalDoc is the template
+ xDocProps->setTemplateName(OUString());
+ ::util::DateTime uDT = aTmplDate.GetUNODateTime();
+ xDocProps->setTemplateDate(uDT);
+ xDocProps->setTemplateURL(rPath);
+ // Set the new doc's title to the text of the "split para".
+ // If the current doc has a title, insert it at the begin.
+ OUString sTitle( xDocProps->getTitle() );
+ if (!sTitle.isEmpty())
+ sTitle += ": ";
+ sTitle += pStartNd->GetTextNode()->GetExpandText(nullptr);
+ xDocProps->setTitle( sTitle );
+
+ // Replace template
+ pDoc->ReplaceStyles( *this );
+
+ // Take over chapter numbering
+ if( mpOutlineRule )
+ pDoc->SetOutlineNumRule( *mpOutlineRule );
+
+ SwNodeRange aRg( *pStartNd, SwNodeOffset(0), aEndIdx.GetNode() );
+ GetDocumentContentOperationsManager().CopyWithFlyInFly(
+ aRg, pDoc->GetNodes().GetEndOfContent(), nullptr, false, false);
+
+ // Delete the initial TextNode
+ SwNodeIndex aIdx( pDoc->GetNodes().GetEndOfExtras(), 2 );
+ if( aIdx.GetIndex() + 1 !=
+ pDoc->GetNodes().GetEndOfContent().GetIndex() )
+ pDoc->GetNodes().Delete( aIdx );
+
+ sFileName = utl::CreateTempURL(sLeading, true, sExt, &sPath);
+ SfxMedium* pTmpMed = new SfxMedium( sFileName,
+ StreamMode::STD_READWRITE );
+ pTmpMed->SetFilter( pFilter );
+
+ // We need to have a Layout for the HTMLFilter, so that
+ // TextFrames/Controls/OLE objects can be exported correctly as graphics.
+ if( SPLITDOC_TO_HTML == eDocType &&
+ !pDoc->GetSpzFrameFormats()->empty() )
+ {
+ SfxViewFrame::LoadHiddenDocument( *xDocSh, SFX_INTERFACE_NONE );
+ }
+ xDocSh->DoSaveAs( *pTmpMed );
+ xDocSh->DoSaveCompleted( pTmpMed );
+
+ // do not insert a FileLinkSection in case of error
+ if( xDocSh->GetErrorIgnoreWarning() )
+ sFileName.clear();
+ }
+ xDocSh->DoClose();
+ }
+
+ // We can now insert the section
+ if( !sFileName.isEmpty() )
+ {
+ switch( eDocType )
+ {
+ case SPLITDOC_TO_HTML:
+ {
+ // Delete all nodes in the section and, in the "start node",
+ // set the Link to the saved document.
+ SwNodeOffset nNodeDiff = aEndIdx.GetIndex() -
+ pStartNd->GetIndex() - 1;
+ if( nNodeDiff )
+ {
+ SwPaM aTmp( *pStartNd, aEndIdx.GetNode(), SwNodeOffset(1), SwNodeOffset(-1) );
+ SwNodeIndex aSIdx( aTmp.GetMark()->GetNode() );
+ SwNodeIndex aEIdx( aTmp.GetPoint()->GetNode() );
+
+ // Try to move past the end
+ if( !aTmp.Move( fnMoveForward, GoInNode ) )
+ {
+ // well then, back to the beginning
+ aTmp.Exchange();
+ if( !aTmp.Move( fnMoveBackward, GoInNode ))
+ {
+ OSL_FAIL( "no more Nodes!" );
+ }
+ }
+ // Move Bookmarks and so forth
+ CorrAbs( aSIdx, aEIdx, *aTmp.GetPoint(), true);
+
+ // If FlyFrames are still around, delete these too
+ auto& rSpzs = *GetSpzFrameFormats();
+ for(sw::FrameFormats<sw::SpzFrameFormat*>::size_type n = 0; n < GetSpzFrameFormats()->size(); ++n)
+ {
+ auto pFly = rSpzs[n];
+ const SwFormatAnchor* pAnchor = &pFly->GetAnchor();
+ SwNode const*const pAnchorNode =
+ pAnchor->GetAnchorNode();
+ if (pAnchorNode &&
+ ((RndStdIds::FLY_AT_PARA == pAnchor->GetAnchorId()) ||
+ (RndStdIds::FLY_AT_CHAR == pAnchor->GetAnchorId())) &&
+ aSIdx <= *pAnchorNode &&
+ *pAnchorNode < aEIdx.GetNode() )
+ {
+ getIDocumentLayoutAccess().DelLayoutFormat( pFly );
+ --n;
+ }
+ }
+
+ GetNodes().Delete( aSIdx, nNodeDiff );
+ }
+
+ // set the link in the StartNode
+ SwFormatINetFormat aINet( sFileName , OUString() );
+ SwTextNode* pTNd = pStartNd->GetTextNode();
+ pTNd->InsertItem(aINet, 0, pTNd->GetText().getLength());
+
+ // If the link cannot be found anymore,
+ // it has to be a bug!
+ if( !pOutlNds->Seek_Entry( pStartNd, &nOutl ))
+ pStartNd = nullptr;
+ ++nOutl ;
+ }
+ break;
+
+ default:
+ {
+ const OUString sNm(INetURLObject(sFileName).GetLastName());
+ SwSectionData aSectData( SectionType::FileLink,
+ GetUniqueSectionName( &sNm ));
+ SwSectionFormat* pFormat = MakeSectionFormat();
+ aSectData.SetLinkFileName(sFileName);
+ aSectData.SetProtectFlag(true);
+
+ --aEndIdx; // in the InsertSection the end is inclusive
+ while( aEndIdx.GetNode().IsStartNode() )
+ --aEndIdx;
+
+ // If any Section ends or starts in the new sectionrange,
+ // they must end or start before or after the range!
+ SwSectionNode* pSectNd = pStartNd->FindSectionNode();
+ while( pSectNd && pSectNd->EndOfSectionIndex()
+ <= aEndIdx.GetIndex() )
+ {
+ const SwNode* pSectEnd = pSectNd->EndOfSectionNode();
+ if( pSectNd->GetIndex() + 1 ==
+ pStartNd->GetIndex() )
+ {
+ bool bMvIdx = aEndIdx == *pSectEnd;
+ DelSectionFormat( pSectNd->GetSection().GetFormat() );
+ if( bMvIdx )
+ --aEndIdx;
+ }
+ else
+ {
+ SwNodeRange aRg( *pStartNd, *pSectEnd );
+ SwNodeIndex aIdx( *pSectEnd, 1 );
+ GetNodes().MoveNodes( aRg, GetNodes(), aIdx.GetNode() );
+ }
+ pSectNd = pStartNd->FindSectionNode();
+ }
+
+ pSectNd = aEndIdx.GetNode().FindSectionNode();
+ while( pSectNd && pSectNd->GetIndex() >
+ pStartNd->GetIndex() )
+ {
+ // #i15712# don't attempt to split sections if
+ // they are fully enclosed in [pSectNd,aEndIdx].
+ if( aEndIdx < pSectNd->EndOfSectionIndex() )
+ {
+ SwNodeRange aRg( *pSectNd, SwNodeOffset(1), aEndIdx.GetNode(), SwNodeOffset(1) );
+ GetNodes().MoveNodes( aRg, GetNodes(), *pSectNd );
+ }
+
+ pSectNd = pStartNd->FindSectionNode();
+ }
+
+ // -> #i26762#
+ // Ensure order of start and end of section is sane.
+ SwNodeIndex aStartIdx(*pStartNd);
+
+ if (aEndIdx >= aStartIdx)
+ {
+ pSectNd = GetNodes().InsertTextSection(aStartIdx.GetNode(),
+ *pFormat, aSectData, nullptr, &aEndIdx.GetNode(), false);
+ }
+ else
+ {
+ pSectNd = GetNodes().InsertTextSection(aEndIdx.GetNode(),
+ *pFormat, aSectData, nullptr, &aStartIdx.GetNode(), false);
+ }
+ // <- #i26762#
+
+ pSectNd->GetSection().CreateLink( LinkCreateType::Connect );
+ }
+ break;
+ }
+ }
+ }
+ } while( pStartNd );
+
+ xTmpOutlNds.reset();
+
+ switch( eDocType )
+ {
+ case SPLITDOC_TO_HTML:
+ if( GetDocumentSettingManager().get(DocumentSettingId::GLOBAL_DOCUMENT) )
+ {
+ // save all remaining sections
+ while( !GetSections().empty() )
+ DelSectionFormat( GetSections().front() );
+
+ SfxFilterContainer* pFCntnr = mpDocShell->GetFactory().GetFilterContainer();
+ pFilter = pFCntnr->GetFilter4EA( pFilter->GetTypeName(), SfxFilterFlags::EXPORT );
+ }
+ break;
+
+ default:
+ // save the Globaldoc
+ GetDocumentSettingManager().set(DocumentSettingId::GLOBAL_DOCUMENT, true);
+ GetDocumentSettingManager().set(DocumentSettingId::GLOBAL_DOCUMENT_SAVE_LINKS, false);
+ }
+
+ // The medium isn't locked after reopening the document.
+ SfxRequest aReq( SID_SAVEASDOC, SfxCallMode::SYNCHRON, GetAttrPool() );
+ aReq.AppendItem( SfxStringItem( SID_FILE_NAME, rPath ) );
+ aReq.AppendItem( SfxBoolItem( SID_SAVETO, true ) );
+ if(pFilter)
+ aReq.AppendItem( SfxStringItem( SID_FILTER_NAME, pFilter->GetName() ) );
+ const SfxPoolItemHolder& rResult(mpDocShell->ExecuteSlot(aReq));
+ const SfxBoolItem *pRet(static_cast<const SfxBoolItem*>(rResult.getItem()));
+
+ return pRet && pRet->GetValue();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/docglos.cxx b/sw/source/core/doc/docglos.cxx
new file mode 100644
index 0000000000..2993a774ad
--- /dev/null
+++ b/sw/source/core/doc/docglos.cxx
@@ -0,0 +1,219 @@
+/* -*- 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 <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
+#include <com/sun/star/document/XDocumentProperties.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/beans/XPropertySetInfo.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <osl/diagnose.h>
+
+#include <doc.hxx>
+#include <IDocumentContentOperations.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <IDocumentFieldsAccess.hxx>
+#include <shellio.hxx>
+#include <pam.hxx>
+#include <swundo.hxx>
+#include <acorrect.hxx>
+#include <crsrsh.hxx>
+#include <docsh.hxx>
+
+using namespace ::com::sun::star;
+
+void SwDoc::ReplaceUserDefinedDocumentProperties(
+ const uno::Reference<document::XDocumentProperties>& xSourceDocProps)
+{
+ OSL_ENSURE(xSourceDocProps.is(), "null reference");
+
+ uno::Reference<document::XDocumentPropertiesSupplier> xDPS(
+ GetDocShell()->GetModel(), uno::UNO_QUERY_THROW);
+ uno::Reference<document::XDocumentProperties> xDocProps(
+ xDPS->getDocumentProperties() );
+ OSL_ENSURE(xDocProps.is(), "null reference");
+
+ uno::Reference<beans::XPropertySet> xSourceUDSet(
+ xSourceDocProps->getUserDefinedProperties(), uno::UNO_QUERY_THROW);
+ uno::Reference<beans::XPropertyContainer> xTargetUD(
+ xDocProps->getUserDefinedProperties());
+ uno::Reference<beans::XPropertySet> xTargetUDSet(xTargetUD,
+ uno::UNO_QUERY_THROW);
+ const uno::Sequence<beans::Property> tgtprops
+ = xTargetUDSet->getPropertySetInfo()->getProperties();
+
+ for (const auto& rTgtProp : tgtprops) {
+ try {
+ xTargetUD->removeProperty(rTgtProp.Name);
+ } catch (uno::Exception &) {
+ // ignore
+ }
+ }
+
+ uno::Reference<beans::XPropertySetInfo> xSetInfo
+ = xSourceUDSet->getPropertySetInfo();
+ const uno::Sequence<beans::Property> srcprops = xSetInfo->getProperties();
+
+ for (const auto& rSrcProp : srcprops) {
+ try {
+ OUString name = rSrcProp.Name;
+ xTargetUD->addProperty(name, rSrcProp.Attributes,
+ xSourceUDSet->getPropertyValue(name));
+ } catch (uno::Exception &) {
+ // ignore
+ }
+ }
+}
+
+void SwDoc::ReplaceDocumentProperties(const SwDoc& rSource, bool mailMerge)
+{
+ uno::Reference<document::XDocumentPropertiesSupplier> xSourceDPS(
+ rSource.GetDocShell()->GetModel(), uno::UNO_QUERY_THROW);
+ uno::Reference<document::XDocumentProperties> xSourceDocProps(
+ xSourceDPS->getDocumentProperties() );
+ OSL_ENSURE(xSourceDocProps.is(), "null reference");
+
+ uno::Reference<document::XDocumentPropertiesSupplier> xDPS(
+ GetDocShell()->GetModel(), uno::UNO_QUERY_THROW);
+ uno::Reference<document::XDocumentProperties> xDocProps(
+ xDPS->getDocumentProperties() );
+ OSL_ENSURE(xDocProps.is(), "null reference");
+
+ xDocProps->setAuthor(xSourceDocProps->getAuthor());
+ xDocProps->setGenerator(xSourceDocProps->getGenerator());
+ xDocProps->setCreationDate(xSourceDocProps->getCreationDate());
+ xDocProps->setTitle(xSourceDocProps->getTitle());
+ xDocProps->setSubject(xSourceDocProps->getSubject());
+ xDocProps->setDescription(xSourceDocProps->getDescription());
+ xDocProps->setKeywords(xSourceDocProps->getKeywords());
+ xDocProps->setLanguage(xSourceDocProps->getLanguage());
+ // Note: These below originally weren't copied for mailmerge, but I don't see why not.
+ xDocProps->setModifiedBy(xSourceDocProps->getModifiedBy());
+ xDocProps->setModificationDate(xSourceDocProps->getModificationDate());
+ xDocProps->setPrintedBy(xSourceDocProps->getPrintedBy());
+ xDocProps->setPrintDate(xSourceDocProps->getPrintDate());
+ xDocProps->setTemplateName(xSourceDocProps->getTemplateName());
+ xDocProps->setTemplateURL(xSourceDocProps->getTemplateURL());
+ xDocProps->setTemplateDate(xSourceDocProps->getTemplateDate());
+ xDocProps->setAutoloadURL(xSourceDocProps->getAutoloadURL());
+ xDocProps->setAutoloadSecs(xSourceDocProps->getAutoloadSecs());
+ xDocProps->setDefaultTarget(xSourceDocProps->getDefaultTarget());
+ xDocProps->setDocumentStatistics(xSourceDocProps->getDocumentStatistics());
+ xDocProps->setEditingCycles(xSourceDocProps->getEditingCycles());
+ xDocProps->setEditingDuration(xSourceDocProps->getEditingDuration());
+
+ if( mailMerge ) // Note: Not sure this is needed.
+ {
+ // Manually set the creation date, otherwise author field isn't filled
+ // during MM, as it's set when saving the document the first time.
+ xDocProps->setCreationDate( xSourceDocProps->getModificationDate() );
+ }
+
+ ReplaceUserDefinedDocumentProperties( xSourceDocProps );
+}
+
+/// inserts an AutoText block
+bool SwDoc::InsertGlossary( SwTextBlocks& rBlock, const OUString& rEntry,
+ SwPaM& rPaM, SwCursorShell* pShell )
+{
+ bool bRet = false;
+ const sal_uInt16 nIdx = rBlock.GetIndex( rEntry );
+ if( USHRT_MAX != nIdx )
+ {
+ bool bSav_IsInsGlossary = mbInsOnlyTextGlssry;
+ mbInsOnlyTextGlssry = rBlock.IsOnlyTextBlock( nIdx );
+
+ if( rBlock.BeginGetDoc( nIdx ) )
+ {
+ SwDoc* pGDoc = rBlock.GetDoc();
+
+ // tdf#53023 - remove the last empty paragraph (check SwXMLTextBlockParContext dtor)
+ if (mbInsOnlyTextGlssry)
+ {
+ SwPaM aPaM(*pGDoc->GetNodes()[pGDoc->GetNodes().GetEndOfContent().GetIndex() - 1]);
+ pGDoc->getIDocumentContentOperations().DelFullPara(aPaM);
+ }
+
+ // Update all fixed fields, with the right DocInfo.
+ // FIXME: UGLY: Because we cannot limit the range in which to do
+ // field updates, we must update the fixed fields at the glossary
+ // entry document.
+ // To be able to do this, we copy the document properties of the
+ // target document to the glossary document
+ // OSL_ENSURE(GetDocShell(), "no SwDocShell"); // may be clipboard!
+ OSL_ENSURE(pGDoc->GetDocShell(), "no SwDocShell at glossary");
+ if (GetDocShell() && pGDoc->GetDocShell())
+ pGDoc->ReplaceDocumentProperties( *this );
+ pGDoc->getIDocumentFieldsAccess().SetFixFields(nullptr);
+
+ // StartAllAction();
+ getIDocumentFieldsAccess().LockExpFields();
+
+ SwNodeIndex aStt( pGDoc->GetNodes().GetEndOfExtras(), 1 );
+ SwContentNode* pContentNd = pGDoc->GetNodes().GoNext( &aStt );
+ const SwTableNode* pTableNd = pContentNd->FindTableNode();
+ SwPaM aCpyPam( pTableNd ? *const_cast<SwNode*>(static_cast<SwNode const *>(pTableNd)) : *static_cast<SwNode*>(pContentNd) );
+ aCpyPam.SetMark();
+
+ // till the nodes array's end
+ aCpyPam.GetPoint()->Assign( pGDoc->GetNodes().GetEndOfContent().GetIndex()-SwNodeOffset(1) );
+ pContentNd = aCpyPam.GetPointContentNode();
+ if (pContentNd)
+ aCpyPam.GetPoint()->SetContent( pContentNd->Len() );
+
+ GetIDocumentUndoRedo().StartUndo( SwUndoId::INSGLOSSARY, nullptr );
+ SwPaM *_pStartCursor = &rPaM, *_pStartCursor2 = _pStartCursor;
+ do {
+
+ SwPosition& rInsPos = *_pStartCursor->GetPoint();
+ SwStartNode* pBoxSttNd = const_cast<SwStartNode*>(rInsPos.GetNode().
+ FindTableBoxStartNode());
+
+ if( pBoxSttNd && SwNodeOffset(2) == pBoxSttNd->EndOfSectionIndex() -
+ pBoxSttNd->GetIndex() &&
+ aCpyPam.GetPoint()->GetNode() != aCpyPam.GetMark()->GetNode() )
+ {
+ // We copy more than one Node to the current Box.
+ // However, we have to remove the BoxAttributes then.
+ ClearBoxNumAttrs( rInsPos.GetNode() );
+ }
+
+ SwDontExpandItem aACD;
+ aACD.SaveDontExpandItems( rInsPos );
+
+ pGDoc->getIDocumentContentOperations().CopyRange(aCpyPam, rInsPos, SwCopyFlags::CheckPosInFly);
+
+ aACD.RestoreDontExpandItems( rInsPos );
+ if( pShell )
+ pShell->SaveTableBoxContent( &rInsPos );
+ } while( (_pStartCursor = _pStartCursor->GetNext()) !=
+ _pStartCursor2 );
+ GetIDocumentUndoRedo().EndUndo( SwUndoId::INSGLOSSARY, nullptr );
+
+ getIDocumentFieldsAccess().UnlockExpFields();
+ if( !getIDocumentFieldsAccess().IsExpFieldsLocked() )
+ getIDocumentFieldsAccess().UpdateExpFields(nullptr, true);
+ bRet = true;
+ }
+ mbInsOnlyTextGlssry = bSav_IsInsGlossary;
+ }
+ rBlock.EndGetDoc();
+ return bRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/doclay.cxx b/sw/source/core/doc/doclay.cxx
new file mode 100644
index 0000000000..2ecea30dc3
--- /dev/null
+++ b/sw/source/core/doc/doclay.cxx
@@ -0,0 +1,1703 @@
+/* -*- 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 <hintids.hxx>
+#include <sot/exchange.hxx>
+#include <svx/svdpage.hxx>
+#include <editeng/keepitem.hxx>
+#include <editeng/ulspitem.hxx>
+#include <editeng/lrspitem.hxx>
+#include <editeng/boxitem.hxx>
+#include <editeng/shaditem.hxx>
+#include <editeng/protitem.hxx>
+#include <editeng/opaqitem.hxx>
+#include <osl/diagnose.h>
+#include <svx/svdouno.hxx>
+#include <editeng/frmdiritem.hxx>
+#include <istype.hxx>
+#include <swmodule.hxx>
+#include <modcfg.hxx>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/embed/XEmbeddedObject.hpp>
+#include <SwStyleNameMapper.hxx>
+#include <drawdoc.hxx>
+#include <fchrfmt.hxx>
+#include <frmatr.hxx>
+#include <txatbase.hxx>
+#include <fmtfld.hxx>
+#include <fmtornt.hxx>
+#include <fmtcntnt.hxx>
+#include <fmtanchr.hxx>
+#include <fmtfsize.hxx>
+#include <fmtsrnd.hxx>
+#include <fmtflcnt.hxx>
+#include <frmfmt.hxx>
+#include <pam.hxx>
+#include <ndtxt.hxx>
+#include <ndnotxt.hxx>
+#include <ndole.hxx>
+#include <doc.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <DocumentSettingManager.hxx>
+#include <IDocumentDrawModelAccess.hxx>
+#include <IDocumentFieldsAccess.hxx>
+#include <IDocumentState.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <IDocumentStylePoolAccess.hxx>
+#include <rootfrm.hxx>
+#include <pagefrm.hxx>
+#include <cntfrm.hxx>
+#include <txtfrm.hxx>
+#include <notxtfrm.hxx>
+#include <dflyobj.hxx>
+#include <dcontact.hxx>
+#include <swundo.hxx>
+#include <flypos.hxx>
+#include <UndoInsert.hxx>
+#include <expfld.hxx>
+#include <poolfmt.hxx>
+#include <docary.hxx>
+#include <swtable.hxx>
+#include <tblsel.hxx>
+#include <txtftn.hxx>
+#include <ftnidx.hxx>
+#include <ftninfo.hxx>
+#include <pagedesc.hxx>
+#include <strings.hrc>
+#include <frameformats.hxx>
+#include <tools/datetimeutils.hxx>
+#include <comphelper/string.hxx>
+#include <o3tl/string_view.hxx>
+
+#include <sortedobjs.hxx>
+
+#include <string_view>
+#include <vector>
+
+using namespace ::com::sun::star;
+
+#define DEF_FLY_WIDTH 2268 // Default width for FlyFrames (2268 == 4cm)
+
+static bool lcl_IsItemSet(const SwContentNode & rNode, sal_uInt16 which)
+{
+ bool bResult = false;
+
+ if (SfxItemState::SET == rNode.GetSwAttrSet().GetItemState(which))
+ bResult = true;
+
+ return bResult;
+}
+
+rtl::Reference<SdrObject> SwDoc::CloneSdrObj( const SdrObject& rObj, bool bMoveWithinDoc,
+ bool bInsInPage )
+{
+ // #i52858# - method name changed
+ SdrPage *pPg = getIDocumentDrawModelAccess().GetOrCreateDrawModel()->GetPage( 0 );
+ if( !pPg )
+ {
+ auto pNewPage = getIDocumentDrawModelAccess().GetDrawModel()->AllocPage( false );
+ getIDocumentDrawModelAccess().GetDrawModel()->InsertPage( pNewPage.get() );
+ pPg = pNewPage.get();
+ }
+
+ // TTTT Clone directly to target SdrModel
+ rtl::Reference<SdrObject> pObj(rObj.CloneSdrObject(*getIDocumentDrawModelAccess().GetDrawModel()));
+
+ if( bMoveWithinDoc && SdrInventor::FmForm == pObj->GetObjInventor() )
+ {
+ // We need to preserve the Name for Controls
+ uno::Reference< awt::XControlModel > xModel = static_cast<SdrUnoObj*>(pObj.get())->GetUnoControlModel();
+ uno::Any aVal;
+ uno::Reference< beans::XPropertySet > xSet(xModel, uno::UNO_QUERY);
+ static constexpr OUString sName(u"Name"_ustr);
+ if( xSet.is() )
+ aVal = xSet->getPropertyValue( sName );
+ if( bInsInPage )
+ pPg->InsertObjectThenMakeNameUnique( pObj.get() );
+ if( xSet.is() )
+ xSet->setPropertyValue( sName, aVal );
+ }
+ else if( bInsInPage )
+ pPg->InsertObjectThenMakeNameUnique( pObj.get() );
+
+ // For drawing objects: set layer of cloned object to invisible layer
+ SdrLayerID nLayerIdForClone = rObj.GetLayer();
+ if ( dynamic_cast<const SwFlyDrawObj*>( pObj.get() ) == nullptr &&
+ dynamic_cast<const SwVirtFlyDrawObj*>( pObj.get() ) == nullptr &&
+ pObj->GetObjIdentifier() != SdrObjKind::NewFrame )
+ {
+ if ( getIDocumentDrawModelAccess().IsVisibleLayerId( nLayerIdForClone ) )
+ {
+ nLayerIdForClone = getIDocumentDrawModelAccess().GetInvisibleLayerIdByVisibleOne( nLayerIdForClone );
+ }
+ }
+ pObj->SetLayer( nLayerIdForClone );
+
+ return pObj;
+}
+
+SwFlyFrameFormat* SwDoc::MakeFlySection_( const SwPosition& rAnchPos,
+ const SwContentNode& rNode,
+ RndStdIds eRequestId,
+ const SfxItemSet* pFlySet,
+ SwFrameFormat* pFrameFormat )
+{
+ if( !pFrameFormat )
+ pFrameFormat = getIDocumentStylePoolAccess().GetFrameFormatFromPool( RES_POOLFRM_FRAME );
+
+ OUString sName;
+ switch( rNode.GetNodeType() )
+ {
+ case SwNodeType::Grf: sName = GetUniqueGrfName(); break;
+ case SwNodeType::Ole: sName = GetUniqueOLEName(); break;
+ default: sName = GetUniqueFrameName(); break;
+ }
+ SwFlyFrameFormat* pFormat = MakeFlyFrameFormat( sName, pFrameFormat );
+
+ // Create content and connect to the format.
+ // Create ContentNode and put it into the autotext selection.
+ SwNodeRange aRange( GetNodes().GetEndOfAutotext(), SwNodeOffset(-1),
+ GetNodes().GetEndOfAutotext() );
+ GetNodes().SectionDown( &aRange, SwFlyStartNode );
+
+ pFormat->SetFormatAttr( SwFormatContent( rNode.StartOfSectionNode() ));
+
+ const SwFormatAnchor* pAnchor = nullptr;
+ if( pFlySet )
+ {
+ pAnchor = pFlySet->GetItemIfSet( RES_ANCHOR, false );
+ if( SfxItemState::SET == pFlySet->GetItemState( RES_CNTNT, false ))
+ {
+ SfxItemSet aTmpSet( *pFlySet );
+ aTmpSet.ClearItem( RES_CNTNT );
+ pFormat->SetFormatAttr( aTmpSet );
+ }
+ else
+ pFormat->SetFormatAttr( *pFlySet );
+ }
+
+ // Anchor not yet set?
+ RndStdIds eAnchorId;
+ // #i107811# Assure that at-page anchored fly frames have a page num or a
+ // content anchor set.
+ if ( !pAnchor ||
+ ( RndStdIds::FLY_AT_PAGE != pAnchor->GetAnchorId() &&
+ !pAnchor->GetAnchorNode() ) ||
+ ( RndStdIds::FLY_AT_PAGE == pAnchor->GetAnchorId() &&
+ !pAnchor->GetAnchorNode() &&
+ pAnchor->GetPageNum() == 0 ) )
+ {
+ // set it again, needed for Undo
+ SwFormatAnchor aAnch( pFormat->GetAnchor() );
+ if (pAnchor && (RndStdIds::FLY_AT_FLY == pAnchor->GetAnchorId()))
+ {
+ SwPosition aPos( *rAnchPos.GetNode().FindFlyStartNode() );
+ aAnch.SetAnchor( &aPos );
+ eAnchorId = RndStdIds::FLY_AT_FLY;
+ }
+ else
+ {
+ if( eRequestId != aAnch.GetAnchorId() &&
+ SfxItemState::SET != pFormat->GetItemState( RES_ANCHOR ) )
+ {
+ aAnch.SetType( eRequestId );
+ }
+
+ eAnchorId = aAnch.GetAnchorId();
+ if ( RndStdIds::FLY_AT_PAGE != eAnchorId || !pAnchor || aAnch.GetPageNum() == 0)
+ {
+ aAnch.SetAnchor( &rAnchPos );
+ }
+ }
+ pFormat->SetFormatAttr( aAnch );
+ }
+ else
+ eAnchorId = pFormat->GetAnchor().GetAnchorId();
+
+ if ( RndStdIds::FLY_AS_CHAR == eAnchorId )
+ {
+ const sal_Int32 nStt = rAnchPos.GetContentIndex();
+ SwTextNode * pTextNode = rAnchPos.GetNode().GetTextNode();
+
+ OSL_ENSURE(pTextNode!= nullptr, "There should be a SwTextNode!");
+
+ if (pTextNode != nullptr)
+ {
+ SwFormatFlyCnt aFormat( pFormat );
+ // may fail if there's no space left or header/ftr
+ if (!pTextNode->InsertItem(aFormat, nStt, nStt))
+ { // pFormat is dead now
+ return nullptr;
+ }
+ }
+ }
+
+ if( SfxItemState::SET != pFormat->GetAttrSet().GetItemState( RES_FRM_SIZE ))
+ {
+ SwFormatFrameSize aFormatSize( SwFrameSize::Variable, 0, DEF_FLY_WIDTH );
+ const SwNoTextNode* pNoTextNode = rNode.GetNoTextNode();
+ if( pNoTextNode )
+ {
+ // Set size
+ Size aSize( pNoTextNode->GetTwipSize() );
+ if( MINFLY > aSize.Width() )
+ aSize.setWidth( DEF_FLY_WIDTH );
+ aFormatSize.SetWidth( aSize.Width() );
+ if( aSize.Height() )
+ {
+ aFormatSize.SetHeight( aSize.Height() );
+ aFormatSize.SetHeightSizeType( SwFrameSize::Fixed );
+ }
+ }
+ pFormat->SetFormatAttr( aFormatSize );
+ }
+
+ // Set up frames
+ if( getIDocumentLayoutAccess().GetCurrentViewShell() )
+ pFormat->MakeFrames(); // ???
+
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ SwNodeOffset nNodeIdx = rAnchPos.GetNodeIndex();
+ const sal_Int32 nCntIdx = rAnchPos.GetContentIndex();
+ GetIDocumentUndoRedo().AppendUndo(
+ std::make_unique<SwUndoInsLayFormat>( pFormat, nNodeIdx, nCntIdx ));
+ }
+
+ getIDocumentState().SetModified();
+ return pFormat;
+}
+
+SwFlyFrameFormat* SwDoc::MakeFlySection( RndStdIds eAnchorType,
+ const SwPosition* pAnchorPos,
+ const SfxItemSet* pFlySet,
+ SwFrameFormat* pFrameFormat, bool bCalledFromShell )
+{
+ SwFlyFrameFormat* pFormat = nullptr;
+ if ( !pAnchorPos && (RndStdIds::FLY_AT_PAGE != eAnchorType) )
+ {
+ const SwFormatAnchor* pAnch;
+ if( (pFlySet && (pAnch = pFlySet->GetItemIfSet( RES_ANCHOR, false ))) ||
+ ( pFrameFormat && (pAnch = pFrameFormat->GetItemIfSet(RES_ANCHOR)) ) )
+ {
+ if ( RndStdIds::FLY_AT_PAGE != pAnch->GetAnchorId() )
+ {
+ pAnchorPos = pAnch->GetContentAnchor();
+ }
+ }
+ }
+
+ if (pAnchorPos)
+ {
+ if( !pFrameFormat )
+ pFrameFormat = getIDocumentStylePoolAccess().GetFrameFormatFromPool( RES_POOLFRM_FRAME );
+
+ sal_uInt16 nCollId = o3tl::narrowing<sal_uInt16>(
+ GetDocumentSettingManager().get(DocumentSettingId::HTML_MODE) ? RES_POOLCOLL_TEXT : RES_POOLCOLL_FRAME );
+
+ /* If there is no adjust item in the paragraph style for the content node of the new fly section
+ propagate an existing adjust item at the anchor to the new content node. */
+ SwContentNode * pNewTextNd = GetNodes().MakeTextNode
+ ( GetNodes().GetEndOfAutotext(),
+ getIDocumentStylePoolAccess().GetTextCollFromPool( nCollId ));
+ SwContentNode * pAnchorNode = pAnchorPos->GetNode().GetContentNode();
+ // pAnchorNode from cursor must be valid, unless a whole table is selected (in which
+ // case the node is not a content node, and pAnchorNode is nullptr). In the latter case,
+ // bCalledFromShell is false.
+ assert(!bCalledFromShell || pAnchorNode);
+
+ const SfxPoolItem * pItem = nullptr;
+
+ if (bCalledFromShell && !lcl_IsItemSet(*pNewTextNd, RES_PARATR_ADJUST) &&
+ SfxItemState::SET == pAnchorNode->GetSwAttrSet().GetItemState(RES_PARATR_ADJUST, true, &pItem))
+ {
+ pNewTextNd->SetAttr(*pItem);
+ }
+
+ pFormat = MakeFlySection_( *pAnchorPos, *pNewTextNd,
+ eAnchorType, pFlySet, pFrameFormat );
+ }
+ return pFormat;
+}
+
+SwFlyFrameFormat* SwDoc::MakeFlyAndMove( const SwPaM& rPam, const SfxItemSet& rSet,
+ const SwSelBoxes* pSelBoxes,
+ SwFrameFormat *pParent )
+{
+ const SwFormatAnchor& rAnch = rSet.Get( RES_ANCHOR );
+
+ GetIDocumentUndoRedo().StartUndo( SwUndoId::INSLAYFMT, nullptr );
+
+ SwFlyFrameFormat* pFormat = MakeFlySection( rAnch.GetAnchorId(), rPam.GetPoint(),
+ &rSet, pParent );
+
+ // If content is selected, it becomes the new frame's content.
+ // Namely, it is moved into the NodeArray's appropriate section.
+
+ if( pFormat )
+ {
+ do { // middle check loop
+ const SwFormatContent &rContent = pFormat->GetContent();
+ OSL_ENSURE( rContent.GetContentIdx(), "No content prepared." );
+ SwNodeIndex aIndex( *(rContent.GetContentIdx()), 1 );
+
+ // Attention: Do not create an index on the stack, or we
+ // cannot delete ContentNode in the end!
+ std::optional<SwPosition> oPos( std::in_place, aIndex );
+
+ if( pSelBoxes && !pSelBoxes->empty() )
+ {
+ // Table selection
+ // Copy parts of a table: create a table with the same width as the
+ // original one and move (copy and delete) the selected boxes.
+ // The size is corrected on a percentage basis.
+
+ SwTableNode* pTableNd = const_cast<SwTableNode*>((*pSelBoxes)[0]->
+ GetSttNd()->FindTableNode());
+ if( !pTableNd )
+ break;
+
+ SwTable& rTable = pTableNd->GetTable();
+
+ // Did we select the whole table?
+ if( pSelBoxes->size() == rTable.GetTabSortBoxes().size() )
+ {
+ // move the whole table
+ SwNodeRange aRg( *pTableNd, SwNodeOffset(0), *pTableNd->EndOfSectionNode(), SwNodeOffset(1) );
+
+ // If we move the whole table and it is located within a
+ // FlyFrame, the we create a TextNode after it.
+ // So that this FlyFrame is preserved.
+ if( aRg.aEnd.GetNode().IsEndNode() )
+ GetNodes().MakeTextNode( aRg.aStart.GetNode(),
+ GetDfltTextFormatColl() );
+
+ // Create undo actions if undo is enabled.
+ getIDocumentContentOperations().MoveNodeRange( aRg, oPos->GetNode(), SwMoveFlags::CREATEUNDOOBJ );
+ }
+ else
+ {
+ rTable.MakeCopy(*this, *oPos, *pSelBoxes);
+ // Don't delete a part of a table with row span!!
+ // You could delete the content instead -> ToDo
+ //rTable.DeleteSel( this, *pSelBoxes, 0, 0, true, true );
+ }
+
+ // If the table is within the frame, then copy without the following TextNode
+ aIndex = rContent.GetContentIdx()->GetNode().EndOfSectionIndex() - 1;
+ OSL_ENSURE( aIndex.GetNode().GetTextNode(),
+ "a TextNode should be here" );
+ oPos.reset(); // Deregister index!
+ // Delete the empty paragraph after the table, in a way that undo is aware of this.
+ SwPaM aPaM(aIndex);
+ getIDocumentContentOperations().DelFullPara(aPaM);
+ }
+ else
+ {
+ // copy all Pams and then delete all
+ bool bOldFlag = mbCopyIsMove;
+ bool const bOldUndo = GetIDocumentUndoRedo().DoesUndo();
+ bool const bOldRedlineMove(getIDocumentRedlineAccess().IsRedlineMove());
+ mbCopyIsMove = true;
+ GetIDocumentUndoRedo().DoUndo(false);
+ getIDocumentRedlineAccess().SetRedlineMove(true);
+ for(const SwPaM& rTmp : rPam.GetRingContainer())
+ {
+ if( rTmp.HasMark() &&
+ *rTmp.GetPoint() != *rTmp.GetMark() )
+ {
+ // aPos is the newly created fly section, so definitely outside rPam, it's pointless to check that again.
+ getIDocumentContentOperations().CopyRange(*const_cast<SwPaM*>(&rTmp), *oPos, SwCopyFlags::IsMoveToFly);
+ }
+ }
+ getIDocumentRedlineAccess().SetRedlineMove(bOldRedlineMove);
+ mbCopyIsMove = bOldFlag;
+ GetIDocumentUndoRedo().DoUndo(bOldUndo);
+
+ for(const SwPaM& rTmp : rPam.GetRingContainer())
+ {
+ if( rTmp.HasMark() &&
+ *rTmp.GetPoint() != *rTmp.GetMark() )
+ {
+ getIDocumentContentOperations().DeleteAndJoin( *const_cast<SwPaM*>(&rTmp) );
+ }
+ }
+ }
+ } while( false );
+ }
+
+ getIDocumentState().SetModified();
+
+ GetIDocumentUndoRedo().EndUndo( SwUndoId::INSLAYFMT, nullptr );
+
+ return pFormat;
+}
+
+
+/*
+ * paragraph frames - o.k. if the PaM includes the paragraph from the beginning
+ * to the beginning of the next paragraph at least
+ * frames at character - o.k. if the PaM starts at least at the same position
+ * as the frame
+ */
+static bool lcl_TstFlyRange( const SwPaM* pPam, const SwFormatAnchor& rFlyFormatAnchor )
+{
+ bool bOk = false;
+ const SwPaM* pTmp = pPam;
+ do {
+ const SwNodeOffset nFlyIndex = rFlyFormatAnchor.GetAnchorNode()->GetIndex();
+ auto [pPaMStart, pPaMEnd] = pTmp->StartEnd(); // SwPosition*
+ const SwNodeOffset nPamStartIndex = pPaMStart->GetNodeIndex();
+ const SwNodeOffset nPamEndIndex = pPaMEnd->GetNodeIndex();
+ if (RndStdIds::FLY_AT_PARA == rFlyFormatAnchor.GetAnchorId())
+ bOk = (nPamStartIndex < nFlyIndex && nPamEndIndex > nFlyIndex) ||
+ (((nPamStartIndex == nFlyIndex) && (pPaMStart->GetContentIndex() == 0)) &&
+ (nPamEndIndex > nFlyIndex));
+ else
+ {
+ const sal_Int32 nFlyContentIndex = rFlyFormatAnchor.GetAnchorContentOffset();
+ const sal_Int32 nPamEndContentIndex = pPaMEnd->GetContentIndex();
+ bOk = (nPamStartIndex < nFlyIndex &&
+ (( nPamEndIndex > nFlyIndex )||
+ ((nPamEndIndex == nFlyIndex) &&
+ (nPamEndContentIndex > nFlyContentIndex))) )
+ ||
+ (((nPamStartIndex == nFlyIndex) &&
+ (pPaMStart->GetContentIndex() <= nFlyContentIndex)) &&
+ ((nPamEndIndex > nFlyIndex) ||
+ (nPamEndContentIndex > nFlyContentIndex )));
+ }
+
+ if( bOk )
+ break;
+ pTmp = pTmp->GetNext();
+ } while( pPam != pTmp );
+ return bOk;
+}
+
+SwPosFlyFrames SwDoc::GetAllFlyFormats( const SwPaM* pCmpRange, bool bDrawAlso,
+ bool bAsCharAlso ) const
+{
+ SwPosFlyFrames aRetval;
+ const SwStartNode* pDirectFly = nullptr;
+ if (pCmpRange && *pCmpRange->GetPoint() == *pCmpRange->GetMark()
+ && (pCmpRange->GetPoint()->GetNode().IsOLENode()
+ || pCmpRange->GetPoint()->GetNode().IsGrfNode()))
+ {
+ pDirectFly = pCmpRange->GetPoint()->GetNode().FindFlyStartNode();
+ }
+
+ // collect all anchored somehow to paragraphs
+ for(sw::SpzFrameFormat* pFly: *GetSpzFrameFormats())
+ {
+ bool bDrawFormat = bDrawAlso && RES_DRAWFRMFMT == pFly->Which();
+ bool bFlyFormat = RES_FLYFRMFMT == pFly->Which();
+ if( bFlyFormat || bDrawFormat )
+ {
+ const SwFormatAnchor& rAnchor = pFly->GetAnchor();
+ SwNode const*const pAnchorNode = rAnchor.GetAnchorNode();
+ if (!pAnchorNode)
+ continue;
+ if (pDirectFly)
+ {
+ const SwFormatContent& rContent = pFly->GetContent();
+ const SwNodeIndex* pContentNodeIndex = rContent.GetContentIdx();
+ if (pContentNodeIndex && pContentNodeIndex->GetIndex() == pDirectFly->GetIndex())
+ {
+ aRetval.insert(SwPosFlyFrame(*pAnchorNode, pFly, aRetval.size()));
+ break;
+ }
+ continue;
+ }
+ if ( (RndStdIds::FLY_AT_PARA == rAnchor.GetAnchorId()) ||
+ (RndStdIds::FLY_AT_FLY == rAnchor.GetAnchorId()) ||
+ (RndStdIds::FLY_AT_CHAR == rAnchor.GetAnchorId()) ||
+ ((RndStdIds::FLY_AS_CHAR == rAnchor.GetAnchorId()) && bAsCharAlso) )
+ {
+ if( pCmpRange && !lcl_TstFlyRange( pCmpRange, rAnchor ))
+ continue; // not a valid FlyFrame
+ aRetval.insert(SwPosFlyFrame(*pAnchorNode, pFly, aRetval.size()));
+ }
+ }
+ }
+
+ // If we don't have a layout we can't get page anchored FlyFrames.
+ // Also, page anchored FlyFrames are only returned if no range is specified.
+ if( !getIDocumentLayoutAccess().GetCurrentViewShell() || pCmpRange )
+ {
+ return aRetval;
+ }
+
+ const SwPageFrame *pPage = static_cast<const SwPageFrame*>(getIDocumentLayoutAccess().GetCurrentLayout()->GetLower());
+ while( pPage )
+ {
+ if( pPage->GetSortedObjs() )
+ {
+ const SwSortedObjs &rObjs = *pPage->GetSortedObjs();
+ for(SwAnchoredObject* pAnchoredObj : rObjs)
+ {
+ SwFrameFormat *pFly;
+ if ( pAnchoredObj->DynCastFlyFrame() != nullptr )
+ pFly = &(pAnchoredObj->GetFrameFormat());
+ else if ( bDrawAlso )
+ pFly = &(pAnchoredObj->GetFrameFormat());
+ else
+ continue;
+
+ const SwFormatAnchor& rAnchor = pFly->GetAnchor();
+ if ((RndStdIds::FLY_AT_PARA != rAnchor.GetAnchorId()) &&
+ (RndStdIds::FLY_AT_FLY != rAnchor.GetAnchorId()) &&
+ (RndStdIds::FLY_AT_CHAR != rAnchor.GetAnchorId()))
+ {
+ const SwContentFrame * pContentFrame = pPage->FindFirstBodyContent();
+ if ( !pContentFrame )
+ {
+ // Oops! An empty page.
+ // In order not to lose the whole frame (RTF) we
+ // look for the last Content before the page.
+ const SwPageFrame *pPrv = static_cast<const SwPageFrame*>(pPage->GetPrev());
+ while ( !pContentFrame && pPrv )
+ {
+ pContentFrame = pPrv->FindFirstBodyContent();
+ pPrv = static_cast<const SwPageFrame*>(pPrv->GetPrev());
+ }
+ }
+ if ( pContentFrame )
+ {
+ const SwNode* pNd( pContentFrame->IsTextFrame()
+ ? static_cast<SwTextFrame const*>(pContentFrame)->GetTextNodeFirst()
+ : static_cast<SwNoTextFrame const*>(pContentFrame)->GetNode() );
+ aRetval.insert(SwPosFlyFrame(*pNd, pFly, aRetval.size()));
+ }
+ }
+ }
+ }
+ pPage = static_cast<const SwPageFrame*>(pPage->GetNext());
+ }
+
+ return aRetval;
+}
+
+/* #i6447# changed behaviour if lcl_CpyAttr:
+
+ If the old item set contains the item to set (no inheritance) copy the item
+ into the new set.
+
+ If the old item set contains the item by inheritance and the new set
+ contains the item, too:
+ If the two items differ copy the item from the old set to the new set.
+
+ Otherwise the new set will not be changed.
+*/
+static void lcl_CpyAttr( SfxItemSet &rNewSet, const SfxItemSet &rOldSet, sal_uInt16 nWhich )
+{
+ const SfxPoolItem *pOldItem = nullptr;
+
+ rOldSet.GetItemState( nWhich, false, &pOldItem);
+ if (pOldItem != nullptr)
+ rNewSet.Put( *pOldItem );
+ else
+ {
+ pOldItem = rOldSet.GetItem( nWhich );
+ if (pOldItem != nullptr)
+ {
+ const SfxPoolItem *pNewItem = rNewSet.GetItem( nWhich );
+ if (pNewItem != nullptr)
+ {
+ if (*pOldItem != *pNewItem)
+ rNewSet.Put( *pOldItem );
+ }
+ else {
+ OSL_FAIL("What am I doing here?");
+ }
+ }
+ else {
+ OSL_FAIL("What am I doing here?");
+ }
+ }
+
+}
+
+static SwFlyFrameFormat *
+lcl_InsertLabel(SwDoc & rDoc, SwTextFormatColls *const pTextFormatCollTable,
+ SwUndoInsertLabel *const pUndo,
+ SwLabelType const eType, std::u16string_view rText, std::u16string_view rSeparator,
+ const OUString& rNumberingSeparator,
+ const bool bBefore, const sal_uInt16 nId, const SwNodeOffset nNdIdx,
+ const OUString& rCharacterStyle,
+ const bool bCpyBrd )
+{
+ ::sw::UndoGuard const undoGuard(rDoc.GetIDocumentUndoRedo());
+
+ bool bTable = false; // To save some code.
+
+ // Get the field first, because we retrieve the TextColl via the field's name
+ OSL_ENSURE( nId == USHRT_MAX || nId < rDoc.getIDocumentFieldsAccess().GetFieldTypes()->size(),
+ "FieldType index out of bounds." );
+ SwFieldType *pType = (nId != USHRT_MAX) ? (*rDoc.getIDocumentFieldsAccess().GetFieldTypes())[nId].get() : nullptr;
+ OSL_ENSURE(!pType || pType->Which() == SwFieldIds::SetExp, "wrong Id for Label");
+
+ SwTextFormatColl * pColl = nullptr;
+ if( pType )
+ {
+ for( auto i = pTextFormatCollTable->size(); i; )
+ {
+ if( (*pTextFormatCollTable)[ --i ]->GetName()==pType->GetName() )
+ {
+ pColl = (*pTextFormatCollTable)[i];
+ break;
+ }
+ }
+ OSL_ENSURE( pColl, "no text collection found" );
+ }
+
+ if( !pColl )
+ {
+ pColl = rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_LABEL );
+ }
+
+ SwTextNode *pNew = nullptr;
+ SwFlyFrameFormat* pNewFormat = nullptr;
+
+ switch ( eType )
+ {
+ case SwLabelType::Table:
+ bTable = true;
+ [[fallthrough]];
+ case SwLabelType::Fly:
+ // At the FlySection's Beginning/End insert the corresponding Node with its Field.
+ // The Frame is created automatically.
+ {
+ SwStartNode *pSttNd = rDoc.GetNodes()[nNdIdx]->GetStartNode();
+ OSL_ENSURE( pSttNd, "No StartNode in InsertLabel." );
+ SwNodeOffset nNode;
+ if( bBefore )
+ {
+ nNode = pSttNd->GetIndex();
+ if( !bTable )
+ ++nNode;
+ }
+ else
+ {
+ nNode = pSttNd->EndOfSectionIndex();
+ if( bTable )
+ ++nNode;
+ }
+
+ if( pUndo )
+ pUndo->SetNodePos( nNode );
+
+ // Create Node for labeling paragraph.
+ SwNodeIndex aIdx( rDoc.GetNodes(), nNode );
+ pNew = rDoc.GetNodes().MakeTextNode( aIdx.GetNode(), pColl );
+ }
+ break;
+
+ case SwLabelType::Object:
+ {
+ // Destroy Frame,
+ // insert new Frame,
+ // insert the corresponding Node with Field into the new Frame,
+ // insert the old Frame with the Object (Picture/OLE) paragraph-bound into the new Frame,
+ // create Frames.
+
+ // Get the FlyFrame's Format and decouple the Layout.
+ SwFrameFormat *pOldFormat = rDoc.GetNodes()[nNdIdx]->GetFlyFormat();
+ OSL_ENSURE( pOldFormat, "Couldn't find the Fly's Format." );
+ // #i115719#
+ // <title> and <description> attributes are lost when calling <DelFrames()>.
+ // Thus, keep them and restore them after the calling <MakeFrames()>
+ auto pOldFlyFrameFormat = dynamic_cast<SwFlyFrameFormat*>(pOldFormat);
+ const OUString sTitle( pOldFlyFrameFormat
+ ? pOldFlyFrameFormat->GetObjTitle()
+ : OUString() );
+ const OUString sDescription( pOldFlyFrameFormat
+ ? pOldFlyFrameFormat->GetObjDescription()
+ : OUString() );
+ pOldFormat->DelFrames();
+
+ pNewFormat = rDoc.MakeFlyFrameFormat( rDoc.GetUniqueFrameName(),
+ rDoc.getIDocumentStylePoolAccess().GetFrameFormatFromPool(RES_POOLFRM_FRAME) );
+
+ /* #i6447#: Only the selected items are copied from the old
+ format. */
+ SwAttrSet aNewSet = pNewFormat->GetAttrSet().CloneAsValue();
+
+ // Copy only the set attributes.
+ // The others should apply from the Templates.
+ lcl_CpyAttr( aNewSet, pOldFormat->GetAttrSet(), RES_PRINT );
+ lcl_CpyAttr( aNewSet, pOldFormat->GetAttrSet(), RES_OPAQUE );
+ lcl_CpyAttr( aNewSet, pOldFormat->GetAttrSet(), RES_PROTECT );
+ lcl_CpyAttr( aNewSet, pOldFormat->GetAttrSet(), RES_SURROUND );
+ lcl_CpyAttr( aNewSet, pOldFormat->GetAttrSet(), RES_VERT_ORIENT );
+ lcl_CpyAttr( aNewSet, pOldFormat->GetAttrSet(), RES_HORI_ORIENT );
+ lcl_CpyAttr( aNewSet, pOldFormat->GetAttrSet(), RES_LR_SPACE );
+ lcl_CpyAttr( aNewSet, pOldFormat->GetAttrSet(), RES_UL_SPACE );
+ lcl_CpyAttr( aNewSet, pOldFormat->GetAttrSet(), RES_BACKGROUND );
+ if( bCpyBrd )
+ {
+ // If there's no BoxItem at graphic, but the new Format has one, then set the
+ // default item in the new Set. Because the graphic's size has never changed!
+ const SfxPoolItem *pItem;
+ if( SfxItemState::SET == pOldFormat->GetAttrSet().
+ GetItemState( RES_BOX, true, &pItem ))
+ aNewSet.Put( *pItem );
+ else if( SfxItemState::SET == pNewFormat->GetAttrSet().
+ GetItemState( RES_BOX ))
+ aNewSet.Put( *GetDfltAttr( RES_BOX ) );
+
+ if( SfxItemState::SET == pOldFormat->GetAttrSet().
+ GetItemState( RES_SHADOW, true, &pItem ))
+ aNewSet.Put( *pItem );
+ else if( SfxItemState::SET == pNewFormat->GetAttrSet().
+ GetItemState( RES_SHADOW ))
+ aNewSet.Put( *GetDfltAttr( RES_SHADOW ) );
+ }
+ else
+ {
+ // Hard-set the attributes, because they could come from the Template
+ // and then size calculations could not be correct anymore.
+ aNewSet.Put( SvxBoxItem(RES_BOX) );
+ aNewSet.Put( SvxShadowItem(RES_SHADOW) );
+ }
+
+ // Always transfer the anchor, which is a hard attribute anyways.
+ aNewSet.Put( pOldFormat->GetAnchor() );
+
+ // The new one should be changeable in its height.
+ std::unique_ptr<SwFormatFrameSize> aFrameSize(pOldFormat->GetFrameSize().Clone());
+ aFrameSize->SetHeightSizeType( SwFrameSize::Minimum );
+ aNewSet.Put( std::move(aFrameSize) );
+
+ SwStartNode* pSttNd = rDoc.GetNodes().MakeTextSection(
+ rDoc.GetNodes().GetEndOfAutotext(),
+ SwFlyStartNode, pColl );
+ aNewSet.Put( SwFormatContent( pSttNd ));
+
+ pNewFormat->SetFormatAttr( aNewSet );
+
+ // InContents need to be treated in a special way:
+ // The TextAttribute needs to be destroyed.
+ // Unfortunately, this also destroys the Format next to the Frames.
+ // To avoid this, we disconnect the attribute from the Format.
+
+ const SwFormatAnchor& rAnchor = pNewFormat->GetAnchor();
+ if ( RndStdIds::FLY_AS_CHAR == rAnchor.GetAnchorId() )
+ {
+ SwTextNode *pTextNode = rAnchor.GetAnchorNode()->GetTextNode();
+ OSL_ENSURE( pTextNode->HasHints(), "Missing FlyInCnt-Hint." );
+ const sal_Int32 nIdx = rAnchor.GetAnchorContentOffset();
+ SwTextAttr * const pHint =
+ pTextNode->GetTextAttrForCharAt(nIdx, RES_TXTATR_FLYCNT);
+
+ assert(pHint && "Missing Hint.");
+
+ OSL_ENSURE( pHint->Which() == RES_TXTATR_FLYCNT,
+ "Missing FlyInCnt-Hint." );
+ OSL_ENSURE( pHint->GetFlyCnt().GetFrameFormat() == pOldFormat,
+ "Wrong TextFlyCnt-Hint." );
+
+ const_cast<SwFormatFlyCnt&>(pHint->GetFlyCnt()).SetFlyFormat(
+ pNewFormat );
+ }
+
+ // The old one should not have a flow and it should be adjusted to above and
+ // middle.
+ // Also, the width should be 100% and it should also adjust the height, if changed.
+ aNewSet.ClearItem();
+
+ aNewSet.Put( SwFormatSurround( css::text::WrapTextMode_NONE ) );
+ aNewSet.Put( SvxOpaqueItem( RES_OPAQUE, true ) );
+
+ sal_Int16 eVert = bBefore ? text::VertOrientation::BOTTOM : text::VertOrientation::TOP;
+ aNewSet.Put( SwFormatVertOrient( 0, eVert ) );
+ aNewSet.Put( SwFormatHoriOrient( 0, text::HoriOrientation::CENTER ) );
+
+ aFrameSize.reset(pOldFormat->GetFrameSize().Clone());
+
+ SwOLENode* pOleNode = rDoc.GetNodes()[nNdIdx + 1]->GetOLENode();
+ bool isMath = false;
+ if(pOleNode)
+ {
+ svt::EmbeddedObjectRef& xRef = pOleNode->GetOLEObj().GetObject();
+ if(xRef.is())
+ {
+ SvGlobalName aCLSID( xRef->getClassID() );
+ isMath = ( SotExchange::IsMath( aCLSID ) != 0 );
+ }
+ }
+ aFrameSize->SetWidthPercent(isMath ? 0 : 100);
+ aFrameSize->SetHeightPercent(SwFormatFrameSize::SYNCED);
+ aNewSet.Put( std::move(aFrameSize) );
+
+ // Hard-set the attributes, because they could come from the Template
+ // and then size calculations could not be correct anymore.
+ if( bCpyBrd )
+ {
+ aNewSet.Put( SvxBoxItem(RES_BOX) );
+ aNewSet.Put( SvxShadowItem(RES_SHADOW) );
+ }
+ aNewSet.Put( SvxLRSpaceItem(RES_LR_SPACE) );
+ aNewSet.Put( SvxULSpaceItem(RES_UL_SPACE) );
+
+ // The old one is paragraph-bound to the paragraph in the new one.
+ SwFormatAnchor aAnch( RndStdIds::FLY_AT_PARA );
+ SwNodeIndex aAnchIdx( *pNewFormat->GetContent().GetContentIdx(), 1 );
+ pNew = aAnchIdx.GetNode().GetTextNode();
+ SwPosition aPos( aAnchIdx );
+ aAnch.SetAnchor( &aPos );
+ aNewSet.Put( aAnch );
+
+ if( pUndo )
+ pUndo->SetFlys( *pOldFormat, aNewSet, *pNewFormat );
+ else
+ pOldFormat->SetFormatAttr( aNewSet );
+
+ // Have only the FlyFrames created.
+ // We leave this to established methods (especially for InCntFlys).
+ pNewFormat->MakeFrames();
+ // #i115719#
+ if ( pOldFlyFrameFormat )
+ {
+ pOldFlyFrameFormat->SetObjTitle( sTitle );
+ pOldFlyFrameFormat->SetObjDescription( sDescription );
+ }
+ }
+ break;
+
+ default:
+ OSL_ENSURE(false, "unknown LabelType?");
+ }
+ OSL_ENSURE( pNew, "No Label inserted" );
+ if( pNew )
+ {
+ // #i61007# order of captions
+ bool bOrderNumberingFirst = SW_MOD()->GetModuleConfig()->IsCaptionOrderNumberingFirst();
+ // Work up OUString
+ OUString aText;
+ if( bOrderNumberingFirst )
+ {
+ aText = rNumberingSeparator;
+ }
+ if( pType)
+ {
+ aText += pType->GetName();
+ if( !bOrderNumberingFirst )
+ aText += " ";
+ }
+ sal_Int32 nIdx = aText.getLength();
+ if( !rText.empty() )
+ {
+ aText += rSeparator;
+ }
+ const sal_Int32 nSepIdx = aText.getLength();
+ aText += rText;
+
+ // Insert string
+ SwContentIndex aIdx( pNew, 0 );
+ pNew->InsertText( aText, aIdx );
+
+ // Insert field
+ if(pType)
+ {
+ SwSetExpField aField( static_cast<SwSetExpFieldType*>(pType), OUString(), SVX_NUM_ARABIC);
+ if( bOrderNumberingFirst )
+ nIdx = 0;
+ SwFormatField aFormat( aField );
+ pNew->InsertItem( aFormat, nIdx, nIdx );
+ if(!rCharacterStyle.isEmpty())
+ {
+ SwCharFormat* pCharFormat = rDoc.FindCharFormatByName(rCharacterStyle);
+ if( !pCharFormat )
+ {
+ const sal_uInt16 nMyId = SwStyleNameMapper::GetPoolIdFromUIName(rCharacterStyle, SwGetPoolIdFromName::ChrFmt);
+ pCharFormat = rDoc.getIDocumentStylePoolAccess().GetCharFormatFromPool( nMyId );
+ }
+ if (pCharFormat)
+ {
+ SwFormatCharFormat aCharFormat( pCharFormat );
+ pNew->InsertItem( aCharFormat, 0,
+ nSepIdx + 1, SetAttrMode::DONTEXPAND );
+ }
+ }
+ }
+
+ if ( bTable )
+ {
+ if ( bBefore )
+ {
+ if ( !pNew->GetSwAttrSet().GetKeep().GetValue() )
+ pNew->SetAttr( SvxFormatKeepItem( true, RES_KEEP ) );
+ }
+ else
+ {
+ SwTableNode *const pNd =
+ rDoc.GetNodes()[nNdIdx]->GetStartNode()->GetTableNode();
+ SwTable &rTable = pNd->GetTable();
+ if ( !rTable.GetFrameFormat()->GetKeep().GetValue() )
+ rTable.GetFrameFormat()->SetFormatAttr( SvxFormatKeepItem( true, RES_KEEP ) );
+ if ( pUndo )
+ pUndo->SetUndoKeep();
+ }
+ }
+ rDoc.getIDocumentState().SetModified();
+ }
+
+ return pNewFormat;
+}
+
+SwFlyFrameFormat *
+SwDoc::InsertLabel(
+ SwLabelType const eType, OUString const& rText, OUString const& rSeparator,
+ OUString const& rNumberingSeparator,
+ bool const bBefore, sal_uInt16 const nId, SwNodeOffset const nNdIdx,
+ OUString const& rCharacterStyle,
+ bool const bCpyBrd )
+{
+ std::unique_ptr<SwUndoInsertLabel> pUndo;
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ pUndo.reset(new SwUndoInsertLabel(
+ eType, rText, rSeparator, rNumberingSeparator,
+ bBefore, nId, rCharacterStyle, bCpyBrd, this ));
+ }
+
+ SwFlyFrameFormat *const pNewFormat = lcl_InsertLabel(*this, mpTextFormatCollTable.get(), pUndo.get(),
+ eType, rText, rSeparator, rNumberingSeparator, bBefore,
+ nId, nNdIdx, rCharacterStyle, bCpyBrd);
+
+ if (pUndo)
+ {
+ GetIDocumentUndoRedo().AppendUndo(std::move(pUndo));
+ }
+ else
+ {
+ GetIDocumentUndoRedo().DelAllUndoObj();
+ }
+
+ return pNewFormat;
+}
+
+static SwFlyFrameFormat *
+lcl_InsertDrawLabel( SwDoc & rDoc, SwTextFormatColls *const pTextFormatCollTable,
+ SwUndoInsertLabel *const pUndo, SwDrawFrameFormat *const pOldFormat,
+ OUString const& rText,
+ const OUString& rSeparator,
+ const OUString& rNumberSeparator,
+ const sal_uInt16 nId,
+ const OUString& rCharacterStyle,
+ SdrObject& rSdrObj )
+{
+ ::sw::UndoGuard const undoGuard(rDoc.GetIDocumentUndoRedo());
+ ::sw::DrawUndoGuard const drawUndoGuard(rDoc.GetIDocumentUndoRedo());
+
+ // Because we get by the TextColl's name, we need to create the field first.
+ OSL_ENSURE( nId == USHRT_MAX || nId < rDoc.getIDocumentFieldsAccess().GetFieldTypes()->size(),
+ "FieldType index out of bounds" );
+ SwFieldType *pType = nId != USHRT_MAX ? (*rDoc.getIDocumentFieldsAccess().GetFieldTypes())[nId].get() : nullptr;
+ OSL_ENSURE( !pType || pType->Which() == SwFieldIds::SetExp, "Wrong label id" );
+
+ SwTextFormatColl *pColl = nullptr;
+ if( pType )
+ {
+ for( auto i = pTextFormatCollTable->size(); i; )
+ {
+ if( (*pTextFormatCollTable)[ --i ]->GetName()==pType->GetName() )
+ {
+ pColl = (*pTextFormatCollTable)[i];
+ break;
+ }
+ }
+ OSL_ENSURE( pColl, "no text collection found" );
+ }
+
+ if( !pColl )
+ {
+ pColl = rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_LABEL );
+ }
+
+ SwTextNode* pNew = nullptr;
+ SwFlyFrameFormat* pNewFormat = nullptr;
+
+ // Destroy Frame,
+ // insert new Frame,
+ // insert the corresponding Node with Field into the new Frame,
+ // insert the old Frame with the Object (Picture/OLE) paragraph-bound into the new Frame,
+ // create Frames.
+
+ // Keep layer ID of drawing object before removing
+ // its frames.
+ // Note: The layer ID is passed to the undo and have to be the correct value.
+ // Removing the frames of the drawing object changes its layer.
+ const SdrLayerID nLayerId = rSdrObj.GetLayer();
+
+ pOldFormat->DelFrames();
+
+ // InContents need to be treated in a special way:
+ // The TextAttribute needs to be destroyed.
+ // Unfortunately, this also destroys the Format next to the Frames.
+ // To avoid this, we disconnect the attribute from the Format.
+ SwAttrSet aNewSet = pOldFormat->GetAttrSet().CloneAsValue( false );
+
+ // Protect the Frame's size and position
+ if ( rSdrObj.IsMoveProtect() || rSdrObj.IsResizeProtect() )
+ {
+ SvxProtectItem aProtect(RES_PROTECT);
+ aProtect.SetContentProtect( false );
+ aProtect.SetPosProtect( rSdrObj.IsMoveProtect() );
+ aProtect.SetSizeProtect( rSdrObj.IsResizeProtect() );
+ aNewSet.Put( aProtect );
+ }
+
+ // Take over the text wrap
+ lcl_CpyAttr( aNewSet, pOldFormat->GetAttrSet(), RES_SURROUND );
+
+ // Send the frame to the back, if needed.
+ // Consider the 'invisible' hell layer.
+ if ( rDoc.getIDocumentDrawModelAccess().GetHellId() != nLayerId &&
+ rDoc.getIDocumentDrawModelAccess().GetInvisibleHellId() != nLayerId )
+ {
+ SvxOpaqueItem aOpaque( RES_OPAQUE );
+ aOpaque.SetValue( true );
+ aNewSet.Put( aOpaque );
+ }
+
+ // Take over position
+ // #i26791# - use directly drawing object's positioning attributes
+ aNewSet.Put( pOldFormat->GetHoriOrient() );
+ aNewSet.Put( pOldFormat->GetVertOrient() );
+
+ aNewSet.Put( pOldFormat->GetAnchor() );
+
+ // The new one should be variable in its height!
+ Size aSz( rSdrObj.GetCurrentBoundRect().GetSize() );
+ SwFormatFrameSize aFrameSize( SwFrameSize::Minimum, aSz.Width(), aSz.Height() );
+ aNewSet.Put( aFrameSize );
+
+ // Apply the margin to the new Frame.
+ // Don't set a border, use the one from the Template.
+ aNewSet.Put( pOldFormat->GetLRSpace() );
+ aNewSet.Put( pOldFormat->GetULSpace() );
+
+ SwStartNode* pSttNd =
+ rDoc.GetNodes().MakeTextSection(
+ rDoc.GetNodes().GetEndOfAutotext(),
+ SwFlyStartNode, pColl );
+
+ pNewFormat = rDoc.MakeFlyFrameFormat( rDoc.GetUniqueFrameName(),
+ rDoc.getIDocumentStylePoolAccess().GetFrameFormatFromPool( RES_POOLFRM_FRAME ) );
+
+ // Set border and shadow to default if the template contains any.
+ if( SfxItemState::SET == pNewFormat->GetAttrSet().GetItemState( RES_BOX ))
+ aNewSet.Put( *GetDfltAttr( RES_BOX ) );
+
+ if( SfxItemState::SET == pNewFormat->GetAttrSet().GetItemState(RES_SHADOW))
+ aNewSet.Put( *GetDfltAttr( RES_SHADOW ) );
+
+ pNewFormat->SetFormatAttr( SwFormatContent( pSttNd ));
+ pNewFormat->SetFormatAttr( aNewSet );
+
+ const SwFormatAnchor& rAnchor = pNewFormat->GetAnchor();
+ if ( RndStdIds::FLY_AS_CHAR == rAnchor.GetAnchorId() )
+ {
+ SwTextNode *pTextNode = rAnchor.GetAnchorNode()->GetTextNode();
+ OSL_ENSURE( pTextNode->HasHints(), "Missing FlyInCnt-Hint." );
+ const sal_Int32 nIdx = rAnchor.GetAnchorContentOffset();
+ SwTextAttr * const pHint =
+ pTextNode->GetTextAttrForCharAt( nIdx, RES_TXTATR_FLYCNT );
+
+ assert(pHint && "Missing Hint.");
+
+#if OSL_DEBUG_LEVEL > 0
+ OSL_ENSURE( pHint->Which() == RES_TXTATR_FLYCNT,
+ "Missing FlyInCnt-Hint." );
+ OSL_ENSURE( pHint->GetFlyCnt().
+ GetFrameFormat() == static_cast<SwFrameFormat*>(pOldFormat),
+ "Wrong TextFlyCnt-Hint." );
+#endif
+ const_cast<SwFormatFlyCnt&>(pHint->GetFlyCnt()).SetFlyFormat( pNewFormat );
+ }
+
+ // The old one should not have a flow
+ // and it should be adjusted to above and middle.
+ aNewSet.ClearItem();
+
+ aNewSet.Put( SwFormatSurround( css::text::WrapTextMode_NONE ) );
+ if (nLayerId == rDoc.getIDocumentDrawModelAccess().GetHellId())
+ {
+ // Consider drawing objects in the 'invisible' hell layer
+ rSdrObj.SetLayer( rDoc.getIDocumentDrawModelAccess().GetHeavenId() );
+ }
+ else if (nLayerId == rDoc.getIDocumentDrawModelAccess().GetInvisibleHellId())
+ {
+ rSdrObj.SetLayer( rDoc.getIDocumentDrawModelAccess().GetInvisibleHeavenId() );
+ }
+ aNewSet.Put( SvxLRSpaceItem( RES_LR_SPACE ) );
+ aNewSet.Put( SvxULSpaceItem( RES_UL_SPACE ) );
+
+ // #i26791# - set position of the drawing object, which is labeled.
+ aNewSet.Put( SwFormatVertOrient( 0, text::VertOrientation::TOP, text::RelOrientation::FRAME ) );
+ aNewSet.Put( SwFormatHoriOrient( 0, text::HoriOrientation::CENTER, text::RelOrientation::FRAME ) );
+
+ // The old one is paragraph-bound to the new one's paragraph.
+ SwFormatAnchor aAnch( RndStdIds::FLY_AT_PARA );
+ SwNodeIndex aAnchIdx( *pNewFormat->GetContent().GetContentIdx(), 1 );
+ pNew = aAnchIdx.GetNode().GetTextNode();
+ SwPosition aPos( aAnchIdx );
+ aAnch.SetAnchor( &aPos );
+ aNewSet.Put( aAnch );
+
+ if( pUndo )
+ {
+ pUndo->SetFlys( *pOldFormat, aNewSet, *pNewFormat );
+ // #i26791# - position no longer needed
+ pUndo->SetDrawObj( nLayerId );
+ }
+ else
+ pOldFormat->SetFormatAttr( aNewSet );
+
+ // Have only the FlyFrames created.
+ // We leave this to established methods (especially for InCntFlys).
+ pNewFormat->MakeFrames();
+
+ OSL_ENSURE( pNew, "No Label inserted" );
+
+ if( pNew )
+ {
+ //#i61007# order of captions
+ bool bOrderNumberingFirst = SW_MOD()->GetModuleConfig()->IsCaptionOrderNumberingFirst();
+
+ // prepare string
+ OUString aText;
+ if( bOrderNumberingFirst )
+ {
+ aText = rNumberSeparator;
+ }
+ if ( pType )
+ {
+ aText += pType->GetName();
+ if( !bOrderNumberingFirst )
+ aText += " ";
+ }
+ sal_Int32 nIdx = aText.getLength();
+ aText += rSeparator;
+ const sal_Int32 nSepIdx = aText.getLength();
+ aText += rText;
+
+ // insert text
+ SwContentIndex aIdx( pNew, 0 );
+ pNew->InsertText( aText, aIdx );
+
+ // insert field
+ if ( pType )
+ {
+ SwSetExpField aField( static_cast<SwSetExpFieldType*>(pType), OUString(), SVX_NUM_ARABIC );
+ if( bOrderNumberingFirst )
+ nIdx = 0;
+ SwFormatField aFormat( aField );
+ pNew->InsertItem( aFormat, nIdx, nIdx );
+ if ( !rCharacterStyle.isEmpty() )
+ {
+ SwCharFormat * pCharFormat = rDoc.FindCharFormatByName(rCharacterStyle);
+ if ( !pCharFormat )
+ {
+ const sal_uInt16 nMyId = SwStyleNameMapper::GetPoolIdFromUIName( rCharacterStyle, SwGetPoolIdFromName::ChrFmt );
+ pCharFormat = rDoc.getIDocumentStylePoolAccess().GetCharFormatFromPool( nMyId );
+ }
+ if ( pCharFormat )
+ {
+ SwFormatCharFormat aCharFormat( pCharFormat );
+ pNew->InsertItem( aCharFormat, 0, nSepIdx + 1,
+ SetAttrMode::DONTEXPAND );
+ }
+ }
+ }
+ }
+
+ return pNewFormat;
+}
+
+SwFlyFrameFormat* SwDoc::InsertDrawLabel(
+ OUString const& rText,
+ OUString const& rSeparator,
+ OUString const& rNumberSeparator,
+ sal_uInt16 const nId,
+ OUString const& rCharacterStyle,
+ SdrObject& rSdrObj )
+{
+ SwDrawContact *const pContact =
+ static_cast<SwDrawContact*>(GetUserCall( &rSdrObj ));
+ if (!pContact)
+ return nullptr;
+ OSL_ENSURE( RES_DRAWFRMFMT == pContact->GetFormat()->Which(),
+ "InsertDrawLabel(): not a DrawFrameFormat" );
+
+ SwDrawFrameFormat* pOldFormat = static_cast<SwDrawFrameFormat *>(pContact->GetFormat());
+ if (!pOldFormat)
+ return nullptr;
+
+ std::unique_ptr<SwUndoInsertLabel> pUndo;
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().ClearRedo();
+ pUndo.reset(new SwUndoInsertLabel(
+ SwLabelType::Draw, rText, rSeparator, rNumberSeparator, false,
+ nId, rCharacterStyle, false, this ));
+ }
+
+ SwFlyFrameFormat *const pNewFormat = lcl_InsertDrawLabel(
+ *this, mpTextFormatCollTable.get(), pUndo.get(), pOldFormat,
+ rText, rSeparator, rNumberSeparator, nId, rCharacterStyle, rSdrObj);
+
+ if (pUndo)
+ {
+ GetIDocumentUndoRedo().AppendUndo( std::move(pUndo) );
+ }
+ else
+ {
+ GetIDocumentUndoRedo().DelAllUndoObj();
+ }
+
+ return pNewFormat;
+}
+
+static void lcl_collectUsedNums(std::vector<unsigned int>& rSetFlags, sal_Int32 nNmLen, std::u16string_view rName, std::u16string_view rCmpName)
+{
+ if (o3tl::starts_with(rName, rCmpName))
+ {
+ // Only get and set the Flag
+ const sal_Int32 nNum = o3tl::toInt32(rName.substr(nNmLen)) - 1;
+ if (nNum >= 0)
+ rSetFlags.push_back(nNum);
+ }
+}
+
+static void lcl_collectUsedNums(std::vector<unsigned int>& rSetFlags, sal_Int32 nNmLen, const SdrObject& rObj, const OUString& rCmpName)
+{
+ OUString sName = rObj.GetName();
+ lcl_collectUsedNums(rSetFlags, nNmLen, sName, rCmpName);
+ // tdf#122487 take groups into account, iterate and recurse through their
+ // contents for name collision check
+ if (!rObj.IsGroupObject())
+ return;
+
+ const SdrObjList* pSub(rObj.GetSubList());
+ assert(pSub && "IsGroupObject is implemented as GetSubList != nullptr");
+ for (const rtl::Reference<SdrObject>& pObj : *pSub)
+ {
+ lcl_collectUsedNums(rSetFlags, nNmLen, *pObj, rCmpName);
+ }
+}
+
+namespace
+{
+ int first_available_number(std::vector<unsigned int>& numbers)
+ {
+ std::sort(numbers.begin(), numbers.end());
+ auto last = std::unique(numbers.begin(), numbers.end());
+ numbers.erase(last, numbers.end());
+
+ for (size_t i = 0; i < numbers.size(); ++i)
+ {
+ if (numbers[i] != i)
+ return i;
+ }
+
+ return numbers.size();
+ }
+}
+
+static OUString lcl_GetUniqueFlyName(const SwDoc& rDoc, TranslateId pDefStrId, sal_uInt16 eType, std::u16string_view rPrefix = std::u16string_view(), SwNodeType nNdTyp = SwNodeType::NONE)
+{
+ assert(eType >= RES_FMT_BEGIN && eType < RES_FMT_END);
+ if (rDoc.IsInMailMerge())
+ {
+ OUString newName = "MailMergeFly"
+ + OStringToOUString( DateTimeToOString( DateTime( DateTime::SYSTEM )), RTL_TEXTENCODING_ASCII_US )
+ + OUString::number( rDoc.GetSpzFrameFormats()->size() + 1 );
+ return newName;
+ }
+
+ if (!rPrefix.empty())
+ {
+ // Generate a name that makes it possible to know this is a copy of which original name,
+ // e.g. 'Picture 1 Copy 1'.
+ assert(nNdTyp != SwNodeType::NONE);
+ sal_Int32 nCnt = 1;
+ OUString aPrefix = SwResId(STR_MARK_COPY).replaceFirst("%1", rPrefix);
+ OUString aTmp;
+ while(nCnt < SAL_MAX_INT32)
+ {
+ aTmp = aPrefix + OUString::number(nCnt);
+ ++nCnt;
+ if (!rDoc.FindFlyByName(aTmp, nNdTyp))
+ {
+ break;
+ }
+ }
+ return aTmp;
+ }
+
+ OUString aName(SwResId(pDefStrId));
+ sal_Int32 nNmLen = aName.getLength();
+
+ std::vector<unsigned int> aUsedNums;
+ aUsedNums.reserve(rDoc.GetSpzFrameFormats()->size());
+
+ for(sw::SpzFrameFormat* pFlyFormat: *rDoc.GetSpzFrameFormats())
+ {
+ if (eType != pFlyFormat->Which())
+ continue;
+ if (eType == RES_DRAWFRMFMT)
+ {
+ const SdrObject *pObj = pFlyFormat->FindSdrObject();
+ if (pObj)
+ lcl_collectUsedNums(aUsedNums, nNmLen, *pObj, aName);
+ }
+
+ OUString sName = pFlyFormat->GetName();
+ lcl_collectUsedNums(aUsedNums, nNmLen, sName, aName);
+ }
+
+ // All numbers are flagged accordingly, so determine the right one
+ auto nNum = first_available_number(aUsedNums) + 1;
+ return aName + OUString::number(nNum);
+}
+
+OUString SwDoc::GetUniqueGrfName(std::u16string_view rPrefix) const
+{
+ return lcl_GetUniqueFlyName(*this, STR_GRAPHIC_DEFNAME, RES_FLYFRMFMT, rPrefix, SwNodeType::Grf);
+}
+
+OUString SwDoc::GetUniqueOLEName() const
+{
+ return lcl_GetUniqueFlyName(*this, STR_OBJECT_DEFNAME, RES_FLYFRMFMT);
+}
+
+OUString SwDoc::GetUniqueFrameName() const
+{
+ return lcl_GetUniqueFlyName(*this, STR_FRAME_DEFNAME, RES_FLYFRMFMT);
+}
+
+OUString SwDoc::GetUniqueShapeName() const
+{
+ return lcl_GetUniqueFlyName(*this, STR_SHAPE_DEFNAME, RES_DRAWFRMFMT);
+}
+
+OUString SwDoc::GetUniqueDrawObjectName() const
+{
+ return lcl_GetUniqueFlyName(*this, TranslateId(nullptr, "DrawObject"), RES_DRAWFRMFMT);
+}
+
+const SwFlyFrameFormat* SwDoc::FindFlyByName( const OUString& rName, SwNodeType nNdTyp ) const
+{
+ auto it = GetSpzFrameFormats()->findByTypeAndName( RES_FLYFRMFMT, rName );
+ if( it == GetSpzFrameFormats()->typeAndNameEnd() )
+ return nullptr;
+
+ const SwFrameFormat* pFlyFormat = *it;
+ assert( RES_FLYFRMFMT == pFlyFormat->Which() && pFlyFormat->GetName() == rName );
+ const SwNodeIndex* pIdx = pFlyFormat->GetContent().GetContentIdx();
+ if( pIdx && pIdx->GetNode().GetNodes().IsDocNodes() )
+ {
+ if( nNdTyp != SwNodeType::NONE )
+ {
+ // query for the right NodeType
+ const SwNode* pNd = GetNodes()[ pIdx->GetIndex()+1 ];
+ if( nNdTyp == SwNodeType::Text
+ ? !pNd->IsNoTextNode()
+ : nNdTyp == pNd->GetNodeType() )
+ return static_cast<const SwFlyFrameFormat*>(pFlyFormat);
+ }
+ else
+ return static_cast<const SwFlyFrameFormat*>(pFlyFormat);
+ }
+ return nullptr;
+}
+
+void SwDoc::SetFlyName( SwFlyFrameFormat& rFormat, const OUString& rName )
+{
+ if (rFormat.GetName() == rName)
+ {
+ return;
+ }
+ OUString sName( rName );
+ if( sName.isEmpty() || FindFlyByName( sName ) )
+ {
+ TranslateId pTyp = STR_FRAME_DEFNAME;
+ const SwNodeIndex* pIdx = rFormat.GetContent().GetContentIdx();
+ if( pIdx && pIdx->GetNode().GetNodes().IsDocNodes() )
+ {
+ switch( GetNodes()[ pIdx->GetIndex() + 1 ]->GetNodeType() )
+ {
+ case SwNodeType::Grf:
+ pTyp = STR_GRAPHIC_DEFNAME;
+ break;
+ case SwNodeType::Ole:
+ pTyp = STR_OBJECT_DEFNAME;
+ break;
+ default: break;
+ }
+ }
+ sName = lcl_GetUniqueFlyName(*this, pTyp, RES_FLYFRMFMT);
+ }
+ rFormat.SetFormatName( sName, true );
+ getIDocumentState().SetModified();
+}
+
+void SwDoc::SetAllUniqueFlyNames()
+{
+ sal_Int32 n, nFlyNum = 0, nGrfNum = 0, nOLENum = 0;
+
+ const OUString sFlyNm(SwResId(STR_FRAME_DEFNAME));
+ const OUString sGrfNm(SwResId(STR_GRAPHIC_DEFNAME));
+ const OUString sOLENm(SwResId(STR_OBJECT_DEFNAME));
+
+ n = GetSpzFrameFormats()->size();
+ if( 255 < n )
+ n = 255;
+ SwFrameFormatsV aArr;
+ aArr.reserve( n );
+ SwFrameFormat* pFlyFormat;
+ bool bContainsAtPageObjWithContentAnchor = false;
+
+ for( n = GetSpzFrameFormats()->size(); n; )
+ {
+ pFlyFormat = (*GetSpzFrameFormats())[ --n ];
+ if( RES_FLYFRMFMT == pFlyFormat->Which() )
+ {
+ const OUString& aNm = pFlyFormat->GetName();
+ if ( !aNm.isEmpty() )
+ {
+ sal_Int32 *pNum = nullptr;
+ sal_Int32 nLen = 0;
+ if ( aNm.startsWith(sGrfNm) )
+ {
+ nLen = sGrfNm.getLength();
+ pNum = &nGrfNum;
+ }
+ else if( aNm.startsWith(sFlyNm) )
+ {
+ nLen = sFlyNm.getLength();
+ pNum = &nFlyNum;
+ }
+ else if( aNm.startsWith(sOLENm) )
+ {
+ nLen = sOLENm.getLength();
+ pNum = &nOLENum;
+ }
+
+ if ( pNum )
+ {
+ const sal_Int32 nNewLen = o3tl::toInt32(aNm.subView( nLen ));
+ if (*pNum < nNewLen)
+ *pNum = nNewLen;
+ }
+ }
+ else
+ // we want to set that afterwards
+ aArr.push_back( pFlyFormat );
+
+ }
+ if ( !bContainsAtPageObjWithContentAnchor )
+ {
+ const SwFormatAnchor& rAnchor = pFlyFormat->GetAnchor();
+ if ( (RndStdIds::FLY_AT_PAGE == rAnchor.GetAnchorId()) &&
+ rAnchor.GetAnchorNode() )
+ {
+ bContainsAtPageObjWithContentAnchor = true;
+ }
+ }
+ }
+ SetContainsAtPageObjWithContentAnchor( bContainsAtPageObjWithContentAnchor );
+
+ for( n = aArr.size(); n; )
+ {
+ pFlyFormat = aArr[ --n ];
+ const SwNodeIndex* pIdx = pFlyFormat->GetContent().GetContentIdx();
+ if( pIdx && pIdx->GetNode().GetNodes().IsDocNodes() )
+ {
+ switch( GetNodes()[ pIdx->GetIndex() + 1 ]->GetNodeType() )
+ {
+ case SwNodeType::Grf:
+ pFlyFormat->SetFormatName( sGrfNm + OUString::number( ++nGrfNum ));
+ break;
+ case SwNodeType::Ole:
+ pFlyFormat->SetFormatName( sOLENm + OUString::number( ++nOLENum ));
+ break;
+ default:
+ pFlyFormat->SetFormatName( sFlyNm + OUString::number( ++nFlyNum ));
+ break;
+ }
+ }
+ }
+ aArr.clear();
+
+ if( GetFootnoteIdxs().empty() )
+ return;
+
+ SwTextFootnote::SetUniqueSeqRefNo( *this );
+ // #i52775# Chapter footnotes did not get updated correctly.
+ // Calling UpdateAllFootnote() instead of UpdateFootnote() solves this problem,
+ // but I do not dare to call UpdateAllFootnote() in all cases: Safety first.
+ if ( FTNNUM_CHAPTER == GetFootnoteInfo().m_eNum )
+ {
+ GetFootnoteIdxs().UpdateAllFootnote();
+ }
+ else
+ {
+ SwNodeIndex aTmp( GetNodes() );
+ GetFootnoteIdxs().UpdateFootnote( aTmp.GetNode() );
+ }
+}
+
+bool SwDoc::IsInHeaderFooter( const SwNode& rIdx ) const
+{
+ // That can also be a Fly in a Fly in the Header.
+ // Is also used by sw3io, to determine if a Redline object is
+ // in the Header or Footer.
+ // Because Redlines are also attached to Start and EndNode,
+ // the Index must not necessarily be from a ContentNode.
+ const SwNode* pNd = &rIdx;
+ const SwNode* pFlyNd = pNd->FindFlyStartNode();
+ while( pFlyNd )
+ {
+ // get up by using the Anchor
+#if OSL_DEBUG_LEVEL > 0
+ std::vector<const SwFrameFormat*> checkFormats;
+ for(sw::SpzFrameFormat* pFormat: *GetSpzFrameFormats())
+ {
+ const SwNodeIndex* pIdx = pFormat->GetContent().GetContentIdx();
+ if( pIdx && pFlyNd == &pIdx->GetNode() )
+ checkFormats.push_back( pFormat );
+ }
+#endif
+ std::vector<SwFrameFormat*> const & rFlys(pFlyNd->GetAnchoredFlys());
+ bool bFound(false);
+ for (size_t i = 0; i < rFlys.size(); ++i)
+ {
+ const SwFrameFormat *const pFormat = rFlys[i];
+ const SwNodeIndex* pIdx = pFormat->GetContent().GetContentIdx();
+ if( pIdx && pFlyNd == &pIdx->GetNode() )
+ {
+#if OSL_DEBUG_LEVEL > 0
+ auto checkPos = std::find(
+ checkFormats.begin(), checkFormats.end(), pFormat );
+ assert( checkPos != checkFormats.end());
+ checkFormats.erase( checkPos );
+#endif
+ const SwFormatAnchor& rAnchor = pFormat->GetAnchor();
+ if ((RndStdIds::FLY_AT_PAGE == rAnchor.GetAnchorId()) ||
+ !rAnchor.GetAnchorNode() )
+ {
+ return false;
+ }
+
+ pNd = rAnchor.GetAnchorNode();
+ pFlyNd = pNd->FindFlyStartNode();
+ bFound = true;
+ break;
+ }
+ }
+ if (!bFound)
+ {
+ OSL_ENSURE(mbInReading, "Found a FlySection but not a Format!");
+ return false;
+ }
+ }
+
+ return nullptr != pNd->FindHeaderStartNode() ||
+ nullptr != pNd->FindFooterStartNode();
+}
+
+SvxFrameDirection SwDoc::GetTextDirection( const SwPosition& rPos,
+ const Point* pPt ) const
+{
+ SvxFrameDirection nRet = SvxFrameDirection::Unknown;
+
+ SwContentNode *pNd = rPos.GetNode().GetContentNode();
+
+ // #i42921# - use new method <SwContentNode::GetTextDirection(..)>
+ if ( pNd )
+ {
+ nRet = pNd->GetTextDirection( rPos, pPt );
+ }
+ if ( nRet == SvxFrameDirection::Unknown )
+ {
+ const SvxFrameDirectionItem* pItem = nullptr;
+ if( pNd )
+ {
+ // Are we in a FlyFrame? Then look at that for the correct attribute
+ const SwFrameFormat* pFlyFormat = pNd->GetFlyFormat();
+ while( pFlyFormat )
+ {
+ pItem = &pFlyFormat->GetFrameDir();
+ if( SvxFrameDirection::Environment == pItem->GetValue() )
+ {
+ pItem = nullptr;
+ const SwFormatAnchor* pAnchor = &pFlyFormat->GetAnchor();
+ if ((RndStdIds::FLY_AT_PAGE != pAnchor->GetAnchorId()) &&
+ pAnchor->GetAnchorNode())
+ {
+ pFlyFormat = pAnchor->GetAnchorNode()->GetFlyFormat();
+ }
+ else
+ pFlyFormat = nullptr;
+ }
+ else
+ pFlyFormat = nullptr;
+ }
+
+ if( !pItem )
+ {
+ const SwPageDesc* pPgDsc = pNd->FindPageDesc();
+ if( pPgDsc )
+ pItem = &pPgDsc->GetMaster().GetFrameDir();
+ }
+ }
+ if( !pItem )
+ pItem = &GetAttrPool().GetDefaultItem( RES_FRAMEDIR );
+ nRet = pItem->GetValue();
+ }
+ return nRet;
+}
+
+bool SwDoc::IsInVerticalText( const SwPosition& rPos ) const
+{
+ const SvxFrameDirection nDir = GetTextDirection( rPos );
+ return SvxFrameDirection::Vertical_RL_TB == nDir || SvxFrameDirection::Vertical_LR_TB == nDir;
+}
+
+o3tl::sorted_vector<SwRootFrame*> SwDoc::GetAllLayouts()
+{
+ o3tl::sorted_vector<SwRootFrame*> aAllLayouts;
+ SwViewShell *pStart = getIDocumentLayoutAccess().GetCurrentViewShell();
+ if(pStart)
+ {
+ for(const SwViewShell& rShell : pStart->GetRingContainer())
+ {
+ if(rShell.GetLayout())
+ aAllLayouts.insert(rShell.GetLayout());
+ }
+ }
+ return aAllLayouts;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/docnew.cxx b/sw/source/core/doc/docnew.cxx
new file mode 100644
index 0000000000..d6e885e821
--- /dev/null
+++ b/sw/source/core/doc/docnew.cxx
@@ -0,0 +1,1320 @@
+/* -*- 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 <sal/config.h>
+
+#include <string_view>
+
+#include <config_features.h>
+#include <config_fuzzers.h>
+
+#include <o3tl/sorted_vector.hxx>
+
+#include <doc.hxx>
+#include <proofreadingiterator.hxx>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/text/XFlatParagraphIteratorProvider.hpp>
+#include <com/sun/star/linguistic2/XProofreadingIterator.hpp>
+#include <com/sun/star/i18n/ScriptType.hpp>
+
+#include <comphelper/processfactory.hxx>
+#include <comphelper/random.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/XmlIdRegistry.hxx>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+
+#include <sfx2/linkmgr.hxx>
+#include <editeng/ulspitem.hxx>
+#include <editeng/lrspitem.hxx>
+#include <svl/numformat.hxx>
+#include <unotools/lingucfg.hxx>
+#include <svx/svdpage.hxx>
+#include <fmtcntnt.hxx>
+#include <fmtanchr.hxx>
+#include <fmtfsize.hxx>
+#include <fmtfordr.hxx>
+#include <fmtpdsc.hxx>
+#include <pvprtdat.hxx>
+#include <rootfrm.hxx>
+#include <pagedesc.hxx>
+#include <ndtxt.hxx>
+#include <ftninfo.hxx>
+#include <ftnidx.hxx>
+#include <charfmt.hxx>
+#include <frmfmt.hxx>
+#include <poolfmt.hxx>
+#include <dbmgr.hxx>
+#include <docsh.hxx>
+#include <acorrect.hxx>
+#include <visiturl.hxx>
+#include <docary.hxx>
+#include <lineinfo.hxx>
+#include <drawdoc.hxx>
+#include <extinput.hxx>
+#include <viewsh.hxx>
+#include <doctxm.hxx>
+#include <shellres.hxx>
+#include <laycache.hxx>
+#include <mvsave.hxx>
+#include <istyleaccess.hxx>
+#include "swstylemanager.hxx"
+#include <GrammarContact.hxx>
+#include <OnlineAccessibilityCheck.hxx>
+#include <tblafmt.hxx>
+#include <MarkManager.hxx>
+#include <UndoManager.hxx>
+#include <DocumentDeviceManager.hxx>
+#include <DocumentSettingManager.hxx>
+#include <DocumentDrawModelManager.hxx>
+#include <DocumentChartDataProviderManager.hxx>
+#include <DocumentTimerManager.hxx>
+#include <DocumentLinksAdministrationManager.hxx>
+#include <DocumentListItemsManager.hxx>
+#include <DocumentListsManager.hxx>
+#include <DocumentOutlineNodesManager.hxx>
+#include <DocumentContentOperationsManager.hxx>
+#include <DocumentRedlineManager.hxx>
+#include <DocumentFieldsManager.hxx>
+#include <DocumentStatisticsManager.hxx>
+#include <DocumentStateManager.hxx>
+#include <DocumentLayoutManager.hxx>
+#include <DocumentStylePoolManager.hxx>
+#include <DocumentExternalDataManager.hxx>
+#include <wrtsh.hxx>
+#include <unocrsr.hxx>
+#include <fmthdft.hxx>
+#include <frameformats.hxx>
+
+#include <numrule.hxx>
+
+#include <sfx2/Metadatable.hxx>
+#include <fmtmeta.hxx>
+#include <textcontentcontrol.hxx>
+
+#include <svx/xfillit0.hxx>
+#include <unotools/configmgr.hxx>
+#include <i18nlangtag/mslangid.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::document;
+
+constexpr OUStringLiteral DEFAULT_CHAR_FORMAT_NAME = u"Character style";
+
+/*
+ * global functions...
+ */
+ uno::Reference< linguistic2::XProofreadingIterator > const & SwDoc::GetGCIterator() const
+{
+ if (!m_xGCIterator.is() && SvtLinguConfig().HasGrammarChecker())
+ {
+ uno::Reference< uno::XComponentContext > xContext( comphelper::getProcessComponentContext() );
+ try
+ {
+ m_xGCIterator = sw::proofreadingiterator::get( xContext );
+ }
+ catch (const uno::Exception &)
+ {
+ OSL_FAIL( "No GCIterator" );
+ }
+ }
+
+ return m_xGCIterator;
+}
+
+bool SwDoc::StartGrammarChecking( bool bSkipStart )
+{
+ // check for a visible view
+ bool bVisible = false;
+ bool bStarted = false;
+ const SwDocShell *pDocShell = GetDocShell();
+ SfxViewFrame *pFrame = SfxViewFrame::GetFirst( pDocShell, false );
+ while (pFrame && !bVisible)
+ {
+ if (pFrame->IsVisible())
+ bVisible = true;
+ pFrame = SfxViewFrame::GetNext( *pFrame, pDocShell, false );
+ }
+
+ //!! only documents with visible views need to be checked
+ //!! (E.g. don't check temporary documents created for printing, see printing of notes and selections.
+ //!! Those get created on the fly and get hard deleted a bit later as well, and no one should have
+ //!! a UNO reference to them)
+ if (bVisible)
+ {
+ uno::Reference< linguistic2::XProofreadingIterator > xGCIterator( GetGCIterator() );
+ if ( xGCIterator.is() )
+ {
+ uno::Reference< lang::XComponent > xDoc = GetDocShell()->GetBaseModel();
+ uno::Reference< text::XFlatParagraphIteratorProvider > xFPIP( xDoc, uno::UNO_QUERY );
+
+ // start automatic background checking if not active already
+ if ( xFPIP.is() && !xGCIterator->isProofreading( xDoc ) )
+ {
+ bStarted = true;
+ if ( !bSkipStart )
+ {
+ for (auto pLayout : GetAllLayouts())
+ { // we're starting it now, don't start grammar checker
+ // again until the user modifies the document
+ pLayout->SetNeedGrammarCheck(false);
+ }
+ xGCIterator->startProofreading( xDoc, xFPIP );
+ }
+ }
+ }
+ }
+
+ return bStarted;
+}
+
+/*
+ * internal functions
+ */
+static void lcl_DelFormatIndices( SwFormat const * pFormat )
+{
+ SwFormatContent &rFormatContent = const_cast<SwFormatContent&>(pFormat->GetContent());
+ if ( rFormatContent.GetContentIdx() )
+ rFormatContent.SetNewContentIdx( nullptr );
+ SwFormatAnchor &rFormatAnchor = const_cast<SwFormatAnchor&>(pFormat->GetAnchor());
+ if ( rFormatAnchor.GetAnchorNode() )
+ rFormatAnchor.SetAnchor( nullptr );
+}
+
+/*
+ * exported methods
+ */
+SwDoc::SwDoc()
+ : m_pNodes(new SwNodes(*this)),
+ mpAttrPool(new SwAttrPool(this)),
+ maOLEModifiedIdle( "sw::SwDoc maOLEModifiedIdle" ),
+ mpMarkManager(new ::sw::mark::MarkManager(*this)),
+ m_pMetaFieldManager(new ::sw::MetaFieldManager()),
+ m_pContentControlManager(new ::SwContentControlManager()),
+ m_pDocumentDrawModelManager( new ::sw::DocumentDrawModelManager( *this ) ),
+ m_pDocumentRedlineManager( new ::sw::DocumentRedlineManager( *this ) ),
+ m_pDocumentStateManager( new ::sw::DocumentStateManager( *this ) ),
+ m_pUndoManager(new ::sw::UndoManager(
+ std::shared_ptr<SwNodes>(new SwNodes(*this)), *m_pDocumentDrawModelManager, *m_pDocumentRedlineManager, *m_pDocumentStateManager)),
+ m_pDocumentSettingManager(new ::sw::DocumentSettingManager(*this)),
+ m_pDocumentChartDataProviderManager( new sw::DocumentChartDataProviderManager( *this ) ),
+ m_pDeviceAccess( new ::sw::DocumentDeviceManager( *this ) ),
+ m_pDocumentTimerManager( new ::sw::DocumentTimerManager( *this ) ),
+ m_pDocumentLinksAdministrationManager( new ::sw::DocumentLinksAdministrationManager( *this ) ),
+ m_pDocumentListItemsManager( new ::sw::DocumentListItemsManager() ),
+ m_pDocumentListsManager( new ::sw::DocumentListsManager( *this ) ),
+ m_pDocumentOutlineNodesManager( new ::sw::DocumentOutlineNodesManager( *this ) ),
+ m_pDocumentContentOperationsManager( new ::sw::DocumentContentOperationsManager( *this ) ),
+ m_pDocumentFieldsManager( new ::sw::DocumentFieldsManager( *this ) ),
+ m_pDocumentStatisticsManager( new ::sw::DocumentStatisticsManager( *this ) ),
+ m_pDocumentLayoutManager( new ::sw::DocumentLayoutManager( *this ) ),
+ m_pDocumentStylePoolManager( new ::sw::DocumentStylePoolManager( *this ) ),
+ m_pDocumentExternalDataManager( new ::sw::DocumentExternalDataManager ),
+ mpDfltFrameFormat( new SwFrameFormat( GetAttrPool(), "Frameformat", nullptr ) ),
+ mpEmptyPageFormat( new SwFrameFormat( GetAttrPool(), "Empty Page", mpDfltFrameFormat.get() ) ),
+ mpColumnContFormat( new SwFrameFormat( GetAttrPool(), "Columncontainer", mpDfltFrameFormat.get() ) ),
+ mpDfltCharFormat( new SwCharFormat( GetAttrPool(), DEFAULT_CHAR_FORMAT_NAME, nullptr ) ),
+ mpDfltTextFormatColl( new SwTextFormatColl( GetAttrPool(), "Paragraph style" ) ),
+ mpDfltGrfFormatColl( new SwGrfFormatColl( GetAttrPool(), "Graphikformatvorlage" ) ),
+ mpFrameFormatTable( new sw::FrameFormats<SwFrameFormat*>() ),
+ mpCharFormatTable( new SwCharFormats ),
+ mpSpzFrameFormatTable( new sw::FrameFormats<sw::SpzFrameFormat*>() ),
+ mpSectionFormatTable( new SwSectionFormats ),
+ mpTableFrameFormatTable( new sw::TableFrameFormats() ),
+ mpTextFormatCollTable( new SwTextFormatColls() ),
+ mpGrfFormatCollTable( new SwGrfFormatColls() ),
+ mpTOXTypes( new SwTOXTypes ),
+ mpDefTOXBases( new SwDefTOXBase_Impl() ),
+ mpOutlineRule( nullptr ),
+ mpFootnoteInfo( new SwFootnoteInfo ),
+ mpEndNoteInfo( new SwEndNoteInfo ),
+ mpLineNumberInfo( new SwLineNumberInfo ),
+ mpFootnoteIdxs( new SwFootnoteIdxs ),
+ mpDocShell( nullptr ),
+ mpNumberFormatter( nullptr ),
+ mpNumRuleTable( new SwNumRuleTable ),
+ mpExtInputRing( nullptr ),
+ mpGrammarContact(new sw::GrammarContact),
+ mpOnlineAccessibilityCheck(new sw::OnlineAccessibilityCheck(*this)),
+ mpCellStyles(new SwCellStyleTable),
+ mReferenceCount(0),
+ mbDtor(false),
+ mbCopyIsMove(false),
+ mbInReading(false),
+ mbInWriting(false),
+ mbInMailMerge(false),
+ mbInXMLImport(false),
+ mbInWriterfilterImport(false),
+ mbUpdateTOX(false),
+ mbInLoadAsynchron(false),
+ mbIsAutoFormatRedline(false),
+ mbOLEPrtNotifyPending(false),
+ mbAllOLENotify(false),
+ mbInsOnlyTextGlssry(false),
+ mbContains_MSVBasic(false),
+ mbClipBoard( false ),
+ mbColumnSelection( false ),
+ mbIsPrepareSelAll(false),
+ meDictionaryMissing( MissingDictionary::Undefined ),
+ mbContainsAtPageObjWithContentAnchor(false), //#i119292#, fdo#37024
+ meDocType(DOCTYPE_NATIVE)
+{
+ // The DrawingLayer ItemPool which is used as 2nd pool for Writer documents' pool
+ // has a default for the XFillStyleItem of XFILL_SOLID and the color for it is the default
+ // fill color (blue7 or similar). This is a problem, in Writer we want the default fill
+ // style to be drawing::FillStyle_NONE. This cannot simply be done by changing it in the 2nd pool at the
+ // pool defaults when the DrawingLayer ItemPool is used for Writer, that would lead to
+ // countless problems like DrawObjects initial fill and others.
+ // It is also hard to find all places where the initial ItemSets for Writer (including
+ // style hierarchies) are created and to always set (but only at the root) the FillStyle
+ // to NONE fixed; that will add that attribute to the file format. It will be hard to reset
+ // attribute sets (which is done at import and using UI). Also not a good solution.
+ // Luckily Writer uses pDfltTextFormatColl as default parent for all paragraphs and similar, thus
+ // it is possible to set this attribute here. It will be not reset when importing.
+ mpDfltTextFormatColl->SetFormatAttr(XFillStyleItem(drawing::FillStyle_NONE));
+ mpDfltFrameFormat->SetFormatAttr(XFillStyleItem(drawing::FillStyle_NONE));
+ // prevent paragraph default margins being applied to everything
+ mpDfltFrameFormat->SetFormatAttr(SvxULSpaceItem(RES_UL_SPACE));
+ mpDfltFrameFormat->SetFormatAttr(SvxLRSpaceItem(RES_LR_SPACE));
+
+ /*
+ * DefaultFormats and DefaultFormatCollections (FormatColl)
+ * are inserted at position 0 at the respective array.
+ * The formats in the FormatColls are derived from the
+ * DefaultFormats and are also in the list.
+ */
+ /* Formats */
+ mpFrameFormatTable->push_back(mpDfltFrameFormat.get());
+ mpCharFormatTable->insert(mpDfltCharFormat.get());
+
+ /* FormatColls */
+ // TXT
+ mpTextFormatCollTable->push_back(mpDfltTextFormatColl.get());
+ // GRF
+ mpGrfFormatCollTable->push_back(mpDfltGrfFormatColl.get());
+
+ // Create PageDesc, EmptyPageFormat and ColumnFormat
+ if (m_PageDescs.empty())
+ getIDocumentStylePoolAccess().GetPageDescFromPool( RES_POOLPAGE_STANDARD );
+
+ // Set to "Empty Page"
+ mpEmptyPageFormat->SetFormatAttr( SwFormatFrameSize( SwFrameSize::Fixed ) );
+ // Set BodyFormat for columns
+ mpColumnContFormat->SetFormatAttr( SwFormatFillOrder( ATT_LEFT_TO_RIGHT ) );
+
+ GetDocumentFieldsManager().InitFieldTypes();
+
+ // Create a default OutlineNumRule (for Filters)
+ mpOutlineRule = new SwNumRule( SwNumRule::GetOutlineRuleName(),
+ // #i89178#
+ numfunc::GetDefaultPositionAndSpaceMode(),
+ OUTLINE_RULE );
+ AddNumRule(mpOutlineRule);
+ // Counting of phantoms depends on <IsOldNumbering()>
+ mpOutlineRule->SetCountPhantoms( !GetDocumentSettingManager().get(DocumentSettingId::OLD_NUMBERING) );
+
+ new SwTextNode(
+ GetUndoManager().GetUndoNodes().GetEndOfContent(),
+ mpDfltTextFormatColl.get() );
+ new SwTextNode( GetNodes().GetEndOfContent(),
+ getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_STANDARD ));
+
+ maOLEModifiedIdle.SetPriority( TaskPriority::LOWEST );
+ maOLEModifiedIdle.SetInvokeHandler( LINK( this, SwDoc, DoUpdateModifiedOLE ));
+
+#if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS
+ // Create DBManager
+ m_pOwnDBManager.reset(new SwDBManager(this));
+ m_pDBManager = m_pOwnDBManager.get();
+#else
+ m_pDBManager = nullptr;
+#endif
+
+ // create TOXTypes
+ InitTOXTypes();
+
+ // pass empty item set containing the paragraph's list attributes
+ // as ignorable items to the stype manager.
+ {
+ SfxItemSetFixed<RES_PARATR_LIST_BEGIN, RES_PARATR_LIST_END-1> aIgnorableParagraphItems( GetAttrPool() );
+ mpStyleAccess = createStyleManager( &aIgnorableParagraphItems );
+ }
+
+ static bool bHack = (getenv("LIBO_ONEWAY_STABLE_ODF_EXPORT") != nullptr);
+
+ if (bHack)
+ {
+ mnRsid = 0;
+ }
+ else
+ {
+ // Initialize the session id of the current document to a random number
+ // smaller than 2^21.
+ mnRsid = comphelper::rng::uniform_uint_distribution(1, (1 << 21) - 1);
+ }
+ mnRsidRoot = mnRsid;
+
+ if (!utl::ConfigManager::IsFuzzing())
+ {
+ // Make sure that in case the document language is not set, then we don't return
+ // LANGUAGE_DONTKNOW, but the UI locale.
+ const SvtLinguConfig aLinguConfig;
+ SvtLinguOptions aOptions;
+ aLinguConfig.GetOptions(aOptions);
+ LanguageType eLang = MsLangId::resolveSystemLanguageByScriptType(aOptions.nDefaultLanguage,
+ i18n::ScriptType::LATIN);
+ SetLanguage(eLang, RES_CHRATR_LANGUAGE);
+ eLang = MsLangId::resolveSystemLanguageByScriptType(aOptions.nDefaultLanguage_CJK,
+ i18n::ScriptType::ASIAN);
+ SetLanguage(eLang, RES_CHRATR_CJK_LANGUAGE);
+ eLang = MsLangId::resolveSystemLanguageByScriptType(aOptions.nDefaultLanguage_CTL,
+ i18n::ScriptType::COMPLEX);
+ SetLanguage(eLang, RES_CHRATR_CTL_LANGUAGE);
+ }
+
+ getIDocumentState().ResetModified();
+
+ s_pLast = this;
+}
+
+/**
+ * Speciality: a member of the class SwDoc is located at
+ * position 0 in the array of the Format and GDI objects.
+ * This MUST not be destroyed using 'delete' in any case!
+ */
+SwDoc::~SwDoc()
+{
+ s_pLast = nullptr;
+
+ mxVbaFind.clear();
+
+ // nothing here should create Undo actions!
+ GetIDocumentUndoRedo().DoUndo(false);
+
+ if (mpDocShell)
+ {
+ mpDocShell->SetUndoManager(nullptr);
+ }
+
+ mpGrammarContact.reset();
+ mpOnlineAccessibilityCheck.reset();
+
+ getIDocumentTimerAccess().StopIdling(); // stop idle timer
+
+ mpURLStateChgd.reset();
+
+ // Deactivate Undo notification from Draw
+ if( GetDocumentDrawModelManager().GetDrawModel() )
+ {
+ GetDocumentDrawModelManager().DrawNotifyUndoHdl();
+ ClrContourCache();
+ }
+
+ m_pPgPViewPrtData.reset();
+
+ mbDtor = true;
+
+ //Clear the redline table before the nodes array is destroyed
+ getIDocumentRedlineAccess().GetRedlineTable().DeleteAndDestroyAll();
+ getIDocumentRedlineAccess().GetExtraRedlineTable().DeleteAndDestroyAll();
+
+ const sw::UnoCursorHint aHint;
+ cleanupUnoCursorTable();
+ for(const auto& pWeakCursor : mvUnoCursorTable)
+ {
+ auto pCursor(pWeakCursor.lock());
+ if(pCursor)
+ pCursor->m_aNotifier.Broadcast(aHint);
+ }
+ mpACEWord.reset();
+
+ // Release the BaseLinks
+ {
+ ::sfx2::SvLinkSources aTemp(getIDocumentLinksAdministration().GetLinkManager().GetServers());
+ for( const auto& rpLinkSrc : aTemp )
+ rpLinkSrc->Closed();
+
+ if( !getIDocumentLinksAdministration().GetLinkManager().GetLinks().empty() )
+ getIDocumentLinksAdministration().GetLinkManager().Remove( 0, getIDocumentLinksAdministration().GetLinkManager().GetLinks().size() );
+ }
+
+ // The ChapterNumbers/Numbers need to be deleted before the styles
+ // or we update all the time!
+ m_pNodes->m_aOutlineNodes.clear();
+ SwNodes & rUndoNodes( GetUndoManager().GetUndoNodes() );
+ rUndoNodes.m_aOutlineNodes.clear();
+
+ mpFootnoteIdxs->clear();
+
+ // indices could be registered in attributes
+ m_pUndoManager->DelAllUndoObj();
+
+ // The BookMarks contain indices to the Content. These must be deleted
+ // before deleting the Nodes.
+ mpMarkManager->clearAllMarks();
+
+ if( mpExtInputRing )
+ {
+ SwPaM* pTmp = mpExtInputRing;
+ mpExtInputRing = nullptr;
+ while( pTmp->GetNext() != pTmp )
+ {
+ // coverity[deref_arg] - the SwPaM delete moves a new entry into GetNext()
+ delete pTmp->GetNext();
+ }
+ delete pTmp;
+ }
+
+ // Any of the FrameFormats can still have indices registered.
+ // These need to be destroyed now at the latest.
+ for( SwFrameFormat* pFormat : *mpFrameFormatTable )
+ lcl_DelFormatIndices( pFormat );
+ for( SwFrameFormat* pFormat : *mpSpzFrameFormatTable )
+ lcl_DelFormatIndices( pFormat );
+ for( SwSectionFormat* pFormat : *mpSectionFormatTable )
+ lcl_DelFormatIndices( pFormat );
+
+ // The formats/styles that follow depend on the default formats.
+ // Destroy these only after destroying the FormatIndices, because the content
+ // of headers/footers has to be deleted as well. If in the headers/footers
+ // there are still Flys registered at that point, we have a problem.
+ for( SwPageDesc *pPageDesc : m_PageDescs )
+ delete pPageDesc;
+ m_PageDescs.clear();
+
+ // Delete content selections.
+ // Don't wait for the SwNodes dtor to destroy them; so that Formats
+ // do not have any dependencies anymore.
+ m_pNodes->DelNodes( SwNodeIndex(*m_pNodes), m_pNodes->Count() );
+ rUndoNodes.DelNodes( SwNodeIndex( rUndoNodes ), rUndoNodes.Count() );
+
+ // clear TOX after nodes - TOXMarks are gone now so SwTOXType has no clients
+ for (const auto& pType : *mpTOXTypes)
+ {
+ pType->CallSwClientNotify(sw::DocumentDyingHint());
+ }
+ mpTOXTypes->clear();
+ mpDefTOXBases.reset();
+
+ // Delete Formats, make it permanent some time in the future
+
+ // Delete for Collections
+ // So that we get rid of the dependencies
+ mpFootnoteInfo->EndListeningAll();
+ mpEndNoteInfo->EndListeningAll();
+
+ assert(mpDfltTextFormatColl.get() == (*mpTextFormatCollTable)[0]
+ && "Default-Text-Collection must always be at the start");
+
+ // Optimization: Based on the fact that Standard is always 2nd in the
+ // array, we should delete it as the last. With this we avoid
+ // reparenting the Formats all the time!
+ if( 2 < mpTextFormatCollTable->size() )
+ mpTextFormatCollTable->DeleteAndDestroy(2, mpTextFormatCollTable->size());
+ mpTextFormatCollTable->DeleteAndDestroy(1, mpTextFormatCollTable->size());
+ mpTextFormatCollTable.reset();
+
+ assert(mpDfltGrfFormatColl.get() == (*mpGrfFormatCollTable)[0]
+ && "DefaultGrfCollection must always be at the start");
+
+ mpGrfFormatCollTable->DeleteAndDestroy(1, mpGrfFormatCollTable->size());
+ mpGrfFormatCollTable.reset();
+
+ // Without explicitly freeing the DocumentDeviceManager
+ // and relying on the implicit freeing there would be a crash
+ // due to it happening after SwAttrPool is freed.
+ m_pDeviceAccess.reset();
+
+ /*
+ * DefaultFormats and DefaultFormatCollections (FormatColl)
+ * are at position 0 of their respective arrays.
+ * In order to not be deleted by the array's dtor, we remove them
+ * now.
+ */
+ mpFrameFormatTable->erase( mpFrameFormatTable->begin() );
+
+#if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS
+ // On load, SwDBManager::setEmbeddedName() may register a data source.
+ // If we have an embedded one, then sDataSource points to the registered name, so revoke it here.
+ if (!m_pOwnDBManager->getEmbeddedName().isEmpty() && !maDBData.sDataSource.isEmpty())
+ {
+ // Remove the revoke listener here first, so that we don't remove the data source from the document.
+ m_pOwnDBManager->releaseRevokeListener();
+ SwDBManager::RevokeDataSource(maDBData.sDataSource);
+ SwDBManager::RevokeDataSource(m_pOwnDBManager->getEmbeddedName());
+ }
+ else if (!m_pOwnDBManager->getEmbeddedName().isEmpty())
+ {
+ // Remove the revoke listener here first, so that we don't remove the data source from the document.
+ m_pOwnDBManager->releaseRevokeListener();
+ // Remove connections which was committed but not used.
+ m_pOwnDBManager->RevokeNotUsedConnections();
+ }
+
+ m_pOwnDBManager.reset();
+#endif
+
+ // All Flys need to be destroyed before the Drawing Model,
+ // because Flys can still contain DrawContacts, when no
+ // Layout could be constructed due to a read error.
+ mpSpzFrameFormatTable->DeleteAndDestroyAll();
+
+ // Only now destroy the Model, the drawing objects - which are also
+ // contained in the Undo - need to remove their attributes from the
+ // Model. Also, DrawContacts could exist before this.
+ GetDocumentDrawModelManager().ReleaseDrawModel();
+ // Destroy DrawModel before the LinkManager, because it's always set
+ // in the DrawModel.
+ //The LinkManager gets destroyed automatically with m_pLinksAdministrationManager
+
+ // Clear the Tables before deleting the defaults, or we crash due to
+ // dependencies on defaults.
+ mpFrameFormatTable.reset();
+ mpSpzFrameFormatTable.reset();
+
+ mpStyleAccess.reset();
+
+ mpCharFormatTable.reset();
+ mpSectionFormatTable.reset();
+ mpTableFrameFormatTable.reset();
+ mpDfltTextFormatColl.reset();
+ mpDfltGrfFormatColl.reset();
+ mpNumRuleTable.reset();
+
+ disposeXForms(); // #i113606#, dispose the XForms objects
+
+ {
+ std::scoped_lock lock(mNumberFormatterMutex);
+ delete mpNumberFormatter; mpNumberFormatter= nullptr;
+ }
+ mpFootnoteInfo.reset();
+ mpEndNoteInfo.reset();
+ mpLineNumberInfo.reset();
+ mpFootnoteIdxs.reset();
+ mpTOXTypes.reset();
+ mpEmptyPageFormat.reset();
+ mpColumnContFormat.reset();
+ mpDfltCharFormat.reset();
+ mpDfltFrameFormat.reset();
+ mpLayoutCache.reset();
+ mpAttrPool.clear();
+}
+
+void SwDoc::SetDocShell( SwDocShell* pDSh )
+{
+ if( mpDocShell == pDSh )
+ return;
+
+ if (mpDocShell)
+ {
+ mpDocShell->SetUndoManager(nullptr);
+ }
+ mpDocShell = pDSh;
+ if (mpDocShell)
+ {
+ mpDocShell->SetUndoManager(& GetUndoManager());
+ GetUndoManager().SetDocShell(mpDocShell);
+ }
+
+ getIDocumentLinksAdministration().GetLinkManager().SetPersist( mpDocShell );
+
+ // set DocShell pointer also on DrawModel
+ InitDrawModelAndDocShell(mpDocShell, GetDocumentDrawModelManager().GetDrawModel());
+ assert(!GetDocumentDrawModelManager().GetDrawModel() ||
+ GetDocumentDrawModelManager().GetDrawModel()->GetPersist() == GetPersist());
+}
+
+// Convenience method; to avoid excessive includes from docsh.hxx
+uno::Reference < embed::XStorage > SwDoc::GetDocStorage()
+{
+ if( mpDocShell )
+ return mpDocShell->GetStorage();
+ if( getIDocumentLinksAdministration().GetLinkManager().GetPersist() )
+ return getIDocumentLinksAdministration().GetLinkManager().GetPersist()->GetStorage();
+ return nullptr;
+}
+
+SfxObjectShell* SwDoc::GetPersist() const
+{
+ return mpDocShell ? mpDocShell : getIDocumentLinksAdministration().GetLinkManager().GetPersist();
+}
+
+void SwDoc::ClearDoc()
+{
+ GetIDocumentUndoRedo().DelAllUndoObj();
+ ::sw::UndoGuard const undoGuard(GetIDocumentUndoRedo());
+
+ // Deactivate Undo notification from Draw
+ if( GetDocumentDrawModelManager().GetDrawModel() )
+ {
+ GetDocumentDrawModelManager().DrawNotifyUndoHdl();
+ ClrContourCache();
+ }
+
+ // if there are still FlyFrames dangling around, delete them too
+ while ( !mpSpzFrameFormatTable->empty() )
+ getIDocumentLayoutAccess().DelLayoutFormat((*mpSpzFrameFormatTable)[mpSpzFrameFormatTable->size()-1]);
+ assert(!GetDocumentDrawModelManager().GetDrawModel()
+ || !GetDocumentDrawModelManager().GetDrawModel()->GetPage(0)->GetObjCount());
+
+ getIDocumentRedlineAccess().GetRedlineTable().DeleteAndDestroyAll();
+ getIDocumentRedlineAccess().GetExtraRedlineTable().DeleteAndDestroyAll();
+
+ mpACEWord.reset();
+
+ // The BookMarks contain indices to the Content. These must be deleted
+ // before deleting the Nodes.
+ mpMarkManager->clearAllMarks();
+ InitTOXTypes();
+
+ // create a dummy pagedesc for the layout
+ SwPageDesc* pDummyPgDsc = MakePageDesc("?DUMMY?");
+
+ SwNodeIndex aSttIdx( *GetNodes().GetEndOfContent().StartOfSectionNode(), 1 );
+ // create the first one over and over again (without attributes/style etc.
+ SwTextNode* pFirstNd = GetNodes().MakeTextNode( aSttIdx.GetNode(), mpDfltTextFormatColl.get() );
+
+ if( getIDocumentLayoutAccess().GetCurrentViewShell() )
+ {
+ // set the layout to the dummy pagedesc
+ pFirstNd->SetAttr( SwFormatPageDesc( pDummyPgDsc ));
+
+ SwPosition aPos( *pFirstNd );
+ SwPaM const tmpPaM(aSttIdx.GetNode(), GetNodes().GetEndOfContent());
+ ::PaMCorrAbs(tmpPaM, aPos);
+ }
+
+ GetNodes().Delete( aSttIdx,
+ GetNodes().GetEndOfContent().GetIndex() - aSttIdx.GetIndex() );
+
+ // #i62440#
+ // destruction of numbering rules and creation of new outline rule
+ // *after* the document nodes are deleted.
+ mpOutlineRule = nullptr;
+ for( SwNumRule* pNumRule : *mpNumRuleTable )
+ {
+ getIDocumentListsAccess().deleteListForListStyle(pNumRule->GetName());
+ delete pNumRule;
+ }
+ mpNumRuleTable->clear();
+ maNumRuleMap.clear();
+
+ // creation of new outline numbering rule
+ mpOutlineRule = new SwNumRule( SwNumRule::GetOutlineRuleName(),
+ // #i89178#
+ numfunc::GetDefaultPositionAndSpaceMode(),
+ OUTLINE_RULE );
+ AddNumRule(mpOutlineRule);
+ // Counting of phantoms depends on <IsOldNumbering()>
+ mpOutlineRule->SetCountPhantoms( !GetDocumentSettingManager().get(DocumentSettingId::OLD_NUMBERING) );
+
+ // remove the dummy pagedesc from the array and delete all the old ones
+ size_t nDummyPgDsc = 0;
+ if (FindPageDesc(pDummyPgDsc->GetName(), &nDummyPgDsc))
+ m_PageDescs.erase( nDummyPgDsc );
+ for( SwPageDesc *pPageDesc : m_PageDescs )
+ delete pPageDesc;
+ m_PageDescs.clear();
+
+ // Delete for Collections
+ // So that we get rid of the dependencies
+ mpFootnoteInfo->EndListeningAll();
+ mpEndNoteInfo->EndListeningAll();
+
+ // Optimization: Based on the fact that Standard is always 2nd in the
+ // array, we should delete it as the last. With this we avoid
+ // reparenting the Formats all the time!
+ if( 2 < mpTextFormatCollTable->size() )
+ mpTextFormatCollTable->DeleteAndDestroy(2, mpTextFormatCollTable->size());
+ mpTextFormatCollTable->DeleteAndDestroy(1, mpTextFormatCollTable->size());
+ mpGrfFormatCollTable->DeleteAndDestroy(1, mpGrfFormatCollTable->size());
+ mpCharFormatTable->DeleteAndDestroyAll(/*keepDefault*/true);
+
+ if( getIDocumentLayoutAccess().GetCurrentViewShell() )
+ {
+ // search the FrameFormat of the root frm. This is not allowed to delete
+ mpFrameFormatTable->erase( getIDocumentLayoutAccess().GetCurrentViewShell()->GetLayout()->GetFormat() );
+ mpFrameFormatTable->DeleteAndDestroyAll( true );
+ mpFrameFormatTable->push_back( getIDocumentLayoutAccess().GetCurrentViewShell()->GetLayout()->GetFormat() );
+ }
+ else
+ mpFrameFormatTable->DeleteAndDestroyAll( true );
+
+ GetDocumentFieldsManager().ClearFieldTypes();
+
+ {
+ std::scoped_lock lock(mNumberFormatterMutex);
+ delete mpNumberFormatter; mpNumberFormatter= nullptr;
+ }
+
+ getIDocumentStylePoolAccess().GetPageDescFromPool( RES_POOLPAGE_STANDARD );
+ pFirstNd->ChgFormatColl( getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_STANDARD ));
+ nDummyPgDsc = m_PageDescs.size();
+ m_PageDescs.push_back( pDummyPgDsc );
+ // set the layout back to the new standard pagedesc
+ pFirstNd->ResetAllAttr();
+ // delete now the dummy pagedesc
+ DelPageDesc( nDummyPgDsc );
+}
+
+void SwDoc::SetPreviewPrtData( const SwPagePreviewPrtData* pNew )
+{
+ if( pNew )
+ {
+ if (m_pPgPViewPrtData)
+ {
+ *m_pPgPViewPrtData = *pNew;
+ }
+ else
+ {
+ m_pPgPViewPrtData.reset(new SwPagePreviewPrtData(*pNew));
+ }
+ }
+ else if (m_pPgPViewPrtData)
+ {
+ m_pPgPViewPrtData.reset();
+ }
+ getIDocumentState().SetModified();
+}
+
+void SwDoc::SetOLEObjModified()
+{
+ if( getIDocumentLayoutAccess().GetCurrentViewShell() ) maOLEModifiedIdle.Start();
+}
+
+/** SwDoc: Reading and writing of the layout cache. */
+void SwDoc::ReadLayoutCache( SvStream& rStream )
+{
+ if( !mpLayoutCache )
+ mpLayoutCache.reset( new SwLayoutCache() );
+ if( !mpLayoutCache->IsLocked() )
+ {
+ mpLayoutCache->GetLockCount() |= 0x8000;
+ mpLayoutCache->Read( rStream );
+ mpLayoutCache->GetLockCount() &= 0x7fff;
+ }
+}
+
+void SwDoc::WriteLayoutCache( SvStream& rStream )
+{
+ SwLayoutCache::Write( rStream, *this );
+}
+
+::sfx2::IXmlIdRegistry&
+SwDoc::GetXmlIdRegistry()
+{
+ // UGLY: this relies on SetClipBoard being called before GetXmlIdRegistry!
+ if (!m_pXmlIdRegistry)
+ {
+ m_pXmlIdRegistry.reset( ::sfx2::createXmlIdRegistry( IsClipBoard() ) );
+ }
+ return *m_pXmlIdRegistry;
+}
+
+void SwDoc::InitTOXTypes()
+{
+ ShellResource* pShellRes = SwViewShell::GetShellRes();
+ SwTOXType* pNew = new SwTOXType(*this, TOX_CONTENT, pShellRes->aTOXContentName);
+ mpTOXTypes->emplace_back( pNew );
+ pNew = new SwTOXType(*this, TOX_INDEX, pShellRes->aTOXIndexName);
+ mpTOXTypes->emplace_back( pNew );
+ pNew = new SwTOXType(*this, TOX_USER, pShellRes->aTOXUserName);
+ mpTOXTypes->emplace_back( pNew );
+ pNew = new SwTOXType(*this, TOX_ILLUSTRATIONS, pShellRes->aTOXIllustrationsName);
+ mpTOXTypes->emplace_back( pNew );
+ pNew = new SwTOXType(*this, TOX_OBJECTS, pShellRes->aTOXObjectsName);
+ mpTOXTypes->emplace_back( pNew );
+ pNew = new SwTOXType(*this, TOX_TABLES, pShellRes->aTOXTablesName);
+ mpTOXTypes->emplace_back( pNew );
+ pNew = new SwTOXType(*this, TOX_AUTHORITIES, pShellRes->aTOXAuthoritiesName);
+ mpTOXTypes->emplace_back( pNew );
+ pNew = new SwTOXType(*this, TOX_CITATION, pShellRes->aTOXCitationName);
+ mpTOXTypes->emplace_back( pNew );
+}
+
+void SwDoc::ReplaceDefaults(const SwDoc& rSource)
+{
+ // copy property defaults
+ static const WhichRangesContainer aRangeOfDefaults(svl::Items<
+ RES_CHRATR_BEGIN, RES_CHRATR_END-1,
+ RES_PARATR_BEGIN, RES_PARATR_END-1,
+ RES_PARATR_LIST_BEGIN, RES_PARATR_LIST_END-1,
+ RES_FRMATR_BEGIN, RES_FRMATR_END-1,
+ RES_UNKNOWNATR_BEGIN, RES_UNKNOWNATR_END-1,
+ XATTR_START, XATTR_END-1
+ >);
+
+ SfxItemSet aNewDefaults(GetAttrPool(), aRangeOfDefaults);
+
+ for (const WhichPair& rPair : aRangeOfDefaults)
+ {
+ for (sal_uInt16 nWhich = rPair.first;
+ nWhich <= rPair.second; ++nWhich)
+ {
+ const SfxPoolItem& rSourceAttr =
+ rSource.mpAttrPool->GetDefaultItem(nWhich);
+ if (rSourceAttr != mpAttrPool->GetDefaultItem(nWhich))
+ aNewDefaults.Put(rSourceAttr);
+ }
+ }
+
+ if (aNewDefaults.Count())
+ SetDefault(aNewDefaults);
+}
+
+void SwDoc::ReplaceCompatibilityOptions(const SwDoc& rSource)
+{
+ m_pDocumentSettingManager->ReplaceCompatibilityOptions(rSource.GetDocumentSettingManager());
+}
+
+#ifdef DBG_UTIL
+#define CNTNT_DOC( doc ) \
+ ((doc)->GetNodes().GetEndOfContent().GetIndex() - (doc)->GetNodes().GetEndOfExtras().GetIndex() - SwNodeOffset(2))
+#define CNTNT_IDX( idx ) \
+ ((idx).GetNode().GetIndex() - GetNodes().GetEndOfExtras().GetIndex() - 1)
+#endif
+
+SfxObjectShell* SwDoc::CreateCopy( bool bCallInitNew, bool bEmpty ) const
+{
+ SAL_INFO( "sw.pageframe", "(SwDoc::CreateCopy in" );
+ rtl::Reference<SwDoc> xRet( new SwDoc );
+
+ // we have to use pointer here, since the callee has to decide whether
+ // SfxObjectShellLock or SfxObjectShellRef should be used sometimes the
+ // object will be returned with refcount set to 0 ( if no DoInitNew is done )
+ SfxObjectShell* pRetShell = new SwDocShell( *xRet, SfxObjectCreateMode::STANDARD );
+ if( bCallInitNew )
+ {
+ // it could happen that DoInitNew creates model,
+ // that increases the refcount of the object
+ pRetShell->DoInitNew();
+ }
+
+ xRet->ReplaceDefaults(*this);
+
+ xRet->ReplaceCompatibilityOptions(*this);
+
+ xRet->ReplaceStyles(*this);
+
+ uno::Reference<beans::XPropertySet> const xThisSet(
+ GetDocShell()->GetBaseModel(), uno::UNO_QUERY_THROW);
+ uno::Reference<beans::XPropertySet> const xRetSet(
+ pRetShell->GetBaseModel(), uno::UNO_QUERY_THROW);
+ uno::Sequence<beans::PropertyValue> aInteropGrabBag;
+ xThisSet->getPropertyValue("InteropGrabBag") >>= aInteropGrabBag;
+ xRetSet->setPropertyValue("InteropGrabBag", uno::Any(aInteropGrabBag));
+
+ if( !bEmpty )
+ {
+#ifdef DBG_UTIL
+ SAL_INFO( "sw.createcopy", "CC-Nd-Src: " << CNTNT_DOC( this ) );
+ SAL_INFO( "sw.createcopy", "CC-Nd: " << CNTNT_DOC( xRet ) );
+#endif
+ xRet->AppendDoc(*this, 0, bCallInitNew, 0, 0);
+#ifdef DBG_UTIL
+ SAL_INFO( "sw.createcopy", "CC-Nd: " << CNTNT_DOC( xRet ) );
+#endif
+ }
+
+ // remove the temporary shell if it is there as it was done before
+ xRet->SetTmpDocShell( nullptr );
+
+ SAL_INFO( "sw.pageframe", "SwDoc::CreateCopy out)" );
+ return pRetShell;
+}
+
+// save bulk letters as single documents
+static OUString lcl_FindUniqueName(SwWrtShell* pTargetShell, std::u16string_view rStartingPageDesc, sal_uLong nDocNo )
+{
+ do
+ {
+ OUString sTest = rStartingPageDesc + OUString::number( nDocNo );
+ if( !pTargetShell->FindPageDescByName( sTest ) )
+ return sTest;
+ ++nDocNo;
+ }
+ while( true );
+}
+
+/** Returns whether the passed SwPageDesc& or any of its (transitive) follows
+ contains a header or footer. */
+static bool lcl_PageDescOrFollowContainsHeaderFooter(const SwPageDesc& rPageDesc)
+{
+ // remember already checked page descs to avoid cycle
+ o3tl::sorted_vector<const SwPageDesc*> aCheckedPageDescs;
+ const SwPageDesc* pCurPageDesc = &rPageDesc;
+ while (aCheckedPageDescs.count(pCurPageDesc) == 0)
+ {
+ const SwFrameFormat& rMaster = pCurPageDesc->GetMaster();
+ if (rMaster.GetHeader().IsActive() || rMaster.GetFooter().IsActive())
+ return true;
+
+ aCheckedPageDescs.insert(pCurPageDesc);
+ pCurPageDesc = pCurPageDesc->GetFollow();
+ }
+ return false;
+}
+
+static void lcl_CopyFollowPageDesc(
+ SwWrtShell& rTargetShell,
+ const SwPageDesc& rSourcePageDesc,
+ const SwPageDesc& rTargetPageDesc,
+ const sal_uLong nDocNo )
+{
+ //now copy the follow page desc, too
+ // note: these may at any point form a cycle, so a loop is needed and it
+ // must be detected that the last iteration closes the cycle and doesn't
+ // copy the first page desc of the cycle again.
+ std::map<OUString, OUString> followMap{ { rSourcePageDesc.GetName(), rTargetPageDesc.GetName() } };
+ SwPageDesc const* pCurSourcePageDesc(&rSourcePageDesc);
+ SwPageDesc const* pCurTargetPageDesc(&rTargetPageDesc);
+ do
+ {
+ const SwPageDesc* pFollowPageDesc = pCurSourcePageDesc->GetFollow();
+ OUString sFollowPageDesc = pFollowPageDesc->GetName();
+ if (sFollowPageDesc == pCurSourcePageDesc->GetName())
+ {
+ break;
+ }
+ SwDoc* pTargetDoc = rTargetShell.GetDoc();
+ SwPageDesc* pTargetFollowPageDesc(nullptr);
+ auto const itMapped(followMap.find(sFollowPageDesc));
+ if (itMapped == followMap.end())
+ {
+ OUString sNewFollowPageDesc = lcl_FindUniqueName(&rTargetShell, sFollowPageDesc, nDocNo);
+ pTargetFollowPageDesc = pTargetDoc->MakePageDesc(sNewFollowPageDesc);
+ pTargetDoc->CopyPageDesc(*pFollowPageDesc, *pTargetFollowPageDesc, false);
+ }
+ else
+ {
+ pTargetFollowPageDesc = pTargetDoc->FindPageDesc(itMapped->second);
+ }
+ SwPageDesc aDesc(*pCurTargetPageDesc);
+ aDesc.SetFollow(pTargetFollowPageDesc);
+ pTargetDoc->ChgPageDesc(pCurTargetPageDesc->GetName(), aDesc);
+ if (itMapped != followMap.end())
+ {
+ break; // was already copied
+ }
+ pCurSourcePageDesc = pCurSourcePageDesc->GetFollow();
+ pCurTargetPageDesc = pTargetFollowPageDesc;
+ followMap[pCurSourcePageDesc->GetName()] = pCurTargetPageDesc->GetName();
+ }
+ while (true);
+}
+
+// appends all pages of source SwDoc - based on SwFEShell::Paste( SwDoc* )
+SwNodeIndex SwDoc::AppendDoc(const SwDoc& rSource, sal_uInt16 const nStartPageNumber,
+ bool const bDeletePrevious, int pageOffset, const sal_uLong nDocNo)
+{
+ SAL_INFO( "sw.pageframe", "(SwDoc::AppendDoc in " << bDeletePrevious );
+
+ // GetEndOfExtras + 1 = StartOfContent == no content node!
+ // This ensures it won't be merged in the SwTextNode at the position.
+ SwNodeIndex aSourceIdx( rSource.GetNodes().GetEndOfExtras(), 1 );
+ // CopyRange works on the range a [mark, point[ and considers an
+ // index < point outside the selection.
+ // @see IDocumentContentOperations::CopyRange
+ SwNodeIndex aSourceEndIdx( rSource.GetNodes().GetEndOfContent(), 0 );
+ SwPaM aCpyPam( aSourceIdx, aSourceEndIdx );
+
+#ifdef DBG_UTIL
+ SAL_INFO( "sw.docappend", "NodeType 0x" << std::hex << static_cast<int>(aSourceIdx.GetNode().GetNodeType())
+ << std::dec << " " << aSourceIdx.GetNode().GetIndex() );
+ ++aSourceIdx;
+ SAL_INFO( "sw.docappend", "NodeType 0x" << std::hex << static_cast<int>(aSourceIdx.GetNode().GetNodeType())
+ << std::dec << " " << aSourceIdx.GetNode().GetIndex() );
+ if ( aSourceIdx.GetNode().GetNodeType() != SwNodeType::End ) {
+ ++aSourceIdx;
+ SAL_INFO( "sw.docappend", "NodeType 0x" << std::hex << static_cast<int>(aSourceIdx.GetNode().GetNodeType()) << std::dec );
+ --aSourceIdx;
+ }
+ --aSourceIdx;
+ SAL_INFO( "sw.docappend", ".." );
+ SAL_INFO( "sw.docappend", "NodeType 0x" << std::hex << static_cast<int>(aSourceEndIdx.GetNode().GetNodeType())
+ << std::dec << " " << aSourceEndIdx.GetNode().GetIndex() );
+ SAL_INFO( "sw.docappend", "NodeType 0x" << std::hex << static_cast<int>(aSourceEndIdx.GetNode().GetNodeType())
+ << std::dec << " " << aSourceEndIdx.GetNode().GetIndex() );
+ SAL_INFO( "sw.docappend", "Src-Nd: " << CNTNT_DOC( &rSource ) );
+ SAL_INFO( "sw.docappend", "Nd: " << CNTNT_DOC( this ) );
+#endif
+
+ SwWrtShell* pTargetShell = GetDocShell()->GetWrtShell();
+ SwPageDesc* pTargetPageDesc = nullptr;
+
+ if ( pTargetShell ) {
+#ifdef DBG_UTIL
+ SAL_INFO( "sw.docappend", "Has target write shell" );
+#endif
+ pTargetShell->StartAllAction();
+
+ if( nDocNo > 0 )
+ {
+ // #i72517# put the styles to the target document
+ // if the source uses headers or footers the target document
+ // needs individual page styles
+ const SwWrtShell *pSourceShell = rSource.GetDocShell()->GetWrtShell();
+ const SwPageDesc& rSourcePageDesc = pSourceShell->GetPageDesc(
+ pSourceShell->GetCurPageDesc());
+ const OUString sStartingPageDesc = rSourcePageDesc.GetName();
+ const bool bPageStylesWithHeaderFooter = lcl_PageDescOrFollowContainsHeaderFooter(rSourcePageDesc);
+ if( bPageStylesWithHeaderFooter )
+ {
+ // create a new pagestyle
+ // copy the pagedesc from the current document to the new
+ // document and change the name of the to-be-applied style
+ OUString sNewPageDescName = lcl_FindUniqueName(pTargetShell, sStartingPageDesc, nDocNo );
+ pTargetPageDesc = MakePageDesc( sNewPageDescName );
+ if( pTargetPageDesc )
+ {
+ CopyPageDesc( rSourcePageDesc, *pTargetPageDesc, false );
+ lcl_CopyFollowPageDesc( *pTargetShell, rSourcePageDesc, *pTargetPageDesc, nDocNo );
+ }
+ }
+ else
+ pTargetPageDesc = pTargetShell->FindPageDescByName( sStartingPageDesc );
+ }
+
+ // Otherwise we have to handle SwPlaceholderNodes as first node
+ if ( pTargetPageDesc )
+ {
+ SwNodeIndex aBreakIdx( GetNodes().GetEndOfContent(), -1 );
+ SwPosition aBreakPos( aBreakIdx );
+ // insert new node - will be removed at the end...
+ // (don't SplitNode() as it may move flys to the wrong node)
+ getIDocumentContentOperations().AppendTextNode(aBreakPos);
+ SwFormatPageDesc pageDesc(pTargetPageDesc);
+ pageDesc.SetNumOffset(nStartPageNumber);
+ // set break on the last paragraph
+ getIDocumentContentOperations().InsertPoolItem(SwPaM(aBreakPos),
+ pageDesc, SetAttrMode::DEFAULT, pTargetShell->GetLayout());
+ // tdf#148309 move to the last node - so that the "flush page break"
+ // code below will format the frame of the node with the page break,
+ // which is required for new page frames to be created! Else layout
+ // performance will be terrible.
+ pTargetShell->SttEndDoc(false);
+
+ // There is now a new empty text node on the new page. If it has
+ // any marks, those are from the previous page: move them back
+ // there, otherwise later we can't delete that empty text node.
+ SwNodeIndex aNodeIndex(GetNodes().GetEndOfContent(), -1);
+ if (SwTextNode* pTextNode = aNodeIndex.GetNode().GetTextNode())
+ {
+ // Position of the last paragraph on the previous page.
+ --aNodeIndex;
+ SwPaM aPaM(aNodeIndex);
+ // Collect the marks starting or ending at this text node.
+ o3tl::sorted_vector<sw::mark::IMark*> aSeenMarks;
+ IDocumentMarkAccess* pMarkAccess = getIDocumentMarkAccess();
+ for (const SwContentIndex* pIndex = pTextNode->GetFirstIndex(); pIndex; pIndex = pIndex->GetNext())
+ {
+ sw::mark::IMark* pMark = const_cast<sw::mark::IMark*>(pIndex->GetMark());
+ if (!pMark)
+ continue;
+ if (!aSeenMarks.insert(pMark).second)
+ continue;
+ }
+ // And move them back.
+ for (sw::mark::IMark* pMark : aSeenMarks)
+ pMarkAccess->repositionMark(pMark, aPaM);
+ }
+
+ // Flush the page break, if we want to keep it
+ if ( !bDeletePrevious )
+ {
+ SAL_INFO( "sw.pageframe", "(Flush pagebreak AKA EndAllAction" );
+ assert(pTargetShell->GetCursor()->GetPoint()->GetNode().GetTextNode()->GetSwAttrSet().HasItem(RES_PAGEDESC));
+ pTargetShell->EndAllAction();
+ SAL_INFO( "sw.pageframe", "Flush changes AKA EndAllAction)" );
+ pTargetShell->StartAllAction();
+ }
+ }
+ }
+#ifdef DBG_UTIL
+ SAL_INFO( "sw.docappend", "Nd: " << CNTNT_DOC( this ) );
+#endif
+
+ // -1, otherwise aFixupIdx would move to new EOC
+ SwNodeIndex aFixupIdx( GetNodes().GetEndOfContent(), -1 );
+
+ // append at the end of document / content
+ SwPaM aInsertPam( GetNodes().GetEndOfContent() );
+
+#ifdef DBG_UTIL
+ SAL_INFO( "sw.docappend", "Pam-Nd: " << aCpyPam.GetPointNode().GetIndex() - aCpyPam.GetMarkNode().GetIndex() + 1
+ << " (0x" << std::hex << static_cast<int>(aCpyPam.GetMarkNode().GetNodeType()) << std::dec
+ << " " << aCpyPam.GetMarkNode().GetIndex()
+ << " - 0x" << std::hex << static_cast<int>(aCpyPam.GetPointNode().GetNodeType()) << std::dec
+ << " " << aCpyPam.GetPointNode().GetIndex() << ")" );
+#endif
+
+ GetIDocumentUndoRedo().StartUndo( SwUndoId::INSGLOSSARY, nullptr );
+ getIDocumentFieldsAccess().LockExpFields();
+
+ // Position where the appended doc starts. Will be filled in later.
+ // Initially uses GetEndOfContent() because SwNodeIndex has no default ctor.
+ SwNodeIndex aStartAppendIndex( GetNodes().GetEndOfContent() );
+
+ {
+ // **
+ // ** refer to SwFEShell::Paste, if you change the following code **
+ // **
+
+ SwPosition& rInsPos = *aInsertPam.GetPoint();
+
+ {
+ SwNodeIndex aIndexBefore(rInsPos.GetNode());
+
+ --aIndexBefore;
+#ifdef DBG_UTIL
+ SAL_INFO( "sw.docappend", "CopyRange In: " << CNTNT_DOC( this ) );
+#endif
+ rSource.getIDocumentContentOperations().CopyRange(aCpyPam, rInsPos, SwCopyFlags::CopyAll|SwCopyFlags::CheckPosInFly);
+ // Note: aCpyPam is invalid now
+#ifdef DBG_UTIL
+ SAL_INFO( "sw.docappend", "CopyRange Out: " << CNTNT_DOC( this ) );
+#endif
+
+ ++aIndexBefore;
+ SwPaM aPaM(aIndexBefore.GetNode(), rInsPos.GetNode());
+
+ aPaM.GetDoc().MakeUniqueNumRules(aPaM);
+
+ // Update the rsid of each pasted text node
+ SwNodes &rDestNodes = GetNodes();
+ SwNodeOffset const nEndIdx = aPaM.End()->GetNodeIndex();
+
+ for (SwNodeOffset nIdx = aPaM.Start()->GetNodeIndex();
+ nIdx <= nEndIdx; ++nIdx)
+ {
+ SwTextNode *const pTextNode = rDestNodes[nIdx]->GetTextNode();
+ if ( pTextNode )
+ UpdateParRsid( pTextNode );
+ }
+ }
+
+ {
+ SwNodeOffset iDelNodes(0);
+ SwNodeIndex aDelIdx( aFixupIdx );
+
+ // we just need to set the new page description and reset numbering
+ // this keeps all other settings as in the pasted document
+ if ( nStartPageNumber || pTargetPageDesc ) {
+ std::unique_ptr<SfxPoolItem> pNewItem;
+ SwTextNode *aTextNd = nullptr;
+ SwFormat *pFormat = nullptr;
+
+ // find the first node allowed to contain a RES_PAGEDESC
+ while (true) {
+ ++aFixupIdx;
+
+ SwNode &node = aFixupIdx.GetNode();
+ if ( node.IsTextNode() ) {
+ // every document contains at least one text node!
+ aTextNd = node.GetTextNode();
+ pNewItem.reset(aTextNd->GetAttr( RES_PAGEDESC ).Clone());
+ break;
+ }
+ else if ( node.IsTableNode() ) {
+ pFormat = node.GetTableNode()->GetTable().GetFrameFormat();
+ pNewItem.reset(pFormat->GetFormatAttr( RES_PAGEDESC ).Clone());
+ break;
+ }
+ }
+
+#ifdef DBG_UTIL
+ SAL_INFO( "sw.docappend", "Idx Del " << CNTNT_IDX( aDelIdx ) );
+ SAL_INFO( "sw.docappend", "Idx Fix " << CNTNT_IDX( aFixupIdx ) );
+#endif
+ // just update the original instead of overwriting
+ SwFormatPageDesc *aDesc = static_cast< SwFormatPageDesc* >( pNewItem.get() );
+#ifdef DBG_UTIL
+ if ( aDesc->GetPageDesc() )
+ SAL_INFO( "sw.docappend", "PD Update " << aDesc->GetPageDesc()->GetName() );
+ else
+ SAL_INFO( "sw.docappend", "PD New" );
+#endif
+ if ( nStartPageNumber )
+ aDesc->SetNumOffset( nStartPageNumber );
+ if ( pTargetPageDesc )
+ aDesc->RegisterToPageDesc( *pTargetPageDesc );
+ if ( aTextNd )
+ aTextNd->SetAttr( *aDesc );
+ else
+ pFormat->SetFormatAttr( *aDesc );
+
+#ifdef DBG_UTIL
+ SAL_INFO( "sw.docappend", "Idx " << CNTNT_IDX( aDelIdx ) );
+#endif
+ iDelNodes++;
+ }
+
+ if ( bDeletePrevious )
+ iDelNodes++;
+
+ if ( iDelNodes ) {
+ // delete leading empty page(s), e.g. from InsertPageBreak or
+ // new SwDoc. this has to be done before copying the page bound
+ // frames, otherwise the drawing layer gets confused.
+ if ( pTargetShell )
+ pTargetShell->SttEndDoc( false );
+ aDelIdx -= iDelNodes - 1;
+#ifdef DBG_UTIL
+ SAL_INFO( "sw.docappend", "iDelNodes: " << iDelNodes
+ << " Idx: " << aDelIdx.GetNode().GetIndex()
+ << " EOE: " << GetNodes().GetEndOfExtras().GetIndex() );
+#endif
+ GetNodes().Delete( aDelIdx, iDelNodes );
+ aStartAppendIndex = aFixupIdx;
+ }
+ else
+ {
+ aStartAppendIndex = aFixupIdx;
+ ++aStartAppendIndex;
+ }
+ }
+
+ // finally copy page bound frames
+ for(sw::SpzFrameFormat* pCpyFormat: *rSource.GetSpzFrameFormats())
+ {
+ const SwFrameFormat& rCpyFormat = *pCpyFormat;
+ SwFormatAnchor aAnchor( rCpyFormat.GetAnchor() );
+ if (RndStdIds::FLY_AT_PAGE != aAnchor.GetAnchorId())
+ continue;
+ SAL_INFO( "sw.docappend", "PaAn: " << aAnchor.GetPageNum()
+ << " => " << aAnchor.GetPageNum() + pageOffset );
+ if ( pageOffset != 0 )
+ aAnchor.SetPageNum( aAnchor.GetPageNum() + pageOffset );
+ getIDocumentLayoutAccess().CopyLayoutFormat( rCpyFormat, aAnchor, true, true );
+ }
+ }
+
+ GetIDocumentUndoRedo().EndUndo( SwUndoId::INSGLOSSARY, nullptr );
+
+ getIDocumentFieldsAccess().UnlockExpFields();
+ getIDocumentFieldsAccess().UpdateFields(false);
+
+ if ( pTargetShell )
+ pTargetShell->EndAllAction();
+
+ SAL_INFO( "sw.pageframe", "SwDoc::AppendDoc out)" );
+ return aStartAppendIndex;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/docnum.cxx b/sw/source/core/doc/docnum.cxx
new file mode 100644
index 0000000000..0735380e5d
--- /dev/null
+++ b/sw/source/core/doc/docnum.cxx
@@ -0,0 +1,2665 @@
+/* -*- 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 <hintids.hxx>
+#include <ftninfo.hxx>
+#include <ftnidx.hxx>
+#include <doc.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <IDocumentListsAccess.hxx>
+#include <IDocumentFieldsAccess.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <IDocumentState.hxx>
+#include <IDocumentStylePoolAccess.hxx>
+#include <pam.hxx>
+#include <ndtxt.hxx>
+#include <poolfmt.hxx>
+#include <UndoCore.hxx>
+#include <UndoRedline.hxx>
+#include <UndoNumbering.hxx>
+#include <swundo.hxx>
+#include <SwUndoFmt.hxx>
+#include <rolbck.hxx>
+#include <paratr.hxx>
+#include <docary.hxx>
+#include <mvsave.hxx>
+#include <txtfrm.hxx>
+#include <rootfrm.hxx>
+#include <redline.hxx>
+#include <strings.hrc>
+#include <SwNodeNum.hxx>
+#include <list.hxx>
+#include <calbck.hxx>
+#include <comphelper/string.hxx>
+#include <comphelper/random.hxx>
+#include <o3tl/safeint.hxx>
+#include <o3tl/string_view.hxx>
+#include <osl/diagnose.h>
+#include <tools/datetimeutils.hxx>
+
+#include <map>
+#include <stdlib.h>
+
+#include <wrtsh.hxx>
+
+namespace {
+ void lcl_ResetIndentAttrs(SwDoc *pDoc, const SwPaM &rPam,
+ const o3tl::sorted_vector<sal_uInt16> aResetAttrsArray,
+ SwRootFrame const*const pLayout)
+ {
+ // #i114929#
+ // On a selection setup a corresponding Point-and-Mark in order to get
+ // the indentation attribute reset on all paragraphs touched by the selection
+ if ( rPam.HasMark() &&
+ rPam.End()->GetNode().GetTextNode() )
+ {
+ SwPaM aPam( rPam.Start()->GetNode(), 0,
+ rPam.End()->GetNode(), rPam.End()->GetNode().GetTextNode()->Len() );
+ pDoc->ResetAttrs( aPam, false, aResetAttrsArray, true, pLayout );
+ }
+ else
+ {
+ pDoc->ResetAttrs( rPam, false, aResetAttrsArray, true, pLayout );
+ }
+ }
+
+ void ExpandPamForParaPropsNodes(SwPaM& rPam, SwRootFrame const*const pLayout)
+ {
+ if (!pLayout)
+ return;
+
+ // ensure that selection from the Shell includes the para-props node
+ // to which the attributes should be applied
+ if (rPam.GetPoint()->GetNode().IsTextNode())
+ {
+ rPam.GetPoint()->Assign( *sw::GetParaPropsNode(*pLayout, rPam.GetPoint()->GetNode()) );
+ }
+ if (rPam.GetMark()->GetNode().IsTextNode())
+ {
+ rPam.GetMark()->Assign( *sw::GetParaPropsNode(*pLayout, rPam.GetMark()->GetNode()) );
+ }
+ }
+}
+
+static sal_uInt8 GetUpperLvlChg( sal_uInt8 nCurLvl, sal_uInt8 nLevel, sal_uInt16 nMask )
+{
+ if( 1 < nLevel )
+ {
+ if( nCurLvl + 1 >= nLevel )
+ nCurLvl -= nLevel - 1;
+ else
+ nCurLvl = 0;
+ }
+ return static_cast<sal_uInt8>((nMask - 1) & ~(( 1 << nCurLvl ) - 1));
+}
+
+void SwDoc::SetOutlineNumRule( const SwNumRule& rRule )
+{
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().StartUndo(SwUndoId::OUTLINE_EDIT, nullptr);
+ if (mpOutlineRule)
+ {
+ GetIDocumentUndoRedo().AppendUndo(
+ std::make_unique<SwUndoOutlineEdit>(*mpOutlineRule, rRule, *this));
+ }
+ }
+
+ if( mpOutlineRule )
+ (*mpOutlineRule) = rRule;
+ else
+ {
+ mpOutlineRule = new SwNumRule( rRule );
+
+ AddNumRule(mpOutlineRule); // #i36749#
+ }
+
+ mpOutlineRule->SetRuleType( OUTLINE_RULE );
+ mpOutlineRule->SetName(SwNumRule::GetOutlineRuleName(), getIDocumentListsAccess());
+
+ // assure that the outline numbering rule is an automatic rule
+ mpOutlineRule->SetAutoRule( true );
+
+ // test whether the optional CharFormats are defined in this Document
+ mpOutlineRule->CheckCharFormats( *this );
+
+ // notify text nodes, which are registered at the outline style, about the
+ // changed outline style
+ SwNumRule::tTextNodeList aTextNodeList;
+ mpOutlineRule->GetTextNodeList( aTextNodeList );
+ for ( SwTextNode* pTextNd : aTextNodeList )
+ {
+ pTextNd->NumRuleChgd();
+
+ // assure that list level corresponds to outline level
+ if ( pTextNd->GetTextColl()->IsAssignedToListLevelOfOutlineStyle() &&
+ pTextNd->GetAttrListLevel() != pTextNd->GetTextColl()->GetAssignedOutlineStyleLevel() )
+ {
+ pTextNd->SetAttrListLevel( pTextNd->GetTextColl()->GetAssignedOutlineStyleLevel() );
+ }
+ }
+
+ PropagateOutlineRule();
+ mpOutlineRule->SetInvalidRule(true);
+ UpdateNumRule();
+
+ // update if we have foot notes && numbering by chapter
+ if( !GetFootnoteIdxs().empty() && FTNNUM_CHAPTER == GetFootnoteInfo().m_eNum )
+ GetFootnoteIdxs().UpdateAllFootnote();
+
+ getIDocumentFieldsAccess().UpdateExpFields(nullptr, true);
+
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().EndUndo(SwUndoId::OUTLINE_EDIT, nullptr);
+ }
+
+ getIDocumentState().SetModified();
+}
+
+void SwDoc::PropagateOutlineRule()
+{
+ SwNumRule* pMyOutlineRule = GetOutlineNumRule();
+ if (!pMyOutlineRule)
+ return;
+
+ for (auto pColl : *mpTextFormatCollTable)
+ {
+ if(pColl->IsAssignedToListLevelOfOutlineStyle())
+ {
+ // Check only the list style, which is set at the paragraph style
+ const SwNumRuleItem & rCollRuleItem = pColl->GetNumRule( false );
+
+ if ( rCollRuleItem.GetValue().isEmpty() )
+ {
+ SwNumRuleItem aNumItem( pMyOutlineRule->GetName() );
+ pColl->SetFormatAttr(aNumItem);
+ }
+ }
+ }
+}
+
+// Increase/Decrease
+bool SwDoc::OutlineUpDown(const SwPaM& rPam, short nOffset,
+ SwRootFrame const*const pLayout)
+{
+ if( GetNodes().GetOutLineNds().empty() || !nOffset )
+ return false;
+
+ // calculate the range
+ SwPaM aPam(rPam, nullptr);
+ ExpandPamForParaPropsNodes(aPam, pLayout);
+ const SwOutlineNodes& rOutlNds = GetNodes().GetOutLineNds();
+ SwNode* const pSttNd = &aPam.Start()->GetNode();
+ SwNode* const pEndNd = &aPam.End()->GetNode();
+ SwOutlineNodes::size_type nSttPos, nEndPos;
+
+ if( !rOutlNds.Seek_Entry( pSttNd, &nSttPos ) &&
+ !nSttPos-- )
+ // we're not in an "Outline section"
+ return false;
+
+ if( rOutlNds.Seek_Entry( pEndNd, &nEndPos ) )
+ ++nEndPos;
+
+ // We now have the wanted range in the OutlineNodes array,
+ // so check now if we're not invalidating sublevels
+ // (stepping over the limits)
+
+ // Here we go:
+ // 1. Create the style array:
+ SwTextFormatColl* aCollArr[ MAXLEVEL ];
+ memset( aCollArr, 0, sizeof( SwTextFormatColl* ) * MAXLEVEL );
+
+ for( auto pTextFormatColl : *mpTextFormatCollTable )
+ {
+ if (pTextFormatColl->IsAssignedToListLevelOfOutlineStyle())
+ {
+ const int nLevel = pTextFormatColl->GetAssignedOutlineStyleLevel();
+ aCollArr[ nLevel ] = pTextFormatColl;
+ }
+ }
+
+ int n;
+
+ /* Find the last occupied level (backward). */
+ for (n = MAXLEVEL - 1; n > 0; n--)
+ {
+ if (aCollArr[n] != nullptr)
+ break;
+ }
+
+ /* If an occupied level is found, choose next level (which IS
+ unoccupied) until a valid level is found. If no occupied level
+ was found n is 0 and aCollArr[0] is 0. In this case no demoting
+ is possible. */
+ if (aCollArr[n] != nullptr)
+ {
+ while (n < MAXLEVEL - 1)
+ {
+ n++;
+
+ SwTextFormatColl *aTmpColl =
+ getIDocumentStylePoolAccess().GetTextCollFromPool(o3tl::narrowing<sal_uInt16>(RES_POOLCOLL_HEADLINE1 + n));
+
+ if( aTmpColl->IsAssignedToListLevelOfOutlineStyle() &&
+ aTmpColl->GetAssignedOutlineStyleLevel() == n )
+ {
+ aCollArr[n] = aTmpColl;
+ break;
+ }
+ }
+ }
+
+ /* Find the first occupied level (forward). */
+ for (n = 0; n < MAXLEVEL - 1; n++)
+ {
+ if (aCollArr[n] != nullptr)
+ break;
+ }
+
+ /* If an occupied level is found, choose previous level (which IS
+ unoccupied) until a valid level is found. If no occupied level
+ was found n is MAXLEVEL - 1 and aCollArr[MAXLEVEL - 1] is 0. In
+ this case no demoting is possible. */
+ if (aCollArr[n] != nullptr)
+ {
+ while (n > 0)
+ {
+ n--;
+
+ SwTextFormatColl *aTmpColl =
+ getIDocumentStylePoolAccess().GetTextCollFromPool(o3tl::narrowing<sal_uInt16>(RES_POOLCOLL_HEADLINE1 + n));
+
+ if( aTmpColl->IsAssignedToListLevelOfOutlineStyle() &&
+ aTmpColl->GetAssignedOutlineStyleLevel() == n )
+ {
+ aCollArr[n] = aTmpColl;
+ break;
+ }
+ }
+ }
+
+ /* --> #i13747#
+
+ Build a move table that states from which level to which other level
+ an outline will be moved.
+
+ the move table:
+ aMoveArr[n] = m: replace aCollArr[n] with aCollArr[m]
+ */
+ int aMoveArr[MAXLEVEL];
+ int nStep; // step size for searching in aCollArr: -1 or 1
+ int nNum; // amount of steps for stepping in aCollArr
+
+ if (nOffset < 0)
+ {
+ nStep = -1;
+ nNum = -nOffset;
+ }
+ else
+ {
+ nStep = 1;
+ nNum = nOffset;
+ }
+
+ /* traverse aCollArr */
+ for (n = 0; n < MAXLEVEL; n++)
+ {
+ /* If outline level n has an assigned paragraph style step
+ nNum steps forwards (nStep == 1) or backwards (nStep ==
+ -1). One step is to go to the next non-null entry in
+ aCollArr in the selected direction. If nNum steps were
+ possible write the index of the entry found to aCollArr[n],
+ i.e. outline level n will be replaced by outline level
+ aCollArr[n].
+
+ If outline level n has no assigned paragraph style
+ aMoveArr[n] is set to -1.
+ */
+ if (aCollArr[n] != nullptr)
+ {
+ int m = n;
+ int nCount = nNum;
+
+ while (nCount > 0 && m + nStep >= 0 && m + nStep < MAXLEVEL)
+ {
+ m += nStep;
+
+ if (aCollArr[m] != nullptr)
+ nCount--;
+ }
+
+ if (nCount == 0)
+ aMoveArr[n] = m;
+ else
+ aMoveArr[n] = -1;
+ }
+ else
+ aMoveArr[n] = -1;
+ }
+
+ /* If moving of the outline levels is applicable, i.e. for all
+ outline levels occurring in the document there has to be a valid
+ target outline level implied by aMoveArr. */
+ bool bMoveApplicable = true;
+ for (auto i = nSttPos; i < nEndPos; ++i)
+ {
+ SwTextNode* pTextNd = rOutlNds[ i ]->GetTextNode();
+ if (pLayout && !sw::IsParaPropsNode(*pLayout, *pTextNd))
+ {
+ continue;
+ }
+ SwTextFormatColl* pColl = pTextNd->GetTextColl();
+
+ if( pColl->IsAssignedToListLevelOfOutlineStyle() )
+ {
+ const int nLevel = pColl->GetAssignedOutlineStyleLevel();
+ if (aMoveArr[nLevel] == -1)
+ bMoveApplicable = false;
+ }
+
+ // Check on outline level attribute of text node, if text node is
+ // not an outline via a to outline style assigned paragraph style.
+ else
+ {
+ const int nNewOutlineLevel = pTextNd->GetAttrOutlineLevel() + nOffset;
+ if ( nNewOutlineLevel < 1 || nNewOutlineLevel > MAXLEVEL )
+ {
+ bMoveApplicable = false;
+ }
+ }
+ }
+
+ if (! bMoveApplicable )
+ return false;
+
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().StartUndo(SwUndoId::OUTLINE_LR, nullptr);
+ GetIDocumentUndoRedo().AppendUndo(
+ std::make_unique<SwUndoOutlineLeftRight>(aPam, nOffset) );
+ }
+
+ // 2. Apply the new style to all Nodes
+ for (auto i = nSttPos; i < nEndPos; ++i)
+ {
+ SwTextNode* pTextNd = rOutlNds[ i ]->GetTextNode();
+ if (pLayout && !sw::IsParaPropsNode(*pLayout, *pTextNd))
+ {
+ continue;
+ }
+ SwTextFormatColl* pColl = pTextNd->GetTextColl();
+
+ if( pColl->IsAssignedToListLevelOfOutlineStyle() )
+ {
+ const int nLevel = pColl->GetAssignedOutlineStyleLevel();
+
+ OSL_ENSURE(aMoveArr[nLevel] >= 0,
+ "move table: current TextColl not found when building table!");
+
+ if (nLevel < MAXLEVEL && aMoveArr[nLevel] >= 0)
+ {
+ pColl = aCollArr[ aMoveArr[nLevel] ];
+
+ if (pColl != nullptr)
+ pTextNd->ChgFormatColl( pColl );
+ }
+
+ }
+ else if( pTextNd->GetAttrOutlineLevel() > 0)
+ {
+ int nLevel = pTextNd->GetAttrOutlineLevel() + nOffset;
+ if( 0 <= nLevel && nLevel <= MAXLEVEL)
+ pTextNd->SetAttrOutlineLevel( nLevel );
+
+ }
+ // Undo ???
+ }
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().EndUndo(SwUndoId::OUTLINE_LR, nullptr);
+ }
+
+ ChkCondColls();
+ getIDocumentState().SetModified();
+
+ return true;
+}
+
+// Move up/down
+bool SwDoc::MoveOutlinePara( const SwPaM& rPam, SwOutlineNodes::difference_type nOffset )
+{
+ // Do not move to special sections in the nodes array
+ const SwPosition& rStt = *rPam.Start(),
+ & rEnd = *rPam.End();
+ if( GetNodes().GetOutLineNds().empty() || !nOffset ||
+ (rStt.GetNodeIndex() < GetNodes().GetEndOfExtras().GetIndex()) ||
+ (rEnd.GetNodeIndex() < GetNodes().GetEndOfExtras().GetIndex()))
+ {
+ return false;
+ }
+
+ SwOutlineNodes::size_type nCurrentPos = 0;
+ SwNodeIndex aSttRg( rStt.GetNode() ), aEndRg( rEnd.GetNode() );
+
+ int nOutLineLevel = MAXLEVEL;
+ SwNode* pSrch = &aSttRg.GetNode();
+
+ if( pSrch->IsTextNode())
+ nOutLineLevel = static_cast<sal_uInt8>(pSrch->GetTextNode()->GetAttrOutlineLevel()-1);
+ SwNode* pEndSrch = &aEndRg.GetNode();
+ if( !GetNodes().GetOutLineNds().Seek_Entry( pSrch, &nCurrentPos ) )
+ {
+ if( !nCurrentPos )
+ return false; // Promoting or demoting before the first outline => no.
+ if( --nCurrentPos )
+ aSttRg = *GetNodes().GetOutLineNds()[ nCurrentPos ];
+ else if( 0 > nOffset )
+ return false; // Promoting at the top of document?!
+ else
+ aSttRg = *GetNodes().GetEndOfContent().StartOfSectionNode();
+ }
+ SwOutlineNodes::size_type nTmpPos = 0;
+ // If the given range ends at an outlined text node we have to decide if it has to be a part of
+ // the moving range or not. Normally it will be a sub outline of our chapter
+ // and has to be moved, too. But if the chapter ends with a table(or a section end),
+ // the next text node will be chosen and this could be the next outline of the same level.
+ // The criteria has to be the outline level: sub level => incorporate, same/higher level => no.
+ if( GetNodes().GetOutLineNds().Seek_Entry( pEndSrch, &nTmpPos ) )
+ {
+ if( !pEndSrch->IsTextNode() || pEndSrch == pSrch ||
+ nOutLineLevel < pEndSrch->GetTextNode()->GetAttrOutlineLevel()-1 )
+ ++nTmpPos; // For sub outlines only!
+ }
+
+ aEndRg = nTmpPos < GetNodes().GetOutLineNds().size()
+ ? *GetNodes().GetOutLineNds()[ nTmpPos ]
+ : GetNodes().GetEndOfContent();
+ if( nOffset >= 0 )
+ nCurrentPos = nTmpPos;
+ if( aEndRg == aSttRg )
+ {
+ OSL_FAIL( "Moving outlines: Surprising selection" );
+ ++aEndRg;
+ }
+
+ const SwNode* pNd;
+ // The following code corrects the range to handle sections (start/end nodes)
+ // The range will be extended if the least node before the range is a start node
+ // which ends inside the range => The complete section will be moved.
+ // The range will be shrunk if the last position is a start node.
+ // The range will be shrunk if the last node is an end node which starts before the range.
+ --aSttRg;
+ while( aSttRg.GetNode().IsStartNode() )
+ {
+ pNd = aSttRg.GetNode().EndOfSectionNode();
+ if( pNd->GetIndex() >= aEndRg.GetIndex() )
+ break;
+ --aSttRg;
+ }
+ ++aSttRg;
+
+ --aEndRg;
+ while( aEndRg.GetNode().IsStartNode() )
+ --aEndRg;
+
+ while( aEndRg.GetNode().IsEndNode() )
+ {
+ pNd = aEndRg.GetNode().StartOfSectionNode();
+ if( pNd->GetIndex() >= aSttRg.GetIndex() )
+ break;
+ --aEndRg;
+ }
+ ++aEndRg;
+
+ // calculation of the new position
+ if( nOffset < 0 && nCurrentPos < o3tl::make_unsigned(-nOffset) )
+ pNd = GetNodes().GetEndOfContent().StartOfSectionNode();
+ else if( nCurrentPos + nOffset >= GetNodes().GetOutLineNds().size() )
+ pNd = &GetNodes().GetEndOfContent();
+ else
+ pNd = GetNodes().GetOutLineNds()[ nCurrentPos + nOffset ];
+
+ SwNodeOffset nNewPos = pNd->GetIndex();
+
+ // And now a correction of the insert position if necessary...
+ SwNodeIndex aInsertPos( *pNd, -1 );
+ while( aInsertPos.GetNode().IsStartNode() )
+ {
+ // Just before the insert position starts a section:
+ // when I'm moving forward I do not want to enter the section,
+ // when I'm moving backward I want to stay in the section if I'm already a part of,
+ // I want to stay outside if I was outside before.
+ if( nOffset < 0 )
+ {
+ pNd = aInsertPos.GetNode().EndOfSectionNode();
+ if( pNd->GetIndex() >= aEndRg.GetIndex() )
+ break;
+ }
+ --aInsertPos;
+ --nNewPos;
+ }
+
+ if( nOffset >= 0 )
+ {
+ // When just before the insert position a section ends, it is okay when I'm moving backward
+ // because I want to stay outside the section.
+ // When moving forward I've to check if I started inside or outside the section
+ // because I don't want to enter of leave such a section
+ while( aInsertPos.GetNode().IsEndNode() )
+ {
+ pNd = aInsertPos.GetNode().StartOfSectionNode();
+ if( pNd->GetIndex() >= aSttRg.GetIndex() )
+ break;
+ --aInsertPos;
+ --nNewPos;
+ }
+ }
+ // We do not want to move into tables (at the moment)
+ ++aInsertPos;
+ pNd = &aInsertPos.GetNode();
+ if( pNd->IsTableNode() )
+ pNd = pNd->StartOfSectionNode();
+ if( pNd->FindTableNode() )
+ return false;
+
+ OSL_ENSURE( aSttRg.GetIndex() > nNewPos || nNewPos >= aEndRg.GetIndex(),
+ "Position lies within Move range" );
+
+ // If a Position inside the special nodes array sections was calculated,
+ // set it to document start instead.
+ // Sections or Tables at the document start will be pushed backwards.
+ nNewPos = std::max( nNewPos, GetNodes().GetEndOfExtras().GetIndex() + SwNodeOffset(2) );
+
+ SwNodeOffset nOffs = nNewPos - ( 0 < nOffset ? aEndRg.GetIndex() : aSttRg.GetIndex());
+ SwPaM aPam( aSttRg, aEndRg, SwNodeOffset(0), SwNodeOffset(-1) );
+ return MoveParagraph( aPam, nOffs, true );
+}
+
+static SwTextNode* lcl_FindOutlineName(const SwOutlineNodes& rOutlNds,
+ SwRootFrame const*const pLayout, std::u16string_view aName, bool const bExact)
+{
+ SwTextNode * pExactButDeleted(nullptr);
+ SwTextNode* pSavedNode = nullptr;
+ for( auto pOutlNd : rOutlNds )
+ {
+ SwTextNode* pTextNd = pOutlNd->GetTextNode();
+ const OUString sText( pTextNd->GetExpandText(pLayout) );
+ if (sText.startsWith(aName))
+ {
+ if (sText.getLength() == sal_Int32(aName.size()))
+ {
+ if (pLayout && !sw::IsParaPropsNode(*pLayout, *pTextNd))
+ {
+ pExactButDeleted = pTextNd;
+ }
+ else
+ {
+ // Found "exact", set Pos to the Node
+ return pTextNd;
+ }
+ }
+ if (!bExact && !pSavedNode
+ && (!pLayout || sw::IsParaPropsNode(*pLayout, *pTextNd)))
+ {
+ // maybe we just found the text's first part
+ pSavedNode = pTextNd;
+ }
+ }
+ }
+
+ return bExact ? pExactButDeleted : pSavedNode;
+}
+
+static SwTextNode* lcl_FindOutlineNum(const SwOutlineNodes& rOutlNds,
+ OUString& rName, SwRootFrame const*const pLayout)
+{
+ // Valid numbers are (always just offsets!):
+ // ([Number]+\.)+ (as a regular expression!)
+ // (Number followed by a period, with 5 repetitions)
+ // i.e.: "1.1.", "1.", "1.1.1."
+ sal_Int32 nPos = 0;
+ std::u16string_view sNum = o3tl::getToken(rName, 0, '.', nPos );
+ if( -1 == nPos )
+ return nullptr; // invalid number!
+
+ sal_uInt16 nLevelVal[ MAXLEVEL ]; // numbers of all levels
+ memset( nLevelVal, 0, MAXLEVEL * sizeof( nLevelVal[0] ));
+ int nLevel = 0;
+ std::u16string_view sName( rName );
+
+ while( -1 != nPos )
+ {
+ sal_uInt16 nVal = 0;
+ for( size_t n = 0; n < sNum.size(); ++n )
+ {
+ const sal_Unicode c {sNum[ n ]};
+ if( '0' <= c && c <= '9' )
+ {
+ nVal *= 10;
+ nVal += c - '0';
+ }
+ else if( nLevel )
+ break; // "almost" valid number
+ else
+ return nullptr; // invalid number!
+ }
+
+ if( MAXLEVEL > nLevel )
+ nLevelVal[ nLevel++ ] = nVal;
+
+ sName = sName.substr( nPos );
+ nPos = 0;
+ sNum = o3tl::getToken(sName, 0, '.', nPos );
+ // #i4533# without this check all parts delimited by a dot are treated as outline numbers
+ if(!comphelper::string::isdigitAsciiString(sNum))
+ break;
+ }
+ rName = sName; // that's the follow-up text
+
+ // read all levels, so search the document for this outline
+
+ // Without OutlineNodes searching doesn't pay off
+ // and we save a crash
+ if( rOutlNds.empty() )
+ return nullptr;
+
+ // search in the existing outline nodes for the required outline num array
+ for( auto pOutlNd : rOutlNds )
+ {
+ SwTextNode* pNd = pOutlNd->GetTextNode();
+ if ( pNd->GetAttrOutlineLevel() == nLevel )
+ {
+ // #i51089#, #i68289#
+ // Assure, that text node has the correct numbering level. Otherwise,
+ // its number vector will not fit to the searched level.
+ if (pNd->GetNum(pLayout) && pNd->GetActualListLevel() == nLevel - 1)
+ {
+ const SwNodeNum & rNdNum = *(pNd->GetNum(pLayout));
+ SwNumberTree::tNumberVector aLevelVal = rNdNum.GetNumberVector();
+ // now compare with the one searched for
+ bool bEqual = true;
+ nLevel = std::min<int>(nLevel, MAXLEVEL);
+ for( int n = 0; n < nLevel; ++n )
+ {
+ if ( aLevelVal[n] != nLevelVal[n] )
+ {
+ bEqual = false;
+ break;
+ }
+ }
+ if (bEqual)
+ return pNd;
+ }
+ else
+ {
+ // A text node, which has an outline paragraph style applied and
+ // has as hard attribute 'no numbering' set, has an outline level,
+ // but no numbering tree node. Thus, consider this situation in
+ // the assertion condition.
+ OSL_ENSURE( !pNd->GetNumRule(),
+ "<lcl_FindOutlineNum(..)> - text node with outline level and numbering rule, but without numbering tree node. This is a serious defect" );
+ }
+ }
+ }
+
+ return nullptr;
+}
+
+// rName can contain a Number and/or the Text.
+// First, we try to find the correct Entry via the Number.
+// If it exists, we compare the Text to see if it's the right one.
+// If that's not the case, we search again via the Text. If it is
+// found, we got the right entry. Or else we use the one found by
+// searching for the Number.
+// If we don't have a Number, we search via the Text only.
+bool SwDoc::GotoOutline(SwPosition& rPos, const OUString& rName, SwRootFrame const*const pLayout) const
+{
+ if( !rName.isEmpty() )
+ {
+ const SwOutlineNodes& rOutlNds = GetNodes().GetOutLineNds();
+
+ // 1. step: via the Number:
+ OUString sName( rName );
+ SwTextNode* pNd = ::lcl_FindOutlineNum(rOutlNds, sName, pLayout);
+ if ( pNd )
+ {
+ OUString sExpandedText = pNd->GetExpandText(pLayout);
+ //#i4533# leading numbers followed by a dot have been remove while
+ //searching for the outline position
+ //to compensate this they must be removed from the paragraphs text content, too
+ while(!sExpandedText.isEmpty())
+ {
+ sal_Int32 nPos = 0;
+ std::u16string_view sTempNum = o3tl::getToken(sExpandedText, 0, '.', nPos);
+ if( sTempNum.empty() || -1 == nPos ||
+ !comphelper::string::isdigitAsciiString(sTempNum))
+ break;
+ sExpandedText = sExpandedText.copy(nPos);
+ }
+
+ if( sExpandedText != sName )
+ {
+ SwTextNode *pTmpNd = ::lcl_FindOutlineName(rOutlNds, pLayout, sName, true);
+ if ( pTmpNd ) // found via the Name
+ {
+ if (pLayout && !sw::IsParaPropsNode(*pLayout, *pTmpNd))
+ { // found the correct node but it's deleted!
+ return false; // avoid fallback to inexact search
+ }
+ pNd = pTmpNd;
+ }
+ }
+ rPos.Assign(*pNd);
+ return true;
+ }
+
+ pNd = ::lcl_FindOutlineName(rOutlNds, pLayout, rName, false);
+ if ( pNd )
+ {
+ rPos.Assign(*pNd);
+ return true;
+ }
+
+ // #i68289# additional search on hyperlink URL without its outline numbering part
+ if ( sName != rName )
+ {
+ pNd = ::lcl_FindOutlineName(rOutlNds, pLayout, sName, false);
+ if ( pNd )
+ {
+ rPos.Assign(*pNd);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+static void lcl_ChgNumRule( SwDoc& rDoc, const SwNumRule& rRule )
+{
+ SwNumRule* pOld = rDoc.FindNumRulePtr( rRule.GetName() );
+ if (!pOld) //we cannot proceed without the old NumRule
+ return;
+
+ sal_uInt16 nChgFormatLevel = 0;
+ sal_uInt16 nMask = 1;
+
+ for ( sal_uInt8 n = 0; n < MAXLEVEL; ++n, nMask <<= 1 )
+ {
+ const SwNumFormat& rOldFormat = pOld->Get( n ), &rNewFormat = rRule.Get( n );
+
+ if ( rOldFormat != rNewFormat )
+ {
+ nChgFormatLevel |= nMask;
+ }
+ else if ( SVX_NUM_NUMBER_NONE > rNewFormat.GetNumberingType()
+ && 1 < rNewFormat.GetIncludeUpperLevels()
+ && 0 != ( nChgFormatLevel & GetUpperLvlChg( n, rNewFormat.GetIncludeUpperLevels(), nMask ) ) )
+ {
+ nChgFormatLevel |= nMask;
+ }
+ }
+
+ if( !nChgFormatLevel ) // Nothing has been changed?
+ {
+ const bool bInvalidateNumRule( pOld->IsContinusNum() != rRule.IsContinusNum() );
+ pOld->CheckCharFormats( rDoc );
+ pOld->SetContinusNum( rRule.IsContinusNum() );
+
+ if ( bInvalidateNumRule )
+ {
+ pOld->SetInvalidRule(true);
+ }
+
+ return ;
+ }
+
+ SwNumRule::tTextNodeList aTextNodeList;
+ pOld->GetTextNodeList( aTextNodeList );
+ sal_uInt8 nLvl( 0 );
+ for ( SwTextNode* pTextNd : aTextNodeList )
+ {
+ nLvl = static_cast<sal_uInt8>(pTextNd->GetActualListLevel());
+
+ if( nLvl < MAXLEVEL )
+ {
+ if( nChgFormatLevel & ( 1 << nLvl ))
+ {
+ pTextNd->NumRuleChgd();
+ }
+ }
+ }
+
+ for ( sal_uInt8 n = 0; n < MAXLEVEL; ++n )
+ if ( nChgFormatLevel & ( 1 << n ) )
+ pOld->Set( n, rRule.GetNumFormat( n ) );
+
+ pOld->CheckCharFormats( rDoc );
+ pOld->SetInvalidRule( true );
+ pOld->SetContinusNum( rRule.IsContinusNum() );
+
+ rDoc.UpdateNumRule();
+}
+
+OUString SwDoc::SetNumRule( const SwPaM& rPam,
+ const SwNumRule& rRule,
+ const bool bCreateNewList,
+ SwRootFrame const*const pLayout,
+ const OUString& sContinuedListId,
+ bool bSetItem,
+ const bool bResetIndentAttrs )
+{
+ OUString sListId;
+
+ SwPaM aPam(rPam, nullptr);
+ ExpandPamForParaPropsNodes(aPam, pLayout);
+
+ SwUndoInsNum * pUndo = nullptr;
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ // Start/End for attributes!
+ GetIDocumentUndoRedo().StartUndo( SwUndoId::INSNUM, nullptr );
+ pUndo = new SwUndoInsNum( aPam, rRule );
+ GetIDocumentUndoRedo().AppendUndo(std::unique_ptr<SwUndo>(pUndo));
+ }
+
+ SwNumRule* pNewOrChangedNumRule = FindNumRulePtr( rRule.GetName() );
+ bool bNewNumRuleCreated = false;
+ if ( pNewOrChangedNumRule == nullptr )
+ {
+ // create new numbering rule based on given one
+ pNewOrChangedNumRule = ( *mpNumRuleTable )[MakeNumRule( rRule.GetName(), &rRule )];
+ bNewNumRuleCreated = true;
+ }
+ else if ( rRule != *pNewOrChangedNumRule )
+ {
+ // change existing numbering rule
+ if (pUndo)
+ {
+ pUndo->SaveOldNumRule( *pNewOrChangedNumRule );
+ }
+ ::lcl_ChgNumRule( *this, rRule );
+ if (pUndo)
+ {
+ pUndo->SetLRSpaceEndPos();
+ }
+ }
+
+ if ( bSetItem )
+ {
+ if ( bCreateNewList )
+ {
+ if ( bNewNumRuleCreated )
+ {
+ // apply list id of list, which has been created for the new list style
+ sListId = pNewOrChangedNumRule->GetDefaultListId();
+ }
+ else
+ {
+ // create new list and apply its list id
+ const SwList* pNewList = getIDocumentListsAccess().createList( OUString(), pNewOrChangedNumRule->GetName() );
+ OSL_ENSURE( pNewList,
+ "<SwDoc::SetNumRule(..)> - could not create new list. Serious defect." );
+ sListId = pNewList->GetListId();
+ }
+ }
+ else if ( !sContinuedListId.isEmpty() )
+ {
+ // apply given list id
+ sListId = sContinuedListId;
+ }
+ if (!sListId.isEmpty())
+ {
+ getIDocumentContentOperations().InsertPoolItem(aPam,
+ SfxStringItem(RES_PARATR_LIST_ID, sListId),
+ SetAttrMode::DEFAULT, pLayout);
+ }
+ }
+
+ if (!aPam.HasMark())
+ {
+ SwTextNode * pTextNd = aPam.GetPoint()->GetNode().GetTextNode();
+ // robust code: consider case that the PaM doesn't denote a text node - e.g. it denotes a graphic node
+ if ( pTextNd != nullptr )
+ {
+ assert(!pLayout || sw::IsParaPropsNode(*pLayout, *pTextNd));
+ SwNumRule * pRule = pTextNd->GetNumRule();
+
+ if (pRule && pRule->GetName() == pNewOrChangedNumRule->GetName())
+ {
+ bSetItem = false;
+ if ( !pTextNd->IsInList() )
+ {
+ pTextNd->AddToList();
+ }
+ }
+ // Only clear numbering attribute at text node, if at paragraph
+ // style the new numbering rule is found.
+ else if ( !pRule )
+ {
+ SwTextFormatColl* pColl = pTextNd->GetTextColl();
+ if ( pColl )
+ {
+ SwNumRule* pCollRule = FindNumRulePtr(pColl->GetNumRule().GetValue());
+ if ( pCollRule && pCollRule->GetName() == pNewOrChangedNumRule->GetName() )
+ {
+ pTextNd->ResetAttr( RES_PARATR_NUMRULE );
+ bSetItem = false;
+ }
+ }
+ }
+ }
+ }
+
+ if ( bSetItem )
+ {
+ getIDocumentContentOperations().InsertPoolItem(aPam,
+ SwNumRuleItem(pNewOrChangedNumRule->GetName()),
+ SetAttrMode::DEFAULT, pLayout);
+ }
+
+ if ( bResetIndentAttrs
+ && pNewOrChangedNumRule->Get( 0 ).GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_ALIGNMENT )
+ {
+ const o3tl::sorted_vector<sal_uInt16> attrs{ RES_MARGIN_FIRSTLINE, RES_MARGIN_TEXTLEFT, RES_MARGIN_RIGHT };
+ ::lcl_ResetIndentAttrs(this, aPam, attrs, pLayout);
+ }
+
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().EndUndo( SwUndoId::INSNUM, nullptr );
+ }
+
+ getIDocumentState().SetModified();
+
+ return sListId;
+}
+
+void SwDoc::SetCounted(const SwPaM & rPam, bool bCounted,
+ SwRootFrame const*const pLayout)
+{
+ if ( bCounted )
+ {
+ const o3tl::sorted_vector<sal_uInt16> attrs{ RES_PARATR_LIST_ISCOUNTED };
+ ::lcl_ResetIndentAttrs(this, rPam, attrs, pLayout);
+ }
+ else
+ {
+ getIDocumentContentOperations().InsertPoolItem(rPam,
+ SfxBoolItem(RES_PARATR_LIST_ISCOUNTED, false),
+ SetAttrMode::DEFAULT, pLayout);
+ }
+}
+
+void SwDoc::SetNumRuleStart( const SwPosition& rPos, bool bFlag )
+{
+ SwTextNode* pTextNd = rPos.GetNode().GetTextNode();
+
+ if (!pTextNd)
+ return;
+
+ const SwNumRule* pRule = pTextNd->GetNumRule();
+ if( pRule && !bFlag != !pTextNd->IsListRestart())
+ {
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().AppendUndo(
+ std::make_unique<SwUndoNumRuleStart>(rPos, bFlag) );
+ }
+
+ pTextNd->SetListRestart(bFlag);
+
+ getIDocumentState().SetModified();
+ }
+}
+
+void SwDoc::SetNodeNumStart( const SwPosition& rPos, sal_uInt16 nStt )
+{
+ SwTextNode* pTextNd = rPos.GetNode().GetTextNode();
+
+ if (!pTextNd)
+ return;
+
+ if ( !pTextNd->HasAttrListRestartValue() ||
+ pTextNd->GetAttrListRestartValue() != nStt )
+ {
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().AppendUndo(
+ std::make_unique<SwUndoNumRuleStart>(rPos, nStt) );
+ }
+ pTextNd->SetAttrListRestartValue( nStt );
+
+ getIDocumentState().SetModified();
+ }
+}
+
+// We can only delete if the Rule is unused!
+bool SwDoc::DelNumRule( const OUString& rName, bool bBroadcast )
+{
+ sal_uInt16 nPos = FindNumRule( rName );
+
+ if (nPos == USHRT_MAX)
+ return false;
+
+ if ( (*mpNumRuleTable)[ nPos ] == GetOutlineNumRule() )
+ {
+ OSL_FAIL( "<SwDoc::DelNumRule(..)> - No deletion of outline list style. This is serious defect" );
+ return false;
+ }
+
+ if( !IsUsed( *(*mpNumRuleTable)[ nPos ] ))
+ {
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().AppendUndo(
+ std::make_unique<SwUndoNumruleDelete>(*(*mpNumRuleTable)[nPos], *this));
+ }
+
+ if (bBroadcast)
+ BroadcastStyleOperation(rName, SfxStyleFamily::Pseudo,
+ SfxHintId::StyleSheetErased);
+
+ getIDocumentListsAccess().deleteListForListStyle( rName );
+ getIDocumentListsAccess().deleteListsByDefaultListStyle( rName );
+ // #i34097# DeleteAndDestroy deletes rName if
+ // rName is directly taken from the numrule.
+ const OUString aTmpName( rName );
+ delete (*mpNumRuleTable)[ nPos ];
+ mpNumRuleTable->erase( mpNumRuleTable->begin() + nPos );
+ maNumRuleMap.erase(aTmpName);
+
+ getIDocumentState().SetModified();
+ return true;
+ }
+ return false;
+}
+
+void SwDoc::ChgNumRuleFormats( const SwNumRule& rRule )
+{
+ SwNumRule* pRule = FindNumRulePtr( rRule.GetName() );
+ if( !pRule )
+ return;
+
+ SwUndoInsNum* pUndo = nullptr;
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ pUndo = new SwUndoInsNum( *pRule, rRule, *this );
+ pUndo->GetHistory();
+ GetIDocumentUndoRedo().AppendUndo( std::unique_ptr<SwUndo>(pUndo) );
+ }
+ ::lcl_ChgNumRule( *this, rRule );
+ if (pUndo)
+ {
+ pUndo->SetLRSpaceEndPos();
+ }
+
+ getIDocumentState().SetModified();
+}
+
+bool SwDoc::RenameNumRule(const OUString & rOldName, const OUString & rNewName,
+ bool bBroadcast)
+{
+ assert(!FindNumRulePtr(rNewName));
+
+ bool bResult = false;
+ SwNumRule * pNumRule = FindNumRulePtr(rOldName);
+
+ if (pNumRule)
+ {
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().AppendUndo(
+ std::make_unique<SwUndoNumruleRename>(rOldName, rNewName, *this));
+ }
+
+ SwNumRule::tTextNodeList aTextNodeList;
+ pNumRule->GetTextNodeList( aTextNodeList );
+
+ pNumRule->SetName( rNewName, getIDocumentListsAccess() );
+
+ SwNumRuleItem aItem(rNewName);
+
+ for ( SwTextNode* pTextNd : aTextNodeList )
+ {
+ pTextNd->SetAttr(aItem);
+ }
+
+ bResult = true;
+
+ if (bBroadcast)
+ BroadcastStyleOperation(rOldName, SfxStyleFamily::Pseudo,
+ SfxHintId::StyleSheetModified);
+ }
+
+ return bResult;
+}
+
+void SwDoc::StopNumRuleAnimations( const OutputDevice* pOut )
+{
+ for( sal_uInt16 n = GetNumRuleTable().size(); n; )
+ {
+ SwNumRule::tTextNodeList aTextNodeList;
+ GetNumRuleTable()[ --n ]->GetTextNodeList( aTextNodeList );
+ for ( SwTextNode* pTNd : aTextNodeList )
+ {
+ SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(*pTNd);
+ for(SwTextFrame* pFrame = aIter.First(); pFrame; pFrame = aIter.Next() )
+ if (pFrame->HasAnimation() &&
+ (!pFrame->GetMergedPara() || pFrame->GetMergedPara()->pParaPropsNode == pTNd))
+ {
+ pFrame->StopAnimation( pOut );
+ }
+ }
+ }
+}
+
+void SwDoc::ReplaceNumRule( const SwPosition& rPos,
+ const OUString& rOldRule, const OUString& rNewRule )
+{
+ SwNumRule *pOldRule = FindNumRulePtr( rOldRule ),
+ *pNewRule = FindNumRulePtr( rNewRule );
+ if( !pOldRule || !pNewRule || pOldRule == pNewRule )
+ return;
+
+ SwUndoInsNum* pUndo = nullptr;
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ // Start/End for attributes!
+ GetIDocumentUndoRedo().StartUndo( SwUndoId::START, nullptr );
+ pUndo = new SwUndoInsNum( rPos, *pNewRule, rOldRule );
+ GetIDocumentUndoRedo().AppendUndo(std::unique_ptr<SwUndo>(pUndo));
+ }
+
+ SwNumRule::tTextNodeList aTextNodeList;
+ pOldRule->GetTextNodeList( aTextNodeList );
+ if ( !aTextNodeList.empty() )
+ {
+ SwRegHistory aRegH( pUndo ? pUndo->GetHistory() : nullptr );
+
+ const SwTextNode* pGivenTextNode = rPos.GetNode().GetTextNode();
+ SwNumRuleItem aRule( rNewRule );
+ for ( SwTextNode* pTextNd : aTextNodeList )
+ {
+ if ( pGivenTextNode &&
+ pGivenTextNode->GetListId() == pTextNd->GetListId() )
+ {
+ aRegH.RegisterInModify( pTextNd, *pTextNd );
+
+ pTextNd->SetAttr( aRule );
+ pTextNd->NumRuleChgd();
+ }
+ }
+ GetIDocumentUndoRedo().EndUndo( SwUndoId::END, nullptr );
+ getIDocumentState().SetModified();
+ }
+}
+
+namespace
+{
+ struct ListStyleData
+ {
+ SwNumRule* pReplaceNumRule;
+ bool bCreateNewList;
+ OUString sListId;
+
+ ListStyleData()
+ : pReplaceNumRule( nullptr ),
+ bCreateNewList( false )
+ {}
+ };
+}
+
+void SwDoc::MakeUniqueNumRules(const SwPaM & rPaM)
+{
+ OSL_ENSURE( &rPaM.GetDoc() == this, "need same doc" );
+
+ std::map<SwNumRule *, ListStyleData> aMyNumRuleMap;
+
+ bool bFirst = true;
+
+ const SwNodeOffset nStt = rPaM.Start()->GetNodeIndex();
+ const SwNodeOffset nEnd = rPaM.End()->GetNodeIndex();
+ for (SwNodeOffset n = nStt; n <= nEnd; n++)
+ {
+ SwTextNode * pCNd = GetNodes()[n]->GetTextNode();
+
+ if (pCNd)
+ {
+ SwNumRule * pRule = pCNd->GetNumRule();
+
+ if (pRule && pRule->IsAutoRule() && ! pRule->IsOutlineRule())
+ {
+ ListStyleData aListStyleData = aMyNumRuleMap[pRule];
+
+ if ( aListStyleData.pReplaceNumRule == nullptr )
+ {
+ if (bFirst)
+ {
+ SwPosition aPos(*pCNd);
+ aListStyleData.pReplaceNumRule =
+ const_cast<SwNumRule *>
+ (SearchNumRule( aPos, false, pCNd->HasNumber(),
+ false, 0,
+ aListStyleData.sListId, nullptr, true ));
+ }
+
+ if ( aListStyleData.pReplaceNumRule == nullptr )
+ {
+ aListStyleData.pReplaceNumRule = new SwNumRule(*pRule);
+ aListStyleData.pReplaceNumRule->SetName( GetUniqueNumRuleName(), getIDocumentListsAccess() );
+ aListStyleData.bCreateNewList = true;
+ }
+
+ aMyNumRuleMap[pRule] = aListStyleData;
+ }
+
+ SwPaM aPam(*pCNd);
+
+ SetNumRule( aPam,
+ *aListStyleData.pReplaceNumRule,
+ aListStyleData.bCreateNewList,
+ nullptr,
+ aListStyleData.sListId );
+ if ( aListStyleData.bCreateNewList )
+ {
+ aListStyleData.bCreateNewList = false;
+ aListStyleData.sListId = pCNd->GetListId();
+ aMyNumRuleMap[pRule] = aListStyleData;
+ }
+
+ bFirst = false;
+ }
+ }
+ }
+}
+
+bool SwDoc::NoNum( const SwPaM& rPam )
+{
+
+ bool bRet = getIDocumentContentOperations().SplitNode( *rPam.GetPoint(), false );
+ // Do we actually use Numbering at all?
+ if( bRet )
+ {
+ // Set NoNum and Update
+ SwTextNode* pNd = rPam.GetPoint()->GetNode().GetTextNode();
+ const SwNumRule* pRule = pNd->GetNumRule();
+ if( pRule )
+ {
+ pNd->SetCountedInList(false);
+
+ getIDocumentState().SetModified();
+ }
+ else
+ bRet = false; // no Numbering or just always true?
+ }
+ return bRet;
+}
+
+void SwDoc::DelNumRules(const SwPaM& rPam, SwRootFrame const*const pLayout)
+{
+ SwPaM aPam(rPam, nullptr);
+ ExpandPamForParaPropsNodes(aPam, pLayout);
+ SwNodeOffset nStt = aPam.Start()->GetNodeIndex();
+ SwNodeOffset const nEnd = aPam.End()->GetNodeIndex();
+
+ SwUndoDelNum* pUndo;
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ pUndo = new SwUndoDelNum( aPam );
+ GetIDocumentUndoRedo().AppendUndo(std::unique_ptr<SwUndo>(pUndo));
+ }
+ else
+ pUndo = nullptr;
+
+ SwRegHistory aRegH( pUndo ? pUndo->GetHistory() : nullptr );
+
+ SwNumRuleItem aEmptyRule;
+ const SwNode* pOutlNd = nullptr;
+ for( ; nStt <= nEnd; ++nStt )
+ {
+ SwTextNode* pTNd = GetNodes()[ nStt ]->GetTextNode();
+ if (pLayout && pTNd)
+ {
+ pTNd = sw::GetParaPropsNode(*pLayout, *pTNd);
+ }
+ SwNumRule* pNumRuleOfTextNode = pTNd ? pTNd->GetNumRule() : nullptr;
+ if ( pTNd && pNumRuleOfTextNode )
+ {
+ // recognize changes of attribute for undo
+ aRegH.RegisterInModify( pTNd, *pTNd );
+
+ if( pUndo )
+ pUndo->AddNode( *pTNd );
+
+ // directly set list style attribute is reset, otherwise empty
+ // list style is applied
+ const SfxItemSet* pAttrSet = pTNd->GetpSwAttrSet();
+ if ( pAttrSet &&
+ pAttrSet->GetItemState( RES_PARATR_NUMRULE, false ) == SfxItemState::SET )
+ pTNd->ResetAttr( RES_PARATR_NUMRULE );
+ else
+ pTNd->SetAttr( aEmptyRule );
+
+ pTNd->ResetAttr( RES_PARATR_LIST_ID );
+ pTNd->ResetAttr( RES_PARATR_LIST_LEVEL );
+ pTNd->ResetAttr( RES_PARATR_LIST_ISRESTART );
+ pTNd->ResetAttr( RES_PARATR_LIST_RESTARTVALUE );
+ pTNd->ResetAttr( RES_PARATR_LIST_ISCOUNTED );
+
+ if( RES_CONDTXTFMTCOLL == pTNd->GetFormatColl()->Which() )
+ {
+ pTNd->ChkCondColl();
+ }
+ else if( !pOutlNd &&
+ static_cast<SwTextFormatColl*>(pTNd->GetFormatColl())->IsAssignedToListLevelOfOutlineStyle() )
+ {
+ pOutlNd = pTNd;
+ }
+ }
+ }
+
+ // Finally, update all
+ UpdateNumRule();
+
+ if( pOutlNd )
+ GetNodes().UpdateOutlineIdx( *pOutlNd );
+}
+
+void SwDoc::InvalidateNumRules()
+{
+ for (size_t n = 0; n < mpNumRuleTable->size(); ++n)
+ (*mpNumRuleTable)[n]->SetInvalidRule(true);
+}
+
+// To the next/preceding Bullet at the same Level
+static bool lcl_IsNumOk( sal_uInt8 nSrchNum, sal_uInt8& rLower, sal_uInt8& rUpper,
+ bool bOverUpper, sal_uInt8 nNumber )
+{
+ OSL_ENSURE( nNumber < MAXLEVEL,
+ "<lcl_IsNumOk(..)> - misusage of method" );
+
+ bool bRet = false;
+ {
+ if( bOverUpper ? nSrchNum == nNumber : nSrchNum >= nNumber )
+ bRet = true;
+ else if( nNumber > rLower )
+ rLower = nNumber;
+ else if( nNumber < rUpper )
+ rUpper = nNumber;
+ }
+ return bRet;
+}
+
+static bool lcl_IsValidPrevNextNumNode( const SwNodeIndex& rIdx )
+{
+ bool bRet = false;
+ const SwNode& rNd = rIdx.GetNode();
+ switch( rNd.GetNodeType() )
+ {
+ case SwNodeType::End:
+ bRet = SwTableBoxStartNode == rNd.StartOfSectionNode()->GetStartNodeType() ||
+ rNd.StartOfSectionNode()->IsSectionNode();
+ break;
+
+ case SwNodeType::Start:
+ bRet = SwTableBoxStartNode == static_cast<const SwStartNode&>(rNd).GetStartNodeType();
+ break;
+
+ case SwNodeType::Section: // that one's valid, so proceed
+ bRet = true;
+ break;
+
+ default: break;
+ }
+ return bRet;
+}
+
+namespace sw {
+
+void
+GotoPrevLayoutTextFrame(SwNodeIndex & rIndex, SwRootFrame const*const pLayout)
+{
+ if (pLayout && pLayout->HasMergedParas())
+ {
+ if (rIndex.GetNode().IsTextNode())
+ {
+ if (rIndex.GetNode().GetRedlineMergeFlag() != SwNode::Merge::None)
+ {
+ // not a tracked row deletion in Hide Changes mode
+ if (SwContentFrame* pFrame = rIndex.GetNode().GetTextNode()->getLayoutFrame(pLayout))
+ {
+ if (sw::MergedPara* pMerged = static_cast<SwTextFrame*>(pFrame)->GetMergedPara())
+ {
+ rIndex = pMerged->pFirstNode->GetIndex();
+ }
+ }
+ }
+ }
+ else if (rIndex.GetNode().IsEndNode())
+ {
+ if (rIndex.GetNode().GetRedlineMergeFlag() == SwNode::Merge::Hidden)
+ {
+ rIndex = *rIndex.GetNode().StartOfSectionNode();
+ assert(rIndex.GetNode().IsTableNode());
+ }
+ }
+ }
+ --rIndex;
+ if (pLayout && rIndex.GetNode().IsTextNode())
+ {
+ rIndex = *sw::GetParaPropsNode(*pLayout, *rIndex.GetNode().GetTextNode());
+ }
+}
+
+void
+GotoNextLayoutTextFrame(SwNodeIndex & rIndex, SwRootFrame const*const pLayout)
+{
+ if (pLayout && pLayout->HasMergedParas())
+ {
+ if (rIndex.GetNode().IsTextNode())
+ {
+ if (rIndex.GetNode().GetRedlineMergeFlag() != SwNode::Merge::None)
+ {
+ if (SwContentFrame* pFrame = rIndex.GetNode().GetTextNode()->getLayoutFrame(pLayout))
+ {
+ if (sw::MergedPara* pMerged = static_cast<SwTextFrame*>(pFrame)->GetMergedPara())
+ {
+ rIndex = pMerged->pLastNode->GetIndex();
+ }
+ }
+ }
+ }
+ else if (rIndex.GetNode().IsTableNode())
+ {
+ if (rIndex.GetNode().GetRedlineMergeFlag() == SwNode::Merge::Hidden)
+ {
+ rIndex = *rIndex.GetNode().EndOfSectionNode();
+ }
+ }
+ }
+ ++rIndex;
+ if (pLayout && rIndex.GetNode().IsTextNode())
+ {
+ rIndex = *sw::GetParaPropsNode(*pLayout, *rIndex.GetNode().GetTextNode());
+ }
+}
+
+} // namespace sw
+
+static bool lcl_GotoNextPrevNum( SwPosition& rPos, bool bNext,
+ bool bOverUpper, sal_uInt8* pUpper, sal_uInt8* pLower,
+ SwRootFrame const*const pLayout)
+{
+ const SwTextNode* pNd = rPos.GetNode().GetTextNode();
+ if (pNd && pLayout)
+ {
+ pNd = sw::GetParaPropsNode(*pLayout, *pNd);
+ }
+ if( !pNd || nullptr == pNd->GetNumRule() )
+ return false;
+
+ sal_uInt8 nSrchNum = static_cast<sal_uInt8>(pNd->GetActualListLevel());
+
+ SwNodeIndex aIdx( rPos.GetNode() );
+ if( ! pNd->IsCountedInList() )
+ {
+ bool bError = false;
+ do {
+ sw::GotoPrevLayoutTextFrame(aIdx, pLayout);
+ if( aIdx.GetNode().IsTextNode() )
+ {
+ pNd = aIdx.GetNode().GetTextNode();
+ const SwNumRule* pRule = pNd->GetNumRule();
+
+ if( pRule )
+ {
+ sal_uInt8 nTmpNum = static_cast<sal_uInt8>(pNd->GetActualListLevel());
+ if( pNd->IsCountedInList() || (nTmpNum < nSrchNum ) )
+ break; // found it!
+ }
+ else
+ bError = true;
+ }
+ else
+ bError = !lcl_IsValidPrevNextNumNode( aIdx );
+
+ } while( !bError );
+ if( bError )
+ return false;
+ }
+
+ sal_uInt8 nLower = nSrchNum, nUpper = nSrchNum;
+ bool bRet = false;
+
+ const SwTextNode* pLast;
+ if( bNext )
+ {
+ sw::GotoNextLayoutTextFrame(aIdx, pLayout);
+ pLast = pNd;
+ }
+ else
+ {
+ sw::GotoPrevLayoutTextFrame(aIdx, pLayout);
+ pLast = nullptr;
+ }
+
+ while( bNext ? ( aIdx.GetIndex() < aIdx.GetNodes().Count() - 1 )
+ : aIdx.GetIndex() != SwNodeOffset(0) )
+ {
+ if( aIdx.GetNode().IsTextNode() )
+ {
+ pNd = aIdx.GetNode().GetTextNode();
+ const SwNumRule* pRule = pNd->GetNumRule();
+ if( pRule )
+ {
+ if( ::lcl_IsNumOk( nSrchNum, nLower, nUpper, bOverUpper,
+ static_cast<sal_uInt8>(pNd->GetActualListLevel()) ))
+ {
+ rPos.Assign(aIdx);
+ bRet = true;
+ break;
+ }
+ else
+ pLast = pNd;
+ }
+ else
+ break;
+ }
+ else if( !lcl_IsValidPrevNextNumNode( aIdx ))
+ break;
+
+ if( bNext )
+ sw::GotoNextLayoutTextFrame(aIdx, pLayout);
+ else
+ sw::GotoPrevLayoutTextFrame(aIdx, pLayout);
+ }
+
+ if( !bRet && !bOverUpper && pLast ) // do not iterate over higher numbers, but still to the end
+ {
+ if( bNext )
+ rPos.Assign(aIdx);
+ else
+ rPos.Assign( *pLast );
+ bRet = true;
+ }
+
+ if( bRet )
+ {
+ if( pUpper )
+ *pUpper = nUpper;
+ if( pLower )
+ *pLower = nLower;
+ }
+ return bRet;
+}
+
+bool SwDoc::GotoNextNum(SwPosition& rPos, SwRootFrame const*const pLayout,
+ bool bOverUpper, sal_uInt8* pUpper, sal_uInt8* pLower)
+{
+ return ::lcl_GotoNextPrevNum(rPos, true, bOverUpper, pUpper, pLower, pLayout);
+}
+
+const SwNumRule * SwDoc::SearchNumRule(const SwPosition & rPos,
+ const bool bForward,
+ const bool bNum,
+ const bool bOutline,
+ int nNonEmptyAllowed,
+ OUString& sListId,
+ SwRootFrame const* pLayout,
+ const bool bInvestigateStartNode)
+{
+ const SwNumRule * pResult = nullptr;
+ SwTextNode * pTextNd = rPos.GetNode().GetTextNode();
+ if (pLayout)
+ {
+ pTextNd = sw::GetParaPropsNode(*pLayout, rPos.GetNode());
+ }
+ SwNode * pStartFromNode = pTextNd;
+
+ if (pTextNd)
+ {
+ SwNodeIndex aIdx(rPos.GetNode());
+
+ // - the start node has also been investigated, if requested.
+ const SwNode * pNode = nullptr;
+ do
+ {
+ if ( !bInvestigateStartNode )
+ {
+ if (bForward)
+ sw::GotoNextLayoutTextFrame(aIdx, pLayout);
+ else
+ sw::GotoPrevLayoutTextFrame(aIdx, pLayout);
+ }
+
+ if (aIdx.GetNode().IsTextNode())
+ {
+ pTextNd = aIdx.GetNode().GetTextNode();
+
+ const SwNumRule * pNumRule = pTextNd->GetNumRule();
+ if (pNumRule)
+ {
+ if ( ( pNumRule->IsOutlineRule() == bOutline ) &&
+ ( ( bNum && pNumRule->Get(0).IsEnumeration()) ||
+ ( !bNum && pNumRule->Get(0).IsItemize() ) ) ) // #i22362#, #i29560#
+ {
+ pResult = pTextNd->GetNumRule();
+ // provide also the list id, to which the text node belongs.
+ sListId = pTextNd->GetListId();
+ }
+
+ break;
+ }
+ else if (pTextNd->Len() > 0 || nullptr != pTextNd->GetNumRule())
+ {
+ if (nNonEmptyAllowed == 0)
+ break;
+
+ nNonEmptyAllowed--;
+
+ if (nNonEmptyAllowed < 0)
+ nNonEmptyAllowed = -1;
+ }
+ }
+
+ if ( bInvestigateStartNode )
+ {
+ if (bForward)
+ sw::GotoNextLayoutTextFrame(aIdx, pLayout);
+ else
+ sw::GotoPrevLayoutTextFrame(aIdx, pLayout);
+ }
+
+ pNode = &aIdx.GetNode();
+ }
+ while (pNode != GetNodes().DocumentSectionStartNode(pStartFromNode) &&
+ pNode != GetNodes().DocumentSectionEndNode(pStartFromNode));
+ }
+
+ return pResult;
+}
+
+bool SwDoc::GotoPrevNum(SwPosition& rPos, SwRootFrame const*const pLayout,
+ bool bOverUpper)
+{
+ return ::lcl_GotoNextPrevNum(rPos, false, bOverUpper, nullptr, nullptr, pLayout);
+}
+
+bool SwDoc::NumUpDown(const SwPaM& rPam, bool bDown, SwRootFrame const*const pLayout)
+{
+ SwPaM aPam(rPam, nullptr);
+ ExpandPamForParaPropsNodes(aPam, pLayout);
+ SwNodeOffset nStt = aPam.Start()->GetNodeIndex();
+ SwNodeOffset const nEnd = aPam.End()->GetNodeIndex();
+
+ // -> outline nodes are promoted or demoted differently
+ bool bOnlyOutline = true;
+ bool bOnlyNonOutline = true;
+ for (SwNodeOffset n = nStt; n <= nEnd; n++)
+ {
+ SwTextNode * pTextNd = GetNodes()[n]->GetTextNode();
+
+ if (pTextNd)
+ {
+ if (pLayout)
+ {
+ pTextNd = sw::GetParaPropsNode(*pLayout, *pTextNd);
+ }
+ SwNumRule * pRule = pTextNd->GetNumRule();
+
+ if (pRule)
+ {
+ if (pRule->IsOutlineRule())
+ bOnlyNonOutline = false;
+ else
+ bOnlyOutline = false;
+ }
+ }
+ }
+
+ bool bRet = true;
+ sal_Int8 nDiff = bDown ? 1 : -1;
+
+ if (bOnlyOutline)
+ bRet = OutlineUpDown(rPam, nDiff, pLayout);
+ else if (bOnlyNonOutline)
+ {
+ /* #i24560#
+ Only promote or demote if all selected paragraphs are
+ promotable resp. demotable.
+ */
+ for (SwNodeOffset nTmp = nStt; nTmp <= nEnd; ++nTmp)
+ {
+ SwTextNode* pTNd = GetNodes()[ nTmp ]->GetTextNode();
+
+ // Make code robust: consider case that the node doesn't denote a
+ // text node.
+ if ( pTNd )
+ {
+ if (pLayout)
+ {
+ pTNd = sw::GetParaPropsNode(*pLayout, *pTNd);
+ }
+
+ SwNumRule * pRule = pTNd->GetNumRule();
+
+ if (pRule)
+ {
+ sal_uInt8 nLevel = static_cast<sal_uInt8>(pTNd->GetActualListLevel());
+ if( (-1 == nDiff && 0 >= nLevel) ||
+ (1 == nDiff && MAXLEVEL - 1 <= nLevel))
+ bRet = false;
+ }
+ }
+ }
+
+ if( bRet )
+ {
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().AppendUndo(
+ std::make_unique<SwUndoNumUpDown>(aPam, nDiff) );
+ }
+
+ SwTextNode* pPrev = nullptr;
+ for(SwNodeOffset nTmp = nStt; nTmp <= nEnd; ++nTmp )
+ {
+ SwTextNode* pTNd = GetNodes()[ nTmp ]->GetTextNode();
+
+ if( pTNd)
+ {
+ if (pLayout)
+ {
+ pTNd = sw::GetParaPropsNode(*pLayout, *pTNd);
+ if (pTNd == pPrev)
+ {
+ continue;
+ }
+ pPrev = pTNd;
+ }
+
+ SwNumRule * pRule = pTNd->GetNumRule();
+
+ if (pRule)
+ {
+ sal_uInt8 nLevel = static_cast<sal_uInt8>(pTNd->GetActualListLevel());
+ nLevel = nLevel + nDiff;
+
+ pTNd->SetAttrListLevel(nLevel);
+ }
+ }
+ }
+
+ ChkCondColls();
+ getIDocumentState().SetModified();
+ }
+ }
+
+ return bRet;
+}
+
+// this function doesn't contain any numbering-related code, but it is
+// primarily called to move numbering-relevant paragraphs around, hence
+// it will expand its selection to include full SwTextFrames.
+bool SwDoc::MoveParagraph(SwPaM& rPam, SwNodeOffset nOffset, bool const bIsOutlMv)
+{
+ MakeAllOutlineContentTemporarilyVisible a(this);
+
+ // sw_redlinehide: as long as a layout with Hide mode exists, only
+ // move nodes that have merged frames *completely*
+ SwRootFrame const* pLayout(nullptr);
+ for (SwRootFrame const*const pLay : GetAllLayouts())
+ {
+ if (pLay->HasMergedParas())
+ {
+ pLayout = pLay;
+ }
+ }
+ if (pLayout)
+ {
+ std::pair<SwTextNode *, SwTextNode *> nodes(
+ sw::GetFirstAndLastNode(*pLayout, rPam.Start()->GetNode()));
+ if (nodes.first && nodes.first != &rPam.Start()->GetNode())
+ {
+ assert(nodes.second);
+ if (nOffset < SwNodeOffset(0))
+ {
+ nOffset += rPam.Start()->GetNodeIndex() - nodes.first->GetIndex();
+ if (SwNodeOffset(0) <= nOffset) // hack: there are callers that know what
+ { // node they want; those should never need
+ nOffset = SwNodeOffset(-1); // this; other callers just pass in -1
+ } // and those should still move
+ }
+ if (!rPam.HasMark())
+ {
+ rPam.SetMark();
+ }
+ assert(nodes.first->GetIndex() < rPam.Start()->GetNodeIndex());
+ rPam.Start()->Assign(*nodes.first);
+ }
+ nodes = sw::GetFirstAndLastNode(*pLayout, rPam.End()->GetNode());
+ if (nodes.second && nodes.second != &rPam.End()->GetNode())
+ {
+ assert(nodes.first);
+ if (SwNodeOffset(0) < nOffset)
+ {
+ nOffset -= nodes.second->GetIndex() - rPam.End()->GetNodeIndex();
+ if (nOffset <= SwNodeOffset(0)) // hack: there are callers that know what
+ { // node they want; those should never need
+ nOffset = SwNodeOffset(+1); // this; other callers just pass in +1
+ } // and those should still move
+ }
+ if (!rPam.HasMark())
+ {
+ rPam.SetMark();
+ }
+ assert(rPam.End()->GetNodeIndex() < nodes.second->GetIndex());
+ // until end, otherwise Impl will detect overlapping redline
+ rPam.End()->Assign(*nodes.second, nodes.second->GetTextNode()->Len());
+ }
+
+ if (nOffset > SwNodeOffset(0))
+ { // sw_redlinehide: avoid moving into delete redline, skip forward
+ if (GetNodes().GetEndOfContent().GetIndex() <= rPam.End()->GetNodeIndex() + nOffset)
+ {
+ return false; // can't move
+ }
+ SwNode const* pNode(GetNodes()[rPam.End()->GetNodeIndex() + nOffset + 1]);
+ if ( pNode->GetRedlineMergeFlag() != SwNode::Merge::None
+ && pNode->GetRedlineMergeFlag() != SwNode::Merge::First)
+ {
+ for ( ; ; ++nOffset)
+ {
+ pNode = GetNodes()[rPam.End()->GetNodeIndex() + nOffset];
+ if (pNode->IsTextNode())
+ {
+ nodes = GetFirstAndLastNode(*pLayout, *pNode->GetTextNode());
+ assert(nodes.first && nodes.second);
+ nOffset += nodes.second->GetIndex() - pNode->GetIndex();
+ // on last; will be incremented below to behind-last
+ break;
+ }
+ }
+ }
+ }
+ else
+ { // sw_redlinehide: avoid moving into delete redline, skip backward
+ if (rPam.Start()->GetNodeIndex() + nOffset < SwNodeOffset(1))
+ {
+ return false; // can't move
+ }
+ SwNode const* pNode(GetNodes()[rPam.Start()->GetNodeIndex() + nOffset]);
+ if ( pNode->GetRedlineMergeFlag() != SwNode::Merge::None
+ && pNode->GetRedlineMergeFlag() != SwNode::Merge::First)
+ {
+ for ( ; ; --nOffset)
+ {
+ pNode = GetNodes()[rPam.Start()->GetNodeIndex() + nOffset];
+ if (pNode->IsTextNode())
+ {
+ nodes = GetFirstAndLastNode(*pLayout, *pNode->GetTextNode());
+ assert(nodes.first && nodes.second);
+ nOffset -= pNode->GetIndex() - nodes.first->GetIndex();
+ // on first
+ break;
+ }
+ }
+ }
+ }
+ }
+ return MoveParagraphImpl(rPam, nOffset, bIsOutlMv, pLayout);
+}
+
+bool SwDoc::MoveParagraphImpl(SwPaM& rPam, SwNodeOffset const nOffset,
+ bool const bIsOutlMv, SwRootFrame const*const pLayout)
+{
+ auto [pStt, pEnd] = rPam.StartEnd(); // SwPosition*
+
+ SwNodeOffset nStIdx = pStt->GetNodeIndex();
+ SwNodeOffset nEndIdx = pEnd->GetNodeIndex();
+
+ // Here are some sophisticated checks whether the wished PaM will be moved or not.
+ // For moving outlines (bIsOutlMv) I've already done some checks, so here are two different
+ // checks...
+ SwNode *pTmp1;
+ SwNode *pTmp2;
+ if( bIsOutlMv )
+ {
+ // For moving chapters (outline) the following reason will deny the move:
+ // if a start node is inside the moved range and its end node outside or vice versa.
+ // If a start node is the first moved paragraph, its end node has to be within the moved
+ // range, too (e.g. as last node).
+ // If an end node is the last node of the moved range, its start node has to be a part of
+ // the moved section, too.
+ pTmp1 = GetNodes()[ nStIdx ];
+ if( pTmp1->IsStartNode() )
+ {
+ // coverity[copy_paste_error : FALSE] - First is a start node
+ pTmp2 = pTmp1->EndOfSectionNode();
+ if( pTmp2->GetIndex() > nEndIdx )
+ return false; // Its end node is behind the moved range
+ }
+ pTmp1 = pTmp1->StartOfSectionNode()->EndOfSectionNode();
+ if( pTmp1->GetIndex() <= nEndIdx )
+ return false; // End node inside but start node before moved range => no.
+ pTmp1 = GetNodes()[ nEndIdx ];
+ if( pTmp1->IsEndNode() )
+ { // The last one is an end node
+ pTmp1 = pTmp1->StartOfSectionNode();
+ if( pTmp1->GetIndex() < nStIdx )
+ return false; // Its start node is before the moved range.
+ }
+ pTmp1 = pTmp1->StartOfSectionNode();
+ if( pTmp1->GetIndex() >= nStIdx )
+ return false; // A start node which ends behind the moved range => no.
+ }
+
+ SwNodeOffset nInStIdx, nInEndIdx;
+ SwNodeOffset nOffs = nOffset;
+ if( nOffset > SwNodeOffset(0) )
+ {
+ nInEndIdx = nEndIdx;
+ nEndIdx += nOffset;
+ ++nOffs;
+ }
+ else
+ {
+ // Impossible to move to negative index
+ if( abs( nOffset ) > nStIdx)
+ return false;
+
+ nInEndIdx = nStIdx - 1;
+ nStIdx += nOffset;
+ }
+ nInStIdx = nInEndIdx + 1;
+ // The following paragraphs shall be swapped:
+ // Swap [ nStIdx, nInEndIdx ] with [ nInStIdx, nEndIdx ]
+
+ if( nEndIdx >= GetNodes().GetEndOfContent().GetIndex() )
+ return false;
+
+ if( !bIsOutlMv )
+ { // And here the restrictions for moving paragraphs other than chapters (outlines)
+ // The plan is to exchange [nStIdx,nInEndIdx] and [nStartIdx,nEndIdx]
+ // It will checked if the both "start" nodes as well as the both "end" notes belongs to
+ // the same start-end-section. This is more restrictive than the conditions checked above.
+ // E.g. a paragraph will not escape from a section or be inserted to another section.
+ pTmp1 = GetNodes()[ nStIdx ]->StartOfSectionNode();
+ pTmp2 = GetNodes()[ nInStIdx ]->StartOfSectionNode();
+ if( pTmp1 != pTmp2 )
+ return false; // "start" nodes in different sections
+ pTmp1 = GetNodes()[ nEndIdx ];
+ bool bIsEndNode = pTmp1->IsEndNode();
+ if( !pTmp1->IsStartNode() )
+ {
+ pTmp1 = pTmp1->StartOfSectionNode();
+ if( bIsEndNode ) // For end nodes the first start node is of course inside the range,
+ pTmp1 = pTmp1->StartOfSectionNode(); // I've to check the start node of the start node.
+ }
+ pTmp1 = pTmp1->EndOfSectionNode();
+ pTmp2 = GetNodes()[ nInEndIdx ];
+ if( !pTmp2->IsStartNode() )
+ {
+ bIsEndNode = pTmp2->IsEndNode();
+ pTmp2 = pTmp2->StartOfSectionNode();
+ if( bIsEndNode )
+ pTmp2 = pTmp2->StartOfSectionNode();
+ }
+ pTmp2 = pTmp2->EndOfSectionNode();
+ if( pTmp1 != pTmp2 )
+ return false; // The "end" notes are in different sections
+ }
+
+ // Test for Redlining - Can the Selection be moved at all, actually?
+ if( !getIDocumentRedlineAccess().IsIgnoreRedline() )
+ {
+ SwRedlineTable::size_type nRedlPos = getIDocumentRedlineAccess().GetRedlinePos( pStt->GetNode(), RedlineType::Delete );
+ if( SwRedlineTable::npos != nRedlPos )
+ {
+ SwContentNode* pCNd = pEnd->GetNode().GetContentNode();
+ SwPosition aStPos( pStt->GetNode() );
+ SwPosition aEndPos( pEnd->GetNode(), pCNd, pCNd ? pCNd->Len() : 1 );
+ bool bCheckDel = true;
+
+ // There is a some Redline Delete Object for the range
+ for( ; nRedlPos < getIDocumentRedlineAccess().GetRedlineTable().size(); ++nRedlPos )
+ {
+ const SwRangeRedline* pTmp = getIDocumentRedlineAccess().GetRedlineTable()[ nRedlPos ];
+ if( !bCheckDel || RedlineType::Delete == pTmp->GetType() )
+ {
+ auto [pRStt, pREnd] = pTmp->StartEnd(); // SwPosition*
+ switch( ComparePosition( *pRStt, *pREnd, aStPos, aEndPos ))
+ {
+ case SwComparePosition::CollideStart:
+ case SwComparePosition::Behind: // Pos1 comes after Pos2
+ nRedlPos = getIDocumentRedlineAccess().GetRedlineTable().size();
+ break;
+
+ case SwComparePosition::CollideEnd:
+ case SwComparePosition::Before: // Pos1 comes before Pos2
+ break;
+ case SwComparePosition::Inside: // Pos1 is completely inside Pos2
+ // that's valid, but check all following for overlapping
+ bCheckDel = false;
+ break;
+
+ case SwComparePosition::Outside: // Pos2 is completely inside Pos1
+ case SwComparePosition::Equal: // Pos1 is equal to Pos2
+ case SwComparePosition::OverlapBefore: // Pos1 overlaps Pos2 in the beginning
+ case SwComparePosition::OverlapBehind: // Pos1 overlaps Pos2 at the end
+ return false;
+ }
+ }
+ }
+ }
+ }
+
+ {
+ // Send DataChanged before moving. We then can detect
+ // which objects are still in the range.
+ // After the move they could come before/after the
+ // Position.
+ SwDataChanged aTmp( rPam );
+ }
+
+ SwNodeIndex aIdx( nOffset > SwNodeOffset(0) ? pEnd->GetNode() : pStt->GetNode(), nOffs );
+ SwNodeRange aMvRg( pStt->GetNode(), SwNodeOffset(0), pEnd->GetNode(), SwNodeOffset(+1) );
+
+ SwRangeRedline* pOwnRedl = nullptr;
+ if( getIDocumentRedlineAccess().IsRedlineOn() )
+ {
+ // If the range is completely in the own Redline, we can move it!
+ SwRedlineTable::size_type nRedlPos = getIDocumentRedlineAccess().GetRedlinePos( pStt->GetNode(), RedlineType::Insert );
+ if( SwRedlineTable::npos != nRedlPos )
+ {
+ SwRangeRedline* pTmp = getIDocumentRedlineAccess().GetRedlineTable()[ nRedlPos ];
+ auto [pRStt, pREnd] = pTmp->StartEnd(); // SwPosition*
+ SwRangeRedline aTmpRedl( RedlineType::Insert, rPam );
+ const SwContentNode* pCEndNd = pEnd->GetNode().GetContentNode();
+ // Is completely in the range and is the own Redline too?
+ if( aTmpRedl.IsOwnRedline( *pTmp ) &&
+ (pRStt->GetNode() < pStt->GetNode() ||
+ (pRStt->GetNode() == pStt->GetNode() && !pRStt->GetContentIndex()) ) &&
+ (pEnd->GetNode() < pREnd->GetNode() ||
+ (pEnd->GetNode() == pREnd->GetNode() &&
+ pCEndNd ? pREnd->GetContentIndex() == pCEndNd->Len()
+ : !pREnd->GetContentIndex() )) )
+ {
+ pOwnRedl = pTmp;
+ if( nRedlPos + 1 < getIDocumentRedlineAccess().GetRedlineTable().size() )
+ {
+ pTmp = getIDocumentRedlineAccess().GetRedlineTable()[ nRedlPos+1 ];
+ if( *pTmp->Start() == *pREnd )
+ // then don't!
+ pOwnRedl = nullptr;
+ }
+
+ if( pOwnRedl &&
+ ( pRStt->GetNode() > aIdx.GetNode() || aIdx > pREnd->GetNode() ||
+ // pOwnRedl doesn't start at the beginning of a node, so it's not
+ // possible to resize it to contain the line moved before it
+ ( pRStt->GetNode() == aIdx.GetNode() && pRStt->GetContentIndex() > 0 ) ) )
+ {
+ // it's not in itself, so don't move it
+ pOwnRedl = nullptr;
+ }
+ }
+ }
+
+ if( !pOwnRedl )
+ {
+ GetIDocumentUndoRedo().StartUndo( SwUndoId::START, nullptr );
+
+ // First the Insert, then the Delete
+ SwPosition aInsPos( aIdx );
+
+ std::optional<SwPaM> oPam( std::in_place, pStt->GetNode(), 0, aMvRg.aEnd.GetNode(), 0 );
+
+ SwPaM& rOrigPam(rPam);
+ rOrigPam.DeleteMark();
+ rOrigPam.GetPoint()->Assign(aIdx.GetIndex() - 1);
+
+ bool bDelLastPara = !aInsPos.GetNode().IsContentNode();
+ SwNodeOffset nOrigIdx = aIdx.GetIndex();
+
+ /* When copying to a non-content node Copy will
+ insert a paragraph before that node and insert before
+ that inserted node. Copy creates an SwUndoInserts that
+ does not cover the extra paragraph. Thus we insert the
+ extra paragraph ourselves, _with_ correct undo
+ information. */
+ if (bDelLastPara)
+ {
+ /* aInsPos points to the non-content node. Move it to
+ the previous content node. */
+ SwPaM aInsPam(aInsPos);
+ const bool bMoved = aInsPam.Move(fnMoveBackward);
+ OSL_ENSURE(bMoved, "No content node found!");
+
+ if (bMoved)
+ {
+ /* Append the new node after the content node
+ found. The new position to insert the moved
+ paragraph at is before the inserted
+ paragraph. */
+ getIDocumentContentOperations().AppendTextNode(*aInsPam.GetPoint());
+ aInsPos = *aInsPam.GetPoint();
+ }
+ }
+
+ --aIdx; // move before insertion
+
+ // adjust empty nodes later
+ SwTextNode const*const pIsEmptyNode(nOffset < SwNodeOffset(0)
+ ? aInsPos.GetNode().GetTextNode()
+ : aIdx.GetNode().GetTextNode());
+ bool bIsEmptyNode = pIsEmptyNode && pIsEmptyNode->Len() == 0;
+
+ sal_uInt32 nMovedID = getIDocumentRedlineAccess().GetRedlineTable().getNewMovedID();
+ getIDocumentContentOperations().CopyRange(*oPam, aInsPos, SwCopyFlags::CheckPosInFly,
+ nMovedID);
+
+ // now delete all the delete redlines that were copied
+#ifndef NDEBUG
+ size_t nRedlines(getIDocumentRedlineAccess().GetRedlineTable().size());
+#endif
+ if (nOffset > SwNodeOffset(0))
+ assert(oPam->End()->GetNodeIndex() - oPam->Start()->GetNodeIndex() + nOffset == aInsPos.GetNodeIndex() - oPam->End()->GetNodeIndex());
+ else
+ assert(oPam->Start()->GetNodeIndex() - oPam->End()->GetNodeIndex() + nOffset == aInsPos.GetNodeIndex() - oPam->End()->GetNodeIndex());
+ SwRedlineTable::size_type i;
+ getIDocumentRedlineAccess().GetRedline(*oPam->End(), &i);
+ for ( ; 0 < i; --i)
+ { // iterate backwards and offset via the start nodes difference
+ SwRangeRedline const*const pRedline = getIDocumentRedlineAccess().GetRedlineTable()[i - 1];
+ if (*pRedline->End() < *oPam->Start())
+ {
+ break;
+ }
+ if (pRedline->GetType() == RedlineType::Delete &&
+ // tdf#145066 skip full-paragraph deletion which was jumped over
+ // in Show Changes mode to avoid of deleting an extra row
+ *oPam->Start() <= *pRedline->Start())
+ {
+ SwRangeRedline* pNewRedline;
+ {
+ SwPaM pam(*pRedline, nullptr);
+ SwNodeOffset const nCurrentOffset(
+ nOrigIdx - oPam->Start()->GetNodeIndex());
+ pam.GetPoint()->Assign(pam.GetPoint()->GetNodeIndex() + nCurrentOffset,
+ pam.GetPoint()->GetContentIndex());
+ pam.GetMark()->Assign(pam.GetMark()->GetNodeIndex() + nCurrentOffset,
+ pam.GetMark()->GetContentIndex());
+
+ pNewRedline = new SwRangeRedline( RedlineType::Delete, pam, nMovedID );
+ }
+ // note: effectively this will DeleteAndJoin the pam!
+ getIDocumentRedlineAccess().AppendRedline(pNewRedline, true);
+ assert(getIDocumentRedlineAccess().GetRedlineTable().size() <= nRedlines);
+ }
+ }
+
+ if( bDelLastPara )
+ {
+ // We need to remove the last empty Node again
+ aIdx = aInsPos.GetNode();
+ SwContentNode* pCNd = SwNodes::GoPrevious( &aInsPos );
+ if (pCNd)
+ aInsPos.AssignEndIndex( *pCNd );
+
+ // All, that are in the to-be-deleted Node, need to be
+ // moved to the next Node
+ for(SwRangeRedline* pTmp : getIDocumentRedlineAccess().GetRedlineTable())
+ {
+ SwPosition* pPos = &pTmp->GetBound();
+ if( pPos->GetNode() == aIdx.GetNode() )
+ {
+ pPos->Adjust(SwNodeOffset(1));
+ }
+ pPos = &pTmp->GetBound(false);
+ if( pPos->GetNode() == aIdx.GetNode() )
+ {
+ pPos->Adjust(SwNodeOffset(1));
+ }
+ }
+ CorrRel( aIdx.GetNode(), aInsPos );
+
+ if (pCNd)
+ pCNd->JoinNext();
+ }
+
+ rOrigPam.GetPoint()->Adjust(SwNodeOffset(1));
+ assert(*oPam->GetMark() < *oPam->GetPoint());
+ if (oPam->GetPoint()->GetNode().IsEndNode())
+ { // ensure redline ends on content node
+ oPam->GetPoint()->Adjust(SwNodeOffset(-1));
+ assert(oPam->GetPoint()->GetNode().IsTextNode());
+ SwTextNode *const pNode(oPam->GetPoint()->GetNode().GetTextNode());
+ oPam->GetPoint()->SetContent(pNode->Len());
+ }
+
+ RedlineFlags eOld = getIDocumentRedlineAccess().GetRedlineFlags();
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ // this should no longer happen in calls from the UI but maybe via API
+ SAL_WARN_IF((eOld & RedlineFlags::ShowMask) != RedlineFlags::ShowMask,
+ "sw.core", "redlines will be moved in DeleteAndJoin");
+
+ getIDocumentRedlineAccess().SetRedlineFlags(
+ RedlineFlags::On | RedlineFlags::ShowInsert | RedlineFlags::ShowDelete );
+ GetIDocumentUndoRedo().AppendUndo(
+ std::make_unique<SwUndoRedlineDelete>(*oPam, SwUndoId::DELETE));
+ }
+
+ SwRangeRedline* pNewRedline = new SwRangeRedline( RedlineType::Delete, *oPam, nMovedID );
+
+ // prevent assertion from aPam's target being deleted
+ SwNodeIndex bound1(oPam->GetBound().GetNode());
+ SwNodeIndex bound2(oPam->GetBound(false).GetNode());
+ oPam.reset();
+
+ getIDocumentRedlineAccess().AppendRedline( pNewRedline, true, nMovedID );
+
+ oPam.emplace(bound1, bound2);
+ sw::UpdateFramesForAddDeleteRedline(*this, *oPam);
+
+ // avoid setting empty nodes to tracked insertion
+ if ( bIsEmptyNode )
+ {
+ SwRedlineTable& rTable = getIDocumentRedlineAccess().GetRedlineTable();
+ SwRedlineTable::size_type nRedlPosWithEmpty =
+ getIDocumentRedlineAccess().GetRedlinePos( pStt->GetNode(), RedlineType::Insert );
+ if ( SwRedlineTable::npos != nRedlPosWithEmpty )
+ {
+ pOwnRedl = rTable[nRedlPosWithEmpty];
+ SwPosition *pRPos = nOffset < SwNodeOffset(0) ? pOwnRedl->End() : pOwnRedl->Start();
+ SwNodeIndex aIdx2 ( pRPos->GetNode() );
+ SwTextNode const*const pEmptyNode0(aIdx2.GetNode().GetTextNode());
+ if ( nOffset < SwNodeOffset(0) )
+ {
+ // move up
+ --aIdx2;
+ SwTextNode const*const pEmptyNode(aIdx2.GetNode().GetTextNode());
+ if ( pEmptyNode && pEmptyNode->Len() == 0 )
+ pRPos->Adjust(SwNodeOffset(-1));
+ }
+ else if ( pEmptyNode0 && pEmptyNode0->Len() == 0 )
+ {
+ // move down
+ ++aIdx2;
+ SwTextNode const*const pEmptyNode(aIdx2.GetNode().GetTextNode());
+ if (pEmptyNode)
+ pRPos->Adjust(SwNodeOffset(+1));
+ }
+
+ // sort redlines, when the trimmed range results bad redline order
+ if ( nRedlPosWithEmpty + 1 < rTable.size() &&
+ *rTable[nRedlPosWithEmpty + 1] < *rTable[nRedlPosWithEmpty] )
+ {
+ rTable.Remove(nRedlPosWithEmpty);
+ rTable.Insert(pOwnRedl);
+ }
+ }
+ }
+
+ getIDocumentRedlineAccess().SetRedlineFlags( eOld );
+ GetIDocumentUndoRedo().EndUndo( SwUndoId::END, nullptr );
+ getIDocumentState().SetModified();
+
+ return true;
+ }
+ }
+
+ if( !pOwnRedl && !getIDocumentRedlineAccess().IsIgnoreRedline() && !getIDocumentRedlineAccess().GetRedlineTable().empty() )
+ {
+ SwPaM aTemp(aIdx);
+ getIDocumentRedlineAccess().SplitRedline(aTemp);
+ }
+
+ SwNodeOffset nRedlSttNd(0), nRedlEndNd(0);
+ if( pOwnRedl )
+ {
+ const SwPosition *pRStt = pOwnRedl->Start(), *pREnd = pOwnRedl->End();
+ nRedlSttNd = pRStt->GetNodeIndex();
+ nRedlEndNd = pREnd->GetNodeIndex();
+ }
+
+ std::unique_ptr<SwUndoMoveNum> pUndo;
+ SwNodeOffset nMoved(0);
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ pUndo.reset(new SwUndoMoveNum( rPam, nOffset, bIsOutlMv ));
+ nMoved = rPam.End()->GetNodeIndex() - rPam.Start()->GetNodeIndex() + 1;
+ }
+
+ (void) pLayout; // note: move will insert between aIdx-1 and aIdx
+ assert(!pLayout // check not moving *into* delete redline (caller's fault)
+ || aIdx.GetNode().GetRedlineMergeFlag() == SwNode::Merge::None
+ || aIdx.GetNode().GetRedlineMergeFlag() == SwNode::Merge::First);
+ getIDocumentContentOperations().MoveNodeRange( aMvRg, aIdx.GetNode(), SwMoveFlags::REDLINES );
+
+ if( pUndo )
+ {
+ // i57907: Under circumstances (sections at the end of a chapter)
+ // the rPam.Start() is not moved to the new position.
+ // But aIdx should be at the new end position and as long as the
+ // number of moved paragraphs is nMoved, I know, where the new
+ // position is.
+ pUndo->SetStartNode( aIdx.GetIndex() - nMoved );
+ GetIDocumentUndoRedo().AppendUndo(std::move(pUndo));
+ }
+
+ if( pOwnRedl )
+ {
+ auto [pRStt, pREnd] = pOwnRedl->StartEnd(); // SwPosition*
+ if( pRStt->GetNodeIndex() != nRedlSttNd )
+ {
+ pRStt->Assign(nRedlSttNd);
+ }
+ if( pREnd->GetNodeIndex() != nRedlEndNd )
+ {
+ pREnd->Assign(nRedlEndNd);
+ SwContentNode* pCNd = pREnd->GetNode().GetContentNode();
+ if(pCNd)
+ pREnd->SetContent( pCNd->Len() );
+ }
+ }
+
+ getIDocumentState().SetModified();
+ return true;
+}
+
+bool SwDoc::NumOrNoNum( SwNode& rIdx, bool bDel )
+{
+ bool bResult = false;
+ SwTextNode * pTextNd = rIdx.GetTextNode();
+
+ if (pTextNd && pTextNd->GetNumRule() != nullptr &&
+ (pTextNd->HasNumber() || pTextNd->HasBullet()))
+ {
+ if ( !pTextNd->IsCountedInList() == !bDel)
+ {
+ bool bOldNum = bDel;
+ bool bNewNum = !bDel;
+ pTextNd->SetCountedInList(bNewNum);
+
+ getIDocumentState().SetModified();
+
+ bResult = true;
+
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().AppendUndo(
+ std::make_unique<SwUndoNumOrNoNum>(rIdx, bOldNum, bNewNum));
+ }
+ }
+ else if (bDel && pTextNd->GetNumRule(false) &&
+ pTextNd->GetActualListLevel() >= 0 &&
+ pTextNd->GetActualListLevel() < MAXLEVEL)
+ {
+ SwPaM aPam(*pTextNd);
+ DelNumRules(aPam);
+
+ bResult = true;
+ }
+ }
+
+ return bResult;
+}
+
+SwNumRule* SwDoc::GetNumRuleAtPos(SwPosition& rPos,
+ SwRootFrame const*const pLayout)
+{
+ SwNumRule* pRet = nullptr;
+ SwTextNode* pTNd = rPos.GetNode().GetTextNode();
+
+ if ( pTNd != nullptr )
+ {
+ if (pLayout && !sw::IsParaPropsNode(*pLayout, *pTNd))
+ {
+ pTNd = static_cast<SwTextFrame*>(pTNd->getLayoutFrame(pLayout))->GetMergedPara()->pParaPropsNode;
+ rPos.Assign(*pTNd);
+ }
+ pRet = pTNd->GetNumRule();
+ }
+
+ return pRet;
+}
+
+sal_uInt16 SwDoc::FindNumRule( std::u16string_view rName ) const
+{
+ for( sal_uInt16 n = mpNumRuleTable->size(); n; )
+ if( (*mpNumRuleTable)[ --n ]->GetName() == rName )
+ return n;
+
+ return USHRT_MAX;
+}
+
+SwNumRule* SwDoc::FindNumRulePtr( const OUString& rName ) const
+{
+ SwNumRule * pResult = maNumRuleMap[rName];
+
+ if ( !pResult )
+ {
+ for (size_t n = 0; n < mpNumRuleTable->size(); ++n)
+ {
+ if ((*mpNumRuleTable)[n]->GetName() == rName)
+ {
+ pResult = (*mpNumRuleTable)[n];
+
+ break;
+ }
+ }
+ }
+
+ return pResult;
+}
+
+void SwDoc::AddNumRule(SwNumRule * pRule)
+{
+ if ((SAL_MAX_UINT16 - 1) <= mpNumRuleTable->size())
+ {
+ OSL_ENSURE(false, "SwDoc::AddNumRule: table full.");
+ abort(); // this should never happen on real documents
+ }
+ mpNumRuleTable->push_back(pRule);
+ maNumRuleMap[pRule->GetName()] = pRule;
+ pRule->SetNumRuleMap(&maNumRuleMap);
+
+ getIDocumentListsAccess().createListForListStyle( pRule->GetName() );
+}
+
+sal_uInt16 SwDoc::MakeNumRule( const OUString &rName,
+ const SwNumRule* pCpy,
+ bool bBroadcast,
+ const SvxNumberFormat::SvxNumPositionAndSpaceMode eDefaultNumberFormatPositionAndSpaceMode )
+{
+ SwNumRule* pNew;
+ if( pCpy )
+ {
+ pNew = new SwNumRule( *pCpy );
+
+ pNew->SetName( GetUniqueNumRuleName( &rName ), getIDocumentListsAccess() );
+
+ if( pNew->GetName() != rName )
+ {
+ pNew->SetPoolFormatId( USHRT_MAX );
+ pNew->SetPoolHelpId( USHRT_MAX );
+ pNew->SetPoolHlpFileId( UCHAR_MAX );
+ pNew->SetDefaultListId( OUString() );
+ }
+ pNew->CheckCharFormats( *this );
+ }
+ else
+ {
+ pNew = new SwNumRule( GetUniqueNumRuleName( &rName ),
+ eDefaultNumberFormatPositionAndSpaceMode );
+ }
+
+ sal_uInt16 nRet = mpNumRuleTable->size();
+
+ AddNumRule(pNew);
+
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().AppendUndo(
+ std::make_unique<SwUndoNumruleCreate>(pNew, *this));
+ }
+
+ if (bBroadcast)
+ BroadcastStyleOperation(pNew->GetName(), SfxStyleFamily::Pseudo,
+ SfxHintId::StyleSheetCreated);
+
+ return nRet;
+}
+
+OUString SwDoc::GetUniqueNumRuleName( const OUString* pChkStr, bool bAutoNum ) const
+{
+ // If we got pChkStr, then the caller expects that in case it's not yet
+ // used, it'll be returned.
+ if( IsInMailMerge() && !pChkStr )
+ {
+ OUString newName = "MailMergeNumRule"
+ + OStringToOUString( DateTimeToOString( DateTime( DateTime::SYSTEM )), RTL_TEXTENCODING_ASCII_US )
+ + OUString::number( mpNumRuleTable->size() + 1 );
+ return newName;
+ }
+
+ OUString aName;
+ if( bAutoNum )
+ {
+ static bool bHack = (getenv("LIBO_ONEWAY_STABLE_ODF_EXPORT") != nullptr);
+
+ if (bHack)
+ {
+ static sal_Int64 nIdCounter = SAL_CONST_INT64(8000000000);
+ aName = OUString::number(nIdCounter++);
+ }
+ else
+ {
+ unsigned int const n(comphelper::rng::uniform_uint_distribution(0,
+ std::numeric_limits<unsigned int>::max()));
+ aName = OUString::number(n);
+ }
+ if( pChkStr && pChkStr->isEmpty() )
+ pChkStr = nullptr;
+ }
+ else if( pChkStr && !pChkStr->isEmpty() )
+ aName = *pChkStr;
+ else
+ {
+ pChkStr = nullptr;
+ aName = SwResId( STR_NUMRULE_DEFNAME );
+ }
+
+ sal_uInt16 nNum(0), nTmp, nFlagSize = ( mpNumRuleTable->size() / 8 ) +2;
+ std::unique_ptr<sal_uInt8[]> pSetFlags(new sal_uInt8[ nFlagSize ]);
+ memset( pSetFlags.get(), 0, nFlagSize );
+
+ sal_Int32 nNmLen = aName.getLength();
+ if( !bAutoNum && pChkStr )
+ {
+ while( nNmLen-- && '0' <= aName[nNmLen] && aName[nNmLen] <= '9' )
+ ; //nop
+
+ if( ++nNmLen < aName.getLength() )
+ {
+ aName = aName.copy(0, nNmLen );
+ pChkStr = nullptr;
+ }
+ }
+
+ for( auto const & pNumRule: *mpNumRuleTable )
+ if( nullptr != pNumRule )
+ {
+ const OUString sNm = pNumRule->GetName();
+ if( sNm.startsWith( aName ) )
+ {
+ // Determine Number and set the Flag
+ nNum = o3tl::narrowing<sal_uInt16>(o3tl::toInt32(sNm.subView( nNmLen )));
+ if( nNum-- && nNum < mpNumRuleTable->size() )
+ pSetFlags[ nNum / 8 ] |= (0x01 << ( nNum & 0x07 ));
+ }
+ if( pChkStr && *pChkStr==sNm )
+ pChkStr = nullptr;
+ }
+
+ if( !pChkStr )
+ {
+ // All Numbers have been flagged accordingly, so identify the right Number
+ nNum = mpNumRuleTable->size();
+ for( sal_uInt16 n = 0; n < nFlagSize; ++n )
+ {
+ nTmp = pSetFlags[ n ];
+ if( 0xff != nTmp )
+ {
+ // identify the Number
+ nNum = n * 8;
+ while( nTmp & 1 )
+ {
+ ++nNum;
+ nTmp >>= 1;
+ }
+ break;
+ }
+ }
+ }
+ if( pChkStr && !pChkStr->isEmpty() )
+ return *pChkStr;
+ return aName + OUString::number( ++nNum );
+}
+
+void SwDoc::UpdateNumRule()
+{
+ const SwNumRuleTable& rNmTable = GetNumRuleTable();
+ for( size_t n = 0; n < rNmTable.size(); ++n )
+ if( rNmTable[ n ]->IsInvalidRule() )
+ rNmTable[ n ]->Validate(*this);
+}
+
+void SwDoc::MarkListLevel( const OUString& sListId,
+ const int nListLevel,
+ const bool bValue )
+{
+ SwList* pList = getIDocumentListsAccess().getListByName( sListId );
+
+ if ( pList )
+ {
+ // Set new marked list level and notify all affected nodes of the changed mark.
+ pList->MarkListLevel( nListLevel, bValue );
+ }
+}
+
+bool SwDoc::IsFirstOfNumRuleAtPos(const SwPosition & rPos,
+ SwRootFrame const& rLayout)
+{
+ bool bResult = false;
+
+ const SwTextNode *const pTextNode = sw::GetParaPropsNode(rLayout, rPos.GetNode());
+ if ( pTextNode != nullptr )
+ {
+ bResult = pTextNode->IsFirstOfNumRule(rLayout);
+ }
+
+ return bResult;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/docredln.cxx b/sw/source/core/doc/docredln.cxx
new file mode 100644
index 0000000000..3b0f8d7b9f
--- /dev/null
+++ b/sw/source/core/doc/docredln.cxx
@@ -0,0 +1,2404 @@
+/* -*- 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 <libxml/xmlwriter.h>
+#include <boost/property_tree/json_parser.hpp>
+
+#include <osl/diagnose.h>
+#include <sal/log.hxx>
+#include <tools/datetimeutils.hxx>
+#include <hintids.hxx>
+#include <svl/itemiter.hxx>
+#include <editeng/prntitem.hxx>
+#include <comphelper/lok.hxx>
+#include <comphelper/string.hxx>
+#include <LibreOfficeKit/LibreOfficeKitEnums.h>
+#include <unotools/datetime.hxx>
+#include <sfx2/viewsh.hxx>
+#include <o3tl/string_view.hxx>
+#include <swmodule.hxx>
+#include <doc.hxx>
+#include <docredln.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <DocumentContentOperationsManager.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <IDocumentState.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <IDocumentStylePoolAccess.hxx>
+#include <docary.hxx>
+#include <ndtxt.hxx>
+#include <redline.hxx>
+#include <UndoCore.hxx>
+#include <hints.hxx>
+#include <pamtyp.hxx>
+#include <poolfmt.hxx>
+#include <algorithm>
+#include <limits>
+#include <utility>
+#include <view.hxx>
+#include <viewopt.hxx>
+#include <usrpref.hxx>
+#include <viewsh.hxx>
+#include <viscrs.hxx>
+#include <rootfrm.hxx>
+#include <strings.hrc>
+#include <swtypes.hxx>
+#include <wrtsh.hxx>
+#include <txtfld.hxx>
+
+#include <flowfrm.hxx>
+#include <txtfrm.hxx>
+
+using namespace com::sun::star;
+
+#ifdef DBG_UTIL
+
+ void sw_DebugRedline( const SwDoc* pDoc )
+ {
+ static SwRedlineTable::size_type nWatch = 0; // loplugin:constvars:ignore
+ const SwRedlineTable& rTable = pDoc->getIDocumentRedlineAccess().GetRedlineTable();
+ for( SwRedlineTable::size_type n = 0; n < rTable.size(); ++n )
+ {
+ volatile SwRedlineTable::size_type nDummy = 0;
+ const SwRangeRedline* pCurrent = rTable[ n ];
+ const SwRangeRedline* pNext = n+1 < rTable.size() ? rTable[ n+1 ] : nullptr;
+ if( pCurrent == pNext )
+ (void) nDummy;
+ if( n == nWatch )
+ (void) nDummy; // Possible debugger breakpoint
+ }
+ }
+
+#endif
+
+
+SwExtraRedlineTable::~SwExtraRedlineTable()
+{
+ DeleteAndDestroyAll();
+}
+
+void SwExtraRedlineTable::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwExtraRedlineTable"));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
+
+ for (sal_uInt16 nCurExtraRedlinePos = 0; nCurExtraRedlinePos < GetSize(); ++nCurExtraRedlinePos)
+ {
+ const SwExtraRedline* pExtraRedline = GetRedline(nCurExtraRedlinePos);
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwExtraRedline"));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("symbol"), "%s", BAD_CAST(typeid(*pExtraRedline).name()));
+ (void)xmlTextWriterEndElement(pWriter);
+ }
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+#if OSL_DEBUG_LEVEL > 0
+static bool CheckPosition( const SwPosition* pStt, const SwPosition* pEnd )
+{
+ int nError = 0;
+ SwNode* pSttNode = &pStt->GetNode();
+ SwNode* pEndNode = &pEnd->GetNode();
+ SwNode* pSttTab = pSttNode->StartOfSectionNode()->FindTableNode();
+ SwNode* pEndTab = pEndNode->StartOfSectionNode()->FindTableNode();
+ SwNode* pSttStart = pSttNode;
+ while( pSttStart && (!pSttStart->IsStartNode() || pSttStart->IsSectionNode() ||
+ pSttStart->IsTableNode() ) )
+ pSttStart = pSttStart->StartOfSectionNode();
+ SwNode* pEndStart = pEndNode;
+ while( pEndStart && (!pEndStart->IsStartNode() || pEndStart->IsSectionNode() ||
+ pEndStart->IsTableNode() ) )
+ pEndStart = pEndStart->StartOfSectionNode();
+ assert(pSttTab == pEndTab);
+ if( pSttTab != pEndTab )
+ nError = 1;
+ assert(pSttTab || pSttStart == pEndStart);
+ if( !pSttTab && pSttStart != pEndStart )
+ nError |= 2;
+ if( nError )
+ nError += 10;
+ return nError != 0;
+}
+#endif
+
+bool SwExtraRedlineTable::DeleteAllTableRedlines( SwDoc& rDoc, const SwTable& rTable, bool bSaveInUndo, RedlineType nRedlineTypeToDelete )
+{
+ bool bChg = false;
+
+ if (bSaveInUndo && rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ // #TODO - Add 'Undo' support for deleting 'Table Cell' redlines
+ /*
+ SwUndoRedline* pUndo = new SwUndoRedline( SwUndoId::REDLINE, rRange );
+ if( pUndo->GetRedlSaveCount() )
+ {
+ GetIDocumentUndoRedo().AppendUndo(pUndo);
+ }
+ else
+ delete pUndo;
+ */
+ }
+
+ for (sal_uInt16 nCurRedlinePos = 0; nCurRedlinePos < GetSize(); )
+ {
+ SwExtraRedline* pExtraRedline = GetRedline(nCurRedlinePos);
+ const SwTableCellRedline* pTableCellRedline = dynamic_cast<const SwTableCellRedline*>(pExtraRedline);
+ if (pTableCellRedline)
+ {
+ const SwTableBox *pRedTabBox = &pTableCellRedline->GetTableBox();
+ const SwTable& rRedTable = pRedTabBox->GetSttNd()->FindTableNode()->GetTable();
+ if ( &rRedTable == &rTable )
+ {
+ // Redline for this table
+ const SwRedlineData& aRedlineData = pTableCellRedline->GetRedlineData();
+ const RedlineType nRedlineType = aRedlineData.GetType();
+
+ // Check if this redline object type should be deleted
+ if (RedlineType::Any == nRedlineTypeToDelete || nRedlineTypeToDelete == nRedlineType)
+ {
+
+ DeleteAndDestroy( nCurRedlinePos );
+ bChg = true;
+ continue; // don't increment position after delete
+ }
+ }
+ }
+ ++nCurRedlinePos;
+ }
+
+ if( bChg )
+ rDoc.getIDocumentState().SetModified();
+
+ return bChg;
+}
+
+bool SwExtraRedlineTable::DeleteTableRowRedline( SwDoc* pDoc, const SwTableLine& rTableLine, bool bSaveInUndo, RedlineType nRedlineTypeToDelete )
+{
+ bool bChg = false;
+
+ if (bSaveInUndo && pDoc->GetIDocumentUndoRedo().DoesUndo())
+ {
+ // #TODO - Add 'Undo' support for deleting 'Table Cell' redlines
+ /*
+ SwUndoRedline* pUndo = new SwUndoRedline( SwUndoId::REDLINE, rRange );
+ if( pUndo->GetRedlSaveCount() )
+ {
+ GetIDocumentUndoRedo().AppendUndo(pUndo);
+ }
+ else
+ delete pUndo;
+ */
+ }
+
+ for(sal_uInt16 nCurRedlinePos = 0; nCurRedlinePos < GetSize(); ++nCurRedlinePos )
+ {
+ SwExtraRedline* pExtraRedline = GetRedline(nCurRedlinePos);
+ const SwTableRowRedline* pTableRowRedline = dynamic_cast<const SwTableRowRedline*>(pExtraRedline);
+ const SwTableLine *pRedTabLine = pTableRowRedline ? &pTableRowRedline->GetTableLine() : nullptr;
+ if ( pRedTabLine == &rTableLine )
+ {
+ // Redline for this table row
+ const SwRedlineData& aRedlineData = pTableRowRedline->GetRedlineData();
+ const RedlineType nRedlineType = aRedlineData.GetType();
+
+ // Check if this redline object type should be deleted
+ if( RedlineType::Any != nRedlineTypeToDelete && nRedlineTypeToDelete != nRedlineType )
+ continue;
+
+ DeleteAndDestroy( nCurRedlinePos );
+ bChg = true;
+ }
+ }
+
+ if( bChg )
+ pDoc->getIDocumentState().SetModified();
+
+ return bChg;
+}
+
+bool SwExtraRedlineTable::DeleteTableCellRedline( SwDoc* pDoc, const SwTableBox& rTableBox, bool bSaveInUndo, RedlineType nRedlineTypeToDelete )
+{
+ bool bChg = false;
+
+ if (bSaveInUndo && pDoc->GetIDocumentUndoRedo().DoesUndo())
+ {
+ // #TODO - Add 'Undo' support for deleting 'Table Cell' redlines
+ /*
+ SwUndoRedline* pUndo = new SwUndoRedline( SwUndoId::REDLINE, rRange );
+ if( pUndo->GetRedlSaveCount() )
+ {
+ GetIDocumentUndoRedo().AppendUndo(pUndo);
+ }
+ else
+ delete pUndo;
+ */
+ }
+
+ for(sal_uInt16 nCurRedlinePos = 0; nCurRedlinePos < GetSize(); ++nCurRedlinePos )
+ {
+ SwExtraRedline* pExtraRedline = GetRedline(nCurRedlinePos);
+ const SwTableCellRedline* pTableCellRedline = dynamic_cast<const SwTableCellRedline*>(pExtraRedline);
+ const SwTableBox *pRedTabBox = pTableCellRedline ? &pTableCellRedline->GetTableBox() : nullptr;
+ if ( pRedTabBox == &rTableBox )
+ {
+ // Redline for this table cell
+ const SwRedlineData& aRedlineData = pTableCellRedline->GetRedlineData();
+ const RedlineType nRedlineType = aRedlineData.GetType();
+
+ // Check if this redline object type should be deleted
+ if( RedlineType::Any != nRedlineTypeToDelete && nRedlineTypeToDelete != nRedlineType )
+ continue;
+
+ DeleteAndDestroy( nCurRedlinePos );
+ bChg = true;
+ }
+ }
+
+ if( bChg )
+ pDoc->getIDocumentState().SetModified();
+
+ return bChg;
+}
+
+namespace
+{
+
+void lcl_LOKInvalidateFrames(const sw::BroadcastingModify& rMod, const SwRootFrame* pLayout,
+ SwFrameType const nFrameType, const Point* pPoint)
+{
+ SwIterator<SwFrame, sw::BroadcastingModify, sw::IteratorMode::UnwrapMulti> aIter(rMod);
+
+ for (SwFrame* pTmpFrame = aIter.First(); pTmpFrame; pTmpFrame = aIter.Next() )
+ {
+ if ((pTmpFrame->GetType() & nFrameType) &&
+ (!pLayout || pLayout == pTmpFrame->getRootFrame()) &&
+ (!pTmpFrame->IsFlowFrame() || !SwFlowFrame::CastFlowFrame( pTmpFrame )->IsFollow()))
+ {
+ if (pPoint)
+ {
+ pTmpFrame->InvalidateSize();
+
+ // Also empty the text portion cache, so it gets rebuilt, taking the new redlines
+ // into account.
+ if (pTmpFrame->IsTextFrame())
+ {
+ auto pTextFrame = static_cast<SwTextFrame*>(pTmpFrame);
+ pTextFrame->ClearPara();
+ }
+ }
+ }
+ }
+}
+
+void lcl_LOKInvalidateStartEndFrames(SwShellCursor& rCursor)
+{
+ if (!(rCursor.HasMark() &&
+ rCursor.GetPoint()->GetNode().IsContentNode() &&
+ rCursor.GetPoint()->GetNode().GetContentNode()->getLayoutFrame(rCursor.GetShell()->GetLayout()) &&
+ (rCursor.GetMark()->GetNode() == rCursor.GetPoint()->GetNode() ||
+ (rCursor.GetMark()->GetNode().IsContentNode() &&
+ rCursor.GetMark()->GetNode().GetContentNode()->getLayoutFrame(rCursor.GetShell()->GetLayout())))))
+ {
+ return;
+ }
+
+ auto [pStartPos, pEndPos] = rCursor.StartEnd(); // SwPosition*
+
+ lcl_LOKInvalidateFrames(*(pStartPos->GetNode().GetContentNode()),
+ rCursor.GetShell()->GetLayout(),
+ FRM_CNTNT, &rCursor.GetSttPos());
+
+ lcl_LOKInvalidateFrames(*(pEndPos->GetNode().GetContentNode()),
+ rCursor.GetShell()->GetLayout(),
+ FRM_CNTNT, &rCursor.GetEndPos());
+}
+
+bool lcl_LOKRedlineNotificationEnabled()
+{
+ static bool bDisableRedlineComments = getenv("DISABLE_REDLINE") != nullptr;
+ if (comphelper::LibreOfficeKit::isActive() && !bDisableRedlineComments)
+ return true;
+
+ return false;
+}
+
+} // anonymous namespace
+
+void SwRedlineTable::setMovedIDIfNeeded(sal_uInt32 nMax)
+{
+ if (nMax > m_nMaxMovedID)
+ m_nMaxMovedID = nMax;
+}
+
+/// Emits LOK notification about one addition / removal of a redline item.
+void SwRedlineTable::LOKRedlineNotification(RedlineNotification nType, SwRangeRedline* pRedline)
+{
+ // Disable since usability is very low beyond some small number of changes.
+ if (!lcl_LOKRedlineNotificationEnabled())
+ return;
+
+ boost::property_tree::ptree aRedline;
+ aRedline.put("action", (nType == RedlineNotification::Add ? "Add" :
+ (nType == RedlineNotification::Remove ? "Remove" :
+ (nType == RedlineNotification::Modify ? "Modify" : "???"))));
+ aRedline.put("index", pRedline->GetId());
+ aRedline.put("author", pRedline->GetAuthorString(1).toUtf8().getStr());
+ aRedline.put("type", SwRedlineTypeToOUString(pRedline->GetRedlineData().GetType()).toUtf8().getStr());
+ aRedline.put("comment", pRedline->GetRedlineData().GetComment().toUtf8().getStr());
+ aRedline.put("description", pRedline->GetDescr().toUtf8().getStr());
+ OUString sDateTime = utl::toISO8601(pRedline->GetRedlineData().GetTimeStamp().GetUNODateTime());
+ aRedline.put("dateTime", sDateTime.toUtf8().getStr());
+
+ auto [pStartPos, pEndPos] = pRedline->StartEnd(); // SwPosition*
+ SwContentNode* pContentNd = pRedline->GetPointContentNode();
+ SwView* pView = dynamic_cast<SwView*>(SfxViewShell::Current());
+ if (pView && pContentNd)
+ {
+ SwShellCursor aCursor(pView->GetWrtShell(), *pStartPos);
+ aCursor.SetMark();
+ *aCursor.GetMark() = *pEndPos;
+
+ aCursor.FillRects();
+
+ SwRects* pRects(&aCursor);
+ std::vector<OString> aRects;
+ for(const SwRect& rNextRect : *pRects)
+ aRects.push_back(rNextRect.SVRect().toString());
+
+ const OString sRects = comphelper::string::join("; ", aRects);
+ aRedline.put("textRange", sRects.getStr());
+
+ lcl_LOKInvalidateStartEndFrames(aCursor);
+
+ // When this notify method is called text invalidation is not done yet
+ // Calling FillRects updates the text area so invalidation will not run on the correct rects
+ // So we need to do an own invalidation here. It invalidates text frames containing the redlining
+ SwDoc& rDoc = pRedline->GetDoc();
+ SwViewShell* pSh;
+ if( !rDoc.IsInDtor() )
+ {
+ pSh = rDoc.getIDocumentLayoutAccess().GetCurrentViewShell();
+ if( pSh )
+ for(SwNodeIndex nIdx(pStartPos->GetNode()); nIdx <= pEndPos->GetNode(); ++nIdx)
+ {
+ SwContentNode* pContentNode = nIdx.GetNode().GetContentNode();
+ if (pContentNode)
+ pSh->InvalidateWindows(pContentNode->FindLayoutRect());
+ }
+ }
+ }
+
+ boost::property_tree::ptree aTree;
+ aTree.add_child("redline", aRedline);
+ std::stringstream aStream;
+ boost::property_tree::write_json(aStream, aTree);
+ std::string aPayload = aStream.str();
+
+ SfxViewShell* pViewShell = SfxViewShell::GetFirst();
+ while (pViewShell)
+ {
+ if (pView && pView->GetDocId() == pViewShell->GetDocId())
+ pViewShell->libreOfficeKitViewCallback(nType == RedlineNotification::Modify ? LOK_CALLBACK_REDLINE_TABLE_ENTRY_MODIFIED : LOK_CALLBACK_REDLINE_TABLE_SIZE_CHANGED, OString(aPayload));
+ pViewShell = SfxViewShell::GetNext(*pViewShell);
+ }
+}
+
+bool SwRedlineTable::Insert(SwRangeRedline*& p)
+{
+ if( p->HasValidRange() )
+ {
+ std::pair<vector_type::const_iterator, bool> rv = maVector.insert( p );
+ size_type nP = rv.first - begin();
+ LOKRedlineNotification(RedlineNotification::Add, p);
+
+ // detect text moving by checking nearby redlines, except during Undo
+ // (apply isMoved() during OpenDocument and DOCX import, too, to fix
+ // missing text moving handling in ODF and e.g. web version of MSO)
+ if ( p->GetDoc().GetIDocumentUndoRedo().DoesUndo() ||
+ p->GetDoc().IsInWriterfilterImport() ||
+ p->GetDoc().IsInXMLImport() )
+ {
+ isMoved(nP);
+ }
+
+ p->CallDisplayFunc(nP);
+ if (rv.second)
+ CheckOverlapping(rv.first);
+ return rv.second;
+ }
+ return InsertWithValidRanges( p );
+}
+
+void SwRedlineTable::CheckOverlapping(vector_type::const_iterator it)
+{
+ if (m_bHasOverlappingElements)
+ return;
+ if (maVector.size() <= 1) // a single element cannot be overlapping
+ return;
+ auto pCurr = *it;
+ auto itNext = it + 1;
+ if (itNext != maVector.end())
+ {
+ auto pNext = *itNext;
+ if (pCurr->End()->GetNodeIndex() >= pNext->Start()->GetNodeIndex())
+ {
+ m_bHasOverlappingElements = true;
+ return;
+ }
+ }
+ if (it != maVector.begin())
+ {
+ auto pPrev = *(it - 1);
+ if (pPrev->End()->GetNodeIndex() >= pCurr->Start()->GetNodeIndex())
+ m_bHasOverlappingElements = true;
+ }
+}
+
+bool SwRedlineTable::Insert(SwRangeRedline*& p, size_type& rP)
+{
+ if( p->HasValidRange() )
+ {
+ std::pair<vector_type::const_iterator, bool> rv = maVector.insert( p );
+ rP = rv.first - begin();
+ p->CallDisplayFunc(rP);
+ if (rv.second)
+ CheckOverlapping(rv.first);
+ return rv.second;
+ }
+ return InsertWithValidRanges( p, &rP );
+}
+
+namespace sw {
+
+std::vector<std::unique_ptr<SwRangeRedline>> GetAllValidRanges(std::unique_ptr<SwRangeRedline> p)
+{
+ std::vector<std::unique_ptr<SwRangeRedline>> ret;
+ // Create valid "sub-ranges" from the Selection
+ auto [pStt, pEnd] = p->StartEnd(); // SwPosition*
+ SwPosition aNewStt( *pStt );
+ SwNodes& rNds = aNewStt.GetNodes();
+ SwContentNode* pC;
+
+ if( !aNewStt.GetNode().IsContentNode() )
+ {
+ pC = rNds.GoNext( &aNewStt );
+ if( !pC )
+ aNewStt.Assign(rNds.GetEndOfContent());
+ }
+
+
+ if( aNewStt >= *pEnd )
+ return ret;
+
+ std::unique_ptr<SwRangeRedline> pNew;
+ do {
+ if( !pNew )
+ pNew.reset(new SwRangeRedline( p->GetRedlineData(), aNewStt ));
+ else
+ {
+ pNew->DeleteMark();
+ *pNew->GetPoint() = aNewStt;
+ }
+
+ pNew->SetMark();
+ GoEndSection( pNew->GetPoint() );
+ // i60396: If the redlines starts before a table but the table is the last member
+ // of the section, the GoEndSection will end inside the table.
+ // This will result in an incorrect redline, so we've to go back
+ SwNode* pTab = pNew->GetPoint()->GetNode().StartOfSectionNode()->FindTableNode();
+ // We end in a table when pTab != 0
+ if( pTab && !pNew->GetMark()->GetNode().StartOfSectionNode()->FindTableNode() )
+ { // but our Mark was outside the table => Correction
+ do
+ {
+ // We want to be before the table
+ pNew->GetPoint()->Assign(*pTab);
+ pC = GoPreviousPos( pNew->GetPoint(), false ); // here we are.
+ if( pC )
+ pNew->GetPoint()->SetContent( 0 );
+ pTab = pNew->GetPoint()->GetNode().StartOfSectionNode()->FindTableNode();
+ } while( pTab ); // If there is another table we have to repeat our step backwards
+ }
+
+ // insert dummy character to the empty table rows to keep their changes
+ SwNode& rBoxNode = pNew->GetMark()->GetNode();
+ if ( rBoxNode.GetDoc().GetIDocumentUndoRedo().DoesUndo() && rBoxNode.GetTableBox() &&
+ rBoxNode.GetTableBox()->GetUpper()->IsEmpty() && rBoxNode.GetTextNode() )
+ {
+ ::sw::UndoGuard const undoGuard(rBoxNode.GetDoc().GetIDocumentUndoRedo());
+ rBoxNode.GetTextNode()->InsertDummy();
+ pNew->GetMark()->SetContent( 1 );
+ }
+
+ if( *pNew->GetPoint() > *pEnd )
+ {
+ pC = nullptr;
+ if( aNewStt.GetNode() != pEnd->GetNode() )
+ do {
+ SwNode& rCurNd = aNewStt.GetNode();
+ if( rCurNd.IsStartNode() )
+ {
+ if( rCurNd.EndOfSectionIndex() < pEnd->GetNodeIndex() )
+ aNewStt.Assign( *rCurNd.EndOfSectionNode() );
+ else
+ break;
+ }
+ else if( rCurNd.IsContentNode() )
+ pC = rCurNd.GetContentNode();
+ aNewStt.Adjust(SwNodeOffset(1));
+ } while( aNewStt.GetNodeIndex() < pEnd->GetNodeIndex() );
+
+ if( aNewStt.GetNode() == pEnd->GetNode() )
+ aNewStt.SetContent(pEnd->GetContentIndex());
+ else if( pC )
+ {
+ aNewStt.Assign(*pC, pC->Len() );
+ }
+
+ if( aNewStt <= *pEnd )
+ *pNew->GetPoint() = aNewStt;
+ }
+ else
+ aNewStt = *pNew->GetPoint();
+#if OSL_DEBUG_LEVEL > 0
+ CheckPosition( pNew->GetPoint(), pNew->GetMark() );
+#endif
+
+ if( *pNew->GetPoint() != *pNew->GetMark() &&
+ pNew->HasValidRange())
+ {
+ ret.push_back(std::move(pNew));
+ }
+
+ if( aNewStt >= *pEnd )
+ break;
+ pC = rNds.GoNext( &aNewStt );
+ if( !pC )
+ break;
+ } while( aNewStt < *pEnd );
+
+ return ret;
+}
+
+} // namespace sw
+
+static void lcl_setRowNotTracked(SwNode& rNode)
+{
+ SwDoc& rDoc = rNode.GetDoc();
+ if ( rDoc.GetIDocumentUndoRedo().DoesUndo() && rNode.GetTableBox() )
+ {
+ SvxPrintItem aSetTracking(RES_PRINT, false);
+ SwNodeIndex aInsPos( *(rNode.GetTableBox()->GetSttNd()), 1);
+ SwCursor aCursor( SwPosition(aInsPos), nullptr );
+ ::sw::UndoGuard const undoGuard(rNode.GetDoc().GetIDocumentUndoRedo());
+ rDoc.SetRowNotTracked( aCursor, aSetTracking );
+ }
+}
+
+bool SwRedlineTable::InsertWithValidRanges(SwRangeRedline*& p, size_type* pInsPos)
+{
+ bool bAnyIns = false;
+ bool bInsert = RedlineType::Insert == p->GetType();
+ SwNode* pSttNode = &p->Start()->GetNode();
+
+ std::vector<std::unique_ptr<SwRangeRedline>> redlines(
+ GetAllValidRanges(std::unique_ptr<SwRangeRedline>(p)));
+
+ // tdf#147180 set table change tracking in the empty row with text insertion
+ if ( bInsert )
+ lcl_setRowNotTracked(*pSttNode);
+
+ for (std::unique_ptr<SwRangeRedline> & pRedline : redlines)
+ {
+ assert(pRedline->HasValidRange());
+ size_type nInsPos;
+ auto pTmpRedline = pRedline.release();
+ if (Insert(pTmpRedline, nInsPos))
+ {
+ // tdf#147180 set table tracking to the table row
+ lcl_setRowNotTracked(pTmpRedline->GetPointNode());
+
+ pTmpRedline->CallDisplayFunc(nInsPos);
+ bAnyIns = true;
+ if (pInsPos && *pInsPos < nInsPos)
+ {
+ *pInsPos = nInsPos;
+ }
+ }
+ }
+ p = nullptr;
+ return bAnyIns;
+}
+
+bool CompareSwRedlineTable::operator()(SwRangeRedline* const &lhs, SwRangeRedline* const &rhs) const
+{
+ return *lhs < *rhs;
+}
+
+SwRedlineTable::~SwRedlineTable()
+{
+ maVector.DeleteAndDestroyAll();
+}
+
+SwRedlineTable::size_type SwRedlineTable::GetPos(const SwRangeRedline* p) const
+{
+ vector_type::const_iterator it = maVector.find(const_cast<SwRangeRedline*>(p));
+ if( it == maVector.end() )
+ return npos;
+ return it - maVector.begin();
+}
+
+void SwRedlineTable::Remove( const SwRangeRedline* p )
+{
+ const size_type nPos = GetPos(p);
+ if (nPos == npos)
+ return;
+ Remove(nPos);
+}
+
+void SwRedlineTable::Remove( size_type nP )
+{
+ LOKRedlineNotification(RedlineNotification::Remove, maVector[nP]);
+ SwDoc* pDoc = nullptr;
+ if( !nP && 1 == size() )
+ pDoc = &maVector.front()->GetDoc();
+
+ maVector.erase( maVector.begin() + nP );
+
+ if( pDoc && !pDoc->IsInDtor() )
+ {
+ SwViewShell* pSh = pDoc->getIDocumentLayoutAccess().GetCurrentViewShell();
+ if( pSh )
+ pSh->InvalidateWindows( SwRect( 0, 0, SAL_MAX_INT32, SAL_MAX_INT32 ) );
+ }
+}
+
+void SwRedlineTable::DeleteAndDestroyAll()
+{
+ while (!maVector.empty())
+ {
+ auto const pRedline = maVector.back();
+ maVector.erase_at(maVector.size() - 1);
+ LOKRedlineNotification(RedlineNotification::Remove, pRedline);
+ delete pRedline;
+ }
+ m_bHasOverlappingElements = false;
+}
+
+void SwRedlineTable::DeleteAndDestroy(size_type const nP)
+{
+ auto const pRedline = maVector[nP];
+ maVector.erase(maVector.begin() + nP);
+ LOKRedlineNotification(RedlineNotification::Remove, pRedline);
+ delete pRedline;
+}
+
+SwRedlineTable::size_type SwRedlineTable::FindNextOfSeqNo( size_type nSttPos ) const
+{
+ return nSttPos + 1 < size()
+ ? FindNextSeqNo( operator[]( nSttPos )->GetSeqNo(), nSttPos+1 )
+ : npos;
+}
+
+SwRedlineTable::size_type SwRedlineTable::FindPrevOfSeqNo( size_type nSttPos ) const
+{
+ return nSttPos ? FindPrevSeqNo( operator[]( nSttPos )->GetSeqNo(), nSttPos-1 )
+ : npos;
+}
+
+/// Find the next or preceding Redline with the same seq.no.
+/// We can limit the search using look ahead (0 searches the whole array).
+SwRedlineTable::size_type SwRedlineTable::FindNextSeqNo( sal_uInt16 nSeqNo, size_type nSttPos ) const
+{
+ auto constexpr nLookahead = 20;
+ size_type nRet = npos;
+ if( nSeqNo && nSttPos < size() )
+ {
+ size_type nEnd = size();
+ const size_type nTmp = nSttPos + nLookahead;
+ if (nTmp < nEnd)
+ {
+ nEnd = nTmp;
+ }
+
+ for( ; nSttPos < nEnd; ++nSttPos )
+ if( nSeqNo == operator[]( nSttPos )->GetSeqNo() )
+ {
+ nRet = nSttPos;
+ break;
+ }
+ }
+ return nRet;
+}
+
+SwRedlineTable::size_type SwRedlineTable::FindPrevSeqNo( sal_uInt16 nSeqNo, size_type nSttPos ) const
+{
+ auto constexpr nLookahead = 20;
+ size_type nRet = npos;
+ if( nSeqNo && nSttPos < size() )
+ {
+ size_type nEnd = 0;
+ if( nSttPos > nLookahead )
+ nEnd = nSttPos - nLookahead;
+
+ ++nSttPos;
+ while( nSttPos > nEnd )
+ if( nSeqNo == operator[]( --nSttPos )->GetSeqNo() )
+ {
+ nRet = nSttPos;
+ break;
+ }
+ }
+ return nRet;
+}
+
+const SwRangeRedline* SwRedlineTable::FindAtPosition( const SwPosition& rSttPos,
+ size_type& rPos,
+ bool bNext ) const
+{
+ const SwRangeRedline* pFnd = nullptr;
+ for( ; rPos < maVector.size() ; ++rPos )
+ {
+ const SwRangeRedline* pTmp = (*this)[ rPos ];
+ if( pTmp->HasMark() && pTmp->IsVisible() )
+ {
+ auto [pRStt, pREnd] = pTmp->StartEnd(); // SwPosition*
+ if( bNext ? *pRStt <= rSttPos : *pRStt < rSttPos )
+ {
+ if( bNext ? *pREnd > rSttPos : *pREnd >= rSttPos )
+ {
+ pFnd = pTmp;
+ break;
+ }
+ }
+ else
+ break;
+ }
+ }
+ return pFnd;
+}
+
+namespace
+{
+bool lcl_CanCombineWithRange(SwRangeRedline* pOrigin, SwRangeRedline* pActual,
+ SwRangeRedline* pOther, bool bReverseDir, bool bCheckChilds)
+{
+ if (pOrigin->IsVisible() != pOther->IsVisible())
+ return false;
+
+ if (bReverseDir)
+ {
+ if (*(pOther->End()) != *(pActual->Start()))
+ return false;
+ }
+ else
+ {
+ if (*(pActual->End()) != *(pOther->Start()))
+ return false;
+ }
+
+ if (!pOrigin->GetRedlineData(0).CanCombineForAcceptReject(pOther->GetRedlineData(0)))
+ {
+ if (!bCheckChilds || pOther->GetStackCount() <= 1
+ || !pOrigin->GetRedlineData(0).CanCombineForAcceptReject(pOther->GetRedlineData(1)))
+ return false;
+ }
+ if (pOther->Start()->GetNode().StartOfSectionNode()
+ != pActual->Start()->GetNode().StartOfSectionNode())
+ return false;
+
+ return true;
+}
+}
+
+void SwRedlineTable::getConnectedArea(size_type nPosOrigin, size_type& rPosStart,
+ size_type& rPosEnd, bool bCheckChilds) const
+{
+ // Keep the original redline .. else we should memorize which children was checked
+ // at the last combined redline.
+ SwRangeRedline* pOrigin = (*this)[nPosOrigin];
+ rPosStart = nPosOrigin;
+ rPosEnd = nPosOrigin;
+ SwRangeRedline* pRedline = pOrigin;
+ SwRangeRedline* pOther;
+
+ // connection info is already here..only the actual text is missing at import time
+ // so no need to check Redline->GetContentIdx() here yet.
+ while (rPosStart > 0 && (pOther = (*this)[rPosStart - 1])
+ && lcl_CanCombineWithRange(pOrigin, pRedline, pOther, true, bCheckChilds))
+ {
+ rPosStart--;
+ pRedline = pOther;
+ }
+ pRedline = pOrigin;
+ while (rPosEnd + 1 < size() && (pOther = (*this)[rPosEnd + 1])
+ && lcl_CanCombineWithRange(pOrigin, pRedline, pOther, false, bCheckChilds))
+ {
+ rPosEnd++;
+ pRedline = pOther;
+ }
+}
+
+OUString SwRedlineTable::getTextOfArea(size_type rPosStart, size_type rPosEnd) const
+{
+ // Normally a SwPaM::GetText() would be enough with rPosStart-start and rPosEnd-end
+ // But at import time some text is not present there yet
+ // we have to collect them 1 by 1
+
+ OUString sRet = "";
+
+ for (size_type nIdx = rPosStart; nIdx <= rPosEnd; ++nIdx)
+ {
+ SwRangeRedline* pRedline = (*this)[nIdx];
+ bool bStartWithNonTextNode = false;
+
+ SwPaM *pPaM;
+ bool bDeletePaM = false;
+ if (nullptr == pRedline->GetContentIdx())
+ {
+ pPaM = pRedline;
+ }
+ else // otherwise it is saved in pContentSect, e.g. during ODT import
+ {
+ pPaM = new SwPaM(pRedline->GetContentIdx()->GetNode(),
+ *pRedline->GetContentIdx()->GetNode().EndOfSectionNode());
+ if (!pPaM->Start()->nNode.GetNode().GetTextNode())
+ {
+ bStartWithNonTextNode = true;
+ }
+ bDeletePaM = true;
+ }
+ const OUString sNew = pPaM->GetText();
+
+ if (bStartWithNonTextNode &&
+ sNew[0] == CH_TXTATR_NEWLINE)
+ {
+ sRet += pPaM->GetText().subView(1);
+ }
+ else
+ sRet += pPaM->GetText();
+ if (bDeletePaM)
+ delete pPaM;
+ }
+
+ return sRet;
+}
+
+bool SwRedlineTable::isMoved(size_type rPos) const
+{
+ // If it is already a part of a movement, then don't check it.
+ if ((*this)[rPos]->GetMoved() != 0)
+ return false;
+ // First try with single redline. then try with combined redlines
+ if (isMovedImpl(rPos, false))
+ return true;
+ else
+ return isMovedImpl(rPos, true);
+}
+
+bool SwRedlineTable::isMovedImpl(size_type rPos, bool bTryCombined) const
+{
+ bool bRet = false;
+ auto constexpr nLookahead = 20;
+ SwRangeRedline* pRedline = (*this)[ rPos ];
+
+ // set redline type of the searched pair
+ RedlineType nPairType = pRedline->GetType();
+ if ( RedlineType::Delete == nPairType )
+ nPairType = RedlineType::Insert;
+ else if ( RedlineType::Insert == nPairType )
+ nPairType = RedlineType::Delete;
+ else
+ // only deleted or inserted text can be moved
+ return false;
+
+ bool bDeletePaM = false;
+ SwPaM* pPaM = nullptr;
+ OUString sTrimmed;
+ SwRedlineTable::size_type nPosStart = rPos;
+ SwRedlineTable::size_type nPosEnd = rPos;
+
+ if (bTryCombined)
+ {
+ getConnectedArea(rPos, nPosStart, nPosEnd, false);
+ if (nPosStart != nPosEnd)
+ sTrimmed = getTextOfArea(nPosStart, nPosEnd).trim();
+ }
+
+ if (sTrimmed.isEmpty())
+ {
+ // if this redline is visible the content is in this PaM
+ if (nullptr == pRedline->GetContentIdx())
+ {
+ pPaM = pRedline;
+ }
+ else // otherwise it is saved in pContentSect, e.g. during ODT import
+ {
+ pPaM = new SwPaM(pRedline->GetContentIdx()->GetNode(),
+ *pRedline->GetContentIdx()->GetNode().EndOfSectionNode());
+ bDeletePaM = true;
+ }
+
+ sTrimmed = pPaM->GetText().trim();
+ }
+
+ // detection of move needs at least 6 characters with an inner
+ // space after stripping white spaces of the redline to skip
+ // frequent deleted and inserted articles or other common
+ // word parts, e.g. 'the' and 'of a' to detect as text moving
+ if (sTrimmed.getLength() < 6 || sTrimmed.indexOf(' ') == -1)
+ {
+ if (bDeletePaM)
+ delete pPaM;
+ return false;
+ }
+
+ // Todo: lessen the previous condition..:
+ // if the source / destination is a whole node change then maybe space is not needed
+
+ // search pair around the actual redline
+ size_type nEnd = rPos + nLookahead < size()
+ ? rPos + nLookahead
+ : size();
+ size_type nStart = rPos > nLookahead ? rPos - nLookahead : 0;
+ // first, try to compare to single redlines
+ // next, try to compare to combined redlines
+ for (int nPass = 0; nPass < 2 && !bRet; nPass++)
+ {
+ for (size_type nPosAct = nStart; nPosAct < nEnd && !bRet; ++nPosAct)
+ {
+ SwRangeRedline* pPair = (*this)[nPosAct];
+
+ // redline must be the requested type and from the same author
+ if (nPairType != pPair->GetType() || pRedline->GetAuthor() != pPair->GetAuthor())
+ {
+ continue;
+ }
+
+ bool bDeletePairPaM = false;
+ SwPaM* pPairPaM = nullptr;
+
+ OUString sPairTrimmed = "";
+ SwRedlineTable::size_type nPairStart = nPosAct;
+ SwRedlineTable::size_type nPairEnd = nPosAct;
+
+ if (nPass == 0)
+ {
+ // if this redline is visible the content is in this PaM
+ if (nullptr == pPair->GetContentIdx())
+ {
+ pPairPaM = pPair;
+ }
+ else // otherwise it is saved in pContentSect, e.g. during ODT import
+ {
+ // saved in pContentSect, e.g. during ODT import
+ pPairPaM = new SwPaM(pPair->GetContentIdx()->GetNode(),
+ *pPair->GetContentIdx()->GetNode().EndOfSectionNode());
+ bDeletePairPaM = true;
+ }
+
+ sPairTrimmed = o3tl::trim(pPairPaM->GetText());
+ }
+ else
+ {
+ getConnectedArea(nPosAct, nPairStart, nPairEnd, false);
+ if (nPairStart != nPairEnd)
+ sPairTrimmed = getTextOfArea(nPairStart, nPairEnd).trim();
+ }
+
+ // pair at tracked moving: same text by trimming trailing white spaces
+ if (abs(sTrimmed.getLength() - sPairTrimmed.getLength()) <= 2
+ && sTrimmed == sPairTrimmed)
+ {
+ sal_uInt32 nMID = getNewMovedID();
+ if (nPosStart != nPosEnd)
+ {
+ for (size_type nIdx = nPosStart; nIdx <= nPosEnd; ++nIdx)
+ {
+ (*this)[nIdx]->SetMoved(nMID);
+ if (nIdx != rPos)
+ (*this)[nIdx]->InvalidateRange(SwRangeRedline::Invalidation::Add);
+ }
+ }
+ else
+ pRedline->SetMoved(nMID);
+
+ //in (nPass == 0) it will only call once .. as nPairStart == nPairEnd == nPosAct
+ for (size_type nIdx = nPairStart; nIdx <= nPairEnd; ++nIdx)
+ {
+ (*this)[nIdx]->SetMoved(nMID);
+ (*this)[nIdx]->InvalidateRange(SwRangeRedline::Invalidation::Add);
+ }
+
+ bRet = true;
+ }
+
+ if (bDeletePairPaM)
+ delete pPairPaM;
+
+ //we can skip the combined redlines
+ if (nPass == 1)
+ nPosAct = nPairEnd;
+ }
+ }
+
+ if ( bDeletePaM )
+ delete pPaM;
+
+ return bRet;
+}
+
+void SwRedlineTable::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwRedlineTable"));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
+
+ for (SwRedlineTable::size_type nCurRedlinePos = 0; nCurRedlinePos < size(); ++nCurRedlinePos)
+ operator[](nCurRedlinePos)->dumpAsXml(pWriter);
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+SwRedlineExtraData::~SwRedlineExtraData()
+{
+}
+
+void SwRedlineExtraData::Reject( SwPaM& ) const
+{
+}
+
+bool SwRedlineExtraData::operator == ( const SwRedlineExtraData& ) const
+{
+ return false;
+}
+
+SwRedlineExtraData_FormatColl::SwRedlineExtraData_FormatColl( OUString aColl,
+ sal_uInt16 nPoolFormatId,
+ const SfxItemSet* pItemSet,
+ bool bFormatAll )
+ : m_sFormatNm(std::move(aColl)), m_nPoolId(nPoolFormatId), m_bFormatAll(bFormatAll)
+{
+ if( pItemSet && pItemSet->Count() )
+ m_pSet.reset( new SfxItemSet( *pItemSet ) );
+}
+
+SwRedlineExtraData_FormatColl::~SwRedlineExtraData_FormatColl()
+{
+}
+
+SwRedlineExtraData* SwRedlineExtraData_FormatColl::CreateNew() const
+{
+ return new SwRedlineExtraData_FormatColl( m_sFormatNm, m_nPoolId, m_pSet.get(), m_bFormatAll );
+}
+
+void SwRedlineExtraData_FormatColl::Reject( SwPaM& rPam ) const
+{
+ SwDoc& rDoc = rPam.GetDoc();
+
+ // What about Undo? Is it turned off?
+ SwTextFormatColl* pColl = USHRT_MAX == m_nPoolId
+ ? rDoc.FindTextFormatCollByName( m_sFormatNm )
+ : rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool( m_nPoolId );
+
+ RedlineFlags eOld = rDoc.getIDocumentRedlineAccess().GetRedlineFlags();
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern(eOld & ~RedlineFlags(RedlineFlags::On | RedlineFlags::Ignore));
+
+ SwPaM aPam( *rPam.GetMark(), *rPam.GetPoint() );
+
+ const SwPosition* pEnd = rPam.End();
+
+ if ( !m_bFormatAll || pEnd->GetContentIndex() == 0 )
+ {
+ // don't reject the format of the next paragraph (that is handled by the next redline)
+ if (aPam.GetPoint()->GetNode() > aPam.GetMark()->GetNode())
+ {
+ aPam.GetPoint()->Adjust(SwNodeOffset(-1));
+ SwContentNode* pNode = aPam.GetPoint()->GetNode().GetContentNode();
+ if ( pNode )
+ aPam.GetPoint()->SetContent( pNode->Len() );
+ else
+ // tdf#147507 set it back to a content node to avoid of crashing
+ aPam.GetPoint()->Adjust(SwNodeOffset(+1));
+ }
+ else if (aPam.GetPoint()->GetNode() < aPam.GetMark()->GetNode())
+ {
+ aPam.GetMark()->Adjust(SwNodeOffset(-1));
+ SwContentNode* pNode = aPam.GetMark()->GetNode().GetContentNode();
+ aPam.GetMark()->SetContent( pNode->Len() );
+ }
+ }
+
+ if( pColl )
+ rDoc.SetTextFormatColl( aPam, pColl, false );
+
+ if( m_pSet )
+ rDoc.getIDocumentContentOperations().InsertItemSet( aPam, *m_pSet );
+
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
+}
+
+bool SwRedlineExtraData_FormatColl::operator == ( const SwRedlineExtraData& r) const
+{
+ const SwRedlineExtraData_FormatColl& rCmp = static_cast<const SwRedlineExtraData_FormatColl&>(r);
+ return m_sFormatNm == rCmp.m_sFormatNm && m_nPoolId == rCmp.m_nPoolId &&
+ m_bFormatAll == rCmp.m_bFormatAll &&
+ ( ( !m_pSet && !rCmp.m_pSet ) ||
+ ( m_pSet && rCmp.m_pSet && *m_pSet == *rCmp.m_pSet ) );
+}
+
+void SwRedlineExtraData_FormatColl::SetItemSet( const SfxItemSet& rSet )
+{
+ if( rSet.Count() )
+ m_pSet.reset( new SfxItemSet( rSet ) );
+ else
+ m_pSet.reset();
+}
+
+SwRedlineExtraData_Format::SwRedlineExtraData_Format( const SfxItemSet& rSet )
+{
+ SfxItemIter aIter( rSet );
+ for (const SfxPoolItem* pItem = aIter.GetCurItem(); pItem; pItem = aIter.NextItem())
+ {
+ m_aWhichIds.push_back( pItem->Which() );
+ }
+}
+
+SwRedlineExtraData_Format::SwRedlineExtraData_Format(
+ const SwRedlineExtraData_Format& rCpy )
+ : SwRedlineExtraData()
+{
+ m_aWhichIds.insert( m_aWhichIds.begin(), rCpy.m_aWhichIds.begin(), rCpy.m_aWhichIds.end() );
+}
+
+SwRedlineExtraData_Format::~SwRedlineExtraData_Format()
+{
+}
+
+SwRedlineExtraData* SwRedlineExtraData_Format::CreateNew() const
+{
+ return new SwRedlineExtraData_Format( *this );
+}
+
+void SwRedlineExtraData_Format::Reject( SwPaM& rPam ) const
+{
+ SwDoc& rDoc = rPam.GetDoc();
+
+ RedlineFlags eOld = rDoc.getIDocumentRedlineAccess().GetRedlineFlags();
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern(eOld & ~RedlineFlags(RedlineFlags::On | RedlineFlags::Ignore));
+
+ // Actually we need to reset the Attribute here!
+ for( const auto& rWhichId : m_aWhichIds )
+ {
+ rDoc.getIDocumentContentOperations().InsertPoolItem( rPam, *GetDfltAttr( rWhichId ),
+ SetAttrMode::DONTEXPAND );
+ }
+
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
+}
+
+bool SwRedlineExtraData_Format::operator == ( const SwRedlineExtraData& rCmp ) const
+{
+ const size_t nEnd = m_aWhichIds.size();
+ if( nEnd != static_cast<const SwRedlineExtraData_Format&>(rCmp).m_aWhichIds.size() )
+ return false;
+
+ for( size_t n = 0; n < nEnd; ++n )
+ {
+ if( static_cast<const SwRedlineExtraData_Format&>(rCmp).m_aWhichIds[n] != m_aWhichIds[n])
+ {
+ return false;
+ }
+ }
+ return true;
+}
+
+SwRedlineData::SwRedlineData( RedlineType eT, std::size_t nAut, sal_uInt32 nMovedID )
+ : m_pNext( nullptr ), m_pExtraData( nullptr ),
+ m_aStamp( DateTime::SYSTEM ),
+ m_nAuthor( nAut ), m_eType( eT ), m_nSeqNo( 0 ), m_bAutoFormat(false), m_nMovedID(nMovedID)
+{
+ m_aStamp.SetNanoSec( 0 );
+}
+
+SwRedlineData::SwRedlineData(
+ const SwRedlineData& rCpy,
+ bool bCpyNext )
+ : m_pNext( ( bCpyNext && rCpy.m_pNext ) ? new SwRedlineData( *rCpy.m_pNext ) : nullptr )
+ , m_pExtraData( rCpy.m_pExtraData ? rCpy.m_pExtraData->CreateNew() : nullptr )
+ , m_sComment( rCpy.m_sComment )
+ , m_aStamp( rCpy.m_aStamp )
+ , m_nAuthor( rCpy.m_nAuthor )
+ , m_eType( rCpy.m_eType )
+ , m_nSeqNo( rCpy.m_nSeqNo )
+ , m_bAutoFormat(false)
+ , m_nMovedID( rCpy.m_nMovedID )
+{
+}
+
+// For sw3io: We now own pNext!
+SwRedlineData::SwRedlineData(RedlineType eT, std::size_t nAut, const DateTime& rDT,
+ sal_uInt32 nMovedID, OUString aCmnt, SwRedlineData *pNxt)
+ : m_pNext(pNxt), m_pExtraData(nullptr), m_sComment(std::move(aCmnt)), m_aStamp(rDT),
+ m_nAuthor(nAut), m_eType(eT), m_nSeqNo(0), m_bAutoFormat(false), m_nMovedID(nMovedID)
+{
+}
+
+SwRedlineData::~SwRedlineData()
+{
+ delete m_pExtraData;
+ delete m_pNext;
+}
+
+// Check whether the absolute difference between the two dates is no larger than one minute (can
+// give inaccurate results if at least one of the dates is not valid/normalized):
+static bool deltaOneMinute(DateTime const & t1, DateTime const & t2) {
+ auto const & [min, max] = std::minmax(t1, t2);
+ // Avoid overflow of `min + tools::Time(0, 1)` below when min is close to the maximum valid
+ // DateTime:
+ if (min >= DateTime({31, 12, std::numeric_limits<sal_Int16>::max()}, {23, 59})) {
+ return true;
+ }
+ return max <= min + tools::Time(0, 1);
+}
+
+bool SwRedlineData::CanCombine(const SwRedlineData& rCmp) const
+{
+ return m_nAuthor == rCmp.m_nAuthor &&
+ m_eType == rCmp.m_eType &&
+ m_sComment == rCmp.m_sComment &&
+ deltaOneMinute(GetTimeStamp(), rCmp.GetTimeStamp()) &&
+ m_nMovedID == rCmp.m_nMovedID &&
+ (( !m_pNext && !rCmp.m_pNext ) ||
+ ( m_pNext && rCmp.m_pNext &&
+ m_pNext->CanCombine( *rCmp.m_pNext ))) &&
+ (( !m_pExtraData && !rCmp.m_pExtraData ) ||
+ ( m_pExtraData && rCmp.m_pExtraData &&
+ *m_pExtraData == *rCmp.m_pExtraData ));
+}
+
+// Check if we could/should accept/reject the 2 redlineData at the same time.
+// No need to check its children equality
+bool SwRedlineData::CanCombineForAcceptReject(const SwRedlineData& rCmp) const
+{
+ return m_nAuthor == rCmp.m_nAuthor &&
+ m_eType == rCmp.m_eType &&
+ m_sComment == rCmp.m_sComment &&
+ deltaOneMinute(GetTimeStamp(), rCmp.GetTimeStamp()) &&
+ m_nMovedID == rCmp.m_nMovedID &&
+ (( !m_pExtraData && !rCmp.m_pExtraData ) ||
+ ( m_pExtraData && rCmp.m_pExtraData &&
+ *m_pExtraData == *rCmp.m_pExtraData ));
+}
+
+/// ExtraData is copied. The Pointer's ownership is thus NOT transferred
+/// to the Redline Object!
+void SwRedlineData::SetExtraData( const SwRedlineExtraData* pData )
+{
+ delete m_pExtraData;
+
+ // Check if there is data - and if so - delete it
+ if( pData )
+ m_pExtraData = pData->CreateNew();
+ else
+ m_pExtraData = nullptr;
+}
+
+const TranslateId STR_REDLINE_ARY[] =
+{
+ STR_UNDO_REDLINE_INSERT,
+ STR_UNDO_REDLINE_DELETE,
+ STR_UNDO_REDLINE_FORMAT,
+ STR_UNDO_REDLINE_TABLE,
+ STR_UNDO_REDLINE_FMTCOLL,
+ STR_UNDO_REDLINE_PARAGRAPH_FORMAT,
+ STR_UNDO_REDLINE_TABLE_ROW_INSERT,
+ STR_UNDO_REDLINE_TABLE_ROW_DELETE,
+ STR_UNDO_REDLINE_TABLE_CELL_INSERT,
+ STR_UNDO_REDLINE_TABLE_CELL_DELETE
+};
+
+OUString SwRedlineData::GetDescr() const
+{
+ return SwResId(STR_REDLINE_ARY[static_cast<int>(GetType())]);
+}
+
+void SwRedlineData::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwRedlineData"));
+
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("id"), BAD_CAST(OString::number(GetSeqNo()).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("author"), BAD_CAST(SW_MOD()->GetRedlineAuthor(GetAuthor()).toUtf8().getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("date"), BAD_CAST(DateTimeToOString(GetTimeStamp()).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("descr"), BAD_CAST(GetDescr().toUtf8().getStr()));
+
+ OString sRedlineType;
+ switch (GetType())
+ {
+ case RedlineType::Insert:
+ sRedlineType = "REDLINE_INSERT"_ostr;
+ break;
+ case RedlineType::Delete:
+ sRedlineType = "REDLINE_DELETE"_ostr;
+ break;
+ case RedlineType::Format:
+ sRedlineType = "REDLINE_FORMAT"_ostr;
+ break;
+ default:
+ sRedlineType = "UNKNOWN"_ostr;
+ break;
+ }
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("type"), BAD_CAST(sRedlineType.getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("moved"), BAD_CAST(OString::number(m_nMovedID).getStr()));
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+sal_uInt32 SwRangeRedline::s_nLastId = 1;
+
+SwRangeRedline::SwRangeRedline(RedlineType eTyp, const SwPaM& rPam, sal_uInt32 nMovedID )
+ : SwPaM( *rPam.GetMark(), *rPam.GetPoint() ), m_pRedlineData(
+ new SwRedlineData(eTyp, GetDoc().getIDocumentRedlineAccess().GetRedlineAuthor(), nMovedID ) )
+ ,
+ m_nId( s_nLastId++ )
+{
+ GetBound().SetRedline(this);
+ GetBound(false).SetRedline(this);
+
+ m_bDelLastPara = false;
+ m_bIsVisible = true;
+ if( !rPam.HasMark() )
+ DeleteMark();
+
+ // set default comment for single annotations added or deleted
+ if ( IsAnnotation() )
+ {
+ SetComment( RedlineType::Delete == eTyp
+ ? SwResId(STR_REDLINE_COMMENT_DELETED)
+ : SwResId(STR_REDLINE_COMMENT_ADDED) );
+ }
+}
+
+SwRangeRedline::SwRangeRedline( const SwRedlineData& rData, const SwPaM& rPam )
+ : SwPaM( *rPam.GetMark(), *rPam.GetPoint() ),
+ m_pRedlineData( new SwRedlineData( rData )),
+ m_nId( s_nLastId++ )
+{
+ GetBound().SetRedline(this);
+ GetBound(false).SetRedline(this);
+
+ m_bDelLastPara = false;
+ m_bIsVisible = true;
+ if( !rPam.HasMark() )
+ DeleteMark();
+}
+
+SwRangeRedline::SwRangeRedline( const SwRedlineData& rData, const SwPosition& rPos )
+ : SwPaM( rPos ),
+ m_pRedlineData( new SwRedlineData( rData )),
+ m_nId( s_nLastId++ )
+{
+ GetBound().SetRedline(this);
+ GetBound(false).SetRedline(this);
+
+ m_bDelLastPara = false;
+ m_bIsVisible = true;
+}
+
+SwRangeRedline::SwRangeRedline( const SwRangeRedline& rCpy )
+ : SwPaM( *rCpy.GetMark(), *rCpy.GetPoint() ),
+ m_pRedlineData( new SwRedlineData( *rCpy.m_pRedlineData )),
+ m_nId( s_nLastId++ )
+{
+ GetBound().SetRedline(this);
+ GetBound(false).SetRedline(this);
+
+ m_bDelLastPara = false;
+ m_bIsVisible = true;
+ if( !rCpy.HasMark() )
+ DeleteMark();
+}
+
+SwRangeRedline::~SwRangeRedline()
+{
+ if( m_oContentSect )
+ {
+ // delete the ContentSection
+ if( !GetDoc().IsInDtor() )
+ GetDoc().getIDocumentContentOperations().DeleteSection( &m_oContentSect->GetNode() );
+ m_oContentSect.reset();
+ }
+ delete m_pRedlineData;
+}
+
+void MaybeNotifyRedlineModification(SwRangeRedline& rRedline, SwDoc& rDoc)
+{
+ if (!lcl_LOKRedlineNotificationEnabled())
+ return;
+
+ const SwRedlineTable& rRedTable = rDoc.getIDocumentRedlineAccess().GetRedlineTable();
+ for (SwRedlineTable::size_type i = 0; i < rRedTable.size(); ++i)
+ {
+ if (rRedTable[i] == &rRedline)
+ {
+ SwRedlineTable::LOKRedlineNotification(RedlineNotification::Modify, &rRedline);
+ break;
+ }
+ }
+}
+
+void SwRangeRedline::MaybeNotifyRedlinePositionModification(tools::Long nTop)
+{
+ if (!lcl_LOKRedlineNotificationEnabled())
+ return;
+
+ if(!m_oLOKLastNodeTop || *m_oLOKLastNodeTop != nTop)
+ {
+ m_oLOKLastNodeTop = nTop;
+ SwRedlineTable::LOKRedlineNotification(RedlineNotification::Modify, this);
+ }
+}
+
+void SwRangeRedline::SetStart( const SwPosition& rPos, SwPosition* pSttPtr )
+{
+ if( !pSttPtr ) pSttPtr = Start();
+ *pSttPtr = rPos;
+
+ MaybeNotifyRedlineModification(*this, GetDoc());
+}
+
+void SwRangeRedline::SetEnd( const SwPosition& rPos, SwPosition* pEndPtr )
+{
+ if( !pEndPtr ) pEndPtr = End();
+ *pEndPtr = rPos;
+
+ MaybeNotifyRedlineModification(*this, GetDoc());
+}
+
+/// Do we have a valid Selection?
+bool SwRangeRedline::HasValidRange() const
+{
+ const SwNode* pPtNd = &GetPoint()->GetNode(),
+ * pMkNd = &GetMark()->GetNode();
+ if( pPtNd->StartOfSectionNode() == pMkNd->StartOfSectionNode() &&
+ !pPtNd->StartOfSectionNode()->IsTableNode() &&
+ // invalid if points on the end of content
+ // end-of-content only invalid if no content index exists
+ ( pPtNd != pMkNd || GetContentIdx() != nullptr ||
+ pPtNd != &pPtNd->GetNodes().GetEndOfContent() )
+ )
+ return true;
+ return false;
+}
+
+void SwRangeRedline::CallDisplayFunc(size_t nMyPos)
+{
+ RedlineFlags eShow = RedlineFlags::ShowMask & GetDoc().getIDocumentRedlineAccess().GetRedlineFlags();
+ if (eShow == (RedlineFlags::ShowInsert | RedlineFlags::ShowDelete))
+ Show(0, nMyPos);
+ else if (eShow == RedlineFlags::ShowInsert)
+ Hide(0, nMyPos);
+ else if (eShow == RedlineFlags::ShowDelete)
+ ShowOriginal(0, nMyPos);
+}
+
+void SwRangeRedline::Show(sal_uInt16 nLoop, size_t nMyPos, bool bForced)
+{
+ SwDoc& rDoc = GetDoc();
+
+ bool bIsShowChangesInMargin = false;
+ if ( !bForced )
+ {
+ SwViewShell* pSh = rDoc.getIDocumentLayoutAccess().GetCurrentViewShell();
+ if (pSh)
+ bIsShowChangesInMargin = pSh->GetViewOptions()->IsShowChangesInMargin();
+ else
+ bIsShowChangesInMargin = SW_MOD()->GetUsrPref(false)->IsShowChangesInMargin();
+ }
+
+ if( 1 > nLoop && !bIsShowChangesInMargin )
+ return;
+
+ RedlineFlags eOld = rDoc.getIDocumentRedlineAccess().GetRedlineFlags();
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern(eOld | RedlineFlags::Ignore);
+ ::sw::UndoGuard const undoGuard(rDoc.GetIDocumentUndoRedo());
+
+ switch( GetType() )
+ {
+ case RedlineType::Insert: // Content has been inserted
+ m_bIsVisible = true;
+ MoveFromSection(nMyPos);
+ break;
+
+ case RedlineType::Delete: // Content has been deleted
+ m_bIsVisible = !bIsShowChangesInMargin;
+
+ if (m_bIsVisible)
+ MoveFromSection(nMyPos);
+ else
+ {
+ switch( nLoop )
+ {
+ case 0: MoveToSection(); break;
+ case 1: CopyToSection(); break;
+ case 2: DelCopyOfSection(nMyPos); break;
+ }
+ }
+ break;
+
+ case RedlineType::Format: // Attributes have been applied
+ case RedlineType::Table: // Table structure has been modified
+ InvalidateRange(Invalidation::Add);
+ break;
+ default:
+ break;
+ }
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
+}
+
+void SwRangeRedline::Hide(sal_uInt16 nLoop, size_t nMyPos, bool /*bForced*/)
+{
+ SwDoc& rDoc = GetDoc();
+ RedlineFlags eOld = rDoc.getIDocumentRedlineAccess().GetRedlineFlags();
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern(eOld | RedlineFlags::Ignore);
+ ::sw::UndoGuard const undoGuard(rDoc.GetIDocumentUndoRedo());
+
+ switch( GetType() )
+ {
+ case RedlineType::Insert: // Content has been inserted
+ m_bIsVisible = true;
+ if( 1 <= nLoop )
+ MoveFromSection(nMyPos);
+ break;
+
+ case RedlineType::Delete: // Content has been deleted
+ m_bIsVisible = false;
+ switch( nLoop )
+ {
+ case 0: MoveToSection(); break;
+ case 1: CopyToSection(); break;
+ case 2: DelCopyOfSection(nMyPos); break;
+ }
+ break;
+
+ case RedlineType::Format: // Attributes have been applied
+ case RedlineType::Table: // Table structure has been modified
+ if( 1 <= nLoop )
+ InvalidateRange(Invalidation::Remove);
+ break;
+ default:
+ break;
+ }
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
+}
+
+void SwRangeRedline::ShowOriginal(sal_uInt16 nLoop, size_t nMyPos, bool /*bForced*/)
+{
+ SwDoc& rDoc = GetDoc();
+ RedlineFlags eOld = rDoc.getIDocumentRedlineAccess().GetRedlineFlags();
+ SwRedlineData* pCur;
+
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern(eOld | RedlineFlags::Ignore);
+ ::sw::UndoGuard const undoGuard(rDoc.GetIDocumentUndoRedo());
+
+ // Determine the Type, it's the first on Stack
+ for( pCur = m_pRedlineData; pCur->m_pNext; )
+ pCur = pCur->m_pNext;
+
+ switch( pCur->m_eType )
+ {
+ case RedlineType::Insert: // Content has been inserted
+ m_bIsVisible = false;
+ switch( nLoop )
+ {
+ case 0: MoveToSection(); break;
+ case 1: CopyToSection(); break;
+ case 2: DelCopyOfSection(nMyPos); break;
+ }
+ break;
+
+ case RedlineType::Delete: // Content has been deleted
+ m_bIsVisible = true;
+ if( 1 <= nLoop )
+ MoveFromSection(nMyPos);
+ break;
+
+ case RedlineType::Format: // Attributes have been applied
+ case RedlineType::Table: // Table structure has been modified
+ if( 1 <= nLoop )
+ InvalidateRange(Invalidation::Remove);
+ break;
+ default:
+ break;
+ }
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
+}
+
+// trigger the Layout
+void SwRangeRedline::InvalidateRange(Invalidation const eWhy)
+{
+ auto [pRStt, pREnd] = StartEnd(); // SwPosition*
+ SwNodeOffset nSttNd = pRStt->GetNodeIndex(),
+ nEndNd = pREnd->GetNodeIndex();
+ sal_Int32 nSttCnt = pRStt->GetContentIndex();
+ sal_Int32 nEndCnt = pREnd->GetContentIndex();
+
+ SwNodes& rNds = GetDoc().GetNodes();
+ for (SwNodeOffset n(nSttNd); n <= nEndNd; ++n)
+ {
+ SwNode* pNode = rNds[n];
+
+ if (pNode && pNode->IsTextNode())
+ {
+ SwTextNode* pNd = pNode->GetTextNode();
+
+ SwUpdateAttr aHt(
+ n == nSttNd ? nSttCnt : 0,
+ n == nEndNd ? nEndCnt : pNd->GetText().getLength(),
+ RES_FMT_CHG);
+
+ pNd->TriggerNodeUpdate(sw::LegacyModifyHint(&aHt, &aHt));
+
+ // SwUpdateAttr must be handled first, otherwise indexes are off
+ if (GetType() == RedlineType::Delete)
+ {
+ sal_Int32 const nStart(n == nSttNd ? nSttCnt : 0);
+ sal_Int32 const nLen((n == nEndNd ? nEndCnt : pNd->GetText().getLength()) - nStart);
+ if (eWhy == Invalidation::Add)
+ {
+ sw::RedlineDelText const hint(nStart, nLen);
+ pNd->CallSwClientNotify(hint);
+ }
+ else
+ {
+ sw::RedlineUnDelText const hint(nStart, nLen);
+ pNd->CallSwClientNotify(hint);
+ }
+ }
+ }
+ }
+}
+
+/** Calculates the start and end position of the intersection rTmp and
+ text node nNdIdx */
+void SwRangeRedline::CalcStartEnd( SwNodeOffset nNdIdx, sal_Int32& rStart, sal_Int32& rEnd ) const
+{
+ auto [pRStt, pREnd] = StartEnd(); // SwPosition*
+ if( pRStt->GetNodeIndex() < nNdIdx )
+ {
+ if( pREnd->GetNodeIndex() > nNdIdx )
+ {
+ rStart = 0; // Paragraph is completely enclosed
+ rEnd = COMPLETE_STRING;
+ }
+ else if (pREnd->GetNodeIndex() == nNdIdx)
+ {
+ rStart = 0; // Paragraph is overlapped in the beginning
+ rEnd = pREnd->GetContentIndex();
+ }
+ else // redline ends before paragraph
+ {
+ rStart = COMPLETE_STRING;
+ rEnd = COMPLETE_STRING;
+ }
+ }
+ else if( pRStt->GetNodeIndex() == nNdIdx )
+ {
+ rStart = pRStt->GetContentIndex();
+ if( pREnd->GetNodeIndex() == nNdIdx )
+ rEnd = pREnd->GetContentIndex(); // Within the Paragraph
+ else
+ rEnd = COMPLETE_STRING; // Paragraph is overlapped in the end
+ }
+ else
+ {
+ rStart = COMPLETE_STRING;
+ rEnd = COMPLETE_STRING;
+ }
+}
+
+static void lcl_storeAnnotationMarks(SwDoc& rDoc, const SwPosition* pStt, const SwPosition* pEnd)
+{
+ // tdf#115815 keep original start position of collapsed annotation ranges
+ // as temporary bookmarks (removed after file saving and file loading)
+ IDocumentMarkAccess& rDMA(*rDoc.getIDocumentMarkAccess());
+ for (auto iter = rDMA.getAnnotationMarksBegin();
+ iter != rDMA.getAnnotationMarksEnd(); )
+ {
+ SwPosition const& rStartPos((**iter).GetMarkStart());
+ if ( *pStt <= rStartPos && rStartPos < *pEnd )
+ {
+ IDocumentMarkAccess::const_iterator_t pOldMark =
+ rDMA.findAnnotationBookmark((**iter).GetName());
+ if ( pOldMark == rDMA.getBookmarksEnd() )
+ {
+ // at start of redlines use a 1-character length bookmark range
+ // instead of a 0-character length bookmark position to avoid its losing
+ sal_Int32 nLen = (*pStt == rStartPos) ? 1 : 0;
+ SwPaM aPam( rStartPos.GetNode(), rStartPos.GetContentIndex(),
+ rStartPos.GetNode(), rStartPos.GetContentIndex() + nLen);
+ ::sw::mark::IMark* pMark = rDMA.makeAnnotationBookmark(
+ aPam,
+ (**iter).GetName(),
+ IDocumentMarkAccess::MarkType::BOOKMARK, sw::mark::InsertMode::New);
+ ::sw::mark::IBookmark* pBookmark = dynamic_cast< ::sw::mark::IBookmark* >(pMark);
+ if (pBookmark)
+ {
+ pBookmark->SetKeyCode(vcl::KeyCode());
+ pBookmark->SetShortName(OUString());
+ }
+ }
+ }
+ ++iter;
+ }
+}
+
+void SwRangeRedline::MoveToSection()
+{
+ if( !m_oContentSect )
+ {
+ auto [pStt, pEnd] = StartEnd(); // SwPosition*
+
+ SwDoc& rDoc = GetDoc();
+ SwPaM aPam( *pStt, *pEnd );
+ SwContentNode* pCSttNd = pStt->GetNode().GetContentNode();
+ SwContentNode* pCEndNd = pEnd->GetNode().GetContentNode();
+
+ if( !pCSttNd )
+ {
+ // In order to not move other Redlines' indices, we set them
+ // to the end (is exclusive)
+ const SwRedlineTable& rTable = rDoc.getIDocumentRedlineAccess().GetRedlineTable();
+ for(SwRangeRedline* pRedl : rTable)
+ {
+ if( pRedl->GetBound() == *pStt )
+ pRedl->GetBound() = *pEnd;
+ if( pRedl->GetBound(false) == *pStt )
+ pRedl->GetBound(false) = *pEnd;
+ }
+ }
+
+ SwStartNode* pSttNd;
+ SwNodes& rNds = rDoc.GetNodes();
+ if( pCSttNd || pCEndNd )
+ {
+ SwTextFormatColl* pColl = (pCSttNd && pCSttNd->IsTextNode() )
+ ? pCSttNd->GetTextNode()->GetTextColl()
+ : (pCEndNd && pCEndNd->IsTextNode() )
+ ? pCEndNd->GetTextNode()->GetTextColl()
+ : rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool(RES_POOLCOLL_STANDARD);
+
+ pSttNd = rNds.MakeTextSection( rNds.GetEndOfRedlines(),
+ SwNormalStartNode, pColl );
+ SwTextNode* pTextNd = rNds[ pSttNd->GetIndex() + 1 ]->GetTextNode();
+
+ SwPosition aPos( *pTextNd );
+ if( pCSttNd && pCEndNd )
+ {
+ // tdf#140982 keep annotation ranges in deletions in margin mode
+ lcl_storeAnnotationMarks( rDoc, pStt, pEnd );
+ rDoc.getIDocumentContentOperations().MoveAndJoin( aPam, aPos );
+ }
+ else
+ {
+ if( pCSttNd && !pCEndNd )
+ m_bDelLastPara = true;
+ rDoc.getIDocumentContentOperations().MoveRange( aPam, aPos,
+ SwMoveFlags::DEFAULT );
+ }
+ }
+ else
+ {
+ pSttNd = SwNodes::MakeEmptySection( rNds.GetEndOfRedlines() );
+
+ SwPosition aPos( *pSttNd->EndOfSectionNode() );
+ rDoc.getIDocumentContentOperations().MoveRange( aPam, aPos,
+ SwMoveFlags::DEFAULT );
+ }
+ m_oContentSect.emplace( *pSttNd );
+
+ if( pStt == GetPoint() )
+ Exchange();
+
+ DeleteMark();
+ }
+ else
+ InvalidateRange(Invalidation::Remove);
+}
+
+void SwRangeRedline::CopyToSection()
+{
+ if( m_oContentSect )
+ return;
+
+ auto [pStt, pEnd] = StartEnd(); // SwPosition*
+
+ SwContentNode* pCSttNd = pStt->GetNode().GetContentNode();
+ SwContentNode* pCEndNd = pEnd->GetNode().GetContentNode();
+
+ SwStartNode* pSttNd;
+ SwDoc& rDoc = GetDoc();
+ SwNodes& rNds = rDoc.GetNodes();
+
+ bool bSaveCopyFlag = rDoc.IsCopyIsMove(),
+ bSaveRdlMoveFlg = rDoc.getIDocumentRedlineAccess().IsRedlineMove();
+ rDoc.SetCopyIsMove( true );
+
+ // The IsRedlineMove() flag causes the behaviour of the
+ // DocumentContentOperationsManager::CopyFlyInFlyImpl() method to change,
+ // which will eventually be called by the CopyRange() below.
+ rDoc.getIDocumentRedlineAccess().SetRedlineMove(true);
+
+ if( pCSttNd )
+ {
+ SwTextFormatColl* pColl = pCSttNd->IsTextNode()
+ ? pCSttNd->GetTextNode()->GetTextColl()
+ : rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool(RES_POOLCOLL_STANDARD);
+
+ pSttNd = rNds.MakeTextSection( rNds.GetEndOfRedlines(),
+ SwNormalStartNode, pColl );
+
+ SwPosition aPos( *pSttNd, SwNodeOffset(1) );
+
+ // tdf#115815 keep original start position of collapsed annotation ranges
+ // as temporary bookmarks (removed after file saving and file loading)
+ lcl_storeAnnotationMarks( rDoc, pStt, pEnd );
+ rDoc.getIDocumentContentOperations().CopyRange(*this, aPos, SwCopyFlags::CheckPosInFly);
+
+ // Take over the style from the EndNode if needed
+ // We don't want this in Doc::Copy
+ if( pCEndNd && pCEndNd != pCSttNd )
+ {
+ SwContentNode* pDestNd = aPos.GetNode().GetContentNode();
+ if( pDestNd )
+ {
+ if( pDestNd->IsTextNode() && pCEndNd->IsTextNode() )
+ pCEndNd->GetTextNode()->CopyCollFormat(*pDestNd->GetTextNode());
+ else
+ pDestNd->ChgFormatColl( pCEndNd->GetFormatColl() );
+ }
+ }
+ }
+ else
+ {
+ pSttNd = SwNodes::MakeEmptySection( rNds.GetEndOfRedlines() );
+
+ if( pCEndNd )
+ {
+ SwPosition aPos( *pSttNd->EndOfSectionNode() );
+ rDoc.getIDocumentContentOperations().CopyRange(*this, aPos, SwCopyFlags::CheckPosInFly);
+ }
+ else
+ {
+ SwNodeRange aRg( pStt->GetNode(), SwNodeOffset(0), pEnd->GetNode(), SwNodeOffset(1) );
+ rDoc.GetDocumentContentOperationsManager().CopyWithFlyInFly(aRg, *pSttNd->EndOfSectionNode());
+ }
+ }
+ m_oContentSect.emplace( *pSttNd );
+
+ rDoc.SetCopyIsMove( bSaveCopyFlag );
+ rDoc.getIDocumentRedlineAccess().SetRedlineMove( bSaveRdlMoveFlg );
+}
+
+void SwRangeRedline::DelCopyOfSection(size_t nMyPos)
+{
+ if( !m_oContentSect )
+ return;
+
+ auto [pStt, pEnd] = StartEnd(); // SwPosition*
+
+ SwDoc& rDoc = GetDoc();
+ SwPaM aPam( *pStt, *pEnd );
+ SwContentNode* pCSttNd = pStt->GetNode().GetContentNode();
+ SwContentNode* pCEndNd = pEnd->GetNode().GetContentNode();
+
+ if( !pCSttNd )
+ {
+ // In order to not move other Redlines' indices, we set them
+ // to the end (is exclusive)
+ const SwRedlineTable& rTable = rDoc.getIDocumentRedlineAccess().GetRedlineTable();
+ for(SwRangeRedline* pRedl : rTable)
+ {
+ if( pRedl->GetBound() == *pStt )
+ pRedl->GetBound() = *pEnd;
+ if( pRedl->GetBound(false) == *pStt )
+ pRedl->GetBound(false) = *pEnd;
+ }
+ }
+
+ if( pCSttNd && pCEndNd )
+ {
+ // #i100466# - force a <join next> on <delete and join> operation
+ // tdf#125319 - rather not?
+ rDoc.getIDocumentContentOperations().DeleteAndJoin(aPam/*, true*/);
+ }
+ else if( pCSttNd || pCEndNd )
+ {
+ if( pCSttNd && !pCEndNd )
+ m_bDelLastPara = true;
+ rDoc.getIDocumentContentOperations().DeleteRange( aPam );
+
+ if( m_bDelLastPara )
+ {
+ // To prevent dangling references to the paragraph to
+ // be deleted, redline that point into this paragraph should be
+ // moved to the new end position. Since redlines in the redline
+ // table are sorted and the pEnd position is an endnode (see
+ // bDelLastPara condition above), only redlines before the
+ // current ones can be affected.
+ const SwRedlineTable& rTable = rDoc.getIDocumentRedlineAccess().GetRedlineTable();
+ size_t n = nMyPos;
+ for( bool bBreak = false; !bBreak && n > 0; )
+ {
+ --n;
+ bBreak = true;
+ if( rTable[ n ]->GetBound() == *aPam.GetPoint() )
+ {
+ rTable[ n ]->GetBound() = *pEnd;
+ bBreak = false;
+ }
+ if( rTable[ n ]->GetBound(false) == *aPam.GetPoint() )
+ {
+ rTable[ n ]->GetBound(false) = *pEnd;
+ bBreak = false;
+ }
+ }
+
+ *GetPoint() = *pEnd;
+ *GetMark() = *pEnd;
+ DeleteMark();
+
+ aPam.DeleteMark();
+ aPam.GetPoint()->SetContent(0);;
+ rDoc.getIDocumentContentOperations().DelFullPara( aPam );
+ }
+ }
+ else
+ {
+ rDoc.getIDocumentContentOperations().DeleteRange( aPam );
+ }
+
+ if( pStt == GetPoint() )
+ Exchange();
+
+ DeleteMark();
+}
+
+void SwRangeRedline::MoveFromSection(size_t nMyPos)
+{
+ if( m_oContentSect )
+ {
+ SwDoc& rDoc = GetDoc();
+ const SwRedlineTable& rTable = rDoc.getIDocumentRedlineAccess().GetRedlineTable();
+ std::vector<SwPosition*> aBeforeArr, aBehindArr;
+ bool bBreak = false;
+ SwRedlineTable::size_type n;
+
+ for( n = nMyPos+1; !bBreak && n < rTable.size(); ++n )
+ {
+ bBreak = true;
+ if( rTable[ n ]->GetBound() == *GetPoint() )
+ {
+ SwRangeRedline* pRedl = rTable[n];
+ aBehindArr.push_back(&pRedl->GetBound());
+ bBreak = false;
+ }
+ if( rTable[ n ]->GetBound(false) == *GetPoint() )
+ {
+ SwRangeRedline* pRedl = rTable[n];
+ aBehindArr.push_back(&pRedl->GetBound(false));
+ bBreak = false;
+ }
+ }
+ for( bBreak = false, n = nMyPos; !bBreak && n ; )
+ {
+ --n;
+ bBreak = true;
+ if( rTable[ n ]->GetBound() == *GetPoint() )
+ {
+ SwRangeRedline* pRedl = rTable[n];
+ aBeforeArr.push_back(&pRedl->GetBound());
+ bBreak = false;
+ }
+ if( rTable[ n ]->GetBound(false) == *GetPoint() )
+ {
+ SwRangeRedline* pRedl = rTable[n];
+ aBeforeArr.push_back(&pRedl->GetBound(false));
+ bBreak = false;
+ }
+ }
+
+ const SwNode* pKeptContentSectNode( &m_oContentSect->GetNode() ); // #i95711#
+ {
+ SwPaM aPam( m_oContentSect->GetNode(),
+ *m_oContentSect->GetNode().EndOfSectionNode(), SwNodeOffset(1),
+ SwNodeOffset( m_bDelLastPara ? -2 : -1 ) );
+ SwContentNode* pCNd = aPam.GetPointContentNode();
+ if( pCNd )
+ aPam.GetPoint()->SetContent( pCNd->Len() );
+ else
+ aPam.GetPoint()->Adjust(SwNodeOffset(+1));
+
+ SwFormatColl* pColl = pCNd && pCNd->Len() && aPam.GetPoint()->GetNode() !=
+ aPam.GetMark()->GetNode()
+ ? pCNd->GetFormatColl() : nullptr;
+
+ SwNodeIndex aNdIdx( GetPoint()->GetNode(), -1 );
+ const sal_Int32 nPos = GetPoint()->GetContentIndex();
+
+ SwPosition aPos( *GetPoint() );
+ if( m_bDelLastPara && *aPam.GetPoint() == *aPam.GetMark() )
+ {
+ aPos.Adjust(SwNodeOffset(-1));
+
+ rDoc.getIDocumentContentOperations().AppendTextNode( aPos );
+ }
+ else
+ {
+ rDoc.getIDocumentContentOperations().MoveRange( aPam, aPos,
+ SwMoveFlags::ALLFLYS );
+ }
+
+ SetMark();
+ *GetPoint() = aPos;
+ GetMark()->Assign(aNdIdx.GetIndex() + 1);
+ pCNd = GetMark()->GetNode().GetContentNode();
+ if( pCNd )
+ GetMark()->SetContent( nPos );
+
+ if( m_bDelLastPara )
+ {
+ GetPoint()->Adjust(SwNodeOffset(+1));
+ pCNd = GetPointContentNode();
+ m_bDelLastPara = false;
+ }
+ else if( pColl )
+ pCNd = GetPointContentNode();
+
+ if( pColl && pCNd )
+ pCNd->ChgFormatColl( pColl );
+ }
+
+ // #i95771#
+ // Under certain conditions the previous <SwDoc::Move(..)> has already
+ // removed the change tracking section of this <SwRangeRedline> instance from
+ // the change tracking nodes area.
+ // Thus, check if <pContentSect> still points to the change tracking section
+ // by comparing it with the "indexed" <SwNode> instance copied before
+ // perform the intrinsic move.
+ // Note: Such condition is e.g. a "delete" change tracking only containing a table.
+ if ( &m_oContentSect->GetNode() == pKeptContentSectNode )
+ {
+ rDoc.getIDocumentContentOperations().DeleteSection( &m_oContentSect->GetNode() );
+ }
+ m_oContentSect.reset();
+
+ // adjustment of redline table positions must take start and
+ // end into account, not point and mark.
+ for( auto& pItem : aBeforeArr )
+ *pItem = *Start();
+ for( auto& pItem : aBehindArr )
+ *pItem = *End();
+ }
+ else
+ InvalidateRange(Invalidation::Add);
+}
+
+// for Undo
+void SwRangeRedline::SetContentIdx( const SwNodeIndex& rIdx )
+{
+ if( !m_oContentSect )
+ {
+ m_oContentSect = rIdx;
+ m_bIsVisible = false;
+ }
+ else
+ {
+ OSL_FAIL("SwRangeRedline::SetContentIdx: invalid state");
+ }
+}
+
+// for Undo
+void SwRangeRedline::ClearContentIdx()
+{
+ if( m_oContentSect )
+ {
+ m_oContentSect.reset();
+ }
+ else
+ {
+ OSL_FAIL("SwRangeRedline::ClearContentIdx: invalid state");
+ }
+}
+
+bool SwRangeRedline::CanCombine( const SwRangeRedline& rRedl ) const
+{
+ return IsVisible() && rRedl.IsVisible() &&
+ m_pRedlineData->CanCombine( *rRedl.m_pRedlineData );
+}
+
+void SwRangeRedline::PushData( const SwRangeRedline& rRedl, bool bOwnAsNext )
+{
+ SwRedlineData* pNew = new SwRedlineData( *rRedl.m_pRedlineData, false );
+ if( bOwnAsNext )
+ {
+ pNew->m_pNext = m_pRedlineData;
+ m_pRedlineData = pNew;
+ }
+ else
+ {
+ pNew->m_pNext = m_pRedlineData->m_pNext;
+ m_pRedlineData->m_pNext = pNew;
+ }
+}
+
+bool SwRangeRedline::PopData()
+{
+ if( !m_pRedlineData->m_pNext )
+ return false;
+ SwRedlineData* pCur = m_pRedlineData;
+ m_pRedlineData = pCur->m_pNext;
+ pCur->m_pNext = nullptr;
+ delete pCur;
+ return true;
+}
+
+bool SwRangeRedline::PopAllDataAfter(int depth)
+{
+ assert(depth > 0);
+ SwRedlineData* pCur = m_pRedlineData;
+ while (depth > 1)
+ {
+ pCur = pCur->m_pNext;
+ if (!pCur)
+ return false;
+ depth--;
+ }
+
+ while (pCur->m_pNext)
+ {
+ SwRedlineData* pToDelete = pCur->m_pNext;
+ pCur->m_pNext = pToDelete->m_pNext;
+ delete pToDelete;
+ }
+ return true;
+}
+
+sal_uInt16 SwRangeRedline::GetStackCount() const
+{
+ sal_uInt16 nRet = 1;
+ for( SwRedlineData* pCur = m_pRedlineData; pCur->m_pNext; pCur = pCur->m_pNext )
+ ++nRet;
+ return nRet;
+}
+
+std::size_t SwRangeRedline::GetAuthor( sal_uInt16 nPos ) const
+{
+ return GetRedlineData(nPos).m_nAuthor;
+}
+
+OUString const & SwRangeRedline::GetAuthorString( sal_uInt16 nPos ) const
+{
+ return SW_MOD()->GetRedlineAuthor(GetRedlineData(nPos).m_nAuthor);
+}
+
+sal_uInt32 SwRangeRedline::GetMovedID(sal_uInt16 nPos) const
+{
+ return GetRedlineData(nPos).m_nMovedID;
+}
+
+const DateTime& SwRangeRedline::GetTimeStamp(sal_uInt16 nPos) const
+{
+ return GetRedlineData(nPos).m_aStamp;
+}
+
+RedlineType SwRangeRedline::GetType( sal_uInt16 nPos ) const
+{
+ return GetRedlineData(nPos).m_eType;
+}
+
+bool SwRangeRedline::IsAnnotation() const
+{
+ return GetText().getLength() == 1 && GetText()[0] == CH_TXTATR_INWORD;
+}
+
+const OUString& SwRangeRedline::GetComment( sal_uInt16 nPos ) const
+{
+ return GetRedlineData(nPos).m_sComment;
+}
+
+bool SwRangeRedline::operator<( const SwRangeRedline& rCmp ) const
+{
+ if (*Start() < *rCmp.Start())
+ return true;
+
+ return *Start() == *rCmp.Start() && *End() < *rCmp.End();
+}
+
+const SwRedlineData & SwRangeRedline::GetRedlineData(const sal_uInt16 nPos) const
+{
+ SwRedlineData * pCur = m_pRedlineData;
+
+ sal_uInt16 nP = nPos;
+
+ while (nP > 0 && nullptr != pCur->m_pNext)
+ {
+ pCur = pCur->m_pNext;
+
+ nP--;
+ }
+
+ SAL_WARN_IF( nP != 0, "sw.core", "Pos " << nPos << " is " << nP << " too big");
+
+ return *pCur;
+}
+
+OUString SwRangeRedline::GetDescr(bool bSimplified)
+{
+ // get description of redline data (e.g.: "insert $1")
+ OUString aResult = GetRedlineData().GetDescr();
+
+ SwPaM * pPaM = nullptr;
+ bool bDeletePaM = false;
+
+ // if this redline is visible the content is in this PaM
+ if (!m_oContentSect.has_value())
+ {
+ pPaM = this;
+ }
+ else // otherwise it is saved in pContentSect
+ {
+ pPaM = new SwPaM( m_oContentSect->GetNode(), *m_oContentSect->GetNode().EndOfSectionNode() );
+ bDeletePaM = true;
+ }
+
+ OUString sDescr = DenoteSpecialCharacters(pPaM->GetText().replace('\n', ' '), /*bQuoted=*/!bSimplified);
+ if (const SwTextNode *pTextNode = pPaM->GetPointNode().GetTextNode())
+ {
+ if (const SwTextAttr* pTextAttr = pTextNode->GetFieldTextAttrAt(pPaM->GetPoint()->GetContentIndex() - 1, ::sw::GetTextAttrMode::Default))
+ {
+ sDescr = ( bSimplified ? "" : SwResId(STR_START_QUOTE) )
+ + pTextAttr->GetFormatField().GetField()->GetFieldName()
+ + ( bSimplified ? "" : SwResId(STR_END_QUOTE) );
+ }
+ }
+
+ // replace $1 in description by description of the redlines text
+ const OUString aTmpStr = ShortenString(sDescr, nUndoStringLength, SwResId(STR_LDOTS));
+
+ if (!bSimplified)
+ {
+ SwRewriter aRewriter;
+ aRewriter.AddRule(UndoArg1, aTmpStr);
+
+ aResult = aRewriter.Apply(aResult);
+ }
+ else
+ {
+ aResult = aTmpStr;
+ // more shortening
+ sal_Int32 nPos = aTmpStr.indexOf(SwResId(STR_LDOTS));
+ if (nPos > 5)
+ aResult = aTmpStr.copy(0, nPos + SwResId(STR_LDOTS).getLength());
+ }
+
+ if (bDeletePaM)
+ delete pPaM;
+
+ return aResult;
+}
+
+void SwRangeRedline::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwRangeRedline"));
+
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
+
+ const SwRedlineData* pRedlineData = m_pRedlineData;
+ while (pRedlineData)
+ {
+ pRedlineData->dumpAsXml(pWriter);
+ pRedlineData = pRedlineData->Next();
+ }
+
+ SwPaM::dumpAsXml(pWriter);
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+void SwExtraRedlineTable::Insert( SwExtraRedline* p )
+{
+ m_aExtraRedlines.push_back( p );
+ //p->CallDisplayFunc();
+}
+
+void SwExtraRedlineTable::DeleteAndDestroy(sal_uInt16 const nPos)
+{
+ /*
+ SwDoc* pDoc = 0;
+ if( !nP && nL && nL == size() )
+ pDoc = front()->GetDoc();
+ */
+
+ delete m_aExtraRedlines[nPos];
+ m_aExtraRedlines.erase(m_aExtraRedlines.begin() + nPos);
+
+ /*
+ SwViewShell* pSh;
+ if( pDoc && !pDoc->IsInDtor() &&
+ 0 != ( pSh = pDoc->getIDocumentLayoutAccess().GetCurrentViewShell() ) )
+ pSh->InvalidateWindows( SwRect( 0, 0, SAL_MAX_INT32, SAL_MAX_INT32 ) );
+ */
+}
+
+void SwExtraRedlineTable::DeleteAndDestroyAll()
+{
+ while (!m_aExtraRedlines.empty())
+ {
+ auto const pRedline = m_aExtraRedlines.back();
+ m_aExtraRedlines.pop_back();
+ delete pRedline;
+ }
+}
+
+SwExtraRedline::~SwExtraRedline()
+{
+}
+
+SwTableRowRedline::SwTableRowRedline(const SwRedlineData& rData, const SwTableLine& rTableLine)
+ : m_aRedlineData(rData)
+ , m_rTableLine(rTableLine)
+{
+}
+
+SwTableRowRedline::~SwTableRowRedline()
+{
+}
+
+SwTableCellRedline::SwTableCellRedline(const SwRedlineData& rData, const SwTableBox& rTableBox)
+ : m_aRedlineData(rData)
+ , m_rTableBox(rTableBox)
+{
+}
+
+SwTableCellRedline::~SwTableCellRedline()
+{
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/docruby.cxx b/sw/source/core/doc/docruby.cxx
new file mode 100644
index 0000000000..1a1b84ffd7
--- /dev/null
+++ b/sw/source/core/doc/docruby.cxx
@@ -0,0 +1,320 @@
+/* -*- 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 <string.h>
+
+#include <com/sun/star/i18n/UnicodeType.hpp>
+#include <com/sun/star/i18n/WordType.hpp>
+#include <com/sun/star/i18n/XBreakIterator.hpp>
+
+#include <unotools/charclass.hxx>
+
+#include <hintids.hxx>
+#include <doc.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <IDocumentContentOperations.hxx>
+#include <ndtxt.hxx>
+#include <txatbase.hxx>
+#include <rubylist.hxx>
+#include <pam.hxx>
+#include <swundo.hxx>
+#include <breakit.hxx>
+#include <swcrsr.hxx>
+
+using namespace ::com::sun::star::i18n;
+
+/*
+ * Members in the list:
+ * - String - the orig text
+ * - SwFormatRuby - the ruby attribute
+ */
+sal_uInt16 SwDoc::FillRubyList( const SwPaM& rPam, SwRubyList& rList )
+{
+ const SwPaM *_pStartCursor = rPam.GetNext(),
+ *_pStartCursor2 = _pStartCursor;
+ bool bCheckEmpty = &rPam != _pStartCursor;
+ do {
+ auto [pStt, pEnd] = _pStartCursor->StartEnd(); // SwPosition*
+ if( !bCheckEmpty || ( pStt != pEnd && *pStt != *pEnd ))
+ {
+ SwPaM aPam( *pStt );
+ do {
+ std::unique_ptr<SwRubyListEntry> pNew(new SwRubyListEntry);
+ if( pEnd != pStt )
+ {
+ aPam.SetMark();
+ *aPam.GetMark() = *pEnd;
+ }
+ if( SelectNextRubyChars( aPam, *pNew ))
+ {
+ rList.push_back(std::move(pNew));
+ aPam.DeleteMark();
+ }
+ else
+ {
+ if( *aPam.GetPoint() < *pEnd )
+ {
+ // goto next paragraph
+ aPam.DeleteMark();
+ aPam.Move( fnMoveForward, GoInNode );
+ }
+ else
+ break;
+ }
+ } while( 30 > rList.size() && *aPam.GetPoint() < *pEnd );
+ }
+ if( 30 <= rList.size() )
+ break;
+ _pStartCursor = _pStartCursor->GetNext();
+ } while( _pStartCursor != _pStartCursor2 );
+
+ return rList.size();
+}
+
+void SwDoc::SetRubyList( const SwPaM& rPam, const SwRubyList& rList )
+{
+ GetIDocumentUndoRedo().StartUndo( SwUndoId::SETRUBYATTR, nullptr );
+ const o3tl::sorted_vector<sal_uInt16> aDelArr{ RES_TXTATR_CJK_RUBY };
+
+ SwRubyList::size_type nListEntry = 0;
+
+ const SwPaM *_pStartCursor = rPam.GetNext(),
+ *_pStartCursor2 = _pStartCursor;
+ bool bCheckEmpty = &rPam != _pStartCursor;
+ do {
+ auto [pStt, pEnd] = _pStartCursor->StartEnd(); // SwPosition*
+ if( !bCheckEmpty || ( pStt != pEnd && *pStt != *pEnd ))
+ {
+
+ SwPaM aPam( *pStt );
+ do {
+ SwRubyListEntry aCheckEntry;
+ if( pEnd != pStt )
+ {
+ aPam.SetMark();
+ *aPam.GetMark() = *pEnd;
+ }
+ if( SelectNextRubyChars( aPam, aCheckEntry ))
+ {
+ const SwRubyListEntry* pEntry = rList[ nListEntry++ ].get();
+ if( aCheckEntry.GetRubyAttr() != pEntry->GetRubyAttr() )
+ {
+ // set/reset the attribute
+ if( !pEntry->GetRubyAttr().GetText().isEmpty() )
+ {
+ getIDocumentContentOperations().InsertPoolItem( aPam, pEntry->GetRubyAttr() );
+ }
+ else
+ {
+ ResetAttrs( aPam, true, aDelArr );
+ }
+ }
+
+ if( !pEntry->GetText().isEmpty() &&
+ aCheckEntry.GetText() != pEntry->GetText() )
+ {
+ // text is changed, so replace the original
+ getIDocumentContentOperations().ReplaceRange( aPam, pEntry->GetText(), false );
+ }
+ aPam.DeleteMark();
+ }
+ else
+ {
+ if( *aPam.GetPoint() < *pEnd )
+ {
+ // goto next paragraph
+ aPam.DeleteMark();
+ aPam.Move( fnMoveForward, GoInNode );
+ }
+ else
+ {
+ const SwRubyListEntry* pEntry = rList[ nListEntry++ ].get();
+
+ // set/reset the attribute
+ if( !pEntry->GetRubyAttr().GetText().isEmpty() &&
+ !pEntry->GetText().isEmpty() )
+ {
+ getIDocumentContentOperations().InsertString( aPam, pEntry->GetText() );
+ aPam.SetMark();
+ aPam.GetMark()->AdjustContent( -pEntry->GetText().getLength() );
+ getIDocumentContentOperations().InsertPoolItem(
+ aPam, pEntry->GetRubyAttr(), SetAttrMode::DONTEXPAND );
+ }
+ else
+ break;
+ aPam.DeleteMark();
+ }
+ }
+ } while( nListEntry < rList.size() && *aPam.GetPoint() < *pEnd );
+ }
+ if( 30 <= rList.size() )
+ break;
+ _pStartCursor = _pStartCursor->GetNext();
+ } while( _pStartCursor != _pStartCursor2 );
+
+ GetIDocumentUndoRedo().EndUndo( SwUndoId::SETRUBYATTR, nullptr );
+}
+
+bool SwDoc::SelectNextRubyChars( SwPaM& rPam, SwRubyListEntry& rEntry )
+{
+ // Point must be the startposition, Mark is optional the end position
+ SwPosition* pPos = rPam.GetPoint();
+ const SwTextNode* pTNd = pPos->GetNode().GetTextNode();
+ OUString const& rText = pTNd->GetText();
+ sal_Int32 nStart = pPos->GetContentIndex();
+ sal_Int32 nEnd = rText.getLength();
+
+ bool bHasMark = rPam.HasMark();
+ if( bHasMark )
+ {
+ // in the same node?
+ if( rPam.GetMark()->GetNode() == pPos->GetNode() )
+ {
+ // then use that end
+ const sal_Int32 nTEnd = rPam.GetMark()->GetContentIndex();
+ if( nTEnd < nEnd )
+ nEnd = nTEnd;
+ }
+ rPam.DeleteMark();
+ }
+
+ // search the start
+ // look where a ruby attribute starts
+ const SwpHints* pHts = pTNd->GetpSwpHints();
+ const SwTextAttr* pAttr = nullptr;
+ if( pHts )
+ {
+ for( size_t nHtIdx = 0; nHtIdx < pHts->Count(); ++nHtIdx )
+ {
+ const SwTextAttr* pHt = pHts->Get(nHtIdx);
+ if( RES_TXTATR_CJK_RUBY == pHt->Which() &&
+ pHt->GetAnyEnd() > nStart )
+ {
+ if( pHt->GetStart() < nEnd )
+ {
+ pAttr = pHt;
+ if( !bHasMark && nStart > pAttr->GetStart() )
+ {
+ nStart = pAttr->GetStart();
+ pPos->SetContent(nStart);
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ if( !bHasMark && nStart && ( !pAttr || nStart != pAttr->GetStart()) )
+ {
+ // skip to the word begin!
+ const sal_Int32 nWordStt = g_pBreakIt->GetBreakIter()->getWordBoundary(
+ rText, nStart,
+ g_pBreakIt->GetLocale( pTNd->GetLang( nStart )),
+ WordType::ANYWORD_IGNOREWHITESPACES,
+ true ).startPos;
+ if (nWordStt < nStart && nWordStt >= 0)
+ {
+ nStart = nWordStt;
+ pPos->SetContent(nStart);
+ }
+ }
+
+ bool bAlphaNum = false;
+ sal_Int32 nWordEnd = nEnd;
+ CharClass& rCC = GetAppCharClass();
+ while( nStart < nEnd )
+ {
+ if( pAttr && nStart == pAttr->GetStart() )
+ {
+ pPos->SetContent(nStart);
+ if( !rPam.HasMark() )
+ {
+ rPam.SetMark();
+ pPos->SetContent(pAttr->GetAnyEnd());
+ if( pPos->GetContentIndex() > nEnd )
+ pPos->SetContent(nEnd);
+ rEntry.SetRubyAttr( pAttr->GetRuby() );
+ }
+ break;
+ }
+
+ sal_Int32 nChType = rCC.getType(rText, nStart);
+ bool bIgnoreChar = false, bIsAlphaNum = false, bChkNxtWrd = false;
+ switch( nChType )
+ {
+ case UnicodeType::UPPERCASE_LETTER:
+ case UnicodeType::LOWERCASE_LETTER:
+ case UnicodeType::TITLECASE_LETTER:
+ case UnicodeType::DECIMAL_DIGIT_NUMBER:
+ bChkNxtWrd = bIsAlphaNum = true;
+ break;
+
+ case UnicodeType::SPACE_SEPARATOR:
+ case UnicodeType::CONTROL:
+/*??*/ case UnicodeType::PRIVATE_USE:
+ case UnicodeType::START_PUNCTUATION:
+ case UnicodeType::END_PUNCTUATION:
+ bIgnoreChar = true;
+ break;
+
+ case UnicodeType::OTHER_LETTER:
+ bChkNxtWrd = true;
+ [[fallthrough]];
+ default:
+ bIsAlphaNum = false;
+ break;
+ }
+
+ if( rPam.HasMark() )
+ {
+ if( bIgnoreChar || bIsAlphaNum != bAlphaNum || nStart >= nWordEnd )
+ break;
+ }
+ else if( !bIgnoreChar )
+ {
+ rPam.SetMark();
+ bAlphaNum = bIsAlphaNum;
+ if (bChkNxtWrd)
+ {
+ // search the end of this word
+ nWordEnd = g_pBreakIt->GetBreakIter()->getWordBoundary(
+ rText, nStart,
+ g_pBreakIt->GetLocale( pTNd->GetLang( nStart )),
+ WordType::ANYWORD_IGNOREWHITESPACES,
+ true ).endPos;
+ if( 0 > nWordEnd || nWordEnd > nEnd || nWordEnd == nStart )
+ nWordEnd = nEnd;
+ }
+ }
+ pTNd->GoNext( pPos, SwCursorSkipMode::Chars );
+ nStart = pPos->GetContentIndex();
+ }
+
+ nStart = rPam.GetMark()->GetContentIndex();
+ rEntry.SetText( rText.copy( nStart,
+ rPam.GetPoint()->GetContentIndex() - nStart ));
+ return rPam.HasMark();
+}
+
+SwRubyListEntry::~SwRubyListEntry()
+{
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/docsort.cxx b/sw/source/core/doc/docsort.cxx
new file mode 100644
index 0000000000..d22ab372e3
--- /dev/null
+++ b/sw/source/core/doc/docsort.cxx
@@ -0,0 +1,933 @@
+/* -*- 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 <hintids.hxx>
+#include <osl/diagnose.h>
+#include <unotools/collatorwrapper.hxx>
+#include <unotools/localedatawrapper.hxx>
+#include <comphelper/processfactory.hxx>
+#include <docary.hxx>
+#include <fmtanchr.hxx>
+#include <frmfmt.hxx>
+#include <doc.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <IDocumentFieldsAccess.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <IDocumentState.hxx>
+#include <node.hxx>
+#include <pam.hxx>
+#include <ndtxt.hxx>
+#include <swtable.hxx>
+#include <swundo.hxx>
+#include <sortopt.hxx>
+#include <docsort.hxx>
+#include <UndoSort.hxx>
+#include <UndoRedline.hxx>
+#include <hints.hxx>
+#include <tblsel.hxx>
+#include <cellatr.hxx>
+#include <redline.hxx>
+#include <node2lay.hxx>
+#include <frameformats.hxx>
+#include <svl/numformat.hxx>
+
+#include <set>
+#include <utility>
+
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star;
+
+SwSortOptions* SwSortElement::pOptions = nullptr;
+SwDoc* SwSortElement::pDoc = nullptr;
+const FlatFndBox* SwSortElement::pBox = nullptr;
+CollatorWrapper* SwSortElement::pSortCollator = nullptr;
+lang::Locale* SwSortElement::pLocale = nullptr;
+std::optional<OUString> SwSortElement::xLastAlgorithm;
+LocaleDataWrapper* SwSortElement::pLclData = nullptr;
+
+// List of all sorted elements
+
+/// Construct a SortElement for the Sort
+void SwSortElement::Init( SwDoc* pD, const SwSortOptions& rOpt,
+ FlatFndBox const * pFltBx )
+{
+ OSL_ENSURE( !pDoc && !pOptions && !pBox, "Who forgot to call Finit?" );
+ pDoc = pD;
+ pOptions = new SwSortOptions( rOpt );
+ pBox = pFltBx;
+
+ LanguageType nLang = rOpt.nLanguage;
+ if ( nLang.anyOf(
+ LANGUAGE_NONE,
+ LANGUAGE_DONTKNOW))
+ nLang = GetAppLanguage();
+ pLocale = new lang::Locale( LanguageTag::convertToLocale( nLang ) );
+
+ pSortCollator = new CollatorWrapper( ::comphelper::getProcessComponentContext() );
+}
+
+void SwSortElement::Finit()
+{
+ delete pOptions;
+ pOptions = nullptr;
+ delete pLocale;
+ pLocale = nullptr;
+ xLastAlgorithm.reset();
+ delete pSortCollator;
+ pSortCollator = nullptr;
+ delete pLclData;
+ pLclData = nullptr;
+ pDoc = nullptr;
+ pBox = nullptr;
+}
+
+SwSortElement::~SwSortElement()
+{
+}
+
+double SwSortElement::StrToDouble( std::u16string_view rStr )
+{
+ if( !pLclData )
+ pLclData = new LocaleDataWrapper( LanguageTag( *pLocale ));
+
+ rtl_math_ConversionStatus eStatus;
+ sal_Int32 nEnd;
+ double nRet = pLclData->stringToDouble( rStr, true, &eStatus, &nEnd );
+
+ if( rtl_math_ConversionStatus_Ok != eStatus || nEnd == 0 )
+ nRet = 0.0;
+ return nRet;
+}
+
+int SwSortElement::keycompare(const SwSortElement& rCmp, sal_uInt16 nKey) const
+{
+ int nCmp = 0;
+ // The actual comparison
+ const SwSortElement *pOrig, *pCmp;
+
+ const SwSortKey& rSrtKey = pOptions->aKeys[ nKey ];
+ if( rSrtKey.eSortOrder == SwSortOrder::Ascending )
+ {
+ pOrig = this;
+ pCmp = &rCmp;
+ }
+ else
+ {
+ pOrig = &rCmp;
+ pCmp = this;
+ }
+
+ if( rSrtKey.bIsNumeric )
+ {
+ double n1 = pOrig->GetValue( nKey );
+ double n2 = pCmp->GetValue( nKey );
+
+ nCmp = n1 < n2 ? -1 : n1 == n2 ? 0 : 1;
+ }
+ else
+ {
+ if( !xLastAlgorithm || *xLastAlgorithm != rSrtKey.sSortType )
+ {
+ xLastAlgorithm = rSrtKey.sSortType;
+ pSortCollator->loadCollatorAlgorithm( *xLastAlgorithm,
+ *pLocale,
+ pOptions->bIgnoreCase ? SW_COLLATOR_IGNORES : 0 );
+ }
+
+ nCmp = pSortCollator->compareString(
+ pOrig->GetKey( nKey ), pCmp->GetKey( nKey ));
+ }
+ return nCmp;
+}
+
+bool SwSortElement::operator<(const SwSortElement& rCmp) const
+{
+ // The actual comparison
+ for(size_t nKey = 0; nKey < pOptions->aKeys.size(); ++nKey)
+ {
+ int nCmp = keycompare(rCmp, nKey);
+
+ if (nCmp == 0)
+ continue;
+
+ return nCmp < 0;
+ }
+
+ return false;
+}
+
+double SwSortElement::GetValue( sal_uInt16 nKey ) const
+{
+ return StrToDouble( GetKey( nKey ));
+}
+
+/// SortingElement for Text
+SwSortTextElement::SwSortTextElement(const SwNodeIndex& rPos)
+ : nOrg(rPos.GetIndex()), aPos(rPos)
+{
+}
+
+OUString SwSortTextElement::GetKey(sal_uInt16 nId) const
+{
+ SwTextNode* pTextNd = aPos.GetNode().GetTextNode();
+ if( !pTextNd )
+ return OUString();
+
+ // for TextNodes
+ const OUString& rStr = pTextNd->GetText();
+
+ sal_Unicode nDeli = pOptions->cDeli;
+ sal_uInt16 nDCount = pOptions->aKeys[nId].nColumnId, i = 1;
+ sal_Int32 nStart = 0;
+
+ // Find the delimiter
+ while( nStart != -1 && i < nDCount)
+ {
+ nStart = rStr.indexOf( nDeli, nStart );
+ if( -1 != nStart )
+ {
+ nStart++;
+ i++;
+ }
+ }
+
+ // Found next delimiter or end of String
+ // and copy
+ sal_Int32 nEnd = rStr.indexOf( nDeli, nStart+1 );
+ if (nEnd == -1)
+ return rStr.copy( nStart );
+ return rStr.copy( nStart, nEnd-nStart );
+}
+
+/// SortingElement for Tables
+SwSortBoxElement::SwSortBoxElement( sal_uInt16 nRC )
+ : nRow( nRC )
+{
+}
+
+/// Get Key for a cell
+OUString SwSortBoxElement::GetKey(sal_uInt16 nKey) const
+{
+ const FndBox_* pFndBox;
+ sal_uInt16 nCol = pOptions->aKeys[nKey].nColumnId-1;
+
+ if( SwSortDirection::Rows == pOptions->eDirection )
+ pFndBox = pBox->GetBox(nCol, nRow); // Sort rows
+ else
+ pFndBox = pBox->GetBox(nRow, nCol); // Sort columns
+
+ // Extract the Text
+ OUStringBuffer aRetStr;
+ if( pFndBox )
+ { // Get StartNode and skip it
+ const SwTableBox* pMyBox = pFndBox->GetBox();
+ OSL_ENSURE(pMyBox, "No atomic Box");
+
+ if( pMyBox->GetSttNd() )
+ {
+ // Iterate over all the Box's TextNodes
+ const SwNode *pNd = nullptr, *pEndNd = pMyBox->GetSttNd()->EndOfSectionNode();
+ for( SwNodeOffset nIdx = pMyBox->GetSttIdx() + 1; pNd != pEndNd; ++nIdx )
+ {
+ pNd = pDoc->GetNodes()[ nIdx ];
+ if( pNd->IsTextNode() )
+ aRetStr.append(pNd->GetTextNode()->GetText());
+ }
+ }
+ }
+ return aRetStr.makeStringAndClear();
+}
+
+double SwSortBoxElement::GetValue( sal_uInt16 nKey ) const
+{
+ const FndBox_* pFndBox;
+ sal_uInt16 nCol = pOptions->aKeys[nKey].nColumnId-1;
+
+ if( SwSortDirection::Rows == pOptions->eDirection )
+ pFndBox = pBox->GetBox(nCol, nRow); // Sort rows
+ else
+ pFndBox = pBox->GetBox(nRow, nCol); // Sort columns
+
+ double nVal;
+ if( pFndBox )
+ {
+ const SwFormat *pFormat = pFndBox->GetBox()->GetFrameFormat();
+ if (pDoc->GetNumberFormatter()->IsTextFormat( pFormat->GetTableBoxNumFormat().GetValue()))
+ nVal = SwSortElement::GetValue( nKey );
+ else
+ nVal = pFormat->GetTableBoxValue().GetValue();
+ }
+ else
+ nVal = 0;
+
+ return nVal;
+}
+
+/// Sort Text in the Document
+bool SwDoc::SortText(const SwPaM& rPaM, const SwSortOptions& rOpt)
+{
+ // Check if Frame is in the Text
+ auto [pStart, pEnd] = rPaM.StartEnd(); // SwPosition*
+
+ // Set index to the Selection's start
+ for(sw::SpzFrameFormat* pFormat: *GetSpzFrameFormats())
+ {
+ SwFormatAnchor const*const pAnchor = &pFormat->GetAnchor();
+ SwNode const*const pAnchorNode = pAnchor->GetAnchorNode();
+
+ if (pAnchorNode && (RndStdIds::FLY_AT_PARA == pAnchor->GetAnchorId()) &&
+ pStart->GetNode() <= *pAnchorNode && *pAnchorNode <= pEnd->GetNode() )
+ return false;
+ }
+
+ // Check if only TextNodes are within the Selection
+ {
+ SwNodeOffset nStart = pStart->GetNodeIndex(),
+ nEnd = pEnd->GetNodeIndex();
+ while( nStart <= nEnd )
+ // Iterate over a selected range
+ if( !GetNodes()[ nStart++ ]->IsTextNode() )
+ return false;
+ }
+
+ bool const bUndo = GetIDocumentUndoRedo().DoesUndo();
+ if( bUndo )
+ {
+ GetIDocumentUndoRedo().StartUndo( SwUndoId::START, nullptr );
+ }
+
+ SwPaM* pRedlPam = nullptr;
+ SwUndoRedlineSort* pRedlUndo = nullptr;
+ SwUndoSort* pUndoSort = nullptr;
+
+ // To-Do - add 'SwExtraRedlineTable' also ?
+ if( getIDocumentRedlineAccess().IsRedlineOn() || (!getIDocumentRedlineAccess().IsIgnoreRedline() && !getIDocumentRedlineAccess().GetRedlineTable().empty() ))
+ {
+ pRedlPam = new SwPaM( pStart->GetNode(), pEnd->GetNode(), SwNodeOffset(-1), SwNodeOffset(1) );
+ SwContentNode* pCNd = pRedlPam->GetMarkContentNode();
+ if( pCNd )
+ pRedlPam->GetMark()->SetContent( pCNd->Len() );
+
+ if( getIDocumentRedlineAccess().IsRedlineOn() && !IDocumentRedlineAccess::IsShowOriginal( getIDocumentRedlineAccess().GetRedlineFlags() ) )
+ {
+ if( bUndo )
+ {
+ pRedlUndo = new SwUndoRedlineSort( *pRedlPam,rOpt );
+ GetIDocumentUndoRedo().DoUndo(false);
+ }
+ // First copy the range
+ SwNodeIndex aEndIdx( pEnd->GetNode(), 1 );
+ SwNodeRange aRg( pStart->GetNode(), aEndIdx.GetNode() );
+ GetNodes().Copy_( aRg, aEndIdx.GetNode() );
+
+ // range is new from pEnd->nNode+1 to aEndIdx
+ getIDocumentRedlineAccess().DeleteRedline( *pRedlPam, true, RedlineType::Any );
+
+ pRedlPam->GetMark()->Assign( pEnd->GetNode(), SwNodeOffset(1) );
+
+ pRedlPam->GetPoint()->Assign( aEndIdx.GetNode() );
+ pCNd = pRedlPam->GetPointContentNode();
+ sal_Int32 nCLen = 0;
+ if( !pCNd )
+ {
+ pCNd = GetNodes()[ aEndIdx.GetIndex()-SwNodeOffset(1) ]->GetContentNode();
+ if( pCNd )
+ {
+ nCLen = pCNd->Len();
+ pRedlPam->GetPoint()->Assign( *pCNd );
+ }
+ }
+ if (pCNd)
+ pRedlPam->GetPoint()->SetContent( nCLen );
+
+ if( pRedlUndo )
+ pRedlUndo->SetValues( rPaM );
+ }
+ else
+ {
+ getIDocumentRedlineAccess().DeleteRedline( *pRedlPam, true, RedlineType::Any );
+ delete pRedlPam;
+ pRedlPam = nullptr;
+ }
+ }
+
+ SwNodeIndex aStart(pStart->GetNode());
+ SwSortElement::Init( this, rOpt );
+ std::multiset<SwSortTextElement> aSortSet;
+ while( aStart <= pEnd->GetNode() )
+ {
+ // Iterate over a selected range
+ aSortSet.insert(SwSortTextElement(aStart));
+ ++aStart;
+ }
+
+ // Now comes the tricky part: Move Nodes (and always keep Undo in mind)
+ SwNodeOffset nBeg = pStart->GetNodeIndex();
+ SwNodeRange aRg( aStart, aStart );
+
+ if( bUndo && !pRedlUndo )
+ {
+ pUndoSort = new SwUndoSort(rPaM, rOpt);
+ GetIDocumentUndoRedo().AppendUndo(std::unique_ptr<SwUndo>(pUndoSort));
+ }
+
+ GetIDocumentUndoRedo().DoUndo(false);
+
+ SwNodeOffset n(0);
+ for (const auto& rElem : aSortSet)
+ {
+ aStart = nBeg + n;
+ aRg.aStart = rElem.aPos.GetIndex();
+ aRg.aEnd = aRg.aStart.GetIndex() + 1;
+
+ // Move Nodes
+ getIDocumentContentOperations().MoveNodeRange( aRg, aStart.GetNode(),
+ SwMoveFlags::DEFAULT );
+
+ // Insert Move in Undo
+ if(pUndoSort)
+ {
+ pUndoSort->Insert(rElem.nOrg, nBeg + n);
+ }
+ ++n;
+ }
+ // Delete all elements from the SortArray
+ aSortSet.clear();
+ SwSortElement::Finit();
+
+ if( pRedlPam )
+ {
+ if( pRedlUndo )
+ {
+ pRedlUndo->SetSaveRange( *pRedlPam );
+ // UGLY: temp. enable Undo
+ GetIDocumentUndoRedo().DoUndo(true);
+ GetIDocumentUndoRedo().AppendUndo( std::unique_ptr<SwUndo>(pRedlUndo) );
+ GetIDocumentUndoRedo().DoUndo(false);
+ }
+
+ // nBeg is start of sorted range
+ SwNodeIndex aSttIdx( GetNodes(), nBeg );
+
+ // the copied range is deleted
+ SwRangeRedline *const pDeleteRedline(
+ new SwRangeRedline( RedlineType::Delete, *pRedlPam ));
+
+ // pRedlPam points to nodes that may be deleted (hidden) by
+ // AppendRedline, so adjust it beforehand to prevent ASSERT
+ pRedlPam->GetPoint()->Assign(aSttIdx);
+
+ getIDocumentRedlineAccess().AppendRedline(pDeleteRedline, true);
+
+ // the sorted range is inserted
+ getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( RedlineType::Insert, *pRedlPam ), true);
+
+ if( pRedlUndo )
+ {
+ SwNodeIndex aInsEndIdx( pRedlPam->GetMark()->GetNode(), -1 );
+ SwContentNode *const pContentNode = aInsEndIdx.GetNode().GetContentNode();
+ pRedlPam->GetMark()->Assign( *pContentNode, pContentNode->Len() );
+
+ pRedlUndo->SetValues( *pRedlPam );
+ }
+
+ delete pRedlPam;
+ pRedlPam = nullptr;
+ }
+ GetIDocumentUndoRedo().DoUndo( bUndo );
+ if( bUndo )
+ {
+ GetIDocumentUndoRedo().EndUndo( SwUndoId::END, nullptr );
+ }
+
+ return true;
+}
+
+/// Sort Table in the Document
+bool SwDoc::SortTable(const SwSelBoxes& rBoxes, const SwSortOptions& rOpt)
+{
+ // Via SwDoc for Undo!
+ OSL_ENSURE( !rBoxes.empty(), "no valid Box list" );
+ SwTableNode* pTableNd = const_cast<SwTableNode*>(rBoxes[0]->GetSttNd()->FindTableNode());
+ if( !pTableNd )
+ return false;
+
+ // We begin sorting
+ // Find all Boxes/Lines
+ FndBox_ aFndBox( nullptr, nullptr );
+ {
+ FndPara aPara( rBoxes, &aFndBox );
+ ForEach_FndLineCopyCol( pTableNd->GetTable().GetTabLines(), &aPara );
+ }
+
+ if(aFndBox.GetLines().empty())
+ return false;
+
+ if( !getIDocumentRedlineAccess().IsIgnoreRedline() && !getIDocumentRedlineAccess().GetRedlineTable().empty() )
+ getIDocumentRedlineAccess().DeleteRedline( *pTableNd, true, RedlineType::Any );
+
+ FndLines_t::size_type nStart = 0;
+ if( pTableNd->GetTable().GetRowsToRepeat() > 0 && rOpt.eDirection == SwSortDirection::Rows )
+ {
+ // Uppermost selected Cell
+ FndLines_t& rLines = aFndBox.GetLines();
+
+ while( nStart < rLines.size() )
+ {
+ // Respect Split Merge nesting,
+ // extract the upper most
+ SwTableLine* pLine = rLines[nStart]->GetLine();
+ while ( pLine->GetUpper() )
+ pLine = pLine->GetUpper()->GetUpper();
+
+ if( pTableNd->GetTable().IsHeadline( *pLine ) )
+ nStart++;
+ else
+ break;
+ }
+ // Are all selected in the HeaderLine? -> no Offset
+ if( nStart == rLines.size() )
+ nStart = 0;
+ }
+
+ pTableNd->GetTable().SwitchFormulasToRelativeRepresentation();
+
+ // Table as a flat array structure
+ FlatFndBox aFlatBox(this, aFndBox);
+
+ if(!aFlatBox.IsSymmetric())
+ return false;
+
+ // Delete HTML layout
+ pTableNd->GetTable().SetHTMLTableLayout(std::shared_ptr<SwHTMLTableLayout>());
+
+ // #i37739# A simple 'MakeFrames' after the node sorting
+ // does not work if the table is inside a frame and has no prev/next.
+ SwNode2LayoutSaveUpperFrames aNode2Layout(*pTableNd);
+
+ // Delete the Table's Frames
+ pTableNd->DelFrames();
+ // ? TL_CHART2: ?
+
+ SwUndoSort* pUndoSort = nullptr;
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ pUndoSort = new SwUndoSort( rBoxes[0]->GetSttIdx(),
+ rBoxes.back()->GetSttIdx(),
+ *pTableNd, rOpt, aFlatBox.HasItemSets() );
+ GetIDocumentUndoRedo().AppendUndo(std::unique_ptr<SwUndo>(pUndoSort));
+ }
+ ::sw::UndoGuard const undoGuard(GetIDocumentUndoRedo());
+
+ // Insert KeyElements
+ sal_uInt16 nCount = (rOpt.eDirection == SwSortDirection::Rows) ?
+ aFlatBox.GetRows() : aFlatBox.GetCols();
+
+ // Sort SortList by Key
+ SwSortElement::Init( this, rOpt, &aFlatBox );
+ std::multiset<SwSortBoxElement> aSortList;
+
+ // When sorting, do not include the first row if the HeaderLine is repeated
+ for( sal_uInt16 i = o3tl::narrowing<sal_uInt16>(nStart); i < nCount; ++i)
+ {
+ aSortList.insert(SwSortBoxElement(i));
+ }
+
+ // Move after Sorting
+ SwMovedBoxes aMovedList;
+ sal_uInt16 i = 0;
+ for (const auto& rElem : aSortList)
+ {
+ if(rOpt.eDirection == SwSortDirection::Rows)
+ {
+ MoveRow(this, aFlatBox, rElem.nRow, i+nStart, aMovedList, pUndoSort);
+ }
+ else
+ {
+ MoveCol(this, aFlatBox, rElem.nRow, i+nStart, aMovedList, pUndoSort);
+ }
+ ++i;
+ }
+
+ // Restore table frames:
+ // #i37739# A simple 'MakeFrames' after the node sorting
+ // does not work if the table is inside a frame and has no prev/next.
+ const SwNodeOffset nIdx = pTableNd->GetIndex();
+ aNode2Layout.RestoreUpperFrames( GetNodes(), nIdx, nIdx + 1 );
+
+ // TL_CHART2: need to inform chart of probably changed cell names
+ UpdateCharts( pTableNd->GetTable().GetFrameFormat()->GetName() );
+
+ // Delete all Elements in the SortArray
+ aSortList.clear();
+ SwSortElement::Finit();
+
+ getIDocumentState().SetModified();
+ return true;
+}
+
+/// Move a row
+void MoveRow(SwDoc* pDoc, const FlatFndBox& rBox, sal_uInt16 nS, sal_uInt16 nT,
+ SwMovedBoxes& rMovedList, SwUndoSort* pUD)
+{
+ for( sal_uInt16 i=0; i < rBox.GetCols(); ++i )
+ { // Get old cell position and remember it
+ const FndBox_* pSource = rBox.GetBox(i, nS);
+
+ // new cell position
+ const FndBox_* pTarget = rBox.GetBox(i, nT);
+
+ const SwTableBox* pT = pTarget->GetBox();
+ const SwTableBox* pS = pSource->GetBox();
+
+ bool bMoved = rMovedList.GetPos(pT) != USHRT_MAX;
+
+ // and move it
+ MoveCell(pDoc, pS, pT, bMoved, pUD);
+
+ rMovedList.push_back(pS);
+
+ if( pS != pT )
+ {
+ SwFrameFormat* pTFormat = pT->GetFrameFormat();
+ const SfxItemSet* pSSet = rBox.GetItemSet( i, nS );
+
+ if( pSSet ||
+ SfxItemState::SET == pTFormat->GetItemState( RES_BOXATR_FORMAT ) ||
+ SfxItemState::SET == pTFormat->GetItemState( RES_BOXATR_FORMULA ) ||
+ SfxItemState::SET == pTFormat->GetItemState( RES_BOXATR_VALUE ) )
+ {
+ pTFormat = const_cast<SwTableBox*>(pT)->ClaimFrameFormat();
+ pTFormat->LockModify();
+ if( pTFormat->ResetFormatAttr( RES_BOXATR_FORMAT, RES_BOXATR_VALUE ) )
+ pTFormat->ResetFormatAttr( RES_VERT_ORIENT );
+
+ if( pSSet )
+ pTFormat->SetFormatAttr( *pSSet );
+ pTFormat->UnlockModify();
+ }
+ }
+ }
+}
+
+/// Move a column
+void MoveCol(SwDoc* pDoc, const FlatFndBox& rBox, sal_uInt16 nS, sal_uInt16 nT,
+ SwMovedBoxes& rMovedList, SwUndoSort* pUD)
+{
+ for(sal_uInt16 i=0; i < rBox.GetRows(); ++i)
+ { // Get old cell position and remember it
+ const FndBox_* pSource = rBox.GetBox(nS, i);
+
+ // new cell position
+ const FndBox_* pTarget = rBox.GetBox(nT, i);
+
+ // and move it
+ const SwTableBox* pT = pTarget->GetBox();
+ const SwTableBox* pS = pSource->GetBox();
+
+ // and move it
+ bool bMoved = rMovedList.GetPos(pT) != USHRT_MAX;
+ MoveCell(pDoc, pS, pT, bMoved, pUD);
+
+ rMovedList.push_back(pS);
+
+ if( pS != pT )
+ {
+ SwFrameFormat* pTFormat = pT->GetFrameFormat();
+ const SfxItemSet* pSSet = rBox.GetItemSet( nS, i );
+
+ if( pSSet ||
+ SfxItemState::SET == pTFormat->GetItemState( RES_BOXATR_FORMAT ) ||
+ SfxItemState::SET == pTFormat->GetItemState( RES_BOXATR_FORMULA ) ||
+ SfxItemState::SET == pTFormat->GetItemState( RES_BOXATR_VALUE ) )
+ {
+ pTFormat = const_cast<SwTableBox*>(pT)->ClaimFrameFormat();
+ pTFormat->LockModify();
+ if( pTFormat->ResetFormatAttr( RES_BOXATR_FORMAT, RES_BOXATR_VALUE ) )
+ pTFormat->ResetFormatAttr( RES_VERT_ORIENT );
+
+ if( pSSet )
+ pTFormat->SetFormatAttr( *pSSet );
+ pTFormat->UnlockModify();
+ }
+ }
+ }
+}
+
+/// Move a single Cell
+void MoveCell(SwDoc* pDoc, const SwTableBox* pSource, const SwTableBox* pTar,
+ bool bMovedBefore, SwUndoSort* pUD)
+{
+ OSL_ENSURE(pSource && pTar,"Source or target missing");
+
+ if(pSource == pTar)
+ return;
+
+ if(pUD)
+ pUD->Insert( pSource->GetName(), pTar->GetName() );
+
+ // Set Pam source to the first ContentNode
+ SwNodeRange aRg( *pSource->GetSttNd(), SwNodeOffset(0), *pSource->GetSttNd() );
+ SwNode* pNd = pDoc->GetNodes().GoNext( &aRg.aStart );
+
+ // If the Cell (Source) wasn't moved
+ // -> insert an empty Node and move the rest or the Mark
+ // points to the first ContentNode
+ if( pNd->StartOfSectionNode() == pSource->GetSttNd() )
+ pNd = pDoc->GetNodes().MakeTextNode( aRg.aStart.GetNode(),
+ pDoc->GetDfltTextFormatColl() );
+ aRg.aEnd = *pNd->EndOfSectionNode();
+
+ // If the Target is empty (there is one empty Node)
+ // -> move and delete it
+ SwNodeIndex aTar( *pTar->GetSttNd() );
+ pNd = pDoc->GetNodes().GoNext( &aTar ); // next ContentNode
+ SwNodeOffset nCount = pNd->EndOfSectionIndex() - pNd->StartOfSectionIndex();
+
+ bool bDelFirst = false;
+ if( nCount == SwNodeOffset(2) )
+ {
+ OSL_ENSURE( pNd->GetContentNode(), "No ContentNode");
+ bDelFirst = !pNd->GetContentNode()->Len() && bMovedBefore;
+ }
+
+ if(!bDelFirst)
+ { // We already have Content -> old Content Section Down
+ SwNodeRange aRgTar( aTar.GetNode(), SwNodeOffset(0), *pNd->EndOfSectionNode() );
+ pDoc->GetNodes().SectionDown( &aRgTar );
+ }
+
+ // Insert the Source
+ SwNodeIndex aIns( *pTar->GetSttNd()->EndOfSectionNode() );
+ pDoc->getIDocumentContentOperations().MoveNodeRange( aRg, aIns.GetNode(),
+ SwMoveFlags::DEFAULT );
+
+ // If first Node is empty -> delete it
+ if(bDelFirst)
+ pDoc->GetNodes().Delete( aTar );
+}
+
+/// Generate two-dimensional array of FndBoxes
+FlatFndBox::FlatFndBox(SwDoc* pDocPtr, const FndBox_& rBoxRef) :
+ m_pDoc(pDocPtr),
+ m_nRow(0),
+ m_nCol(0)
+{ // If the array is symmetric
+ m_bSym = CheckLineSymmetry(rBoxRef);
+ if( !m_bSym )
+ return;
+
+ // Determine column/row count
+ m_nCols = GetColCount(rBoxRef);
+ m_nRows = GetRowCount(rBoxRef);
+
+ // Create linear array
+ size_t nCount = static_cast<size_t>(m_nRows) * m_nCols;
+ m_pArr = std::make_unique<FndBox_ const *[]>(nCount);
+ memset(m_pArr.get(), 0, sizeof(const FndBox_*) * nCount);
+
+ FillFlat( rBoxRef );
+}
+
+FlatFndBox::~FlatFndBox()
+{
+}
+
+/// All Lines of a Box need to have same number of Boxes
+bool FlatFndBox::CheckLineSymmetry(const FndBox_& rBox)
+{
+ const FndLines_t &rLines = rBox.GetLines();
+ FndBoxes_t::size_type nBoxes {0};
+
+ for (FndLines_t::size_type i=0; i < rLines.size(); ++i)
+ {
+ const FndLine_* pLn = rLines[i].get();
+ const FndBoxes_t& rBoxes = pLn->GetBoxes();
+
+ // Number of Boxes of all Lines is unequal -> no symmetry
+ if( i && nBoxes != rBoxes.size())
+ return false;
+
+ nBoxes = rBoxes.size();
+ if( !CheckBoxSymmetry( *pLn ) )
+ return false;
+ }
+ return true;
+}
+
+/// Check Box for symmetry (All Boxes of a Line need to have same number of Lines)
+bool FlatFndBox::CheckBoxSymmetry(const FndLine_& rLn)
+{
+ const FndBoxes_t &rBoxes = rLn.GetBoxes();
+ FndLines_t::size_type nLines {0};
+
+ for (FndBoxes_t::size_type i = 0; i < rBoxes.size(); ++i)
+ {
+ FndBox_ const*const pBox = rBoxes[i].get();
+ const FndLines_t& rLines = pBox->GetLines();
+
+ // Number of Lines of all Boxes is unequal -> no symmetry
+ if( i && nLines != rLines.size() )
+ return false;
+
+ nLines = rLines.size();
+ if( nLines && !CheckLineSymmetry( *pBox ) )
+ return false;
+ }
+ return true;
+}
+
+/// Maximum count of Columns (Boxes)
+sal_uInt16 FlatFndBox::GetColCount(const FndBox_& rBox)
+{
+ const FndLines_t& rLines = rBox.GetLines();
+ // Iterate over Lines
+ if( rLines.empty() )
+ return 1;
+
+ sal_uInt16 nSum = 0;
+ for (const auto & pLine : rLines)
+ {
+ // The Boxes of a Line
+ sal_uInt16 nCount = 0;
+ const FndBoxes_t& rBoxes = pLine->GetBoxes();
+ for (const auto &rpB : rBoxes)
+ { // Iterate recursively over the Lines
+ nCount += rpB->GetLines().empty() ? 1 : GetColCount(*rpB);
+ }
+
+ if( nSum < nCount )
+ nSum = nCount;
+ }
+ return nSum;
+}
+
+/// Maximum count of Rows (Lines)
+sal_uInt16 FlatFndBox::GetRowCount(const FndBox_& rBox)
+{
+ const FndLines_t& rLines = rBox.GetLines();
+ if( rLines.empty() )
+ return 1;
+
+ sal_uInt16 nLines = 0;
+ for (const auto & pLine : rLines)
+ { // The Boxes of a Line
+ const FndBoxes_t& rBoxes = pLine->GetBoxes();
+ sal_uInt16 nLn = 1;
+ for (const auto &rpB : rBoxes)
+ {
+ if (!rpB->GetLines().empty())
+ { // Iterate recursively over the Lines
+ nLn = std::max(GetRowCount(*rpB), nLn);
+ }
+ }
+
+ nLines = nLines + nLn;
+ }
+ return nLines;
+}
+
+/// Create a linear array of atomic FndBoxes
+void FlatFndBox::FillFlat(const FndBox_& rBox, bool bLastBox)
+{
+ bool bModRow = false;
+ const FndLines_t& rLines = rBox.GetLines();
+
+ // Iterate over Lines
+ sal_uInt16 nOldRow = m_nRow;
+ for (const auto & pLine : rLines)
+ {
+ // The Boxes of a Line
+ const FndBoxes_t& rBoxes = pLine->GetBoxes();
+ sal_uInt16 nOldCol = m_nCol;
+ for( FndBoxes_t::size_type j = 0; j < rBoxes.size(); ++j )
+ {
+ // Check the Box if it's an atomic one
+ const FndBox_ *const pBox = rBoxes[j].get();
+
+ if( pBox->GetLines().empty() )
+ {
+ // save it
+ sal_uInt16 nOff = m_nRow * m_nCols + m_nCol;
+ m_pArr[nOff] = pBox;
+
+ // Save the Formula/Format/Value values
+ const SwFrameFormat* pFormat = pBox->GetBox()->GetFrameFormat();
+ if( SfxItemState::SET == pFormat->GetItemState( RES_BOXATR_FORMAT ) ||
+ SfxItemState::SET == pFormat->GetItemState( RES_BOXATR_FORMULA ) ||
+ SfxItemState::SET == pFormat->GetItemState( RES_BOXATR_VALUE ) )
+ {
+ SfxItemSetFixed<
+ RES_VERT_ORIENT, RES_VERT_ORIENT,
+ RES_BOXATR_FORMAT, RES_BOXATR_VALUE>
+ aSet(m_pDoc->GetAttrPool());
+ aSet.Put( pFormat->GetAttrSet() );
+ if( m_vItemSets.empty() )
+ {
+ size_t nCount = static_cast<size_t>(m_nRows) * m_nCols;
+ m_vItemSets.resize(nCount);
+ }
+ m_vItemSets[nOff].emplace(std::move(aSet));
+ }
+
+ bModRow = true;
+ }
+ else
+ {
+ // Iterate recursively over the Lines of a Box
+ FillFlat( *pBox, ( j+1 == rBoxes.size() ) );
+ }
+ m_nCol++;
+ }
+ if(bModRow)
+ m_nRow++;
+ m_nCol = nOldCol;
+ }
+ if(!bLastBox)
+ m_nRow = nOldRow;
+}
+
+/// Access a specific Cell
+const FndBox_* FlatFndBox::GetBox(sal_uInt16 n_Col, sal_uInt16 n_Row) const
+{
+ sal_uInt16 nOff = n_Row * m_nCols + n_Col;
+ const FndBox_* pTmp = m_pArr[nOff];
+
+ OSL_ENSURE(n_Col < m_nCols && n_Row < m_nRows && pTmp, "invalid array access");
+ return pTmp;
+}
+
+const SfxItemSet* FlatFndBox::GetItemSet(sal_uInt16 n_Col, sal_uInt16 n_Row) const
+{
+ OSL_ENSURE( m_vItemSets.empty() || ( n_Col < m_nCols && n_Row < m_nRows), "invalid array access");
+
+ if (m_vItemSets.empty()) {
+ return nullptr;
+ }
+ auto const & el = m_vItemSets[unsigned(n_Row * m_nCols) + n_Col];
+ return el ? &*el : nullptr;
+}
+
+sal_uInt16 SwMovedBoxes::GetPos(const SwTableBox* pTableBox) const
+{
+ std::vector<const SwTableBox*>::const_iterator it = std::find(mBoxes.begin(), mBoxes.end(), pTableBox);
+ return it == mBoxes.end() ? USHRT_MAX : it - mBoxes.begin();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/docstat.cxx b/sw/source/core/doc/docstat.cxx
new file mode 100644
index 0000000000..959c61833d
--- /dev/null
+++ b/sw/source/core/doc/docstat.cxx
@@ -0,0 +1,53 @@
+/* -*- 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 <docstat.hxx>
+
+SwDocStat::SwDocStat() :
+ nTable(0),
+ nGrf(0),
+ nOLE(0),
+ nPage(1),
+ nPara(1),
+ nAllPara(1),
+ nWord(0),
+ nAsianWord(0),
+ nChar(0),
+ nCharExcludingSpaces(0),
+ nComments(0),
+ bModified(true)
+{}
+
+void SwDocStat::Reset()
+{
+ nTable = 0;
+ nGrf = 0;
+ nOLE = 0;
+ nPage = 1;
+ nPara = 1;
+ nAllPara= 1;
+ nWord = 0;
+ nAsianWord = 0;
+ nChar = 0;
+ nCharExcludingSpaces = 0;
+ nComments = 0;
+ bModified = true;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/doctxm.cxx b/sw/source/core/doc/doctxm.cxx
new file mode 100644
index 0000000000..f03687d810
--- /dev/null
+++ b/sw/source/core/doc/doctxm.cxx
@@ -0,0 +1,2135 @@
+/* -*- 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 <limits.h>
+#include <hintids.hxx>
+#include <editeng/formatbreakitem.hxx>
+#include <comphelper/classids.hxx>
+#include <o3tl/string_view.hxx>
+#include <docsh.hxx>
+#include <ndole.hxx>
+#include <txttxmrk.hxx>
+#include <fmtpdsc.hxx>
+#include <frmatr.hxx>
+#include <pagedesc.hxx>
+#include <doc.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <DocumentSettingManager.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <IDocumentFieldsAccess.hxx>
+#include <IDocumentState.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <IDocumentStylePoolAccess.hxx>
+#include <pagefrm.hxx>
+#include <ndtxt.hxx>
+#include <swtable.hxx>
+#include <doctxm.hxx>
+#include <txmsrt.hxx>
+#include <rolbck.hxx>
+#include <poolfmt.hxx>
+#include <txtfrm.hxx>
+#include <rootfrm.hxx>
+#include <UndoAttribute.hxx>
+#include <UndoSection.hxx>
+#include <swundo.hxx>
+#include <mdiexp.hxx>
+#include <docary.hxx>
+#include <charfmt.hxx>
+#include <fchrfmt.hxx>
+#include <fldbas.hxx>
+#include <fmtfld.hxx>
+#include <txtfld.hxx>
+#include <expfld.hxx>
+#include <mvsave.hxx>
+#include <node2lay.hxx>
+#include <SwStyleNameMapper.hxx>
+#include <breakit.hxx>
+#include <calbck.hxx>
+#include <ToxTextGenerator.hxx>
+#include <ToxTabStopTokenHandler.hxx>
+#include <frameformats.hxx>
+#include <tools/datetimeutils.hxx>
+#include <tools/globname.hxx>
+#include <com/sun/star/embed/XEmbeddedObject.hpp>
+#include <o3tl/safeint.hxx>
+#include <osl/diagnose.h>
+
+#include <memory>
+
+using namespace ::com::sun::star;
+
+template<typename T, typename... Args> static
+typename std::enable_if<!std::is_array<T>::value, std::unique_ptr<T>>::type
+MakeSwTOXSortTabBase(SwRootFrame const*const pLayout, Args&& ... args)
+{
+ std::unique_ptr<T> pRet(new T(std::forward<Args>(args)...));
+ pRet->InitText(pLayout); // ensure it's expanded with the layout
+ return pRet;
+}
+
+void SwDoc::GetTOIKeys(SwTOIKeyType eTyp, std::vector<OUString>& rArr,
+ SwRootFrame const& rLayout) const
+{
+ rArr.clear();
+
+ // Look up all Primary and Secondary via the Pool
+ for (const SfxPoolItem* pPoolItem : GetAttrPool().GetItemSurrogates(RES_TXTATR_TOXMARK))
+ {
+ const SwTOXMark* pItem = dynamic_cast<const SwTOXMark*>(pPoolItem);
+ if( !pItem )
+ continue;
+ const SwTOXType* pTOXType = pItem->GetTOXType();
+ if ( !pTOXType || pTOXType->GetType()!=TOX_INDEX )
+ continue;
+ const SwTextTOXMark* pMark = pItem->GetTextTOXMark();
+ if ( pMark && pMark->GetpTextNd() &&
+ pMark->GetpTextNd()->GetNodes().IsDocNodes() &&
+ (!rLayout.IsHideRedlines()
+ || !sw::IsMarkHintHidden(rLayout, *pMark->GetpTextNd(), *pMark)))
+ {
+ const OUString sStr = TOI_PRIMARY == eTyp
+ ? pItem->GetPrimaryKey()
+ : pItem->GetSecondaryKey();
+
+ if( !sStr.isEmpty() )
+ rArr.push_back( sStr );
+ }
+ }
+}
+
+/// Get current table of contents Mark.
+sal_uInt16 SwDoc::GetCurTOXMark( const SwPosition& rPos,
+ SwTOXMarks& rArr )
+{
+ // search on Position rPos for all SwTOXMarks
+ SwTextNode *const pTextNd = rPos.GetNode().GetTextNode();
+ if( !pTextNd || !pTextNd->GetpSwpHints() )
+ return 0;
+
+ const SwpHints & rHts = *pTextNd->GetpSwpHints();
+ sal_Int32 nSttIdx;
+ const sal_Int32 *pEndIdx;
+
+ const sal_Int32 nCurrentPos = rPos.GetContentIndex();
+
+ for( size_t n = 0; n < rHts.Count(); ++n )
+ {
+ const SwTextAttr* pHt = rHts.Get(n);
+ if( RES_TXTATR_TOXMARK != pHt->Which() )
+ continue;
+ if( ( nSttIdx = pHt->GetStart() ) < nCurrentPos )
+ {
+ // also check the end
+ pEndIdx = pHt->End();
+ if( nullptr == pEndIdx || *pEndIdx <= nCurrentPos )
+ continue; // keep searching
+ }
+ else if( nSttIdx > nCurrentPos )
+ // If Hint's Start is greater than rPos, break, because
+ // the attributes are sorted by Start!
+ break;
+
+ SwTOXMark* pTMark = const_cast<SwTOXMark*>(&pHt->GetTOXMark());
+ rArr.push_back( pTMark );
+ }
+ return rArr.size();
+}
+
+/// Delete table of contents Mark
+void SwDoc::DeleteTOXMark( const SwTOXMark* pTOXMark )
+{
+ const SwTextTOXMark* pTextTOXMark = pTOXMark->GetTextTOXMark();
+ assert(pTextTOXMark);
+
+ SwTextNode& rTextNd = const_cast<SwTextNode&>(pTextTOXMark->GetTextNode());
+ assert(rTextNd.GetpSwpHints());
+
+ if (pTextTOXMark->HasDummyChar())
+ {
+ // tdf#106377 don't use SwUndoResetAttr, it uses NOTXTATRCHR
+ SwPaM tmp(rTextNd, pTextTOXMark->GetStart(),
+ rTextNd, pTextTOXMark->GetStart()+1);
+ assert(rTextNd.GetText()[pTextTOXMark->GetStart()] == CH_TXTATR_INWORD);
+ getIDocumentContentOperations().DeleteRange(tmp);
+ }
+ else
+ {
+ std::unique_ptr<SwRegHistory> aRHst;
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ // save attributes for Undo
+ SwUndoResetAttr* pUndo = new SwUndoResetAttr(
+ SwPosition( rTextNd, pTextTOXMark->GetStart() ),
+ RES_TXTATR_TOXMARK );
+ GetIDocumentUndoRedo().AppendUndo( std::unique_ptr<SwUndo>(pUndo) );
+
+ aRHst.reset(new SwRegHistory(rTextNd, &pUndo->GetHistory()));
+ rTextNd.GetpSwpHints()->Register(aRHst.get());
+ }
+
+ rTextNd.DeleteAttribute( const_cast<SwTextTOXMark*>(pTextTOXMark) );
+
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ if( rTextNd.GetpSwpHints() )
+ rTextNd.GetpSwpHints()->DeRegister();
+ }
+ }
+
+ getIDocumentState().SetModified();
+}
+
+namespace {
+
+/// Travel between table of content Marks
+class CompareNodeContent
+{
+ SwNodeOffset m_nNode;
+ sal_Int32 m_nContent;
+public:
+ CompareNodeContent( SwNodeOffset nNd, sal_Int32 nCnt )
+ : m_nNode( nNd ), m_nContent( nCnt ) {}
+
+ bool operator==( const CompareNodeContent& rCmp ) const
+ { return m_nNode == rCmp.m_nNode && m_nContent == rCmp.m_nContent; }
+ bool operator!=( const CompareNodeContent& rCmp ) const
+ { return m_nNode != rCmp.m_nNode || m_nContent != rCmp.m_nContent; }
+ bool operator< ( const CompareNodeContent& rCmp ) const
+ { return m_nNode < rCmp.m_nNode ||
+ ( m_nNode == rCmp.m_nNode && m_nContent < rCmp.m_nContent); }
+ bool operator<=( const CompareNodeContent& rCmp ) const
+ { return m_nNode < rCmp.m_nNode ||
+ ( m_nNode == rCmp.m_nNode && m_nContent <= rCmp.m_nContent); }
+ bool operator> ( const CompareNodeContent& rCmp ) const
+ { return m_nNode > rCmp.m_nNode ||
+ ( m_nNode == rCmp.m_nNode && m_nContent > rCmp.m_nContent); }
+ bool operator>=( const CompareNodeContent& rCmp ) const
+ { return m_nNode > rCmp.m_nNode ||
+ ( m_nNode == rCmp.m_nNode && m_nContent >= rCmp.m_nContent); }
+};
+
+}
+
+const SwTOXMark& SwDoc::GotoTOXMark( const SwTOXMark& rCurTOXMark,
+ SwTOXSearch eDir, bool bInReadOnly )
+{
+ const SwTextTOXMark* pMark = rCurTOXMark.GetTextTOXMark();
+
+ CompareNodeContent aAbsIdx(pMark ? pMark->GetpTextNd()->GetIndex() : SwNodeOffset(0), pMark ? pMark->GetStart() : 0);
+ CompareNodeContent aPrevPos( SwNodeOffset(0), 0 );
+ CompareNodeContent aNextPos( NODE_OFFSET_MAX, SAL_MAX_INT32 );
+ CompareNodeContent aMax( SwNodeOffset(0), 0 );
+ CompareNodeContent aMin( NODE_OFFSET_MAX, SAL_MAX_INT32 );
+
+ const SwTOXMark* pNew = nullptr;
+ const SwTOXMark* pMax = &rCurTOXMark;
+ const SwTOXMark* pMin = &rCurTOXMark;
+
+ const SwTOXType* pType = rCurTOXMark.GetTOXType();
+ SwTOXMarks aMarks;
+ pType->CollectTextMarks(aMarks);
+
+ for(SwTOXMark* pTOXMark : aMarks)
+ {
+ // Item PtrCompare needed here
+ if (areSfxPoolItemPtrsEqual( pTOXMark, &rCurTOXMark ))
+ continue;
+
+ pMark = pTOXMark->GetTextTOXMark();
+ if (!pMark)
+ continue;
+
+ SwTextNode const*const pTOXSrc = pMark->GetpTextNd();
+ if (!pTOXSrc)
+ continue;
+
+ Point aPt;
+ std::pair<Point, bool> const tmp(aPt, false);
+ const SwContentFrame* pCFrame = pTOXSrc->getLayoutFrame(
+ getIDocumentLayoutAccess().GetCurrentLayout(), nullptr, &tmp);
+ if (!pCFrame)
+ continue;
+
+ if ( bInReadOnly || !pCFrame->IsProtected() )
+ {
+ CompareNodeContent aAbsNew( pTOXSrc->GetIndex(), pMark->GetStart() );
+ switch( eDir )
+ {
+ // The following (a bit more complicated) statements make it
+ // possible to also travel across Entries on the same (!)
+ // position. If someone has time, please feel free to optimize.
+ case TOX_SAME_PRV:
+ if (pTOXMark->GetText(nullptr) != rCurTOXMark.GetText(nullptr))
+ break;
+ [[fallthrough]];
+ case TOX_PRV:
+ if ( (aAbsNew < aAbsIdx && aAbsNew > aPrevPos) ||
+ (aAbsIdx == aAbsNew &&
+ (reinterpret_cast<sal_uLong>(&rCurTOXMark) > reinterpret_cast<sal_uLong>(pTOXMark) &&
+ (!pNew || aPrevPos < aAbsIdx || reinterpret_cast<sal_uLong>(pNew) < reinterpret_cast<sal_uLong>(pTOXMark) ) )) ||
+ (aPrevPos == aAbsNew && aAbsIdx != aAbsNew &&
+ reinterpret_cast<sal_uLong>(pTOXMark) > reinterpret_cast<sal_uLong>(pNew)) )
+ {
+ pNew = pTOXMark;
+ aPrevPos = aAbsNew;
+ if ( aAbsNew >= aMax )
+ {
+ aMax = aAbsNew;
+ pMax = pTOXMark;
+ }
+ }
+ break;
+
+ case TOX_SAME_NXT:
+ if (pTOXMark->GetText(nullptr) != rCurTOXMark.GetText(nullptr))
+ break;
+ [[fallthrough]];
+ case TOX_NXT:
+ if ( (aAbsNew > aAbsIdx && aAbsNew < aNextPos) ||
+ (aAbsIdx == aAbsNew &&
+ (reinterpret_cast<sal_uLong>(&rCurTOXMark) < reinterpret_cast<sal_uLong>(pTOXMark) &&
+ (!pNew || aNextPos > aAbsIdx || reinterpret_cast<sal_uLong>(pNew) > reinterpret_cast<sal_uLong>(pTOXMark)) )) ||
+ (aNextPos == aAbsNew && aAbsIdx != aAbsNew &&
+ reinterpret_cast<sal_uLong>(pTOXMark) < reinterpret_cast<sal_uLong>(pNew)) )
+ {
+ pNew = pTOXMark;
+ aNextPos = aAbsNew;
+ if ( aAbsNew <= aMin )
+ {
+ aMin = aAbsNew;
+ pMin = pTOXMark;
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ // We couldn't find a successor
+ // Use minimum or maximum
+ if(!pNew)
+ {
+ switch(eDir)
+ {
+ case TOX_PRV:
+ case TOX_SAME_PRV:
+ pNew = pMax;
+ break;
+ case TOX_NXT:
+ case TOX_SAME_NXT:
+ pNew = pMin;
+ break;
+ default:
+ pNew = &rCurTOXMark;
+ }
+ }
+ return *pNew;
+}
+
+SwTOXBaseSection* SwDoc::InsertTableOf( const SwPosition& rPos,
+ const SwTOXBase& rTOX,
+ const SfxItemSet* pSet,
+ bool bExpand,
+ SwRootFrame const*const pLayout)
+{
+ SwPaM aPam( rPos );
+ return InsertTableOf( aPam, rTOX, pSet, bExpand, pLayout );
+}
+
+SwTOXBaseSection* SwDoc::InsertTableOf( const SwPaM& aPam,
+ const SwTOXBase& rTOX,
+ const SfxItemSet* pSet,
+ bool bExpand,
+ SwRootFrame const*const pLayout )
+{
+ assert(!bExpand || pLayout != nullptr);
+ GetIDocumentUndoRedo().StartUndo( SwUndoId::INSTOX, nullptr );
+
+ OUString sSectNm = GetUniqueTOXBaseName( *rTOX.GetTOXType(), rTOX.GetTOXName() );
+ SwSectionData aSectionData( SectionType::ToxContent, sSectNm );
+
+ std::tuple<SwTOXBase const*, sw::RedlineMode, sw::FieldmarkMode, sw::ParagraphBreakMode> const tmp(
+ &rTOX,
+ pLayout && pLayout->IsHideRedlines()
+ ? sw::RedlineMode::Hidden
+ : sw::RedlineMode::Shown,
+ pLayout ? pLayout->GetFieldmarkMode() : sw::FieldmarkMode::ShowBoth,
+ pLayout ? pLayout->GetParagraphBreakMode() : sw::ParagraphBreakMode::Shown);
+ SwTOXBaseSection *const pNewSection = dynamic_cast<SwTOXBaseSection *>(
+ InsertSwSection(aPam, aSectionData, & tmp, pSet, false));
+ if (pNewSection)
+ {
+ SwSectionNode *const pSectNd = pNewSection->GetFormat()->GetSectionNode();
+ pNewSection->SetTOXName(sSectNm); // rTOX may have had no name...
+
+ if( bExpand )
+ {
+ // add value for 2nd parameter = true to
+ // indicate, that a creation of a new table of content has to be performed.
+ // Value of 1st parameter = default value.
+ pNewSection->Update( nullptr, pLayout, true );
+ }
+ else if( rTOX.GetTitle().getLength()==1 && IsInReading() )
+ // insert title of TOX
+ {
+ // then insert the headline section
+ SwNodeIndex aIdx( *pSectNd, +1 );
+
+ SwTextNode* pHeadNd = GetNodes().MakeTextNode( aIdx.GetNode(),
+ getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_STANDARD ) );
+
+ SwSectionData headerData( SectionType::ToxHeader, pNewSection->GetTOXName()+"_Head" );
+
+ --aIdx;
+ SwSectionFormat* pSectFormat = MakeSectionFormat();
+ GetNodes().InsertTextSection(
+ *pHeadNd, *pSectFormat, headerData, nullptr, &aIdx.GetNode(), true, false);
+ }
+ }
+
+ GetIDocumentUndoRedo().EndUndo( SwUndoId::INSTOX, nullptr );
+
+ return pNewSection;
+}
+
+void SwDoc::InsertTableOf( SwNodeOffset nSttNd, SwNodeOffset nEndNd,
+ const SwTOXBase& rTOX,
+ const SfxItemSet* pSet )
+{
+ // check for recursive TOX
+ SwNode* pNd = GetNodes()[ nSttNd ];
+ SwSectionNode* pSectNd = pNd->FindSectionNode();
+ while( pSectNd )
+ {
+ SectionType eT = pSectNd->GetSection().GetType();
+ if( SectionType::ToxHeader == eT || SectionType::ToxContent == eT )
+ return;
+ pSectNd = pSectNd->StartOfSectionNode()->FindSectionNode();
+ }
+
+ const OUString sSectNm = GetUniqueTOXBaseName(*rTOX.GetTOXType(), rTOX.GetTOXName());
+
+ SwSectionData aSectionData( SectionType::ToxContent, sSectNm );
+
+ SwNodeIndex aStt( GetNodes(), nSttNd ), aEnd( GetNodes(), nEndNd );
+ SwSectionFormat* pFormat = MakeSectionFormat();
+ if(pSet)
+ pFormat->SetFormatAttr(*pSet);
+
+ SwSectionNode *const pNewSectionNode =
+ GetNodes().InsertTextSection(aStt.GetNode(), *pFormat, aSectionData, &rTOX, &aEnd.GetNode());
+ if (!pNewSectionNode)
+ {
+ DelSectionFormat( pFormat );
+ return;
+ }
+
+ SwTOXBaseSection *const pNewSection(
+ dynamic_cast<SwTOXBaseSection*>(& pNewSectionNode->GetSection()));
+ if (pNewSection)
+ pNewSection->SetTOXName(sSectNm); // rTOX may have had no name...
+}
+
+/// Get current table of contents
+SwTOXBase* SwDoc::GetCurTOX( const SwPosition& rPos )
+{
+ SwNode& rNd = rPos.GetNode();
+ SwSectionNode* pSectNd = rNd.FindSectionNode();
+ while( pSectNd )
+ {
+ SectionType eT = pSectNd->GetSection().GetType();
+ if( SectionType::ToxContent == eT )
+ {
+ assert( dynamic_cast< const SwTOXBaseSection *>( &pSectNd->GetSection()) &&
+ "no TOXBaseSection!" );
+ SwTOXBaseSection& rTOXSect = static_cast<SwTOXBaseSection&>(
+ pSectNd->GetSection());
+ return &rTOXSect;
+ }
+ pSectNd = pSectNd->StartOfSectionNode()->FindSectionNode();
+ }
+ return nullptr;
+}
+
+const SwAttrSet& SwDoc::GetTOXBaseAttrSet(const SwTOXBase& rTOXBase)
+{
+ assert( dynamic_cast<const SwTOXBaseSection*>( &rTOXBase) && "no TOXBaseSection!" );
+ const SwTOXBaseSection& rTOXSect = static_cast<const SwTOXBaseSection&>(rTOXBase);
+ SwSectionFormat const * pFormat = rTOXSect.GetFormat();
+ OSL_ENSURE( pFormat, "invalid TOXBaseSection!" );
+ return pFormat->GetAttrSet();
+}
+
+const SwTOXBase* SwDoc::GetDefaultTOXBase( TOXTypes eTyp, bool bCreate )
+{
+ std::unique_ptr<SwTOXBase>* prBase = nullptr;
+ switch(eTyp)
+ {
+ case TOX_CONTENT: prBase = &mpDefTOXBases->pContBase; break;
+ case TOX_INDEX: prBase = &mpDefTOXBases->pIdxBase; break;
+ case TOX_USER: prBase = &mpDefTOXBases->pUserBase; break;
+ case TOX_TABLES: prBase = &mpDefTOXBases->pTableBase; break;
+ case TOX_OBJECTS: prBase = &mpDefTOXBases->pObjBase; break;
+ case TOX_ILLUSTRATIONS: prBase = &mpDefTOXBases->pIllBase; break;
+ case TOX_AUTHORITIES: prBase = &mpDefTOXBases->pAuthBase; break;
+ case TOX_BIBLIOGRAPHY: prBase = &mpDefTOXBases->pBiblioBase; break;
+ case TOX_CITATION: /** TODO */break;
+ }
+ if (!prBase)
+ return nullptr;
+ if(!(*prBase) && bCreate)
+ {
+ SwForm aForm(eTyp);
+ const SwTOXType* pType = GetTOXType(eTyp, 0);
+ prBase->reset(new SwTOXBase(pType, aForm, SwTOXElement::NONE, pType->GetTypeName()));
+ }
+ return prBase->get();
+}
+
+void SwDoc::SetDefaultTOXBase(const SwTOXBase& rBase)
+{
+ std::unique_ptr<SwTOXBase>* prBase = nullptr;
+ switch(rBase.GetType())
+ {
+ case TOX_CONTENT: prBase = &mpDefTOXBases->pContBase; break;
+ case TOX_INDEX: prBase = &mpDefTOXBases->pIdxBase; break;
+ case TOX_USER: prBase = &mpDefTOXBases->pUserBase; break;
+ case TOX_TABLES: prBase = &mpDefTOXBases->pTableBase; break;
+ case TOX_OBJECTS: prBase = &mpDefTOXBases->pObjBase; break;
+ case TOX_ILLUSTRATIONS: prBase = &mpDefTOXBases->pIllBase; break;
+ case TOX_AUTHORITIES: prBase = &mpDefTOXBases->pAuthBase; break;
+ case TOX_BIBLIOGRAPHY: prBase = &mpDefTOXBases->pBiblioBase; break;
+ case TOX_CITATION: /** TODO */break;
+ }
+ if (!prBase)
+ return;
+ prBase->reset(new SwTOXBase(rBase));
+}
+
+/// Delete table of contents
+bool SwDoc::DeleteTOX( const SwTOXBase& rTOXBase, bool bDelNodes )
+{
+ // We only delete the TOX, not the Nodes
+ bool bRet = false;
+ assert( dynamic_cast<const SwTOXBaseSection*>( &rTOXBase) && "no TOXBaseSection!" );
+
+ const SwTOXBaseSection& rTOXSect = static_cast<const SwTOXBaseSection&>(rTOXBase);
+ SwSectionFormat const * pFormat = rTOXSect.GetFormat();
+ /* Save the start node of the TOX' section. */
+ SwSectionNode const * pMyNode = pFormat ? pFormat->GetSectionNode() : nullptr;
+ if (pMyNode)
+ {
+ GetIDocumentUndoRedo().StartUndo( SwUndoId::CLEARTOXRANGE, nullptr );
+
+ /* Save start node of section's surrounding. */
+ SwNode const * pStartNd = pMyNode->StartOfSectionNode();
+
+ /* Look for the point where to move the cursors in the area to
+ delete to. This is done by first searching forward from the
+ end of the TOX' section. If no content node is found behind
+ the TOX one is searched before it. If this is not
+ successful, too, insert new text node behind the end of
+ the TOX' section. The cursors from the TOX' section will be
+ moved to the content node found or the new text node. */
+
+ /* Set PaM to end of TOX' section and search following content node.
+ aSearchPam will contain the point where to move the cursors
+ to. */
+ SwPaM aSearchPam(*pMyNode->EndOfSectionNode());
+ SwPosition aEndPos(*pStartNd->EndOfSectionNode());
+ if (! aSearchPam.Move() /* no content node found */
+ || *aSearchPam.GetPoint() >= aEndPos /* content node found
+ outside surrounding */
+ )
+ {
+ /* Set PaM to beginning of TOX' section and search previous
+ content node */
+ SwPaM aTmpPam(*pMyNode);
+ aSearchPam = aTmpPam;
+ SwPosition aStartPos(*pStartNd);
+
+ if ( ! aSearchPam.Move(fnMoveBackward) /* no content node found */
+ || *aSearchPam.GetPoint() <= aStartPos /* content node
+ found outside
+ surrounding */
+ )
+ {
+ /* There is no content node in the surrounding of
+ TOX'. Append text node behind TOX' section. */
+
+ SwPosition aInsPos(*pMyNode->EndOfSectionNode());
+ getIDocumentContentOperations().AppendTextNode(aInsPos);
+
+ SwPaM aTmpPam1(aInsPos);
+ aSearchPam = aTmpPam1;
+ }
+ }
+
+ /* PaM containing the TOX. */
+ SwPaM aPam(*pMyNode->EndOfSectionNode(), *pMyNode);
+
+ /* Move cursors contained in TOX to the above calculated point. */
+ PaMCorrAbs(aPam, *aSearchPam.GetPoint());
+
+ if( !bDelNodes )
+ {
+ SwSections aArr( 0 );
+ pFormat->GetChildSections( aArr, SectionSort::Not, false );
+ for( const auto pSect : aArr )
+ {
+ if( SectionType::ToxHeader == pSect->GetType() )
+ {
+ DelSectionFormat( pSect->GetFormat(), bDelNodes );
+ }
+ }
+ }
+
+ DelSectionFormat( const_cast<SwSectionFormat *>(pFormat), bDelNodes );
+
+ GetIDocumentUndoRedo().EndUndo( SwUndoId::CLEARTOXRANGE, nullptr );
+ bRet = true;
+ }
+
+ return bRet;
+}
+
+/// Manage table of content types
+sal_uInt16 SwDoc::GetTOXTypeCount(TOXTypes eTyp) const
+{
+ sal_uInt16 nCnt = 0;
+ for( auto const & pTOXType : *mpTOXTypes )
+ if( eTyp == pTOXType->GetType() )
+ ++nCnt;
+ return nCnt;
+}
+
+const SwTOXType* SwDoc::GetTOXType( TOXTypes eTyp, sal_uInt16 nId ) const
+{
+ sal_uInt16 nCnt = 0;
+ for( auto const & pTOXType : *mpTOXTypes )
+ if( eTyp == pTOXType->GetType() && nCnt++ == nId )
+ return pTOXType.get();
+ return nullptr;
+}
+
+const SwTOXType* SwDoc::InsertTOXType( const SwTOXType& rTyp )
+{
+ SwTOXType * pNew = new SwTOXType(rTyp);
+ mpTOXTypes->emplace_back( pNew );
+ return pNew;
+}
+
+OUString SwDoc::GetUniqueTOXBaseName( const SwTOXType& rType,
+ const OUString& sChkStr ) const
+{
+ if( IsInMailMerge())
+ {
+ OUString newName = "MailMergeTOX"
+ + OStringToOUString( DateTimeToOString( DateTime( DateTime::SYSTEM )), RTL_TEXTENCODING_ASCII_US )
+ + OUString::number( mpSectionFormatTable->size() + 1 );
+ if( !sChkStr.isEmpty())
+ newName += sChkStr;
+ return newName;
+ }
+
+ bool bUseChkStr = !sChkStr.isEmpty();
+ const OUString& aName( rType.GetTypeName() );
+ const sal_Int32 nNmLen = aName.getLength();
+
+ SwSectionFormats::size_type nNum = 0;
+ const SwSectionFormats::size_type nFlagSize = ( mpSectionFormatTable->size() / 8 ) +2;
+ std::unique_ptr<sal_uInt8[]> pSetFlags(new sal_uInt8[ nFlagSize ]);
+ memset( pSetFlags.get(), 0, nFlagSize );
+
+ for( auto pSectionFormat : *mpSectionFormatTable )
+ {
+ const SwSectionNode *pSectNd = pSectionFormat->GetSectionNode();
+ if ( !pSectNd )
+ continue;
+
+ const SwSection& rSect = pSectNd->GetSection();
+ if (rSect.GetType()==SectionType::ToxContent)
+ {
+ const OUString& rNm = rSect.GetSectionName();
+ if ( rNm.startsWith(aName) )
+ {
+ // Calculate number and set the Flag
+ nNum = o3tl::toInt32(rNm.subView( nNmLen ));
+ if( nNum-- && nNum < mpSectionFormatTable->size() )
+ pSetFlags[ nNum / 8 ] |= (0x01 << ( nNum & 0x07 ));
+ }
+ if ( bUseChkStr && sChkStr==rNm )
+ bUseChkStr = false;
+ }
+ }
+
+ if( !bUseChkStr )
+ {
+ // All Numbers have been flagged accordingly, so get the right Number
+ nNum = mpSectionFormatTable->size();
+ for( SwSectionFormats::size_type n = 0; n < nFlagSize; ++n )
+ {
+ sal_uInt8 nTmp = pSetFlags[ n ];
+ if( nTmp != 0xff )
+ {
+ // so get the Number
+ nNum = n * 8;
+ while( nTmp & 1 )
+ {
+ ++nNum;
+ nTmp >>= 1;
+ }
+ break;
+ }
+ }
+ }
+ if ( bUseChkStr )
+ return sChkStr;
+ return aName + OUString::number( ++nNum );
+}
+
+bool SwDoc::SetTOXBaseName(const SwTOXBase& rTOXBase, const OUString& rName)
+{
+ assert( dynamic_cast<const SwTOXBaseSection*>( &rTOXBase) && "no TOXBaseSection!" );
+ SwTOXBaseSection* pTOX = const_cast<SwTOXBaseSection*>(static_cast<const SwTOXBaseSection*>(&rTOXBase));
+
+ if (GetUniqueTOXBaseName(*rTOXBase.GetTOXType(), rName) == rName)
+ {
+ pTOX->SetTOXName(rName);
+ pTOX->SetSectionName(rName);
+ getIDocumentState().SetModified();
+ return true;
+ }
+ return false;
+}
+
+static const SwTextNode* lcl_FindChapterNode( const SwNode& rNd,
+ SwRootFrame const*const pLayout, sal_uInt8 const nLvl = 0 )
+{
+ const SwNode* pNd = &rNd;
+ if( pNd->GetNodes().GetEndOfExtras().GetIndex() > pNd->GetIndex() )
+ {
+ // then find the "Anchor" (Body) position
+ Point aPt;
+ SwNode2Layout aNode2Layout( *pNd, pNd->GetIndex() );
+ const SwFrame* pFrame = aNode2Layout.GetFrame( &aPt );
+
+ if( pFrame )
+ {
+ SwPosition aPos( *pNd );
+ pNd = GetBodyTextNode( pNd->GetDoc(), aPos, *pFrame );
+ OSL_ENSURE( pNd, "Where's the paragraph?" );
+ // tdf#151462 - search for outline node containing the current node
+ return pNd ? pNd->FindOutlineNodeOfLevel(pNd->GetSectionLevel() - 1, pLayout) : nullptr;
+ }
+ }
+ return pNd->FindOutlineNodeOfLevel(nLvl, pLayout);
+}
+
+static bool IsHeadingContained(const SwTextNode* pChptrNd, const SwNode& rNd)
+{
+ const SwNode* pNd = &rNd;
+ const SwOutlineNodes& rONds = pNd->GetNodes().GetOutLineNds();
+ bool bIsHeadingContained = false;
+ if (!rONds.empty())
+ {
+ bool bCheckFirst = false;
+ SwOutlineNodes::size_type nPos;
+
+ if (!rONds.Seek_Entry(const_cast<SwNode*>(pNd), &nPos))
+ {
+ if (nPos == 0)
+ bCheckFirst = true;
+ else
+ nPos--;
+ }
+
+ if (bCheckFirst)
+ {
+ const SwContentNode* pCNd = pNd->GetContentNode();
+
+ Point aPt(0, 0);
+ std::pair<Point, bool> const tmp(aPt, false);
+
+ const SwFrame* pChptrFrame = pChptrNd ? pChptrNd->getLayoutFrame(
+ pChptrNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(), nullptr, &tmp) : nullptr;
+ const SwPageFrame* pChptrPgFrame = pChptrFrame ? pChptrFrame->FindPageFrame() : nullptr;
+ const SwFrame* pNdFrame
+ = pCNd ? pCNd->getLayoutFrame(
+ pCNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(), nullptr, &tmp)
+ : nullptr;
+
+ // Check if the one asking doesn't precede the page of the specified chapter note
+ bIsHeadingContained
+ = pNdFrame && pChptrPgFrame
+ && pChptrPgFrame->getFrameArea().Top() <= pNdFrame->getFrameArea().Top();
+ // Check if the one asking doesn't succeed the specified chapter note
+ if (bIsHeadingContained)
+ {
+ const SwNode* aChptrNd = pChptrNd;
+ if (!rONds.Seek_Entry(const_cast<SwNode*>(aChptrNd), &nPos) && nPos)
+ nPos--;
+ // Search for the next outline node with a larger level than the specified chapter node
+ while (nPos < rONds.size() - 1
+ && pChptrNd->GetAttrOutlineLevel()
+ < rONds[nPos + 1]->GetTextNode()->GetAttrOutlineLevel())
+ nPos++;
+ // If there exists such an outline node, check if the one asking doesn't succeed
+ // the specified chapter node
+ if (nPos < rONds.size() - 1) {
+ nPos++;
+ const auto aONdsTxtNd = rONds[nPos]->GetTextNode();
+ pChptrFrame = aONdsTxtNd->getLayoutFrame(
+ aONdsTxtNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(), nullptr,
+ &tmp);
+ pChptrPgFrame = pChptrFrame ? pChptrFrame->FindPageFrame() : nullptr;
+ bIsHeadingContained
+ = pNdFrame && pChptrPgFrame
+ && pChptrPgFrame->getFrameArea().Top() >= pNdFrame->getFrameArea().Top();
+ }
+ }
+ }
+ else
+ {
+ // Search for the next outline node which lies not within the current chapter node
+ while (nPos > 0
+ && pChptrNd->GetAttrOutlineLevel()
+ < rONds[nPos]->GetTextNode()->GetAttrOutlineLevel())
+ nPos--;
+ bIsHeadingContained = pChptrNd == rONds[nPos]->GetTextNode();
+ }
+ }
+ else
+ {
+ // If there are no outline nodes, consider the heading contained,
+ // otherwise the _XDocumentIndex._update() test fails
+ bIsHeadingContained = true;
+ }
+ return bIsHeadingContained;
+}
+
+// Table of contents class
+SwTOXBaseSection::SwTOXBaseSection(SwTOXBase const& rBase, SwSectionFormat & rFormat)
+ : SwTOXBase( rBase )
+ , SwSection( SectionType::ToxContent, OUString(), rFormat )
+{
+ SetProtect( rBase.IsProtected() );
+ SetSectionName( GetTOXName() );
+}
+
+SwTOXBaseSection::~SwTOXBaseSection()
+{
+}
+
+bool SwTOXBaseSection::SetPosAtStartEnd( SwPosition& rPos ) const
+{
+ bool bRet = false;
+ const SwSectionNode* pSectNd = GetFormat()->GetSectionNode();
+ if( pSectNd )
+ {
+ rPos.Assign(*pSectNd);
+ pSectNd->GetDoc().GetNodes().GoNext( &rPos );
+ bRet = true;
+ }
+ return bRet;
+}
+
+/// Collect table of contents content
+void SwTOXBaseSection::Update(const SfxItemSet* pAttr,
+ SwRootFrame const*const pLayout,
+ const bool _bNewTOX)
+{
+ if (!GetFormat())
+ return;
+ SwSectionNode const*const pSectNd(GetFormat()->GetSectionNode());
+ if (nullptr == pSectNd ||
+ !pSectNd->GetNodes().IsDocNodes() ||
+ IsHiddenFlag() ||
+ (pLayout->HasMergedParas() && pSectNd->GetRedlineMergeFlag() == SwNode::Merge::Hidden))
+ {
+ return;
+ }
+
+ if ( !mbKeepExpression )
+ {
+ maMSTOCExpression.clear();
+ }
+
+ SwDoc& rDoc = const_cast<SwDoc&>(pSectNd->GetDoc());
+
+ if (pAttr && GetFormat())
+ rDoc.ChgFormat(*GetFormat(), *pAttr);
+
+ // determine default page description, which will be used by the content nodes,
+ // if no appropriate one is found.
+ const SwPageDesc* pDefaultPageDesc;
+ {
+ pDefaultPageDesc =
+ pSectNd->GetSection().GetFormat()->GetPageDesc().GetPageDesc();
+ if ( !_bNewTOX && !pDefaultPageDesc )
+ {
+ // determine page description of table-of-content
+ SwNodeOffset nPgDescNdIdx = pSectNd->GetIndex() + 1;
+ SwNodeOffset* pPgDescNdIdx = &nPgDescNdIdx;
+ pDefaultPageDesc = pSectNd->FindPageDesc( pPgDescNdIdx );
+ if ( nPgDescNdIdx < pSectNd->GetIndex() )
+ {
+ pDefaultPageDesc = nullptr;
+ }
+ }
+ // consider end node of content section in the node array.
+ if ( !pDefaultPageDesc &&
+ ( pSectNd->EndOfSectionNode()->GetIndex() <
+ (pSectNd->GetNodes().GetEndOfContent().GetIndex() - 1) )
+ )
+ {
+ // determine page description of content after table-of-content
+ SwNodeIndex aIdx( *(pSectNd->EndOfSectionNode()) );
+ const SwContentNode* pNdAfterTOX = pSectNd->GetNodes().GoNext( &aIdx );
+ const SwAttrSet& aNdAttrSet = pNdAfterTOX->GetSwAttrSet();
+ const SvxBreak eBreak = aNdAttrSet.GetBreak().GetBreak();
+ if ( eBreak != SvxBreak::PageBefore && eBreak != SvxBreak::PageBoth )
+ {
+ pDefaultPageDesc = pNdAfterTOX->FindPageDesc();
+ }
+ }
+ // consider start node of content section in the node array.
+ if ( !pDefaultPageDesc &&
+ ( pSectNd->GetIndex() >
+ (pSectNd->GetNodes().GetEndOfContent().StartOfSectionIndex() + 1) )
+ )
+ {
+ // determine page description of content before table-of-content
+ SwNodeIndex aIdx( *pSectNd );
+ SwContentNode* pTmp = SwNodes::GoPrevious( &aIdx );
+ assert(pTmp); // make coverity happy
+ pDefaultPageDesc = pTmp->FindPageDesc();
+
+ }
+ if ( !pDefaultPageDesc )
+ {
+ // determine default page description
+ pDefaultPageDesc = &rDoc.GetPageDesc( 0 );
+ }
+ }
+
+ rDoc.getIDocumentState().SetModified();
+
+ // get current Language
+ SwTOXInternational aIntl( GetLanguage(),
+ TOX_INDEX == GetTOXType()->GetType() ?
+ GetOptions() : SwTOIOptions::NONE,
+ GetSortAlgorithm() );
+
+ m_aSortArr.clear();
+
+ // find the first layout node for this TOX, if it only find the content
+ // in his own chapter
+ const SwSectionNode* pChapterSectNd = IsFromChapter() ? pSectNd->FindSectionNode() : nullptr;
+ const SwTextNode* pOwnChapterNode = pChapterSectNd
+ ? ::lcl_FindChapterNode( *pSectNd, pLayout, pChapterSectNd->GetSectionLevel() + 1 )
+ : nullptr;
+
+ SwNode2LayoutSaveUpperFrames aN2L(*pSectNd);
+ const_cast<SwSectionNode*>(pSectNd)->DelFrames();
+
+ // This would be a good time to update the Numbering
+ rDoc.UpdateNumRule();
+
+ if( GetCreateType() & SwTOXElement::Mark )
+ UpdateMarks( aIntl, pOwnChapterNode, pLayout );
+
+ if( GetCreateType() & SwTOXElement::OutlineLevel )
+ UpdateOutline( pOwnChapterNode, pLayout );
+
+ if( GetCreateType() & SwTOXElement::Template )
+ UpdateTemplate( pOwnChapterNode, pLayout );
+
+ if( GetCreateType() & SwTOXElement::Ole ||
+ TOX_OBJECTS == SwTOXBase::GetType())
+ UpdateContent( SwTOXElement::Ole, pOwnChapterNode, pLayout );
+
+ if( GetCreateType() & SwTOXElement::Table ||
+ (TOX_TABLES == SwTOXBase::GetType() && IsFromObjectNames()) )
+ UpdateTable( pOwnChapterNode, pLayout );
+
+ if( GetCreateType() & SwTOXElement::Graphic ||
+ (TOX_ILLUSTRATIONS == SwTOXBase::GetType() && IsFromObjectNames()))
+ UpdateContent( SwTOXElement::Graphic, pOwnChapterNode, pLayout );
+
+ if( !GetSequenceName().isEmpty() && !IsFromObjectNames() &&
+ (TOX_TABLES == SwTOXBase::GetType() ||
+ TOX_ILLUSTRATIONS == SwTOXBase::GetType() ) )
+ UpdateSequence( pOwnChapterNode, pLayout );
+
+ if( GetCreateType() & SwTOXElement::Frame )
+ UpdateContent( SwTOXElement::Frame, pOwnChapterNode, pLayout );
+
+ if(TOX_AUTHORITIES == SwTOXBase::GetType())
+ UpdateAuthorities( aIntl, pLayout );
+
+ // Insert AlphaDelimiters if needed (just for keywords)
+ if( TOX_INDEX == SwTOXBase::GetType() &&
+ ( GetOptions() & SwTOIOptions::AlphaDelimiter ) )
+ InsertAlphaDelimiter( aIntl );
+
+ // remove old content an insert one empty textnode (to hold the layout!)
+ SwTextNode* pFirstEmptyNd;
+
+ SwUndoUpdateIndex * pUndo(nullptr);
+ {
+ rDoc.getIDocumentRedlineAccess().DeleteRedline( *pSectNd, true, RedlineType::Any );
+
+ SwNodeIndex aSttIdx( *pSectNd, +1 );
+ SwNodeIndex aEndIdx( *pSectNd->EndOfSectionNode() );
+ pFirstEmptyNd = rDoc.GetNodes().MakeTextNode( aEndIdx.GetNode(),
+ rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_TEXT ) );
+
+ {
+ // Task 70995 - save and restore PageDesc and Break Attributes
+ SwNodeIndex aNxtIdx( aSttIdx );
+ const SwContentNode* pCNd = aNxtIdx.GetNode().GetContentNode();
+ if( !pCNd )
+ pCNd = rDoc.GetNodes().GoNext( &aNxtIdx );
+ assert(pCNd != pFirstEmptyNd);
+ assert(pCNd->GetIndex() < pFirstEmptyNd->GetIndex());
+ if( pCNd->HasSwAttrSet() )
+ {
+ SfxItemSet aBrkSet( rDoc.GetAttrPool(), aBreakSetRange );
+ aBrkSet.Put( *pCNd->GetpSwAttrSet() );
+ if( aBrkSet.Count() )
+ pFirstEmptyNd->SetAttr( aBrkSet );
+ }
+ }
+
+ if (rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ // note: this will first append a SwUndoDelSection from the ctor...
+ pUndo = new SwUndoUpdateIndex(*this);
+ // tdf#123313 insert Undo *after* all CrossRefBookmark Undos have
+ // been inserted by the Update*() functions
+ rDoc.GetIDocumentUndoRedo().AppendUndo(std::unique_ptr<SwUndoUpdateIndex>(pUndo));
+ }
+ else
+ {
+ --aEndIdx;
+ SwPosition aPos( aEndIdx, pFirstEmptyNd, 0 );
+ SwDoc::CorrAbs( aSttIdx, aEndIdx, aPos, true );
+
+ // delete flys in whole range including start node which requires
+ // giving the node before start node as Mark parameter, hence -1.
+ // (flys must be deleted because the anchor nodes are removed)
+ DelFlyInRange( SwNodeIndex(aSttIdx, -1).GetNode(), aEndIdx.GetNode() );
+
+ rDoc.GetNodes().Delete( aSttIdx, aEndIdx.GetIndex() - aSttIdx.GetIndex() );
+ }
+ }
+
+ // insert title of TOX
+ if ( !GetTitle().isEmpty() )
+ {
+ // then insert the headline section
+ SwNodeIndex aIdx( *pSectNd, +1 );
+
+ SwTextNode* pHeadNd = rDoc.GetNodes().MakeTextNode( aIdx.GetNode(),
+ GetTextFormatColl( FORM_TITLE ) );
+ pHeadNd->InsertText( GetTitle(), SwContentIndex( pHeadNd ) );
+
+ SwSectionData headerData( SectionType::ToxHeader, GetTOXName()+"_Head" );
+
+ --aIdx;
+ SwSectionFormat* pSectFormat = rDoc.MakeSectionFormat();
+ rDoc.GetNodes().InsertTextSection(
+ *pHeadNd, *pSectFormat, headerData, nullptr, &aIdx.GetNode(), true, false);
+
+ if (pUndo)
+ {
+ pUndo->TitleSectionInserted(*pSectFormat);
+ }
+ }
+
+ // Sort the List of all TOC Marks and TOC Sections
+ std::vector<SwTextFormatColl*> aCollArr( GetTOXForm().GetFormMax(), nullptr );
+ std::unordered_map<OUString, int> markURLs;
+ SwNodeIndex aInsPos( *pFirstEmptyNd, 1 );
+ for( size_t nCnt = 0; nCnt < m_aSortArr.size(); ++nCnt )
+ {
+ ::SetProgressState( 0, rDoc.GetDocShell() );
+
+ // Put the Text into the TOC
+ sal_uInt16 nLvl = m_aSortArr[ nCnt ]->GetLevel();
+ SwTextFormatColl* pColl = aCollArr[ nLvl ];
+ if( !pColl )
+ {
+ pColl = GetTextFormatColl( nLvl );
+ aCollArr[ nLvl ] = pColl;
+ }
+
+ // Generate: Set dynamic TabStops
+ SwTextNode* pTOXNd = rDoc.GetNodes().MakeTextNode( aInsPos.GetNode() , pColl );
+ m_aSortArr[ nCnt ]->pTOXNd = pTOXNd;
+
+ // Generate: Evaluate Form and insert the place holder for the
+ // page number. If it is a TOX_INDEX and the SwForm IsCommaSeparated()
+ // then a range of entries must be generated into one paragraph
+ size_t nRange = 1;
+ if(TOX_INDEX == SwTOXBase::GetType() &&
+ GetTOXForm().IsCommaSeparated() &&
+ m_aSortArr[nCnt]->GetType() == TOX_SORT_INDEX)
+ {
+ const SwTOXMark& rMark = m_aSortArr[nCnt]->pTextMark->GetTOXMark();
+ const OUString& sPrimKey = rMark.GetPrimaryKey();
+ const OUString& sSecKey = rMark.GetSecondaryKey();
+ const SwTOXMark* pNextMark = nullptr;
+ while(m_aSortArr.size() > (nCnt + nRange) &&
+ m_aSortArr[nCnt + nRange]->GetType() == TOX_SORT_INDEX )
+ {
+ pNextMark = &(m_aSortArr[nCnt + nRange]->pTextMark->GetTOXMark());
+ if( !pNextMark ||
+ pNextMark->GetPrimaryKey() != sPrimKey ||
+ pNextMark->GetSecondaryKey() != sSecKey)
+ break;
+ nRange++;
+ }
+ }
+ // pass node index of table-of-content section and default page description
+ // to method <GenerateText(..)>.
+ ::SetProgressState( 0, rDoc.GetDocShell() );
+
+ std::shared_ptr<sw::ToxTabStopTokenHandler> tabStopTokenHandler =
+ std::make_shared<sw::DefaultToxTabStopTokenHandler>(
+ pSectNd->GetIndex(), *pDefaultPageDesc, GetTOXForm().IsRelTabPos(),
+ rDoc.GetDocumentSettingManager().get(DocumentSettingId::TABS_RELATIVE_TO_INDENT) ?
+ sw::DefaultToxTabStopTokenHandler::TABSTOPS_RELATIVE_TO_INDENT :
+ sw::DefaultToxTabStopTokenHandler::TABSTOPS_RELATIVE_TO_PAGE);
+ sw::ToxTextGenerator ttgn(GetTOXForm(), tabStopTokenHandler);
+ ttgn.GenerateText(GetFormat()->GetDoc(), markURLs, m_aSortArr, nCnt, nRange, pLayout);
+ nCnt += nRange - 1;
+ }
+
+ // delete the first dummy node and remove all Cursor into the previous node
+ aInsPos = *pFirstEmptyNd;
+ {
+ SwPaM aCorPam( *pFirstEmptyNd );
+ if( !aCorPam.Move( fnMoveForward ) )
+ aCorPam.Move( fnMoveBackward );
+ SwNodeIndex aEndIdx( aInsPos, 1 );
+ SwDoc::CorrAbs( aInsPos, aEndIdx, *aCorPam.GetPoint(), true );
+
+ // Task 70995 - save and restore PageDesc and Break Attributes
+ if( pFirstEmptyNd->HasSwAttrSet() )
+ {
+ if( !GetTitle().isEmpty() )
+ aEndIdx = *pSectNd;
+ else
+ aEndIdx = *pFirstEmptyNd;
+ SwContentNode* pCNd = rDoc.GetNodes().GoNext( &aEndIdx );
+ if( pCNd ) // Robust against defect documents, e.g. i60336
+ pCNd->SetAttr( *pFirstEmptyNd->GetpSwAttrSet() );
+ }
+ }
+
+ // now create the new Frames
+ SwNodeOffset nIdx = pSectNd->GetIndex();
+ // don't delete if index is empty
+ if(nIdx + SwNodeOffset(2) < pSectNd->EndOfSectionIndex())
+ rDoc.GetNodes().Delete( aInsPos );
+
+ aN2L.RestoreUpperFrames( rDoc.GetNodes(), nIdx, nIdx + 1 );
+ o3tl::sorted_vector<SwRootFrame*> aAllLayouts = rDoc.GetAllLayouts();
+ for ( const auto& rpLayout : aAllLayouts )
+ {
+ SwFrame::CheckPageDescs( static_cast<SwPageFrame*>(rpLayout->Lower()) );
+ }
+
+ SetProtect( SwTOXBase::IsProtected() );
+}
+
+void SwTOXBaseSection::InsertAlphaDelimiter( const SwTOXInternational& rIntl )
+{
+ SwDoc* pDoc = GetFormat()->GetDoc();
+ OUString sLastDeli;
+ size_t i = 0;
+ while( i < m_aSortArr.size() )
+ {
+ ::SetProgressState( 0, pDoc->GetDocShell() );
+
+ sal_uInt16 nLevel = m_aSortArr[i]->GetLevel();
+
+ // Skip AlphaDelimiter
+ if( nLevel == FORM_ALPHA_DELIMITER )
+ continue;
+
+ const OUString sDeli = rIntl.GetIndexKey( m_aSortArr[i]->GetText(),
+ m_aSortArr[i]->GetLocale() );
+
+ // Do we already have a Delimiter?
+ if( !sDeli.isEmpty() && sLastDeli != sDeli )
+ {
+ // We skip all that are less than a small Blank (these are special characters)
+ if( ' ' <= sDeli[0] )
+ {
+ std::unique_ptr<SwTOXCustom> pCst(
+ MakeSwTOXSortTabBase<SwTOXCustom>(nullptr,
+ TextAndReading(sDeli, OUString()),
+ FORM_ALPHA_DELIMITER,
+ rIntl, m_aSortArr[i]->GetLocale() ));
+ m_aSortArr.insert( m_aSortArr.begin() + i, std::move(pCst));
+ i++;
+ }
+ sLastDeli = sDeli;
+ }
+
+ // Skip until we get to the same or a lower Level
+ do {
+ i++;
+ } while (i < m_aSortArr.size() && m_aSortArr[i]->GetLevel() > nLevel);
+ }
+}
+
+/// Evaluate Template
+SwTextFormatColl* SwTOXBaseSection::GetTextFormatColl( sal_uInt16 nLevel )
+{
+ SwDoc* pDoc = GetFormat()->GetDoc();
+ const OUString& rName = GetTOXForm().GetTemplate( nLevel );
+ SwTextFormatColl* pColl = !rName.isEmpty() ? pDoc->FindTextFormatCollByName(rName) :nullptr;
+ if( !pColl )
+ {
+ sal_uInt16 nPoolFormat = 0;
+ const TOXTypes eMyType = SwTOXBase::GetType();
+ switch( eMyType )
+ {
+ case TOX_INDEX: nPoolFormat = RES_POOLCOLL_TOX_IDXH; break;
+ case TOX_USER:
+ if( nLevel < 6 )
+ nPoolFormat = RES_POOLCOLL_TOX_USERH;
+ else
+ nPoolFormat = RES_POOLCOLL_TOX_USER6 - 6;
+ break;
+ case TOX_ILLUSTRATIONS: nPoolFormat = RES_POOLCOLL_TOX_ILLUSH; break;
+ case TOX_OBJECTS: nPoolFormat = RES_POOLCOLL_TOX_OBJECTH; break;
+ case TOX_TABLES: nPoolFormat = RES_POOLCOLL_TOX_TABLESH; break;
+ case TOX_AUTHORITIES:
+ case TOX_BIBLIOGRAPHY:
+ nPoolFormat = RES_POOLCOLL_TOX_AUTHORITIESH; break;
+ case TOX_CITATION: /** TODO */break;
+ case TOX_CONTENT:
+ // There's a jump in the ContentArea!
+ if( nLevel < 6 )
+ nPoolFormat = RES_POOLCOLL_TOX_CNTNTH;
+ else
+ nPoolFormat = RES_POOLCOLL_TOX_CNTNT6 - 6;
+ break;
+ }
+
+ if(eMyType == TOX_AUTHORITIES && nLevel)
+ nPoolFormat = nPoolFormat + 1;
+ else if(eMyType == TOX_INDEX && nLevel)
+ {
+ // pool: Level 1,2,3, Delimiter
+ // SwForm: Delimiter, Level 1,2,3
+ nPoolFormat += 1 == nLevel ? nLevel + 3 : nLevel - 1;
+ }
+ else
+ nPoolFormat = nPoolFormat + nLevel;
+ pColl = pDoc->getIDocumentStylePoolAccess().GetTextCollFromPool( nPoolFormat );
+ }
+ return pColl;
+}
+
+void SwTOXBaseSection::SwClientNotify(const SwModify& rModify, const SfxHint& rHint)
+{
+ if (auto pFindHint = dynamic_cast<const sw::FindContentFrameHint*>(&rHint))
+ {
+ if(pFindHint->m_rpContentFrame)
+ return;
+ auto pSectFormat = GetFormat();
+ if(!pSectFormat)
+ return;
+ const SwSectionNode* pSectNd = pSectFormat->GetSectionNode();
+ if(!pSectNd)
+ return;
+ SwNodeIndex aIdx(*pSectNd, 1);
+ SwContentNode* pCNd = aIdx.GetNode().GetContentNode();
+ if(!pCNd)
+ pCNd = pFindHint->m_rDoc.GetNodes().GoNext(&aIdx);
+ if(!pCNd)
+ return;
+ if(pCNd->EndOfSectionIndex() >= pSectNd->EndOfSectionIndex())
+ return;
+ pFindHint->m_rpContentFrame = pCNd->getLayoutFrame(&pFindHint->m_rLayout);
+ } else
+ SwTOXBase::SwClientNotify(rModify, rHint);
+}
+
+/// Create from Marks
+void SwTOXBaseSection::UpdateMarks(const SwTOXInternational& rIntl,
+ const SwTextNode* pOwnChapterNode,
+ SwRootFrame const*const pLayout)
+{
+ const auto pType = static_cast<SwTOXType*>(SwTOXBase::GetRegisteredIn());
+ auto pShell = GetFormat()->GetDoc()->GetDocShell();
+ const TOXTypes eTOXTyp = GetTOXType()->GetType();
+ std::vector<std::reference_wrapper<SwTextTOXMark>> vMarks;
+ pType->CollectTextTOXMarksForLayout(vMarks, pLayout);
+ for(auto& rMark: vMarks)
+ {
+ ::SetProgressState(0, pShell);
+ auto& rNode = rMark.get().GetTextNode();
+ if(IsFromChapter() && !IsHeadingContained(pOwnChapterNode, rNode))
+ continue;
+ auto rTOXMark = rMark.get().GetTOXMark();
+ if(TOX_INDEX == eTOXTyp)
+ {
+ // index entry mark
+ assert(g_pBreakIt);
+ lang::Locale aLocale = g_pBreakIt->GetLocale(rNode.GetLang(rMark.get().GetStart()));
+ InsertSorted(MakeSwTOXSortTabBase<SwTOXIndex>(pLayout, rNode, &rMark.get(), GetOptions(), FORM_ENTRY, rIntl, aLocale));
+ if(GetOptions() & SwTOIOptions::KeyAsEntry && !rTOXMark.GetPrimaryKey().isEmpty())
+ {
+ InsertSorted(MakeSwTOXSortTabBase<SwTOXIndex>(pLayout, rNode, &rMark.get(), GetOptions(), FORM_PRIMARY_KEY, rIntl, aLocale));
+ if (!rTOXMark.GetSecondaryKey().isEmpty())
+ {
+ InsertSorted(MakeSwTOXSortTabBase<SwTOXIndex>(pLayout, rNode, &rMark.get(), GetOptions(), FORM_SECONDARY_KEY, rIntl, aLocale));
+ }
+ }
+ }
+ else if(TOX_USER == eTOXTyp || rTOXMark.GetLevel() <= GetLevel())
+ { // table of content mark, also used for user marks
+ InsertSorted(MakeSwTOXSortTabBase<SwTOXContent>(pLayout, rNode, &rMark.get(), rIntl));
+ }
+ }
+}
+
+/// Generate table of contents from outline
+void SwTOXBaseSection::UpdateOutline( const SwTextNode* pOwnChapterNode,
+ SwRootFrame const*const pLayout)
+{
+ SwDoc* pDoc = GetFormat()->GetDoc();
+ SwNodes& rNds = pDoc->GetNodes();
+
+ const SwOutlineNodes& rOutlNds = rNds.GetOutLineNds();
+ for( auto pOutlineNode : rOutlNds )
+ {
+ ::SetProgressState( 0, pDoc->GetDocShell() );
+ SwTextNode* pTextNd = pOutlineNode->GetTextNode();
+ if( pTextNd && pTextNd->Len() && pTextNd->HasWriterListeners() &&
+ o3tl::make_unsigned( pTextNd->GetAttrOutlineLevel()) <= GetLevel() &&
+ pTextNd->getLayoutFrame(pLayout) &&
+ !pTextNd->IsHiddenByParaField() &&
+ !pTextNd->HasHiddenCharAttribute( true ) &&
+ (!pLayout || !pLayout->HasMergedParas()
+ || static_cast<SwTextFrame*>(pTextNd->getLayoutFrame(pLayout))->GetTextNodeForParaProps() == pTextNd) &&
+ ( !IsFromChapter() || IsHeadingContained(pOwnChapterNode, *pTextNd) ))
+ {
+ InsertSorted(MakeSwTOXSortTabBase<SwTOXPara>(pLayout, *pTextNd, SwTOXElement::OutlineLevel));
+ }
+ }
+}
+
+/// Generate table of contents from template areas
+void SwTOXBaseSection::UpdateTemplate(const SwTextNode* pOwnChapterNode,
+ SwRootFrame const*const pLayout)
+{
+ SwDoc* pDoc = GetFormat()->GetDoc();
+ for(sal_uInt16 i = 0; i < MAXLEVEL; i++)
+ {
+ const OUString sTmpStyleNames = GetStyleNames(i);
+ if (sTmpStyleNames.isEmpty())
+ continue;
+
+ sal_Int32 nIndex = 0;
+ while (nIndex >= 0)
+ {
+ SwTextFormatColl* pColl = pDoc->FindTextFormatCollByName(
+ sTmpStyleNames.getToken( 0, TOX_STYLE_DELIMITER, nIndex ));
+ //TODO: no outline Collections in content indexes if OutlineLevels are already included
+ if( !pColl ||
+ ( TOX_CONTENT == SwTOXBase::GetType() &&
+ GetCreateType() & SwTOXElement::OutlineLevel &&
+ pColl->IsAssignedToListLevelOfOutlineStyle()) )
+ continue;
+
+ SwIterator<SwTextNode,SwFormatColl> aIter( *pColl );
+ for( SwTextNode* pTextNd = aIter.First(); pTextNd; pTextNd = aIter.Next() )
+ {
+ ::SetProgressState( 0, pDoc->GetDocShell() );
+
+ if (pTextNd->GetText().getLength() &&
+ pTextNd->getLayoutFrame(pLayout) &&
+ pTextNd->GetNodes().IsDocNodes() &&
+ // tdf#40142 - consider level settings of the various text nodes
+ o3tl::make_unsigned(pTextNd->GetAttrOutlineLevel()) <= GetLevel() &&
+ (!pLayout || !pLayout->HasMergedParas()
+ || static_cast<SwTextFrame*>(pTextNd->getLayoutFrame(pLayout))->GetTextNodeForParaProps() == pTextNd) &&
+ (!IsFromChapter() || IsHeadingContained(pOwnChapterNode, *pTextNd)))
+ {
+ InsertSorted(MakeSwTOXSortTabBase<SwTOXPara>(pLayout, *pTextNd, SwTOXElement::Template, i + 1));
+ }
+ }
+ }
+ }
+}
+
+/// Generate content from sequence fields
+void SwTOXBaseSection::UpdateSequence(const SwTextNode* pOwnChapterNode,
+ SwRootFrame const*const pLayout)
+{
+ SwDoc* pDoc = GetFormat()->GetDoc();
+ SwFieldType* pSeqField = pDoc->getIDocumentFieldsAccess().GetFieldType(SwFieldIds::SetExp, GetSequenceName(), false);
+ if(!pSeqField)
+ return;
+
+ std::vector<SwFormatField*> vFields;
+ pSeqField->GatherFields(vFields);
+ for(auto pFormatField: vFields)
+ {
+ const SwTextField* pTextField = pFormatField->GetTextField();
+ SwTextNode& rTextNode = pTextField->GetTextNode();
+ ::SetProgressState( 0, pDoc->GetDocShell() );
+
+ if (rTextNode.GetText().getLength() &&
+ rTextNode.getLayoutFrame(pLayout) &&
+ ( !IsFromChapter() || IsHeadingContained(pOwnChapterNode, rTextNode))
+ && (!pLayout || !pLayout->IsHideRedlines()
+ || !sw::IsFieldDeletedInModel(pDoc->getIDocumentRedlineAccess(), *pTextField)))
+ {
+ const SwSetExpField& rSeqField = dynamic_cast<const SwSetExpField&>(*(pFormatField->GetField()));
+ const OUString sName = GetSequenceName()
+ + OUStringChar(cSequenceMarkSeparator)
+ + OUString::number( rSeqField.GetSeqNumber() );
+ std::unique_ptr<SwTOXPara> pNew(new SwTOXPara( rTextNode, SwTOXElement::Sequence, 1, sName ));
+ // set indexes if the number or the reference text are to be displayed
+ if( GetCaptionDisplay() == CAPTION_TEXT )
+ {
+ pNew->SetStartIndex(
+ SwGetExpField::GetReferenceTextPos( *pFormatField, *pDoc ));
+ }
+ else if(GetCaptionDisplay() == CAPTION_NUMBER)
+ {
+ pNew->SetEndIndex(pTextField->GetStart() + 1);
+ }
+ pNew->InitText(pLayout);
+ InsertSorted(std::move(pNew));
+ }
+ }
+}
+
+void SwTOXBaseSection::UpdateAuthorities(const SwTOXInternational& rIntl,
+ SwRootFrame const*const pLayout)
+{
+ SwDoc* pDoc = GetFormat()->GetDoc();
+ SwFieldType* pAuthField = pDoc->getIDocumentFieldsAccess().GetFieldType(SwFieldIds::TableOfAuthorities, OUString(), false);
+ if(!pAuthField)
+ return;
+
+ std::vector<SwFormatField*> vFields;
+ pAuthField->GatherFields(vFields);
+ for(auto pFormatField: vFields)
+ {
+ const auto pTextField = pFormatField->GetTextField();
+ const SwTextNode& rTextNode = pFormatField->GetTextField()->GetTextNode();
+ ::SetProgressState( 0, pDoc->GetDocShell() );
+
+ if (rTextNode.GetText().getLength() &&
+ rTextNode.getLayoutFrame(pLayout) &&
+ (!pLayout || !pLayout->IsHideRedlines()
+ || !sw::IsFieldDeletedInModel(pDoc->getIDocumentRedlineAccess(), *pTextField)))
+ {
+ //#106485# the body node has to be used!
+ SwContentFrame *const pFrame = rTextNode.getLayoutFrame(pLayout);
+ SwPosition aFieldPos(rTextNode);
+ const SwTextNode* pTextNode = nullptr;
+ if(pFrame && !pFrame->IsInDocBody())
+ pTextNode = GetBodyTextNode( *pDoc, aFieldPos, *pFrame );
+ if(!pTextNode)
+ pTextNode = &rTextNode;
+
+ InsertSorted(MakeSwTOXSortTabBase<SwTOXAuthority>(pLayout, *pTextNode, *pFormatField, rIntl));
+ }
+ }
+}
+
+static SwTOOElements lcl_IsSOObject( const SvGlobalName& rFactoryNm )
+{
+ static const struct SoObjType {
+ SwTOOElements nFlag;
+ // GlobalNameId
+ struct {
+ sal_uInt32 n1;
+ sal_uInt16 n2, n3;
+ sal_uInt8 b8, b9, b10, b11, b12, b13, b14, b15;
+ } aGlNmIds[4];
+ } aArr[] = {
+ { SwTOOElements::Math,
+ { {SO3_SM_CLASSID_60},{SO3_SM_CLASSID_50},
+ {SO3_SM_CLASSID_40},{SO3_SM_CLASSID_30} } },
+ { SwTOOElements::Chart,
+ { {SO3_SCH_CLASSID_60},{SO3_SCH_CLASSID_50},
+ {SO3_SCH_CLASSID_40},{SO3_SCH_CLASSID_30} } },
+ { SwTOOElements::Calc,
+ { {SO3_SC_CLASSID_60},{SO3_SC_CLASSID_50},
+ {SO3_SC_CLASSID_40},{SO3_SC_CLASSID_30} } },
+ { SwTOOElements::DrawImpress,
+ { {SO3_SIMPRESS_CLASSID_60},{SO3_SIMPRESS_CLASSID_50},
+ {SO3_SIMPRESS_CLASSID_40},{SO3_SIMPRESS_CLASSID_30} } },
+ { SwTOOElements::DrawImpress,
+ { {SO3_SDRAW_CLASSID_60},{SO3_SDRAW_CLASSID_50} } }
+ };
+
+ for( SoObjType const & rArr : aArr )
+ for (auto & rId : rArr.aGlNmIds)
+ {
+ if( !rId.n1 )
+ break;
+ SvGlobalName aGlbNm( rId.n1, rId.n2, rId.n3,
+ rId.b8, rId.b9, rId.b10, rId.b11,
+ rId.b12, rId.b13, rId.b14, rId.b15 );
+ if( rFactoryNm == aGlbNm )
+ {
+ return rArr.nFlag;
+ }
+ }
+
+ return SwTOOElements::NONE;
+}
+
+void SwTOXBaseSection::UpdateContent( SwTOXElement eMyType,
+ const SwTextNode* pOwnChapterNode,
+ SwRootFrame const*const pLayout)
+{
+ SwDoc* pDoc = GetFormat()->GetDoc();
+ SwNodes& rNds = pDoc->GetNodes();
+ // on the 1st Node of the 1st Section
+ SwNodeOffset nIdx = rNds.GetEndOfAutotext().StartOfSectionIndex() + SwNodeOffset(2),
+ nEndIdx = rNds.GetEndOfAutotext().GetIndex();
+
+ while( nIdx < nEndIdx )
+ {
+ ::SetProgressState( 0, pDoc->GetDocShell() );
+
+ SwNode* pNd = rNds[ nIdx ];
+ SwContentNode* pCNd = nullptr;
+ switch( eMyType )
+ {
+ case SwTOXElement::Frame:
+ if( !pNd->IsNoTextNode() )
+ {
+ pCNd = pNd->GetContentNode();
+ if( !pCNd )
+ {
+ SwNodeIndex aTmp( *pNd );
+ pCNd = rNds.GoNext( &aTmp );
+ }
+ }
+ break;
+ case SwTOXElement::Graphic:
+ if( pNd->IsGrfNode() )
+ pCNd = static_cast<SwContentNode*>(pNd);
+ break;
+ case SwTOXElement::Ole:
+ if( pNd->IsOLENode() )
+ {
+ bool bInclude = true;
+ if(TOX_OBJECTS == SwTOXBase::GetType())
+ {
+ SwOLENode* pOLENode = pNd->GetOLENode();
+ SwTOOElements nMyOLEOptions = GetOLEOptions();
+ SwOLEObj& rOLEObj = pOLENode->GetOLEObj();
+
+ if( rOLEObj.IsOleRef() ) // Not yet loaded
+ {
+ SvGlobalName aTmpName( rOLEObj.GetOleRef()->getClassID() );
+ SwTOOElements nObj = ::lcl_IsSOObject( aTmpName );
+ bInclude = ( (nMyOLEOptions & SwTOOElements::Other) && SwTOOElements::NONE == nObj )
+ || (nMyOLEOptions & nObj);
+ }
+ else
+ {
+ OSL_FAIL("OLE Object no loaded?");
+ bInclude = false;
+ }
+ }
+
+ if(bInclude)
+ pCNd = static_cast<SwContentNode*>(pNd);
+ }
+ break;
+ default: break;
+ }
+
+ if( pCNd )
+ {
+ // find node in body text
+ int nSetLevel = USHRT_MAX;
+
+ //#111105# tables of tables|illustrations|objects don't support hierarchies
+ if( IsLevelFromChapter() &&
+ TOX_TABLES != SwTOXBase::GetType() &&
+ TOX_ILLUSTRATIONS != SwTOXBase::GetType() &&
+ TOX_OBJECTS != SwTOXBase::GetType() )
+ {
+ const SwTextNode* pOutlNd = ::lcl_FindChapterNode( *pCNd,
+ pLayout, MAXLEVEL - 1);
+ if( pOutlNd )
+ {
+ if( pOutlNd->GetTextColl()->IsAssignedToListLevelOfOutlineStyle())
+ {
+ nSetLevel = pOutlNd->GetTextColl()->GetAttrOutlineLevel();
+ }
+ }
+ }
+
+ if (pCNd->getLayoutFrame(pLayout)
+ && (!pLayout || !pLayout->HasMergedParas()
+ || pCNd->GetRedlineMergeFlag() != SwNode::Merge::Hidden)
+ && ( !IsFromChapter() || IsHeadingContained(pOwnChapterNode, *pCNd)))
+ {
+ std::unique_ptr<SwTOXPara> pNew( MakeSwTOXSortTabBase<SwTOXPara>(
+ pLayout, *pCNd, eMyType,
+ ( USHRT_MAX != nSetLevel )
+ ? o3tl::narrowing<sal_uInt16>(nSetLevel)
+ : FORM_ALPHA_DELIMITER ) );
+ InsertSorted( std::move(pNew) );
+ }
+ }
+
+ nIdx = pNd->StartOfSectionNode()->EndOfSectionIndex() + SwNodeOffset(2); // 2 == End/Start Node
+ }
+}
+
+/// Collect table entries
+void SwTOXBaseSection::UpdateTable(const SwTextNode* pOwnChapterNode,
+ SwRootFrame const*const pLayout)
+{
+ SwDoc* pDoc = GetFormat()->GetDoc();
+ SwNodes& rNds = pDoc->GetNodes();
+
+ for(SwTableFormat* pFrameFormat: *pDoc->GetTableFrameFormats())
+ {
+ ::SetProgressState( 0, pDoc->GetDocShell() );
+
+ SwTable* pTmpTable = SwTable::FindTable( pFrameFormat );
+ SwTableBox* pFBox;
+ if( pTmpTable && nullptr != (pFBox = pTmpTable->GetTabSortBoxes()[0] ) &&
+ pFBox->GetSttNd() && pFBox->GetSttNd()->GetNodes().IsDocNodes() )
+ {
+ const SwTableNode* pTableNd = pFBox->GetSttNd()->FindTableNode();
+ SwNodeIndex aContentIdx( *pTableNd, 1 );
+
+ SwContentNode* pCNd;
+ while( nullptr != ( pCNd = rNds.GoNext( &aContentIdx ) ) &&
+ aContentIdx.GetIndex() < pTableNd->EndOfSectionIndex() )
+ {
+ if (pCNd->getLayoutFrame(pLayout)
+ && (!pLayout || !pLayout->HasMergedParas()
+ || pCNd->GetRedlineMergeFlag() != SwNode::Merge::Hidden)
+ && (!IsFromChapter() || IsHeadingContained(pOwnChapterNode, *pCNd)))
+ {
+ std::unique_ptr<SwTOXTable> pNew(new SwTOXTable( *pCNd ));
+ if( IsLevelFromChapter() && TOX_TABLES != SwTOXBase::GetType())
+ {
+ const SwTextNode* pOutlNd =
+ ::lcl_FindChapterNode(*pCNd, pLayout, MAXLEVEL - 1);
+ if( pOutlNd )
+ {
+ if( pOutlNd->GetTextColl()->IsAssignedToListLevelOfOutlineStyle())
+ {
+ const int nTmp = pOutlNd->GetTextColl()->GetAttrOutlineLevel();
+ pNew->SetLevel(o3tl::narrowing<sal_uInt16>(nTmp));
+ }
+ }
+ }
+ pNew->InitText(pLayout);
+ InsertSorted(std::move(pNew));
+ break;
+ }
+ }
+ }
+ }
+}
+
+/// Calculate PageNumber and insert after formatting
+void SwTOXBaseSection::UpdatePageNum()
+{
+ if( m_aSortArr.empty() )
+ return ;
+
+ // Insert the current PageNumber into the TOC
+ SwPageFrame* pCurrentPage = nullptr;
+ sal_uInt16 nPage = 0;
+ SwDoc* pDoc = GetFormat()->GetDoc();
+
+ SwTOXInternational aIntl( GetLanguage(),
+ TOX_INDEX == GetTOXType()->GetType() ?
+ GetOptions() : SwTOIOptions::NONE,
+ GetSortAlgorithm() );
+
+ for( size_t nCnt = 0; nCnt < m_aSortArr.size(); ++nCnt )
+ {
+ // Loop over all SourceNodes
+
+ // process run in lines
+ size_t nRange = 0;
+ if(GetTOXForm().IsCommaSeparated() &&
+ m_aSortArr[nCnt]->GetType() == TOX_SORT_INDEX)
+ {
+ const SwTOXMark& rMark = m_aSortArr[nCnt]->pTextMark->GetTOXMark();
+ const OUString& sPrimKey = rMark.GetPrimaryKey();
+ const OUString& sSecKey = rMark.GetSecondaryKey();
+ const SwTOXMark* pNextMark = nullptr;
+ while(m_aSortArr.size() > (nCnt + nRange)&&
+ m_aSortArr[nCnt + nRange]->GetType() == TOX_SORT_INDEX &&
+ nullptr != (pNextMark = &(m_aSortArr[nCnt + nRange]->pTextMark->GetTOXMark())) &&
+ pNextMark->GetPrimaryKey() == sPrimKey &&
+ pNextMark->GetSecondaryKey() == sSecKey)
+ nRange++;
+ }
+ else
+ nRange = 1;
+
+ for(size_t nRunInEntry = nCnt; nRunInEntry < nCnt + nRange; ++nRunInEntry)
+ {
+ std::vector<sal_uInt16> aNums; // the PageNumber
+ std::vector<SwPageDesc*> aDescs; // The PageDescriptors matching the PageNumbers
+ std::vector<sal_uInt16> aMainNums; // contains page numbers of main entries
+ SwTOXSortTabBase* pSortBase = m_aSortArr[nRunInEntry].get();
+ size_t nSize = pSortBase->aTOXSources.size();
+ for (size_t j = 0; j < nSize; ++j)
+ {
+ ::SetProgressState( 0, pDoc->GetDocShell() );
+
+ SwTOXSource& rTOXSource = pSortBase->aTOXSources[j];
+ if( rTOXSource.pNd )
+ {
+ SwContentFrame* pFrame = rTOXSource.pNd->getLayoutFrame( pDoc->getIDocumentLayoutAccess().GetCurrentLayout() );
+ OSL_ENSURE( pFrame || pDoc->IsUpdateTOX(), "TOX, no Frame found");
+ if( !pFrame )
+ continue;
+ if( pFrame->IsTextFrame() && static_cast<SwTextFrame*>(pFrame)->HasFollow() )
+ {
+ // find the right one
+ SwTextFrame* pNext;
+ TextFrameIndex const nPos(static_cast<SwTextFrame*>(pFrame)
+ ->MapModelToView(static_cast<SwTextNode const*>(rTOXSource.pNd),
+ rTOXSource.nPos));
+ for (;;)
+ {
+ pNext = static_cast<SwTextFrame*>(pFrame->GetFollow());
+ if (!pNext || nPos < pNext->GetOffset())
+ break;
+ pFrame = pNext;
+ }
+ }
+
+ SwPageFrame* pTmpPage = pFrame->FindPageFrame();
+ if( pTmpPage != pCurrentPage )
+ {
+ nPage = pTmpPage->GetVirtPageNum();
+ pCurrentPage = pTmpPage;
+ }
+
+ // Insert as sorted
+ std::vector<sal_uInt16>::size_type i;
+ for( i = 0; i < aNums.size() && aNums[i] < nPage; ++i )
+ ;
+
+ if( i >= aNums.size() || aNums[ i ] != nPage )
+ {
+ aNums.insert(aNums.begin() + i, nPage);
+ aDescs.insert(aDescs.begin() + i, pCurrentPage->GetPageDesc() );
+ }
+ // is it a main entry?
+ if(TOX_SORT_INDEX == pSortBase->GetType() &&
+ rTOXSource.bMainEntry)
+ {
+ aMainNums.push_back(nPage);
+ }
+ }
+ }
+ // Insert the PageNumber into the TOC TextNode
+ const SwTOXSortTabBase* pBase = m_aSortArr[ nCnt ].get();
+ if(pBase->pTOXNd)
+ {
+ const SwTextNode* pTextNd = pBase->pTOXNd->GetTextNode();
+ OSL_ENSURE( pTextNd, "no TextNode, wrong TOC" );
+
+ UpdatePageNum_( const_cast<SwTextNode*>(pTextNd), aNums, aDescs, &aMainNums,
+ aIntl );
+ }
+ }
+ }
+ // Delete the mapping array after setting the right PageNumber
+ m_aSortArr.clear();
+}
+
+/// Replace the PageNumber place holders. Search for the page no. in the array
+/// of main entry page numbers.
+static bool lcl_HasMainEntry( const std::vector<sal_uInt16>* pMainEntryNums, sal_uInt16 nToFind )
+{
+ if (!pMainEntryNums)
+ return false;
+
+ for( auto nMainEntry : *pMainEntryNums )
+ if (nToFind == nMainEntry)
+ return true;
+ return false;
+}
+
+void SwTOXBaseSection::UpdatePageNum_( SwTextNode* pNd,
+ const std::vector<sal_uInt16>& rNums,
+ const std::vector<SwPageDesc*>& rDescs,
+ const std::vector<sal_uInt16>* pMainEntryNums,
+ const SwTOXInternational& rIntl )
+{
+ // collect starts end ends of main entry character style
+ std::optional< std::vector<sal_uInt16> > xCharStyleIdx;
+ if (pMainEntryNums)
+ xCharStyleIdx.emplace();
+
+ OUString sSrchStr
+ = OUStringChar(C_NUM_REPL) + SwTOXMark::S_PAGE_DELI + OUStringChar(C_NUM_REPL);
+ sal_Int32 nStartPos = pNd->GetText().indexOf(sSrchStr);
+ sSrchStr = OUStringChar(C_NUM_REPL) + OUStringChar(C_END_PAGE_NUM);
+ sal_Int32 nEndPos = pNd->GetText().indexOf(sSrchStr);
+
+ if (-1 == nEndPos || rNums.empty())
+ return;
+
+ if (-1 == nStartPos || nStartPos > nEndPos)
+ nStartPos = nEndPos;
+
+ sal_uInt16 nOld = rNums[0],
+ nBeg = nOld,
+ nCount = 0;
+ OUString aNumStr( rDescs[0]->GetNumType().GetNumStr( nBeg ) );
+ if( xCharStyleIdx && lcl_HasMainEntry( pMainEntryNums, nBeg ))
+ {
+ xCharStyleIdx->push_back( 0 );
+ }
+
+ // Delete place holder
+ SwContentIndex aPos(pNd, nStartPos);
+ SwCharFormat* pPageNoCharFormat = nullptr;
+ SwpHints* pHints = pNd->GetpSwpHints();
+ if(pHints)
+ for(size_t nHintIdx = 0; nHintIdx < pHints->Count(); ++nHintIdx)
+ {
+ const SwTextAttr* pAttr = pHints->Get(nHintIdx);
+ const sal_Int32 nTmpEnd = pAttr->End() ? *pAttr->End() : 0;
+ if( nStartPos >= pAttr->GetStart() &&
+ (nStartPos + 2) <= nTmpEnd &&
+ pAttr->Which() == RES_TXTATR_CHARFMT)
+ {
+ pPageNoCharFormat = pAttr->GetCharFormat().GetCharFormat();
+ break;
+ }
+ }
+ pNd->EraseText(aPos, nEndPos - nStartPos + 2);
+
+ std::vector<sal_uInt16>::size_type i;
+ for( i = 1; i < rNums.size(); ++i)
+ {
+ SvxNumberType aType( rDescs[i]->GetNumType() );
+ if( TOX_INDEX == SwTOXBase::GetType() )
+ { // Summarize for the following
+ // Add up all following
+ // break up if main entry starts or ends and
+ // insert a char style index
+ bool bMainEntryChanges = lcl_HasMainEntry(pMainEntryNums, nOld)
+ != lcl_HasMainEntry(pMainEntryNums, rNums[i]);
+
+ if(nOld == rNums[i]-1 && !bMainEntryChanges &&
+ (GetOptions() & (SwTOIOptions::FF|SwTOIOptions::Dash)))
+ nCount++;
+ else
+ {
+ // Flush for the following old values
+ if(GetOptions() & SwTOIOptions::FF)
+ {
+ if ( nCount >= 1 )
+ aNumStr += rIntl.GetFollowingText( nCount > 1 );
+ }
+ else if (nCount) //#58127# If nCount == 0, then the only PageNumber is already in aNumStr!
+ {
+ if (nCount == 1 )
+ aNumStr += SwTOXMark::S_PAGE_DELI;
+ else
+ aNumStr += "-";
+
+ aNumStr += aType.GetNumStr( nBeg + nCount );
+ }
+
+ // Create new String
+ nBeg = rNums[i];
+ aNumStr += SwTOXMark::S_PAGE_DELI;
+ //the change of the character style must apply after sPageDeli is appended
+ if (xCharStyleIdx && bMainEntryChanges)
+ {
+ xCharStyleIdx->push_back(aNumStr.getLength());
+ }
+ aNumStr += aType.GetNumStr( nBeg );
+ nCount = 0;
+ }
+ nOld = rNums[i];
+ }
+ else
+ { // Insert all Numbers
+ aNumStr += aType.GetNumStr( rNums[i] );
+ if (i+1 != rNums.size())
+ aNumStr += SwTOXMark::S_PAGE_DELI;
+ }
+ }
+ // Flush when ending and the following old values
+ if( TOX_INDEX == SwTOXBase::GetType() )
+ {
+ if(GetOptions() & SwTOIOptions::FF)
+ {
+ if( nCount >= 1 )
+ aNumStr += rIntl.GetFollowingText( nCount > 1 );
+ }
+ else
+ {
+ if(nCount >= 2)
+ aNumStr += "-";
+ else if(nCount == 1)
+ aNumStr += SwTOXMark::S_PAGE_DELI;
+ //#58127# If nCount == 0, then the only PageNumber is already in aNumStr!
+ if(nCount)
+ aNumStr += rDescs[i-1]->GetNumType().GetNumStr( nBeg+nCount );
+ }
+ }
+ pNd->InsertText( aNumStr, aPos, SwInsertFlags::EMPTYEXPAND | SwInsertFlags::FORCEHINTEXPAND );
+ if(pPageNoCharFormat)
+ {
+ SwFormatCharFormat aCharFormat( pPageNoCharFormat );
+ pNd->InsertItem(aCharFormat, nStartPos, nStartPos + aNumStr.getLength(), SetAttrMode::DONTEXPAND);
+ }
+
+ // The main entries should get their character style
+ if (!xCharStyleIdx || xCharStyleIdx->empty() || GetMainEntryCharStyle().isEmpty())
+ return;
+
+ // eventually the last index must me appended
+ if (xCharStyleIdx->size()&0x01)
+ xCharStyleIdx->push_back(aNumStr.getLength());
+
+ // search by name
+ SwDoc& rDoc = pNd->GetDoc();
+ sal_uInt16 nPoolId = SwStyleNameMapper::GetPoolIdFromUIName( GetMainEntryCharStyle(), SwGetPoolIdFromName::ChrFmt );
+ SwCharFormat* pCharFormat = nullptr;
+ if(USHRT_MAX != nPoolId)
+ pCharFormat = rDoc.getIDocumentStylePoolAccess().GetCharFormatFromPool(nPoolId);
+ else
+ pCharFormat = rDoc.FindCharFormatByName( GetMainEntryCharStyle() );
+ if(!pCharFormat)
+ pCharFormat = rDoc.MakeCharFormat(GetMainEntryCharStyle(), nullptr);
+
+ // find the page numbers in aNumStr and set the character style
+ sal_Int32 nOffset = pNd->GetText().getLength() - aNumStr.getLength();
+ SwFormatCharFormat aCharFormat(pCharFormat);
+ for (size_t j = 0; j < xCharStyleIdx->size(); j += 2)
+ {
+ sal_Int32 nStartIdx = (*xCharStyleIdx)[j] + nOffset;
+ sal_Int32 nEndIdx = (*xCharStyleIdx)[j + 1] + nOffset;
+ pNd->InsertItem(aCharFormat, nStartIdx, nEndIdx, SetAttrMode::DONTEXPAND);
+ }
+}
+
+void SwTOXBaseSection::InsertSorted(std::unique_ptr<SwTOXSortTabBase> pNew)
+{
+ Range aRange(0, m_aSortArr.size());
+ if( TOX_INDEX == SwTOXBase::GetType() && pNew->pTextMark )
+ {
+ const SwTOXMark& rMark = pNew->pTextMark->GetTOXMark();
+ // Evaluate Key
+ // Calculate the range where to insert
+ if( !(GetOptions() & SwTOIOptions::KeyAsEntry) &&
+ !rMark.GetPrimaryKey().isEmpty() )
+ {
+ aRange = GetKeyRange( rMark.GetPrimaryKey(),
+ rMark.GetPrimaryKeyReading(),
+ *pNew, FORM_PRIMARY_KEY, aRange );
+
+ if( !rMark.GetSecondaryKey().isEmpty() )
+ aRange = GetKeyRange( rMark.GetSecondaryKey(),
+ rMark.GetSecondaryKeyReading(),
+ *pNew, FORM_SECONDARY_KEY, aRange );
+ }
+ }
+ // Search for identical entries and remove the trailing one
+ if(TOX_AUTHORITIES == SwTOXBase::GetType())
+ {
+ for(short i = static_cast<short>(aRange.Min()); i < static_cast<short>(aRange.Max()); ++i)
+ {
+ SwTOXSortTabBase* pOld = m_aSortArr[i].get();
+ if (pOld->equivalent(*pNew))
+ {
+ if (pOld->sort_lt(*pNew))
+ {
+ return;
+ }
+ else
+ {
+ // remove the old content
+ m_aSortArr.erase( m_aSortArr.begin() + i );
+ aRange.Max()--;
+ break;
+ }
+ }
+ }
+ }
+
+ // find position and insert
+ tools::Long i;
+
+ for( i = aRange.Min(); i < aRange.Max(); ++i)
+ { // Only check for same level
+ SwTOXSortTabBase* pOld = m_aSortArr[i].get();
+ if (pOld->equivalent(*pNew))
+ {
+ if(TOX_AUTHORITIES != SwTOXBase::GetType())
+ {
+ // Own entry for double entries or keywords
+ if( pOld->GetType() == TOX_SORT_CUSTOM &&
+ SwTOXSortTabBase::GetOptions() & SwTOIOptions::KeyAsEntry)
+ continue;
+
+ if(!(SwTOXSortTabBase::GetOptions() & SwTOIOptions::SameEntry))
+ { // Own entry
+ m_aSortArr.insert(m_aSortArr.begin() + i, std::move(pNew));
+ return;
+ }
+ // If the own entry is already present, add it to the references list
+ pOld->aTOXSources.push_back(pNew->aTOXSources[0]);
+
+ return;
+ }
+#if OSL_DEBUG_LEVEL > 0
+ else
+ OSL_FAIL("Bibliography entries cannot be found here");
+#endif
+ }
+ if (pNew->sort_lt(*pOld))
+ break;
+ }
+ // Skip SubLevel
+ while( TOX_INDEX == SwTOXBase::GetType() && i < aRange.Max() &&
+ m_aSortArr[i]->GetLevel() > pNew->GetLevel() )
+ i++;
+
+ // Insert at position i
+ m_aSortArr.insert(m_aSortArr.begin()+i, std::move(pNew));
+}
+
+/// Find Key Range and insert if possible
+Range SwTOXBaseSection::GetKeyRange(const OUString& rStr, const OUString& rStrReading,
+ const SwTOXSortTabBase& rNew,
+ sal_uInt16 nLevel, const Range& rRange )
+{
+ const SwTOXInternational& rIntl = *rNew.pTOXIntl;
+ TextAndReading aToCompare(rStr, rStrReading);
+
+ if( SwTOIOptions::InitialCaps & GetOptions() )
+ {
+ aToCompare.sText = rIntl.ToUpper( aToCompare.sText, 0 )
+ + aToCompare.sText.subView(1);
+ }
+
+ OSL_ENSURE(rRange.Min() >= 0 && rRange.Max() >= 0, "Min Max < 0");
+
+ const tools::Long nMin = rRange.Min();
+ const tools::Long nMax = rRange.Max();
+
+ tools::Long i;
+
+ for( i = nMin; i < nMax; ++i)
+ {
+ SwTOXSortTabBase* pBase = m_aSortArr[i].get();
+
+ if( rIntl.IsEqual( pBase->GetText(), pBase->GetLocale(),
+ aToCompare, rNew.GetLocale() ) &&
+ pBase->GetLevel() == nLevel )
+ break;
+ }
+ if(i == nMax)
+ { // If not already present, create and insert
+ std::unique_ptr<SwTOXCustom> pKey(MakeSwTOXSortTabBase<SwTOXCustom>(
+ nullptr, aToCompare, nLevel, rIntl, rNew.GetLocale() ));
+ for(i = nMin; i < nMax; ++i)
+ {
+ if (nLevel == m_aSortArr[i]->GetLevel() && pKey->sort_lt(*m_aSortArr[i]))
+ break;
+ }
+ m_aSortArr.insert(m_aSortArr.begin() + i, std::move(pKey));
+ }
+ const tools::Long nStart = i+1;
+ const tools::Long nEnd = m_aSortArr.size();
+
+ // Find end of range
+ for(i = nStart; i < nEnd; ++i)
+ {
+ if(m_aSortArr[i]->GetLevel() <= nLevel)
+ {
+ return Range(nStart, i);
+ }
+ }
+ return Range(nStart, nEnd);
+}
+
+bool SwTOXBase::IsTOXBaseInReadonly() const
+{
+ const SwTOXBaseSection *pSect = dynamic_cast<const SwTOXBaseSection*>(this);
+ if (!pSect || !pSect->GetFormat())
+ return false;
+
+ const SwSectionNode* pSectNode = pSect->GetFormat()->GetSectionNode();
+ if (!pSectNode)
+ return false;
+
+ const SwDocShell* pDocSh = pSectNode->GetDoc().GetDocShell();
+ if (!pDocSh)
+ return false;
+
+ if (pDocSh->IsReadOnly())
+ return true;
+
+ pSectNode = pSectNode->StartOfSectionNode()->FindSectionNode();
+ if (!pSectNode)
+ return false;
+
+ return pSectNode->GetSection().IsProtectFlag();
+}
+
+const SfxItemSet* SwTOXBase::GetAttrSet() const
+{
+ const SwTOXBaseSection *pSect = dynamic_cast<const SwTOXBaseSection*>(this);
+ if(pSect && pSect->GetFormat())
+ return &pSect->GetFormat()->GetAttrSet();
+ return nullptr;
+}
+
+void SwTOXBase::SetAttrSet( const SfxItemSet& rSet )
+{
+ SwTOXBaseSection *pSect = dynamic_cast<SwTOXBaseSection*>(this);
+ if( pSect && pSect->GetFormat() )
+ pSect->GetFormat()->SetFormatAttr( rSet );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/docxforms.cxx b/sw/source/core/doc/docxforms.cxx
new file mode 100644
index 0000000000..5cd7792c99
--- /dev/null
+++ b/sw/source/core/doc/docxforms.cxx
@@ -0,0 +1,129 @@
+/* -*- 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 <doc.hxx>
+#include <docsh.hxx>
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/frame/XModule.hpp>
+#include <com/sun/star/xforms/Model.hpp>
+#include <com/sun/star/xforms/XModel2.hpp>
+#include <com/sun/star/xforms/XFormsUIHelper1.hpp>
+#include <com/sun/star/xforms/XForms.hpp>
+#include <comphelper/processfactory.hxx>
+#include <osl/diagnose.h>
+#include <com/sun/star/container/XIndexAccess.hpp>
+
+using namespace ::com::sun::star;
+
+using uno::Reference;
+using uno::UNO_QUERY;
+using uno::Any;
+using uno::Exception;
+using xforms::XModel2;
+using frame::XModule;
+using xforms::XFormsUIHelper1;
+using com::sun::star::container::XIndexAccess;
+
+
+bool SwDoc::isXForms() const
+{
+ return mxXForms.is();
+}
+
+void SwDoc::initXForms( bool bCreateDefaultModel )
+{
+ OSL_ENSURE( ! isXForms(), "please initialize only once" );
+
+ try
+ {
+ // create XForms components
+ mxXForms = xforms::XForms::create( comphelper::getProcessComponentContext() );
+
+ // change our module identifier, to be able to have a dedicated UI
+ Reference< XModule > xModule;
+ SwDocShell* pShell( GetDocShell() );
+ if ( pShell )
+ xModule.set(pShell->GetModel(), css::uno::UNO_QUERY);
+ OSL_ENSURE( xModule.is(), "SwDoc::initXForms: no XModule at the document!" );
+ if ( xModule.is() )
+ xModule->setIdentifier( "com.sun.star.xforms.XMLFormDocument" );
+
+ // create default model
+ if( bCreateDefaultModel && mxXForms.is() )
+ {
+ OUString sName("Model 1");
+ Reference<XModel2> xModel = xforms::Model::create( comphelper::getProcessComponentContext() );
+ xModel->setID( sName );
+ Reference<XFormsUIHelper1>( xModel, uno::UNO_QUERY_THROW )->newInstance(
+ "Instance 1",
+ OUString(), true );
+ xModel->initialize();
+ mxXForms->insertByName( sName, Any( xModel ) );
+ OSL_ENSURE( mxXForms->hasElements(), "can't create XForms model" );
+ }
+
+ OSL_ENSURE( isXForms(), "initialization failed" );
+ }
+ catch( const Exception& )
+ {
+ }
+}
+
+// #i113606#, to release the cyclic reference between XFormModel and bindings/submissions.
+void SwDoc::disposeXForms( )
+{
+ // get XForms models
+ if( !mxXForms.is() )
+ return;
+
+ // iterate over all models
+ const uno::Sequence<OUString> aNames = mxXForms->getElementNames();
+ for( const OUString& rName : aNames )
+ {
+ Reference< xforms::XModel > xModel(
+ mxXForms->getByName( rName ), UNO_QUERY );
+
+ if( xModel.is() )
+ {
+ // ask model for bindings
+ Reference< XIndexAccess > xBindings(
+ xModel->getBindings(), UNO_QUERY );
+
+ // Then release them one by one
+ int nCount = xBindings->getCount();
+ for( int i = nCount-1; i >= 0; i-- )
+ {
+ xModel->getBindings()->remove(xBindings->getByIndex( i ));
+ }
+
+ // ask model for Submissions
+ Reference< XIndexAccess > xSubmissions(
+ xModel->getSubmissions(), UNO_QUERY );
+
+ // Then release them one by one
+ nCount = xSubmissions->getCount();
+ for( int i = nCount-1; i >= 0; i-- )
+ {
+ xModel->getSubmissions()->remove(xSubmissions->getByIndex( i ));
+ }
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/extinput.cxx b/sw/source/core/doc/extinput.cxx
new file mode 100644
index 0000000000..c8563facbd
--- /dev/null
+++ b/sw/source/core/doc/extinput.cxx
@@ -0,0 +1,309 @@
+/* -*- 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 <algorithm>
+
+#include <com/sun/star/i18n/ScriptType.hpp>
+
+#include <editeng/langitem.hxx>
+#include <osl/diagnose.h>
+#include <svl/languageoptions.hxx>
+#include <vcl/commandevent.hxx>
+
+#include <hintids.hxx>
+#include <extinput.hxx>
+#include <doc.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <contentindex.hxx>
+#include <ndtxt.hxx>
+#include <swundo.hxx>
+
+using namespace ::com::sun::star;
+
+SwExtTextInput::SwExtTextInput( const SwPaM& rPam, Ring* pRing )
+ : SwPaM( *rPam.GetPoint(), static_cast<SwPaM*>(pRing) ),
+ m_eInputLanguage(LANGUAGE_DONTKNOW)
+{
+ m_bIsOverwriteCursor = false;
+ m_bInsText = true;
+}
+
+SwExtTextInput::~SwExtTextInput()
+{
+ SwDoc& rDoc = GetDoc();
+ if (rDoc.IsInDtor()) { return; /* #i58606# */ }
+
+ SwTextNode* pTNd = GetPoint()->GetNode().GetTextNode();
+ if( !pTNd )
+ return;
+
+ SwPosition& rPtPos = *GetPoint();
+ sal_Int32 nSttCnt = rPtPos.GetContentIndex();
+ sal_Int32 nEndCnt = GetMark()->GetContentIndex();
+ if( nEndCnt == nSttCnt )
+ return;
+
+ // Prevent IME edited text being grouped with non-IME edited text.
+ bool bKeepGroupUndo = rDoc.GetIDocumentUndoRedo().DoesGroupUndo();
+ bool bWasIME = rDoc.GetIDocumentUndoRedo().GetUndoActionCount() == 0 || rDoc.getIDocumentContentOperations().GetIME();
+ if (!bWasIME)
+ {
+ rDoc.GetIDocumentUndoRedo().DoGroupUndo(false);
+ }
+ rDoc.getIDocumentContentOperations().SetIME(true);
+ if( nEndCnt < nSttCnt )
+ {
+ std::swap(nSttCnt, nEndCnt);
+ }
+
+ // In order to get Undo/Redlining etc. working correctly,
+ // we need to go through the Doc interface
+ rPtPos.SetContent(nSttCnt);
+ const OUString sText( pTNd->GetText().copy(nSttCnt, nEndCnt - nSttCnt));
+ if( m_bIsOverwriteCursor && !m_sOverwriteText.isEmpty() )
+ {
+ const sal_Int32 nLen = sText.getLength();
+ const sal_Int32 nOWLen = m_sOverwriteText.getLength();
+ if( nLen > nOWLen )
+ {
+ rPtPos.AdjustContent(+nOWLen);
+ pTNd->EraseText( rPtPos, nLen - nOWLen );
+ rPtPos.SetContent(nSttCnt);
+ pTNd->ReplaceText( rPtPos, nOWLen, m_sOverwriteText );
+ if( m_bInsText )
+ {
+ rPtPos.SetContent(nSttCnt);
+ rDoc.GetIDocumentUndoRedo().StartUndo( SwUndoId::OVERWRITE, nullptr );
+ rDoc.getIDocumentContentOperations().Overwrite( *this, sText.copy( 0, nOWLen ) );
+ rDoc.getIDocumentContentOperations().InsertString( *this, sText.copy( nOWLen ) );
+ rDoc.GetIDocumentUndoRedo().EndUndo( SwUndoId::OVERWRITE, nullptr );
+ }
+ }
+ else
+ {
+ pTNd->ReplaceText( rPtPos, nLen, m_sOverwriteText.copy( 0, nLen ));
+ if( m_bInsText )
+ {
+ rPtPos.SetContent(nSttCnt);
+ rDoc.getIDocumentContentOperations().Overwrite( *this, sText );
+ }
+ }
+ }
+ else
+ {
+ // 1. Insert text at start position with EMPTYEXPAND to use correct formatting
+ // ABC<NEW><OLD>
+ // 2. Then remove old (not tracked) content
+ // ABC<NEW>
+
+ sal_Int32 nLenghtOfOldString = nEndCnt - nSttCnt;
+
+ if( m_bInsText )
+ {
+ rPtPos.SetContent(nSttCnt);
+ rDoc.getIDocumentContentOperations().InsertString( *this, sText, SwInsertFlags::EMPTYEXPAND );
+ }
+
+ pTNd->EraseText( rPtPos, nLenghtOfOldString );
+ }
+ if (!bWasIME)
+ {
+ rDoc.GetIDocumentUndoRedo().DoGroupUndo(bKeepGroupUndo);
+ }
+ if (m_eInputLanguage == LANGUAGE_DONTKNOW)
+ return;
+
+ sal_uInt16 nWhich = RES_CHRATR_LANGUAGE;
+ sal_Int16 nScriptType = SvtLanguageOptions::GetI18NScriptTypeOfLanguage(m_eInputLanguage);
+ switch(nScriptType)
+ {
+ case i18n::ScriptType::ASIAN:
+ nWhich = RES_CHRATR_CJK_LANGUAGE; break;
+ case i18n::ScriptType::COMPLEX:
+ nWhich = RES_CHRATR_CTL_LANGUAGE; break;
+ }
+ // #i41974# Only set language attribute for CJK/CTL scripts.
+ if (RES_CHRATR_LANGUAGE != nWhich && pTNd->GetLang( nSttCnt, nEndCnt-nSttCnt, nScriptType) != m_eInputLanguage)
+ {
+ SvxLanguageItem aLangItem( m_eInputLanguage, nWhich );
+ rPtPos.SetContent(nSttCnt);
+ GetMark()->SetContent(nEndCnt);
+ rDoc.getIDocumentContentOperations().InsertPoolItem(*this, aLangItem );
+ }
+}
+
+void SwExtTextInput::SetInputData( const CommandExtTextInputData& rData )
+{
+ SwTextNode* pTNd = GetPoint()->GetNode().GetTextNode();
+ if( !pTNd )
+ return;
+
+ sal_Int32 nSttCnt = Start()->GetContentIndex();
+ sal_Int32 nEndCnt = End()->GetContentIndex();
+
+ SwContentIndex aIdx( pTNd, nSttCnt );
+ const OUString& rNewStr = rData.GetText();
+
+ if( m_bIsOverwriteCursor && !m_sOverwriteText.isEmpty() )
+ {
+ sal_Int32 nReplace = nEndCnt - nSttCnt;
+ const sal_Int32 nNewLen = rNewStr.getLength();
+ if( nNewLen < nReplace )
+ {
+ // We have to insert some characters from the saved original text
+ nReplace -= nNewLen;
+ aIdx += nNewLen;
+ pTNd->ReplaceText( aIdx, nReplace,
+ m_sOverwriteText.copy( nNewLen, nReplace ));
+ aIdx = nSttCnt;
+ nReplace = nNewLen;
+ }
+ else
+ {
+ const sal_Int32 nOWLen = m_sOverwriteText.getLength();
+ if( nOWLen < nReplace )
+ {
+ aIdx += nOWLen;
+ pTNd->EraseText( aIdx, nReplace-nOWLen );
+ aIdx = nSttCnt;
+ nReplace = nOWLen;
+ }
+ else
+ {
+ nReplace = std::min(nOWLen, nNewLen);
+ }
+ }
+
+ pTNd->ReplaceText( aIdx, nReplace, rNewStr );
+ if( !HasMark() )
+ SetMark();
+ GetMark()->Assign(*aIdx.GetContentNode(), aIdx.GetIndex());
+ }
+ else
+ {
+ if( nSttCnt < nEndCnt )
+ {
+ pTNd->EraseText( aIdx, nEndCnt - nSttCnt );
+ }
+
+ // NOHINTEXPAND so we can use correct formatting in destructor when we finish composing
+ pTNd->InsertText( rNewStr, aIdx, SwInsertFlags::NOHINTEXPAND );
+ if( !HasMark() )
+ SetMark();
+ }
+
+ GetPoint()->SetContent(nSttCnt);
+
+ m_aAttrs.clear();
+ if( rData.GetTextAttr() )
+ {
+ const ExtTextInputAttr *pAttrs = rData.GetTextAttr();
+ m_aAttrs.insert( m_aAttrs.begin(), pAttrs, pAttrs + rData.GetText().getLength() );
+ }
+}
+
+void SwExtTextInput::SetOverwriteCursor( bool bFlag )
+{
+ m_bIsOverwriteCursor = bFlag;
+ if (!m_bIsOverwriteCursor)
+ return;
+
+ const SwTextNode *const pTNd = GetPoint()->GetNode().GetTextNode();
+ if (!pTNd)
+ return;
+
+ const sal_Int32 nSttCnt = GetPoint()->GetContentIndex();
+ const sal_Int32 nEndCnt = GetMark()->GetContentIndex();
+ m_sOverwriteText = pTNd->GetText().copy( std::min(nSttCnt, nEndCnt) );
+ if( m_sOverwriteText.isEmpty() )
+ return;
+
+ const sal_Int32 nInPos = m_sOverwriteText.indexOf( CH_TXTATR_INWORD );
+ const sal_Int32 nBrkPos = m_sOverwriteText.indexOf( CH_TXTATR_BREAKWORD );
+
+ // Find the first attr found, if any.
+ sal_Int32 nPos = std::min(nInPos, nBrkPos);
+ if (nPos<0)
+ {
+ nPos = std::max(nInPos, nBrkPos);
+ }
+ if (nPos>=0)
+ {
+ m_sOverwriteText = m_sOverwriteText.copy( 0, nPos );
+ }
+}
+
+// The Doc interfaces
+
+SwExtTextInput* SwDoc::CreateExtTextInput( const SwPaM& rPam )
+{
+ SwExtTextInput* pNew = new SwExtTextInput( rPam, mpExtInputRing );
+ if( !mpExtInputRing )
+ mpExtInputRing = pNew;
+ pNew->SetMark();
+ return pNew;
+}
+
+void SwDoc::DeleteExtTextInput( SwExtTextInput* pDel )
+{
+ if( pDel == mpExtInputRing )
+ {
+ if( pDel->GetNext() != mpExtInputRing )
+ mpExtInputRing = pDel->GetNext();
+ else
+ mpExtInputRing = nullptr;
+ }
+ delete pDel;
+}
+
+SwExtTextInput* SwDoc::GetExtTextInput( const SwNode& rNd,
+ sal_Int32 nContentPos ) const
+{
+ SwExtTextInput* pRet = nullptr;
+ if( mpExtInputRing )
+ {
+ SwNodeOffset nNdIdx = rNd.GetIndex();
+ SwExtTextInput* pTmp = mpExtInputRing;
+ do {
+ SwNodeOffset nStartNode = pTmp->Start()->GetNodeIndex(),
+ nEndNode = pTmp->End()->GetNodeIndex();
+ sal_Int32 nStartCnt = pTmp->Start()->GetContentIndex();
+ sal_Int32 nEndCnt = pTmp->End()->GetContentIndex();
+
+ if( nStartNode <= nNdIdx && nNdIdx <= nEndNode &&
+ ( nContentPos<0 ||
+ ( nStartCnt <= nContentPos && nContentPos <= nEndCnt )))
+ {
+ pRet = pTmp;
+ break;
+ }
+ pTmp = pTmp->GetNext();
+ } while ( pTmp!=mpExtInputRing );
+ }
+ return pRet;
+}
+
+SwExtTextInput* SwDoc::GetExtTextInput() const
+{
+ OSL_ENSURE( !mpExtInputRing || !mpExtInputRing->IsMultiSelection(),
+ "more than one InputEngine available" );
+ return mpExtInputRing;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/fmtcol.cxx b/sw/source/core/doc/fmtcol.cxx
new file mode 100644
index 0000000000..4d87241a03
--- /dev/null
+++ b/sw/source/core/doc/fmtcol.cxx
@@ -0,0 +1,723 @@
+/* -*- 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 <libxml/xmlwriter.h>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/lrspitem.hxx>
+#include <editeng/ulspitem.hxx>
+#include <osl/diagnose.h>
+#include <sal/macros.h>
+#include <svl/intitem.hxx>
+#include <calbck.hxx>
+#include <doc.hxx>
+#include <fmtcol.hxx>
+#include <fmtcolfunc.hxx>
+#include <hintids.hxx>
+#include <hints.hxx>
+#include <node.hxx>
+#include <numrule.hxx>
+#include <paratr.hxx>
+#include <swfntcch.hxx>
+
+namespace TextFormatCollFunc
+{
+ // #i71574#
+ void CheckTextFormatCollForDeletionOfAssignmentToOutlineStyle(
+ SwFormat* pFormat,
+ const SwNumRuleItem* pNewNumRuleItem )
+ {
+ SwTextFormatColl* pTextFormatColl = dynamic_cast<SwTextFormatColl*>(pFormat);
+ if ( !pTextFormatColl )
+ {
+ OSL_FAIL( "<TextFormatCollFunc::CheckTextFormatCollFuncForDeletionOfAssignmentToOutlineStyle> - misuse of method - it's only for instances of <SwTextFormatColl>" );
+ return;
+ }
+
+ // #i73790#
+ if ( pTextFormatColl->StayAssignedToListLevelOfOutlineStyle() ||
+ !pTextFormatColl->IsAssignedToListLevelOfOutlineStyle() )
+ return;
+
+ if (!pNewNumRuleItem)
+ {
+ pNewNumRuleItem = pTextFormatColl->GetItemIfSet(RES_PARATR_NUMRULE, false);
+ }
+ if (pNewNumRuleItem)
+ {
+ const OUString& sNumRuleName = pNewNumRuleItem->GetValue();
+ if ( sNumRuleName.isEmpty() ||
+ sNumRuleName != pTextFormatColl->GetDoc()->GetOutlineNumRule()->GetName() )
+ {
+ // delete assignment of paragraph style to list level of outline style.
+ pTextFormatColl->DeleteAssignmentToListLevelOfOutlineStyle();
+ }
+ }
+ }
+
+ SwNumRule* GetNumRule( SwTextFormatColl& rTextFormatColl )
+ {
+ SwNumRule* pNumRule( nullptr );
+
+ const SwNumRuleItem* pNumRuleItem = rTextFormatColl.GetItemIfSet(RES_PARATR_NUMRULE, false);
+ if (pNumRuleItem)
+ {
+ const OUString& sNumRuleName = pNumRuleItem->GetValue();
+ if ( !sNumRuleName.isEmpty() )
+ {
+ pNumRule = rTextFormatColl.GetDoc()->FindNumRulePtr( sNumRuleName );
+ }
+ }
+
+ return pNumRule;
+ }
+
+ void AddToNumRule( SwTextFormatColl& rTextFormatColl )
+ {
+ SwNumRule* pNumRule = GetNumRule( rTextFormatColl );
+ if ( pNumRule )
+ {
+ pNumRule->AddParagraphStyle( rTextFormatColl );
+ }
+ }
+
+ void RemoveFromNumRule( SwTextFormatColl& rTextFormatColl )
+ {
+ SwNumRule* pNumRule = GetNumRule( rTextFormatColl );
+ if ( pNumRule )
+ {
+ pNumRule->RemoveParagraphStyle( rTextFormatColl );
+ }
+ }
+} // end of namespace TextFormatCollFunc
+
+SwTextFormatColl::~SwTextFormatColl()
+{
+ if(m_bInSwFntCache)
+ pSwFontCache->Delete( this );
+
+ if (GetDoc()->IsInDtor())
+ {
+ return;
+ }
+
+ for (const auto& pCharFormat : *GetDoc()->GetCharFormats())
+ {
+ if (pCharFormat->GetLinkedParaFormat() == this)
+ {
+ pCharFormat->SetLinkedParaFormat(nullptr);
+ }
+ }
+}
+void SwTextFormatColl::SwClientNotify(const SwModify& rModify, const SfxHint& rHint)
+{
+ if (rHint.GetId() == SfxHintId::SwAutoFormatUsedHint)
+ {
+ CallSwClientNotify(rHint);
+ return;
+ }
+ if (rHint.GetId() != SfxHintId::SwLegacyModify)
+ return;
+ auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
+ if(GetDoc()->IsInDtor())
+ {
+ SwFormatColl::SwClientNotify(rModify, rHint);
+ return;
+ }
+ bool bNewParent( false ); // #i66431# - adjust type of <bNewParent>
+ const SvxULSpaceItem *pNewULSpace = nullptr, *pOldULSpace = nullptr;
+ const SvxFirstLineIndentItem *pNewFirstLineIndent = nullptr;
+ const SvxTextLeftMarginItem *pNewTextLeftMargin = nullptr;
+ const SvxRightMarginItem *pNewRightMargin = nullptr;
+ const SvxFontHeightItem* aFontSizeArr[3] = {nullptr,nullptr,nullptr};
+ // #i70223#
+ const bool bAssignedToListLevelOfOutlineStyle(IsAssignedToListLevelOfOutlineStyle());
+ const SwNumRuleItem* pNewNumRuleItem( nullptr );
+
+ const SwAttrSetChg *pNewChgSet = nullptr, *pOldChgSet = nullptr;
+ const auto pOld = pLegacy->m_pOld;
+ const auto pNew = pLegacy->m_pNew;
+ switch( pLegacy->GetWhich() )
+ {
+ case RES_ATTRSET_CHG:
+ // Only recalculate if we're not the sender!
+ pNewChgSet = &pNew->StaticWhichCast(RES_ATTRSET_CHG);
+ pOldChgSet = &pOld->StaticWhichCast(RES_ATTRSET_CHG);
+ pNewFirstLineIndent = pNewChgSet->GetChgSet()->GetItemIfSet(RES_MARGIN_FIRSTLINE, false);
+ pNewTextLeftMargin = pNewChgSet->GetChgSet()->GetItemIfSet(RES_MARGIN_TEXTLEFT, false);
+ pNewRightMargin = pNewChgSet->GetChgSet()->GetItemIfSet(RES_MARGIN_RIGHT, false);
+ pNewULSpace = pNewChgSet->GetChgSet()->GetItemIfSet( RES_UL_SPACE, false );
+ aFontSizeArr[0] = pNewChgSet->GetChgSet()->GetItemIfSet( RES_CHRATR_FONTSIZE, false );
+ aFontSizeArr[1] = pNewChgSet->GetChgSet()->GetItemIfSet( RES_CHRATR_CJK_FONTSIZE, false );
+ aFontSizeArr[2] = pNewChgSet->GetChgSet()->GetItemIfSet( RES_CHRATR_CTL_FONTSIZE, false );
+ // #i70223#, #i84745#
+ // check, if attribute set is applied to this paragraph style
+ if ( bAssignedToListLevelOfOutlineStyle &&
+ pNewChgSet->GetTheChgdSet() == &GetAttrSet() )
+ {
+ pNewNumRuleItem = pNewChgSet->GetChgSet()->GetItemIfSet( RES_PARATR_NUMRULE, false );
+ }
+
+ break;
+
+ case RES_FMT_CHG:
+ if( GetAttrSet().GetParent() )
+ {
+ const SfxItemSet* pParent = GetAttrSet().GetParent();
+ pNewFirstLineIndent = &pParent->Get(RES_MARGIN_FIRSTLINE);
+ pNewTextLeftMargin = &pParent->Get(RES_MARGIN_TEXTLEFT);
+ pNewRightMargin = &pParent->Get(RES_MARGIN_RIGHT);
+ pNewULSpace = &pParent->Get( RES_UL_SPACE );
+ aFontSizeArr[0] = &pParent->Get( RES_CHRATR_FONTSIZE );
+ aFontSizeArr[1] = &pParent->Get( RES_CHRATR_CJK_FONTSIZE );
+ aFontSizeArr[2] = &pParent->Get( RES_CHRATR_CTL_FONTSIZE );
+ // #i66431# - modify has to be propagated, because of new parent format.
+ bNewParent = true;
+ }
+ break;
+
+ case RES_MARGIN_FIRSTLINE:
+ pNewFirstLineIndent = &pNew->StaticWhichCast(RES_MARGIN_FIRSTLINE);
+ break;
+ case RES_MARGIN_TEXTLEFT:
+ pNewTextLeftMargin = &pNew->StaticWhichCast(RES_MARGIN_TEXTLEFT);
+ break;
+ case RES_MARGIN_RIGHT:
+ pNewRightMargin = &pNew->StaticWhichCast(RES_MARGIN_RIGHT);
+ break;
+ case RES_UL_SPACE:
+ pNewULSpace = &pNew->StaticWhichCast(RES_UL_SPACE);
+ break;
+ case RES_CHRATR_FONTSIZE:
+ aFontSizeArr[0] = &pNew->StaticWhichCast(RES_CHRATR_CJK_FONTSIZE);
+ break;
+ case RES_CHRATR_CJK_FONTSIZE:
+ aFontSizeArr[1] = &pNew->StaticWhichCast(RES_CHRATR_CJK_FONTSIZE);
+ break;
+ case RES_CHRATR_CTL_FONTSIZE:
+ aFontSizeArr[2] = &pNew->StaticWhichCast(RES_CHRATR_CTL_FONTSIZE);
+ break;
+ // #i70223#
+ case RES_PARATR_NUMRULE:
+ if (bAssignedToListLevelOfOutlineStyle)
+ {
+ pNewNumRuleItem = &pNew->StaticWhichCast(RES_PARATR_NUMRULE);
+ }
+ break;
+ default:
+ break;
+ }
+
+ // #i70223#
+ if ( bAssignedToListLevelOfOutlineStyle && pNewNumRuleItem )
+ {
+ TextFormatCollFunc::CheckTextFormatCollForDeletionOfAssignmentToOutlineStyle(
+ this, pNewNumRuleItem );
+ }
+
+ bool bContinue = true;
+
+ // Check against the own attributes
+ const SvxFirstLineIndentItem *pOldFirstLineIndent(GetItemIfSet(RES_MARGIN_FIRSTLINE, false));
+ if (pNewFirstLineIndent && pOldFirstLineIndent)
+ {
+ if (!SfxPoolItem::areSame(pOldFirstLineIndent, pNewFirstLineIndent)) // Avoid recursion (SetAttr!)
+ {
+ bool bChg = false;
+ SvxFirstLineIndentItem aNew(*pOldFirstLineIndent);
+ // We had a relative value -> recalculate
+ if( 100 != aNew.GetPropTextFirstLineOffset() )
+ {
+ short nTmp = aNew.GetTextFirstLineOffset(); // keep so that we can compare
+ aNew.SetTextFirstLineOffset(pNewFirstLineIndent->GetTextFirstLineOffset(),
+ aNew.GetPropTextFirstLineOffset() );
+ bChg |= nTmp != aNew.GetTextFirstLineOffset();
+ }
+ if( bChg )
+ {
+ SetFormatAttr( aNew );
+ bContinue = nullptr != pOldChgSet || bNewParent;
+ }
+ // We set it to absolute -> do not propagate it further, unless
+ // we set it!
+ else if( pNewChgSet )
+ bContinue = pNewChgSet->GetTheChgdSet() == &GetAttrSet();
+ }
+ }
+ const SvxTextLeftMarginItem *pOldTextLeftMargin(GetItemIfSet(RES_MARGIN_TEXTLEFT, false));
+ if (pNewTextLeftMargin && pOldTextLeftMargin)
+ {
+ if (!SfxPoolItem::areSame(pOldTextLeftMargin, pNewTextLeftMargin)) // Avoid recursion (SetAttr!)
+ {
+ bool bChg = false;
+ SvxTextLeftMarginItem aNew(*pOldTextLeftMargin);
+ // We had a relative value -> recalculate
+ if( 100 != aNew.GetPropLeft() )
+ {
+ // note: changing from Left to TextLeft - looked wrong with Left
+ tools::Long nTmp = aNew.GetTextLeft(); // keep so that we can compare
+ aNew.SetTextLeft(pNewTextLeftMargin->GetTextLeft(), aNew.GetPropLeft());
+ bChg |= nTmp != aNew.GetTextLeft();
+ }
+ if( bChg )
+ {
+ SetFormatAttr( aNew );
+ bContinue = nullptr != pOldChgSet || bNewParent;
+ }
+ // We set it to absolute -> do not propagate it further, unless
+ // we set it!
+ else if( pNewChgSet )
+ bContinue = pNewChgSet->GetTheChgdSet() == &GetAttrSet();
+ }
+ }
+ const SvxRightMarginItem *pOldRightMargin(GetItemIfSet(RES_MARGIN_RIGHT, false));
+ if (pNewRightMargin && pOldRightMargin)
+ {
+ if (!SfxPoolItem::areSame(pOldRightMargin, pNewRightMargin)) // Avoid recursion (SetAttr!)
+ {
+ bool bChg = false;
+ SvxRightMarginItem aNew(*pOldRightMargin);
+ // We had a relative value -> recalculate
+ if( 100 != aNew.GetPropRight() )
+ {
+ tools::Long nTmp = aNew.GetRight(); // keep so that we can compare
+ aNew.SetRight(pNewRightMargin->GetRight(), aNew.GetPropRight());
+ bChg |= nTmp != aNew.GetRight();
+ }
+ if( bChg )
+ {
+ SetFormatAttr( aNew );
+ bContinue = nullptr != pOldChgSet || bNewParent;
+ }
+ // We set it to absolute -> do not propagate it further, unless
+ // we set it!
+ else if( pNewChgSet )
+ bContinue = pNewChgSet->GetTheChgdSet() == &GetAttrSet();
+ }
+ }
+
+ if( pNewULSpace && (pOldULSpace = GetItemIfSet(RES_UL_SPACE, false)) &&
+ !SfxPoolItem::areSame(pOldULSpace, pNewULSpace) ) // Avoid recursion (SetAttr!)
+ {
+ SvxULSpaceItem aNew( *pOldULSpace );
+ bool bChg = false;
+ // We had a relative value -> recalculate
+ if( 100 != aNew.GetPropUpper() )
+ {
+ sal_uInt16 nTmp = aNew.GetUpper(); // keep so that we can compare
+ aNew.SetUpper( pNewULSpace->GetUpper(), aNew.GetPropUpper() );
+ bChg |= nTmp != aNew.GetUpper();
+ }
+ // We had a relative value -> recalculate
+ if( 100 != aNew.GetPropLower() )
+ {
+ sal_uInt16 nTmp = aNew.GetLower(); // keep so that we can compare
+ aNew.SetLower( pNewULSpace->GetLower(), aNew.GetPropLower() );
+ bChg |= nTmp != aNew.GetLower();
+ }
+ if( bChg )
+ {
+ SetFormatAttr( aNew );
+ bContinue = nullptr != pOldChgSet || bNewParent;
+ }
+ // We set it to absolute -> do not propagate it further, unless
+ // we set it!
+ else if( pNewChgSet )
+ bContinue = pNewChgSet->GetTheChgdSet() == &GetAttrSet();
+ }
+
+ for( int nC = 0; nC < int(SAL_N_ELEMENTS(aFontSizeArr)); ++nC )
+ {
+ const SvxFontHeightItem *pFSize = aFontSizeArr[ nC ], *pOldFSize;
+ if( pFSize && (SfxItemState::SET == GetItemState(
+ pFSize->Which(), false, reinterpret_cast<const SfxPoolItem**>(&pOldFSize) )) &&
+ // Avoid recursion (SetAttr!)
+ !SfxPoolItem::areSame(pFSize, pOldFSize) )
+ {
+ if( 100 == pOldFSize->GetProp() &&
+ MapUnit::MapRelative == pOldFSize->GetPropUnit() )
+ {
+ // We set it to absolute -> do not propagate it further, unless
+ // we set it!
+ if( pNewChgSet )
+ bContinue = pNewChgSet->GetTheChgdSet() == &GetAttrSet();
+ }
+ else
+ {
+ // We had a relative value -> recalculate
+ sal_uInt32 nTmp = pOldFSize->GetHeight(); // keep so that we can compare
+ SvxFontHeightItem aNew(240 , 100, pFSize->Which());
+ aNew.SetHeight( pFSize->GetHeight(), pOldFSize->GetProp(),
+ pOldFSize->GetPropUnit() );
+ if( nTmp != aNew.GetHeight() )
+ {
+ SetFormatAttr( aNew );
+ bContinue = nullptr != pOldChgSet || bNewParent;
+ }
+ // We set it to absolute -> do not propagate it further, unless
+ // we set it!
+ else if( pNewChgSet )
+ bContinue = pNewChgSet->GetTheChgdSet() == &GetAttrSet();
+ }
+ }
+ }
+
+ if( bContinue )
+ SwFormatColl::SwClientNotify(rModify, rHint);
+}
+
+void SwTextFormatColl::SetLinkedCharFormat(SwCharFormat* pLink) { mpLinkedCharFormat = pLink; }
+
+const SwCharFormat* SwTextFormatColl::GetLinkedCharFormat() const { return mpLinkedCharFormat; }
+
+bool SwTextFormatColl::IsAtDocNodeSet() const
+{
+ SwIterator<SwContentNode,SwFormatColl> aIter( *this );
+ const SwNodes& rNds = GetDoc()->GetNodes();
+ for( SwContentNode* pNode = aIter.First(); pNode; pNode = aIter.Next() )
+ if( &(pNode->GetNodes()) == &rNds )
+ return true;
+
+ return false;
+}
+
+bool SwTextFormatColl::SetFormatAttr( const SfxPoolItem& rAttr )
+{
+ const bool bIsNumRuleItem = rAttr.Which() == RES_PARATR_NUMRULE;
+ if ( bIsNumRuleItem )
+ {
+ TextFormatCollFunc::RemoveFromNumRule( *this );
+ }
+
+ const bool bRet = SwFormatColl::SetFormatAttr( rAttr );
+
+ if ( bIsNumRuleItem )
+ {
+ TextFormatCollFunc::AddToNumRule( *this );
+ }
+
+ return bRet;
+}
+
+bool SwTextFormatColl::SetFormatAttr( const SfxItemSet& rSet )
+{
+ const bool bIsNumRuleItemAffected =
+ rSet.GetItemState( RES_PARATR_NUMRULE, false ) == SfxItemState::SET;
+ if ( bIsNumRuleItemAffected )
+ {
+ TextFormatCollFunc::RemoveFromNumRule( *this );
+ }
+
+ const bool bRet = SwFormatColl::SetFormatAttr( rSet );
+
+ if ( bIsNumRuleItemAffected )
+ {
+ TextFormatCollFunc::AddToNumRule( *this );
+ }
+
+ return bRet;
+}
+
+bool SwTextFormatColl::ResetFormatAttr( sal_uInt16 nWhich1, sal_uInt16 nWhich2 )
+{
+ const bool bIsNumRuleItemAffected =
+ ( nWhich2 != 0 && nWhich2 > nWhich1 )
+ ? ( nWhich1 <= RES_PARATR_NUMRULE &&
+ RES_PARATR_NUMRULE <= nWhich2 )
+ : nWhich1 == RES_PARATR_NUMRULE;
+ if ( bIsNumRuleItemAffected )
+ {
+ TextFormatCollFunc::RemoveFromNumRule( *this );
+ }
+
+ const bool bRet = SwFormatColl::ResetFormatAttr( nWhich1, nWhich2 );
+
+ return bRet;
+}
+
+// #i73790#
+sal_uInt16 SwTextFormatColl::ResetAllFormatAttr()
+{
+ const bool bOldState( mbStayAssignedToListLevelOfOutlineStyle );
+ mbStayAssignedToListLevelOfOutlineStyle = true;
+ // #i70748#
+ // Outline level is no longer a member, it is an attribute now.
+ // Thus, it needs to be restored, if the paragraph style is assigned
+ // to the outline style
+ const int nAssignedOutlineStyleLevel = IsAssignedToListLevelOfOutlineStyle()
+ ? GetAssignedOutlineStyleLevel()
+ : -1;
+
+ sal_uInt16 nRet = SwFormatColl::ResetAllFormatAttr();
+
+ // #i70748#
+ if ( nAssignedOutlineStyleLevel != -1 )
+ {
+ AssignToListLevelOfOutlineStyle( nAssignedOutlineStyleLevel );
+ }
+
+ mbStayAssignedToListLevelOfOutlineStyle = bOldState;
+
+ return nRet;
+}
+
+::sw::ListLevelIndents SwTextFormatColl::AreListLevelIndentsApplicable() const
+{
+ ::sw::ListLevelIndents ret(::sw::ListLevelIndents::No);
+ if (AreListLevelIndentsApplicableImpl(RES_MARGIN_FIRSTLINE))
+ {
+ ret |= ::sw::ListLevelIndents::FirstLine;
+ }
+ if (AreListLevelIndentsApplicableImpl(RES_MARGIN_TEXTLEFT))
+ {
+ ret |= ::sw::ListLevelIndents::LeftMargin;
+ }
+ return ret;
+}
+
+bool SwTextFormatColl::AreListLevelIndentsApplicableImpl(sal_uInt16 const nWhich) const
+{
+ bool bAreListLevelIndentsApplicable( true );
+
+ if ( GetItemState( RES_PARATR_NUMRULE ) != SfxItemState::SET )
+ {
+ // no list style applied to paragraph style
+ bAreListLevelIndentsApplicable = false;
+ }
+ else if (GetItemState(nWhich, false ) == SfxItemState::SET)
+ {
+ // paragraph style has hard-set indent attributes
+ bAreListLevelIndentsApplicable = false;
+ }
+ else if ( GetItemState( RES_PARATR_NUMRULE, false ) == SfxItemState::SET )
+ {
+ // list style is directly applied to paragraph style and paragraph
+ // style has no hard-set indent attributes
+ bAreListLevelIndentsApplicable = true;
+ }
+ else
+ {
+ // list style is applied through one of the parent paragraph styles and
+ // paragraph style has no hard-set indent attributes
+
+ // check parent paragraph styles
+ const SwTextFormatColl* pColl = dynamic_cast<const SwTextFormatColl*>(DerivedFrom());
+ while ( pColl )
+ {
+ if (pColl->GetAttrSet().GetItemState(nWhich, false) == SfxItemState::SET)
+ {
+ // indent attributes found in the paragraph style hierarchy.
+ bAreListLevelIndentsApplicable = false;
+ break;
+ }
+
+ if ( pColl->GetAttrSet().GetItemState( RES_PARATR_NUMRULE, false ) == SfxItemState::SET )
+ {
+ // paragraph style with the list style found and until now no
+ // indent attributes are found in the paragraph style hierarchy.
+ bAreListLevelIndentsApplicable = true;
+ break;
+ }
+
+ pColl = dynamic_cast<const SwTextFormatColl*>(pColl->DerivedFrom());
+ OSL_ENSURE( pColl,
+ "<SwTextFormatColl::AreListLevelIndentsApplicable()> - something wrong in paragraph style hierarchy. The applied list style is not found." );
+ }
+ }
+
+ return bAreListLevelIndentsApplicable;
+}
+
+void SwTextFormatColl::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwTextFormatColl"));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("symbol"), "%s", BAD_CAST(typeid(*this).name()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("name"), BAD_CAST(GetName().toUtf8().getStr()));
+ if (mpNextTextFormatColl)
+ {
+ (void)xmlTextWriterWriteAttribute(
+ pWriter, BAD_CAST("next"), BAD_CAST(mpNextTextFormatColl->GetName().toUtf8().getStr()));
+ }
+ if (mpLinkedCharFormat)
+ {
+ (void)xmlTextWriterWriteAttribute(
+ pWriter, BAD_CAST("linked"), BAD_CAST(mpLinkedCharFormat->GetName().toUtf8().getStr()));
+ }
+ GetAttrSet().dumpAsXml(pWriter);
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+void SwTextFormatColls::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwTextFormatColls"));
+ for (size_t i = 0; i < size(); ++i)
+ GetFormat(i)->dumpAsXml(pWriter);
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+//FEATURE::CONDCOLL
+
+SwCollCondition::SwCollCondition( SwTextFormatColl* pColl, Master_CollCondition nMasterCond,
+ sal_uInt32 nSubCond )
+ : SwClient( pColl ), m_nCondition( nMasterCond ),
+ m_nSubCondition( nSubCond )
+{
+}
+
+SwCollCondition::SwCollCondition( const SwCollCondition& rCopy )
+ : SwClient( const_cast<sw::BroadcastingModify*>(static_cast<const sw::BroadcastingModify*>(rCopy.GetRegisteredIn())) ),
+ m_nCondition( rCopy.m_nCondition ),
+ m_nSubCondition( rCopy.m_nSubCondition )
+{
+}
+
+SwCollCondition::~SwCollCondition()
+{
+}
+
+void SwCollCondition::RegisterToFormat( SwFormat& rFormat )
+{
+ rFormat.Add( this );
+}
+
+bool SwCollCondition::operator==( const SwCollCondition& rCmp ) const
+{
+ return ( m_nCondition == rCmp.m_nCondition )
+ && ( m_nSubCondition == rCmp.m_nSubCondition );
+}
+
+void SwCollCondition::SetCondition( Master_CollCondition nCond, sal_uInt32 nSubCond )
+{
+ m_nCondition = nCond;
+ m_nSubCondition = nSubCond;
+}
+
+SwConditionTextFormatColl::~SwConditionTextFormatColl()
+{
+}
+
+const SwCollCondition* SwConditionTextFormatColl::HasCondition(
+ const SwCollCondition& rCond ) const
+{
+ for (const auto &rpFnd : m_CondColls)
+ {
+ if (*rpFnd == rCond)
+ return rpFnd.get();
+ }
+
+ return nullptr;
+}
+
+void SwConditionTextFormatColl::InsertCondition( const SwCollCondition& rCond )
+{
+ for (SwFormatCollConditions::size_type n = 0; n < m_CondColls.size(); ++n)
+ {
+ if (*m_CondColls[ n ] == rCond)
+ {
+ m_CondColls.erase( m_CondColls.begin() + n );
+ break;
+ }
+ }
+
+ // Not found -> so insert it
+ m_CondColls.push_back( std::make_unique<SwCollCondition> (rCond) );
+}
+
+void SwConditionTextFormatColl::RemoveCondition( const SwCollCondition& rCond )
+{
+ for (SwFormatCollConditions::size_type n = 0; n < m_CondColls.size(); ++n)
+ {
+ if (*m_CondColls[ n ] == rCond)
+ {
+ m_CondColls.erase( m_CondColls.begin() + n );
+ }
+ }
+}
+
+void SwConditionTextFormatColl::SetConditions( const SwFormatCollConditions& rCndClls )
+{
+ // Copy the Conditions, but first delete the old ones
+ m_CondColls.clear();
+ SwDoc& rDoc = *GetDoc();
+ for (const auto &rpFnd : rCndClls)
+ {
+ SwTextFormatColl *const pTmpColl = rpFnd->GetTextFormatColl()
+ ? rDoc.CopyTextColl( *rpFnd->GetTextFormatColl() )
+ : nullptr;
+ std::unique_ptr<SwCollCondition> pNew;
+ pNew.reset(new SwCollCondition( pTmpColl, rpFnd->GetCondition(),
+ rpFnd->GetSubCondition() ));
+ m_CondColls.push_back( std::move(pNew) );
+ }
+}
+
+void SwTextFormatColl::SetAttrOutlineLevel( int nLevel)
+{
+ OSL_ENSURE( 0 <= nLevel && nLevel <= MAXLEVEL ,"SwTextFormatColl: Level Out Of Range" );
+ SetFormatAttr( SfxUInt16Item( RES_PARATR_OUTLINELEVEL,
+ o3tl::narrowing<sal_uInt16>(nLevel) ) );
+}
+
+int SwTextFormatColl::GetAttrOutlineLevel() const
+{
+ return GetFormatAttr(RES_PARATR_OUTLINELEVEL).GetValue();
+}
+
+int SwTextFormatColl::GetAssignedOutlineStyleLevel() const
+{
+ OSL_ENSURE( IsAssignedToListLevelOfOutlineStyle(),
+ "<SwTextFormatColl::GetAssignedOutlineStyleLevel()> - misuse of method");
+ return GetAttrOutlineLevel() - 1;
+}
+
+void SwTextFormatColl::AssignToListLevelOfOutlineStyle(const int nAssignedListLevel)
+{
+ mbAssignedToOutlineStyle = true;
+ SetAttrOutlineLevel(nAssignedListLevel+1);
+
+ // #i100277#
+ SwIterator<SwTextFormatColl,SwFormatColl> aIter( *this );
+ SwTextFormatColl* pDerivedTextFormatColl = aIter.First();
+ while ( pDerivedTextFormatColl != nullptr )
+ {
+ if ( !pDerivedTextFormatColl->IsAssignedToListLevelOfOutlineStyle() )
+ {
+ if ( pDerivedTextFormatColl->GetItemState( RES_PARATR_NUMRULE, false ) == SfxItemState::DEFAULT )
+ {
+ SwNumRuleItem aItem;
+ pDerivedTextFormatColl->SetFormatAttr( aItem );
+ }
+ if ( pDerivedTextFormatColl->GetItemState( RES_PARATR_OUTLINELEVEL, false ) == SfxItemState::DEFAULT )
+ {
+ pDerivedTextFormatColl->SetAttrOutlineLevel( 0 );
+ }
+ }
+
+ pDerivedTextFormatColl = aIter.Next();
+ }
+}
+
+void SwTextFormatColl::DeleteAssignmentToListLevelOfOutlineStyle()
+{
+ mbAssignedToOutlineStyle = false;
+ ResetFormatAttr(RES_PARATR_OUTLINELEVEL);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/ftnidx.cxx b/sw/source/core/doc/ftnidx.cxx
new file mode 100644
index 0000000000..81f6378c5a
--- /dev/null
+++ b/sw/source/core/doc/ftnidx.cxx
@@ -0,0 +1,519 @@
+/* -*- 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 <txtftn.hxx>
+#include <fmtftn.hxx>
+#include <ftninfo.hxx>
+#include <doc.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <redline.hxx>
+#include <ftnidx.hxx>
+#include <ndtxt.hxx>
+#include <ndindex.hxx>
+#include <section.hxx>
+#include <fmtftntx.hxx>
+#include <rootfrm.hxx>
+#include <txtfrm.hxx>
+
+namespace sw {
+
+bool IsFootnoteDeleted(IDocumentRedlineAccess const& rIDRA,
+ SwTextFootnote const& rTextFootnote)
+{
+ SwRedlineTable::size_type tmp;
+ SwPosition const pos(rTextFootnote.GetTextNode(), rTextFootnote.GetStart());
+ SwRangeRedline const*const pRedline(rIDRA.GetRedline(pos, &tmp));
+ return (pRedline
+ && pRedline->GetType() == RedlineType::Delete
+ && *pRedline->GetPoint() != *pRedline->GetMark());
+}
+
+}
+
+using sw::IsFootnoteDeleted;
+
+bool CompareSwFootnoteIdxs::operator()(SwTextFootnote* const& lhs, SwTextFootnote* const& rhs) const
+{
+ SwNodeOffset nIdxLHS = SwTextFootnote_GetIndex( lhs );
+ SwNodeOffset nIdxRHS = SwTextFootnote_GetIndex( rhs );
+ return ( nIdxLHS == nIdxRHS && lhs->GetStart() < rhs->GetStart() ) || nIdxLHS < nIdxRHS;
+}
+
+void SwFootnoteIdxs::UpdateFootnote( const SwNode& rStt )
+{
+ if( empty() )
+ return;
+
+ // Get the NodesArray using the first foot note's StartIndex
+ SwDoc& rDoc = const_cast<SwDoc&>(rStt.GetDoc());
+ if( rDoc.IsInReading() )
+ return ;
+ SwTextFootnote* pTextFootnote;
+
+ const SwEndNoteInfo& rEndInfo = rDoc.GetEndNoteInfo();
+ const SwFootnoteInfo& rFootnoteInfo = rDoc.GetFootnoteInfo();
+ IDocumentRedlineAccess const& rIDRA(rDoc.getIDocumentRedlineAccess());
+
+ // For normal foot notes we treat per-chapter and per-document numbering
+ // separately. For Endnotes we only have per-document numbering.
+ if( FTNNUM_CHAPTER == rFootnoteInfo.m_eNum )
+ {
+ SwRootFrame const* pLayout(nullptr);
+ o3tl::sorted_vector<SwRootFrame*> layouts = rDoc.GetAllLayouts();
+ // sw_redlinehide: here we need to know if there's *any* layout with
+ // IsHideRedlines(), because then the hidden-numbers have to be updated
+ for (SwRootFrame const* pTmp : layouts)
+ {
+ if (pTmp->IsHideRedlines())
+ {
+ pLayout = pTmp;
+ }
+ }
+
+ const SwOutlineNodes& rOutlNds = rDoc.GetNodes().GetOutLineNds();
+ const SwNode *pChapterStartHidden(&rDoc.GetNodes().GetEndOfExtras());
+ SwNodeOffset nChapterStart(pChapterStartHidden->GetIndex());
+ SwNodeOffset nChapterEnd(rDoc.GetNodes().GetEndOfContent().GetIndex());
+ SwNodeOffset nChapterEndHidden(nChapterEnd);
+ if( !rOutlNds.empty() )
+ {
+ // Find the Chapter's start, which contains rStt
+ size_t n = 0;
+
+ for( ; n < rOutlNds.size(); ++n )
+ if( rOutlNds[ n ]->GetIndex() > rStt.GetIndex() )
+ break; // found it!
+ else if ( rOutlNds[ n ]->GetTextNode()->GetAttrOutlineLevel() == 1 )
+ {
+ nChapterStart = rOutlNds[ n ]->GetIndex();
+ if (!pLayout || sw::IsParaPropsNode(*pLayout, *rOutlNds[n]->GetTextNode()))
+ {
+ pChapterStartHidden = rOutlNds[ n ];
+ }
+ }
+ // now find the end of the range
+ for( ; n < rOutlNds.size(); ++n )
+ if ( rOutlNds[ n ]->GetTextNode()->GetAttrOutlineLevel() == 1 )
+ {
+ nChapterEnd = rOutlNds[ n ]->GetIndex();
+ break;
+ }
+
+ // continue to find end of hidden-chapter
+ for ( ; n < rOutlNds.size(); ++n)
+ {
+ if (rOutlNds[n]->GetTextNode()->GetAttrOutlineLevel() == 1
+ && (!pLayout || sw::IsParaPropsNode(*pLayout, *rOutlNds[n]->GetTextNode())))
+ {
+ nChapterEndHidden = rOutlNds[n]->GetIndex();
+ break;
+ }
+ }
+ }
+
+ size_t nPos = 0;
+ size_t nFootnoteNo = 1;
+ size_t nFootnoteNoHidden = 1;
+ if (SeekEntry( *pChapterStartHidden, &nPos ) && nPos)
+ {
+ // Step forward until the Index is not the same anymore
+ const SwNode* pCmpNd = &rStt;
+ while( nPos && pCmpNd == &((*this)[ --nPos ]->GetTextNode()) )
+ ;
+ ++nPos;
+ }
+
+ if( nPos == size() ) // nothing found
+ return;
+
+ if( rOutlNds.empty() )
+ {
+ nFootnoteNo = nPos+1;
+ if (nPos)
+ {
+ nFootnoteNoHidden = (*this)[nPos - 1]->GetFootnote().GetNumberRLHidden() + 1;
+ }
+ }
+
+ for( ; nPos < size(); ++nPos )
+ {
+ pTextFootnote = (*this)[ nPos ];
+ SwNodeOffset const nNode(pTextFootnote->GetTextNode().GetIndex());
+ if (nChapterEndHidden <= nNode)
+ break;
+
+ const SwFormatFootnote &rFootnote = pTextFootnote->GetFootnote();
+ if( rFootnote.GetNumStr().isEmpty() && !rFootnote.IsEndNote() &&
+ !SwUpdFootnoteEndNtAtEnd::FindSectNdWithEndAttr( *pTextFootnote ))
+ {
+ pTextFootnote->SetNumber(
+ (nChapterStart <= nNode && nNode < nChapterEnd)
+ ? rFootnoteInfo.m_nFootnoteOffset + nFootnoteNo
+ : rFootnote.GetNumber(),
+ rFootnoteInfo.m_nFootnoteOffset + nFootnoteNoHidden,
+ rFootnote.GetNumStr() );
+ if (nChapterStart <= nNode && nNode < nChapterEnd)
+ {
+ ++nFootnoteNo;
+ }
+ if (!IsFootnoteDeleted(rIDRA, *pTextFootnote))
+ {
+ ++nFootnoteNoHidden;
+ }
+ }
+ }
+ }
+
+ SwUpdFootnoteEndNtAtEnd aNumArr;
+
+ // unless we have per-document numbering, only look at endnotes here
+ const bool bEndNoteOnly = FTNNUM_DOC != rFootnoteInfo.m_eNum;
+
+ size_t nPos;
+ size_t nFootnoteNo = 1;
+ size_t nEndNo = 1;
+ size_t nFootnoteNoHidden = 1;
+ size_t nEndNoHidden = 1;
+ SwNodeOffset nUpdNdIdx = rStt.GetIndex();
+ for( nPos = 0; nPos < size(); ++nPos )
+ {
+ pTextFootnote = (*this)[ nPos ];
+ if( nUpdNdIdx <= pTextFootnote->GetTextNode().GetIndex() )
+ break;
+
+ const SwFormatFootnote &rFootnote = pTextFootnote->GetFootnote();
+ if( rFootnote.GetNumStr().isEmpty() )
+ {
+ if (!aNumArr.ChkNumber(rIDRA, *pTextFootnote).first)
+ {
+ if( pTextFootnote->GetFootnote().IsEndNote() )
+ {
+ nEndNo++;
+ if (!IsFootnoteDeleted(rIDRA, *pTextFootnote))
+ {
+ ++nEndNoHidden;
+ }
+ }
+ else
+ {
+ nFootnoteNo++;
+ if (!IsFootnoteDeleted(rIDRA, *pTextFootnote))
+ {
+ ++nFootnoteNoHidden;
+ }
+ }
+ }
+ }
+ }
+
+ // Set the array number for all footnotes starting from nPos
+ for( ; nPos < size(); ++nPos )
+ {
+ pTextFootnote = (*this)[ nPos ];
+ const SwFormatFootnote &rFootnote = pTextFootnote->GetFootnote();
+ if( rFootnote.GetNumStr().isEmpty() )
+ {
+ std::pair<sal_uInt16, sal_uInt16> nSectNo = aNumArr.ChkNumber(rIDRA, *pTextFootnote);
+ if (!nSectNo.first && (rFootnote.IsEndNote() || !bEndNoteOnly))
+ {
+ if (rFootnote.IsEndNote())
+ {
+ nSectNo.first = rEndInfo.m_nFootnoteOffset + nEndNo;
+ ++nEndNo;
+ nSectNo.second = rEndInfo.m_nFootnoteOffset + nEndNoHidden;
+ if (!IsFootnoteDeleted(rIDRA, *pTextFootnote))
+ {
+ ++nEndNoHidden;
+ }
+ }
+ else
+ {
+ nSectNo.first = rFootnoteInfo.m_nFootnoteOffset + nFootnoteNo;
+ ++nFootnoteNo;
+ nSectNo.second = rFootnoteInfo.m_nFootnoteOffset + nFootnoteNoHidden;
+ if (!IsFootnoteDeleted(rIDRA, *pTextFootnote))
+ {
+ ++nFootnoteNoHidden;
+ }
+ }
+ }
+
+ if (nSectNo.first)
+ {
+ pTextFootnote->SetNumber(nSectNo.first, nSectNo.second, rFootnote.GetNumStr());
+ }
+ }
+ }
+}
+
+void SwFootnoteIdxs::UpdateAllFootnote()
+{
+ if( empty() )
+ return;
+
+ // Get the NodesArray via the StartIndex of the first Footnote
+ SwDoc& rDoc = const_cast<SwDoc&>((*this)[ 0 ]->GetTextNode().GetDoc());
+ SwTextFootnote* pTextFootnote;
+ const SwEndNoteInfo& rEndInfo = rDoc.GetEndNoteInfo();
+ const SwFootnoteInfo& rFootnoteInfo = rDoc.GetFootnoteInfo();
+ IDocumentRedlineAccess const& rIDRA(rDoc.getIDocumentRedlineAccess());
+
+ SwUpdFootnoteEndNtAtEnd aNumArr;
+
+ SwRootFrame const* pLayout = rDoc.getIDocumentLayoutAccess().GetCurrentLayout();
+ o3tl::sorted_vector<SwRootFrame*> aAllLayouts = rDoc.GetAllLayouts();
+ // For normal Footnotes per-chapter and per-document numbering are treated separately.
+ // For Endnotes we only have document-wise numbering.
+ if( FTNNUM_CHAPTER == rFootnoteInfo.m_eNum )
+ {
+ // sw_redlinehide: here we need to know if there's *any* layout with
+ // IsHideRedlines(), because then the hidden-numbers have to be updated
+ for (SwRootFrame const* pTmp : aAllLayouts)
+ {
+ if (pTmp->IsHideRedlines())
+ {
+ pLayout = pTmp;
+ }
+ }
+
+ const SwOutlineNodes& rOutlNds = rDoc.GetNodes().GetOutLineNds();
+ sal_uInt16 nNo = 1; // Number for the Footnotes
+ sal_uInt16 nNoNo = 1;
+ size_t nFootnoteIdx = 0; // Index into theFootnoteIdx array
+ for( size_t n = 0; n < rOutlNds.size(); ++n )
+ {
+ if ( rOutlNds[ n ]->GetTextNode()->GetAttrOutlineLevel() == 1 )
+ {
+ SwNodeOffset nCapStt = rOutlNds[ n ]->GetIndex(); // Start of a new chapter
+ for( ; nFootnoteIdx < size(); ++nFootnoteIdx )
+ {
+ pTextFootnote = (*this)[ nFootnoteIdx ];
+ if( pTextFootnote->GetTextNode().GetIndex() >= nCapStt )
+ break;
+
+ // Endnotes are per-document only
+ const SwFormatFootnote &rFootnote = pTextFootnote->GetFootnote();
+ if( !rFootnote.IsEndNote() && rFootnote.GetNumStr().isEmpty() &&
+ !SwUpdFootnoteEndNtAtEnd::FindSectNdWithEndAttr( *pTextFootnote ))
+ {
+ pTextFootnote->SetNumber(
+ rFootnoteInfo.m_nFootnoteOffset + nNo,
+ rFootnoteInfo.m_nFootnoteOffset + nNoNo,
+ rFootnote.GetNumStr() );
+ ++nNo;
+ if (!IsFootnoteDeleted(rIDRA, *pTextFootnote))
+ {
+ ++nNoNo;
+ }
+ }
+ }
+ if( nFootnoteIdx >= size() )
+ break; // ok, everything is updated
+ nNo = 1;
+ // sw_redlinehide: this means the numbers are layout dependent in chapter case
+ if (!pLayout || sw::IsParaPropsNode(*pLayout, *rOutlNds[ n ]->GetTextNode()))
+ {
+ nNoNo = 1;
+ }
+ }
+ }
+
+ for (nNo = 1, nNoNo = 1; nFootnoteIdx < size(); ++nFootnoteIdx)
+ {
+ // Endnotes are per-document
+ pTextFootnote = (*this)[ nFootnoteIdx ];
+ const SwFormatFootnote &rFootnote = pTextFootnote->GetFootnote();
+ if( !rFootnote.IsEndNote() && rFootnote.GetNumStr().isEmpty() &&
+ !SwUpdFootnoteEndNtAtEnd::FindSectNdWithEndAttr( *pTextFootnote ))
+ {
+ pTextFootnote->SetNumber(
+ rFootnoteInfo.m_nFootnoteOffset + nNo,
+ rFootnoteInfo.m_nFootnoteOffset + nNoNo,
+ rFootnote.GetNumStr() );
+ ++nNo;
+ if (!IsFootnoteDeleted(rIDRA, *pTextFootnote))
+ {
+ ++nNoNo;
+ }
+ }
+ }
+ }
+
+ // We use bool here, so that we also iterate through the Endnotes with a chapter setting.
+ const bool bEndNoteOnly = FTNNUM_DOC != rFootnoteInfo.m_eNum;
+ sal_uInt16 nFootnoteNo = 1;
+ sal_uInt16 nEndnoteNo = 1;
+ sal_uInt16 nFootnoteNoHidden = 1;
+ sal_uInt16 nEndnoteNoHidden = 1;
+ for( size_t nPos = 0; nPos < size(); ++nPos )
+ {
+ pTextFootnote = (*this)[ nPos ];
+ const SwFormatFootnote &rFootnote = pTextFootnote->GetFootnote();
+ if( rFootnote.GetNumStr().isEmpty() )
+ {
+ std::pair<sal_uInt16, sal_uInt16> nSectNo = aNumArr.ChkNumber(rIDRA, *pTextFootnote);
+ if (!nSectNo.first && (rFootnote.IsEndNote() || !bEndNoteOnly))
+ {
+ if (rFootnote.IsEndNote())
+ {
+ nSectNo.first = rEndInfo.m_nFootnoteOffset + nEndnoteNo;
+ ++nEndnoteNo;
+ nSectNo.second = rEndInfo.m_nFootnoteOffset + nEndnoteNoHidden;
+ if (!IsFootnoteDeleted(rIDRA, *pTextFootnote))
+ {
+ ++nEndnoteNoHidden;
+ }
+ }
+ else
+ {
+ nSectNo.first = rFootnoteInfo.m_nFootnoteOffset + nFootnoteNo;
+ ++nFootnoteNo;
+ nSectNo.second = rFootnoteInfo.m_nFootnoteOffset + nFootnoteNoHidden;
+ if (!IsFootnoteDeleted(rIDRA, *pTextFootnote))
+ {
+ ++nFootnoteNoHidden;
+ }
+ }
+ }
+
+ if (nSectNo.first)
+ {
+ pTextFootnote->SetNumber(nSectNo.first, nSectNo.second, rFootnote.GetNumStr());
+ }
+ }
+ }
+
+ if (pLayout && FTNNUM_PAGE == rFootnoteInfo.m_eNum)
+ for( auto aLayout : aAllLayouts )
+ aLayout->UpdateFootnoteNums();
+}
+
+SwTextFootnote* SwFootnoteIdxs::SeekEntry( const SwNode& rPos, size_t* pFndPos ) const
+{
+ SwNodeOffset nIdx = rPos.GetIndex();
+
+ size_t nO = size();
+ size_t nU = 0;
+ if( nO > 0 )
+ {
+ nO--;
+ while( nU <= nO )
+ {
+ const size_t nM = nU + ( nO - nU ) / 2;
+ SwNodeOffset nNdIdx = SwTextFootnote_GetIndex( (*this)[ nM ] );
+ if( nNdIdx == nIdx )
+ {
+ if( pFndPos )
+ *pFndPos = nM;
+ return (*this)[ nM ];
+ }
+ else if( nNdIdx < nIdx )
+ nU = nM + 1;
+ else if( nM == 0 )
+ {
+ if( pFndPos )
+ *pFndPos = nU;
+ return nullptr;
+ }
+ else
+ nO = nM - 1;
+ }
+ }
+ if( pFndPos )
+ *pFndPos = nU;
+ return nullptr;
+}
+
+const SwSectionNode* SwUpdFootnoteEndNtAtEnd::FindSectNdWithEndAttr(
+ const SwTextFootnote& rTextFootnote )
+{
+ sal_uInt16 nWh = rTextFootnote.GetFootnote().IsEndNote() ?
+ sal_uInt16(RES_END_AT_TXTEND) : sal_uInt16(RES_FTN_AT_TXTEND);
+ const SwSectionNode* pNd = rTextFootnote.GetTextNode().FindSectionNode();
+ while( pNd )
+ {
+ sal_uInt16 nVal = static_cast<const SwFormatFootnoteEndAtTextEnd&>(pNd->GetSection().GetFormat()->
+ GetFormatAttr( nWh )).GetValue();
+ if( FTNEND_ATTXTEND_OWNNUMSEQ == nVal || FTNEND_ATTXTEND_OWNNUMANDFMT == nVal )
+ break;
+ pNd = pNd->StartOfSectionNode()->FindSectionNode();
+ }
+
+ return pNd;
+}
+
+std::pair<sal_uInt16, sal_uInt16> SwUpdFootnoteEndNtAtEnd::GetNumber(
+ IDocumentRedlineAccess const& rIDRA,
+ const SwTextFootnote& rTextFootnote,
+ const SwSectionNode& rNd )
+{
+ std::pair<sal_uInt16, sal_uInt16> nRet(0, 0);
+ sal_uInt16 nWh;
+ std::vector<const SwSectionNode*>* pArr;
+ std::vector<std::pair<sal_uInt16, sal_uInt16>> *pNum;
+ if( rTextFootnote.GetFootnote().IsEndNote() )
+ {
+ pArr = &m_aEndSections;
+ pNum = &m_aEndNumbers;
+ nWh = RES_END_AT_TXTEND;
+ }
+ else
+ {
+ pArr = &m_aFootnoteSections;
+ pNum = &m_aFootnoteNumbers;
+ nWh = RES_FTN_AT_TXTEND;
+ }
+
+ for( size_t n = pArr->size(); n; )
+ if( (*pArr)[ --n ] == &rNd )
+ {
+ nRet.first = ++((*pNum)[ n ].first);
+ if (!IsFootnoteDeleted(rIDRA, rTextFootnote))
+ {
+ ++((*pNum)[ n ].second);
+ }
+ nRet.second = ((*pNum)[ n ].second);
+ break;
+ }
+
+ if (!nRet.first)
+ {
+ pArr->push_back( &rNd );
+ sal_uInt16 const tmp = static_cast<const SwFormatFootnoteEndAtTextEnd&>(
+ rNd.GetSection().GetFormat()->
+ GetFormatAttr( nWh )).GetOffset();
+ nRet.first = tmp + 1;
+ nRet.second = tmp + 1;
+ pNum->push_back( nRet );
+ }
+ return nRet;
+}
+
+std::pair<sal_uInt16, sal_uInt16> SwUpdFootnoteEndNtAtEnd::ChkNumber(
+ IDocumentRedlineAccess const& rIDRA,
+ const SwTextFootnote& rTextFootnote)
+{
+ const SwSectionNode* pSectNd = FindSectNdWithEndAttr( rTextFootnote );
+ return pSectNd
+ ? GetNumber(rIDRA, rTextFootnote, *pSectNd)
+ : std::pair<sal_uInt16, sal_uInt16>(0, 0);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/gctable.cxx b/sw/source/core/doc/gctable.cxx
new file mode 100644
index 0000000000..30937cadd5
--- /dev/null
+++ b/sw/source/core/doc/gctable.cxx
@@ -0,0 +1,463 @@
+/* -*- 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 <hintids.hxx>
+#include <tblrwcl.hxx>
+#include <algorithm>
+#include <editeng/borderline.hxx>
+#include <editeng/boxitem.hxx>
+#include <osl/diagnose.h>
+
+using namespace ::editeng;
+
+static const SvxBorderLine* GetLineTB( const SvxBoxItem* pBox, bool bTop )
+{
+ return bTop ? pBox->GetTop() : pBox->GetBottom();
+}
+
+bool SwGCBorder_BoxBrd::CheckLeftBorderOfFormat( const SwFrameFormat& rFormat )
+{
+ if( const SvxBoxItem* pItem = rFormat.GetItemIfSet( RES_BOX ) )
+ {
+ const SvxBorderLine* pBrd = pItem->GetLeft();
+ if( pBrd )
+ {
+ if( *m_pBorderLine == *pBrd )
+ m_bAnyBorderFind = true;
+ return true;
+ }
+ }
+ return false;
+}
+
+static bool lcl_GCBorder_ChkBoxBrd_B( const SwTableBox* pBox, SwGCBorder_BoxBrd* pPara );
+
+static bool lcl_GCBorder_ChkBoxBrd_L( const SwTableLine* pLine, SwGCBorder_BoxBrd* pPara )
+{
+ const SwTableBox* pBox = pLine->GetTabBoxes().front();
+ return lcl_GCBorder_ChkBoxBrd_B( pBox, pPara );
+}
+
+static bool lcl_GCBorder_ChkBoxBrd_B( const SwTableBox* pBox, SwGCBorder_BoxBrd* pPara )
+{
+ if( !pBox->GetTabLines().empty() )
+ {
+ for( auto pLine : pBox->GetTabLines() )
+ {
+ if (!lcl_GCBorder_ChkBoxBrd_L( pLine, pPara ))
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ return pPara->CheckLeftBorderOfFormat( *pBox->GetFrameFormat() );
+}
+
+static void lcl_GCBorder_GetLastBox_B( const SwTableBox* pBox, SwTableBoxes* pPara );
+
+static void lcl_GCBorder_GetLastBox_L( const SwTableLine* pLine, SwTableBoxes* pPara )
+{
+ const SwTableBoxes& rBoxes = pLine->GetTabBoxes();
+ SwTableBox* pBox = rBoxes.back();
+ lcl_GCBorder_GetLastBox_B( pBox, pPara );
+}
+
+static void lcl_GCBorder_GetLastBox_B( const SwTableBox* pBox, SwTableBoxes* pPara )
+{
+ const SwTableLines& rLines = pBox->GetTabLines();
+ if( !rLines.empty() )
+ {
+ for( const SwTableLine* pLine : rLines )
+ lcl_GCBorder_GetLastBox_L( pLine, pPara );
+ }
+ else
+ pPara->push_back( const_cast<SwTableBox*>(pBox) );
+}
+
+// Find the "end" of the passed BorderLine. Returns the "Layout"Pos!
+static sal_uInt16 lcl_FindEndPosOfBorder( const SwCollectTableLineBoxes& rCollTLB,
+ const SvxBorderLine& rBrdLn, size_t& rStt, bool bTop )
+{
+ sal_uInt16 nPos, nLastPos = 0;
+ for( size_t nEnd = rCollTLB.Count(); rStt < nEnd; ++rStt )
+ {
+ const SvxBorderLine* pBrd;
+ const SwTableBox& rBox = rCollTLB.GetBox( rStt, &nPos );
+ const SvxBoxItem* pItem = rBox.GetFrameFormat()->GetItemIfSet(RES_BOX);
+
+ if( !pItem )
+ break;
+ pBrd = GetLineTB( pItem, bTop );
+ if( !pBrd || *pBrd != rBrdLn )
+ break;
+ nLastPos = nPos;
+ }
+ return nLastPos;
+}
+
+static const SvxBorderLine* lcl_GCBorder_GetBorder( const SwTableBox& rBox,
+ bool bTop,
+ const SvxBoxItem** ppItem )
+{
+ *ppItem = rBox.GetFrameFormat()->GetItemIfSet( RES_BOX );
+ if (*ppItem)
+ return GetLineTB( *ppItem, bTop );
+ return nullptr;
+}
+
+static void lcl_GCBorder_DelBorder( const SwCollectTableLineBoxes& rCollTLB,
+ size_t& rStt, bool bTop,
+ const SvxBorderLine& rLine,
+ const SvxBoxItem* pItem,
+ sal_uInt16 nEndPos,
+ SwShareBoxFormats* pShareFormats )
+{
+ SwTableBox* pBox = const_cast<SwTableBox*>(&rCollTLB.GetBox( rStt ));
+ sal_uInt16 nNextPos;
+ const SvxBorderLine* pLn = &rLine;
+
+ do {
+ if( pLn && *pLn == rLine )
+ {
+ SvxBoxItem aBox( *pItem );
+ if( bTop )
+ aBox.SetLine( nullptr, SvxBoxItemLine::TOP );
+ else
+ aBox.SetLine( nullptr, SvxBoxItemLine::BOTTOM );
+
+ if( pShareFormats )
+ pShareFormats->SetAttr( *pBox, aBox );
+ else
+ pBox->ClaimFrameFormat()->SetFormatAttr( aBox );
+ }
+
+ if( ++rStt >= rCollTLB.Count() )
+ break;
+
+ pBox = const_cast<SwTableBox*>(&rCollTLB.GetBox( rStt, &nNextPos ));
+ if( nNextPos > nEndPos )
+ break;
+
+ pLn = lcl_GCBorder_GetBorder( *pBox, bTop, &pItem );
+
+ } while( true );
+}
+
+static void lcl_GC_Box_Border( const SwTableBox* pBox, SwGCLineBorder* pPara );
+
+void sw_GC_Line_Border( const SwTableLine* pLine, SwGCLineBorder* pGCPara )
+{
+ // First the right edge with the left edge of the succeeding Box within this Line
+ {
+ SwGCBorder_BoxBrd aBPara;
+ const SvxBorderLine* pBrd;
+ const SwTableBoxes& rBoxes = pLine->GetTabBoxes();
+ for( SwTableBoxes::size_type n = 0, nBoxes = rBoxes.size() - 1; n < nBoxes; ++n )
+ {
+ SwTableBoxes aBoxes;
+ {
+ SwTableBox* pBox = rBoxes[ n ];
+ if( pBox->GetSttNd() )
+ aBoxes.insert( aBoxes.begin(), pBox );
+ else
+ lcl_GCBorder_GetLastBox_B( pBox, &aBoxes );
+ }
+
+ for( SwTableBoxes::size_type i = aBoxes.size(); i; )
+ {
+ SwTableBox* pBox = aBoxes[ --i ];
+ if( const SvxBoxItem* pItem = pBox->GetFrameFormat()->GetItemIfSet( RES_BOX ) )
+ {
+ pBrd = pItem->GetRight();
+ if( pBrd )
+ {
+ aBPara.SetBorder( *pBrd );
+ const SwTableBox* pNextBox = rBoxes[n+1];
+ if( lcl_GCBorder_ChkBoxBrd_B( pNextBox, &aBPara ) &&
+ aBPara.IsAnyBorderFound() )
+ {
+ SvxBoxItem aBox( *pItem );
+ aBox.SetLine( nullptr, SvxBoxItemLine::RIGHT );
+ if( pGCPara->pShareFormats )
+ pGCPara->pShareFormats->SetAttr( *pBox, aBox );
+ else
+ pBox->ClaimFrameFormat()->SetFormatAttr( aBox );
+ }
+ }
+ }
+ }
+
+ aBoxes.clear();
+ }
+ }
+
+ // And now the own bottom edge with the succeeding top edge
+ if( !pGCPara->IsLastLine() )
+ {
+ SwCollectTableLineBoxes aBottom( false );
+ SwCollectTableLineBoxes aTop( true );
+
+ sw_Line_CollectBox( pLine, &aBottom );
+
+ const SwTableLine* pNextLine = (*pGCPara->pLines)[ pGCPara->nLinePos+1 ];
+ sw_Line_CollectBox( pNextLine, &aTop );
+
+ // remove all "duplicated" Lines that are the same
+ sal_uInt16 nBtmPos, nTopPos;
+
+ size_t nSttBtm {0};
+ size_t nSttTop {0};
+ const size_t nEndBtm {aBottom.Count()};
+ const size_t nEndTop {aTop.Count()};
+
+ const SwTableBox *pBtmBox = &aBottom.GetBox( nSttBtm++, &nBtmPos );
+ const SwTableBox *pTopBox = &aTop.GetBox( nSttTop++, &nTopPos );
+ const SvxBoxItem *pBtmItem = nullptr, *pTopItem = nullptr;
+ const SvxBorderLine *pBtmLine(nullptr), *pTopLine(nullptr);
+ bool bGetTopItem = true, bGetBtmItem = true;
+
+ do {
+ if( bGetBtmItem )
+ pBtmLine = lcl_GCBorder_GetBorder( *pBtmBox, false, &pBtmItem );
+ if( bGetTopItem )
+ pTopLine = lcl_GCBorder_GetBorder( *pTopBox, true, &pTopItem );
+
+ if( pTopLine && pBtmLine && *pTopLine == *pBtmLine )
+ {
+ // We can remove one, but which one?
+ const size_t nSavSttBtm {nSttBtm};
+ const size_t nSavSttTop {nSttTop};
+ sal_uInt16 nBtmEndPos = ::lcl_FindEndPosOfBorder( aBottom,
+ *pTopLine, nSttBtm, false );
+ if( !nBtmEndPos ) nBtmEndPos = nBtmPos;
+ sal_uInt16 nTopEndPos = ::lcl_FindEndPosOfBorder( aTop,
+ *pTopLine, nSttTop, true );
+ if( !nTopEndPos ) nTopEndPos = nTopPos;
+
+ if( nTopEndPos <= nBtmEndPos )
+ {
+ // Delete the TopBorders until BottomEndPos
+ nSttTop = nSavSttTop;
+ if( nTopPos <= nBtmEndPos )
+ lcl_GCBorder_DelBorder( aTop, --nSttTop, true,
+ *pBtmLine, pTopItem, nBtmEndPos,
+ pGCPara->pShareFormats );
+ else
+ nSttBtm = nSavSttBtm;
+ }
+ else
+ {
+ // Else delete the BottomBorders until TopEndPos
+ nSttBtm = nSavSttBtm;
+ if( nBtmPos <= nTopEndPos )
+ lcl_GCBorder_DelBorder( aBottom, --nSttBtm, false,
+ *pTopLine, pBtmItem, nTopEndPos,
+ pGCPara->pShareFormats );
+ else
+ nSttTop = nSavSttTop;
+ }
+ nTopPos = nBtmPos;
+ }
+
+ if( nTopPos == nBtmPos )
+ {
+ if( nSttBtm >= nEndBtm || nSttTop >= nEndTop )
+ break;
+
+ pBtmBox = &aBottom.GetBox( nSttBtm++, &nBtmPos );
+ pTopBox = &aTop.GetBox( nSttTop++, &nTopPos );
+ bGetTopItem = bGetBtmItem = true;
+ }
+ else if( nTopPos < nBtmPos )
+ {
+ if( nSttTop >= nEndTop )
+ break;
+ pTopBox = &aTop.GetBox( nSttTop++, &nTopPos );
+ bGetTopItem = true;
+ bGetBtmItem = false;
+ }
+ else
+ {
+ if( nSttBtm >= nEndBtm )
+ break;
+ pBtmBox = &aBottom.GetBox( nSttBtm++, &nBtmPos );
+ bGetTopItem = false;
+ bGetBtmItem = true;
+ }
+
+ } while( true );
+ }
+
+ for( const auto& rpBox : pLine->GetTabBoxes() )
+ lcl_GC_Box_Border(rpBox, pGCPara );
+
+ ++pGCPara->nLinePos;
+}
+
+static void lcl_GC_Box_Border( const SwTableBox* pBox, SwGCLineBorder* pPara )
+{
+ if( !pBox->GetTabLines().empty() )
+ {
+ SwGCLineBorder aPara( *pBox );
+ aPara.pShareFormats = pPara->pShareFormats;
+ for( const SwTableLine* pLine : pBox->GetTabLines() )
+ sw_GC_Line_Border( pLine, &aPara );
+ }
+}
+
+namespace {
+
+struct GCLinePara
+{
+ SwTableLines* pLns;
+ SwShareBoxFormats* pShareFormats;
+
+ GCLinePara( SwTableLines& rLns, GCLinePara* pPara = nullptr )
+ : pLns( &rLns ), pShareFormats( pPara ? pPara->pShareFormats : nullptr )
+ {}
+};
+
+}
+
+static bool lcl_MergeGCLine(SwTableLine* pLine, GCLinePara* pPara);
+
+static bool lcl_MergeGCBox(SwTableBox* pTableBox, GCLinePara* pPara)
+{
+ if( !pTableBox->GetTabLines().empty() )
+ {
+ // ATTENTION: The Line count can change!
+ GCLinePara aPara( pTableBox->GetTabLines(), pPara );
+ for( SwTableLines::size_type n = 0;
+ n < pTableBox->GetTabLines().size() && lcl_MergeGCLine( pTableBox->GetTabLines()[n], &aPara );
+ ++n )
+ ;
+
+ if( 1 == pTableBox->GetTabLines().size() )
+ {
+ // we have a box with a single line, so we just replace it by the line's boxes
+ SwTableLine* pInsLine = pTableBox->GetUpper();
+ SwTableLine* pCpyLine = pTableBox->GetTabLines()[0];
+ SwTableBoxes::iterator it = std::find( pInsLine->GetTabBoxes().begin(), pInsLine->GetTabBoxes().end(), pTableBox );
+ for( auto pTabBox : pCpyLine->GetTabBoxes() )
+ pTabBox->SetUpper( pInsLine );
+
+ SfxPoolItem const* pRowBrush(nullptr);
+ pCpyLine->GetFrameFormat()->GetItemState(RES_BACKGROUND, true, &pRowBrush);
+ if (pRowBrush)
+ {
+ for (auto pBox : pCpyLine->GetTabBoxes())
+ {
+ if (pBox->GetFrameFormat()->GetItemState(RES_BACKGROUND) != SfxItemState::SET)
+ { // set inner row background on inner cell
+ pBox->ClaimFrameFormat();
+ pBox->GetFrameFormat()->SetFormatAttr(*pRowBrush);
+ }
+ }
+ }
+
+ // remove the old box from its parent line
+ it = pInsLine->GetTabBoxes().erase( it );
+ // insert the nested line's boxes in its place
+ pInsLine->GetTabBoxes().insert( it, pCpyLine->GetTabBoxes().begin(), pCpyLine->GetTabBoxes().end());
+ pCpyLine->GetTabBoxes().clear();
+ // destroy the removed box
+ delete pTableBox;
+
+ return false; // set up anew
+ }
+ }
+ return true;
+}
+
+static bool lcl_MergeGCLine(SwTableLine* pLn, GCLinePara* pGCPara)
+{
+ SwTableBoxes::size_type nBoxes = pLn->GetTabBoxes().size();
+ if( nBoxes )
+ {
+ while( 1 == nBoxes )
+ {
+ // We have a Box with Lines
+ SwTableBox* pBox = pLn->GetTabBoxes().front();
+ if( pBox->GetTabLines().empty() )
+ break;
+
+ SwTableLine* pLine = pBox->GetTabLines()[0];
+
+ // pLine turns into the current Line (that is rpLine), the rest is moved
+ // into the LinesArray past the current one.
+ // The LinesArray is in pPara!
+ SwTableLines::size_type nLines = pBox->GetTabLines().size();
+
+ SwTableLines& rLns = *pGCPara->pLns;
+ sal_uInt16 nInsPos = rLns.GetPos( pLn );
+ OSL_ENSURE( USHRT_MAX != nInsPos, "Could not find Line!" );
+
+ SwTableBox* pUpper = pLn->GetUpper();
+
+ rLns.erase( rLns.begin() + nInsPos ); // remove the Line from the array
+ rLns.insert( rLns.begin() + nInsPos, pBox->GetTabLines().begin(), pBox->GetTabLines().end() );
+
+ // JP 31.03.99: Bug 60000
+ // Pass the attributes of the to-be-deleted Lines to the "inserted" one
+ const SfxPoolItem* pItem;
+ if( SfxItemState::SET == pLn->GetFrameFormat()->GetItemState(
+ RES_BACKGROUND, true, &pItem ))
+ {
+ SwTableLines& rBoxLns = pBox->GetTabLines();
+ for( auto pBoxLine : rBoxLns )
+ if( SfxItemState::SET != pBoxLine->GetFrameFormat()->
+ GetItemState( RES_BACKGROUND ))
+ pGCPara->pShareFormats->SetAttr( *pBoxLine, *pItem );
+ }
+
+ pBox->GetTabLines().erase( pBox->GetTabLines().begin(), pBox->GetTabLines().begin() + nLines ); // Remove Lines from the array
+
+ delete pLn;
+
+ // Set the dependency anew
+ while( nLines-- )
+ rLns[ nInsPos++ ]->SetUpper( pUpper );
+
+ pLn = pLine; // and set up anew
+ nBoxes = pLn->GetTabBoxes().size();
+ }
+
+ // ATTENTION: The number of boxes can change!
+ for( SwTableBoxes::size_type nLen = 0; nLen < pLn->GetTabBoxes().size(); ++nLen )
+ if( !lcl_MergeGCBox( pLn->GetTabBoxes()[nLen], pGCPara ))
+ --nLen;
+ }
+ return true;
+}
+
+// Clean structure a bit
+void SwTable::GCLines()
+{
+ // ATTENTION: The Line count can change!
+ GCLinePara aPara( GetTabLines() );
+ SwShareBoxFormats aShareFormats;
+ aPara.pShareFormats = &aShareFormats;
+ for( SwTableLines::size_type n = 0; n < GetTabLines().size() &&
+ lcl_MergeGCLine( GetTabLines()[n], &aPara ); ++n )
+ ;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/htmltbl.cxx b/sw/source/core/doc/htmltbl.cxx
new file mode 100644
index 0000000000..4711a123ad
--- /dev/null
+++ b/sw/source/core/doc/htmltbl.cxx
@@ -0,0 +1,1774 @@
+/* -*- 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 <sal/config.h>
+#include <sal/log.hxx>
+
+#include <algorithm>
+#include <memory>
+
+#include <fmtornt.hxx>
+#include <fmtfsize.hxx>
+#include <frmfmt.hxx>
+#include <ndtxt.hxx>
+#include <doc.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <swtable.hxx>
+#include <rootfrm.hxx>
+#include <flyfrm.hxx>
+#include <poolfmt.hxx>
+#include <utility>
+#include <viewsh.hxx>
+#include <tabfrm.hxx>
+#include <viewopt.hxx>
+#include <htmltbl.hxx>
+#include <calbck.hxx>
+#include <o3tl/numeric.hxx>
+#include <osl/diagnose.h>
+#ifdef DBG_UTIL
+#include <tblrwcl.hxx>
+#endif
+
+using namespace ::com::sun::star;
+
+#define COLFUZZY 20
+#define MAX_TABWIDTH (USHRT_MAX - 2001)
+
+namespace {
+
+class SwHTMLTableLayoutConstraints
+{
+ sal_uInt16 m_nRow; // start row
+ sal_uInt16 m_nCol; // start column
+ sal_uInt16 m_nColSpan; // the column's COLSPAN
+
+ std::unique_ptr<SwHTMLTableLayoutConstraints> m_pNext; // the next constraint
+
+ sal_uLong m_nMinNoAlign, m_nMaxNoAlign; // provisional result of AL-Pass 1
+
+public:
+ SwHTMLTableLayoutConstraints( sal_uLong nMin, sal_uLong nMax, sal_uInt16 nRow,
+ sal_uInt16 nCol, sal_uInt16 nColSp );
+
+ sal_uLong GetMinNoAlign() const { return m_nMinNoAlign; }
+ sal_uLong GetMaxNoAlign() const { return m_nMaxNoAlign; }
+
+ SwHTMLTableLayoutConstraints *InsertNext( SwHTMLTableLayoutConstraints *pNxt );
+ SwHTMLTableLayoutConstraints* GetNext() const { return m_pNext.get(); }
+
+ sal_uInt16 GetColSpan() const { return m_nColSpan; }
+ sal_uInt16 GetColumn() const { return m_nCol; }
+};
+
+}
+
+SwHTMLTableLayoutCnts::SwHTMLTableLayoutCnts(const SwStartNode *pSttNd,
+ std::shared_ptr<SwHTMLTableLayout> xTab,
+ bool bNoBrTag,
+ std::shared_ptr<SwHTMLTableLayoutCnts> xNxt ) :
+ m_xNext( std::move(xNxt) ), m_pBox( nullptr ), m_xTable( std::move(xTab) ), m_pStartNode( pSttNd ),
+ m_nPass1Done( 0 ), m_nWidthSet( 0 ), m_bNoBreakTag( bNoBrTag )
+{}
+
+const SwStartNode *SwHTMLTableLayoutCnts::GetStartNode() const
+{
+ return m_pBox ? m_pBox->GetSttNd() : m_pStartNode;
+}
+
+SwHTMLTableLayoutCell::SwHTMLTableLayoutCell(std::shared_ptr<SwHTMLTableLayoutCnts> xCnts,
+ sal_uInt16 nRSpan, sal_uInt16 nCSpan,
+ sal_uInt16 nWidth, bool bPercentWidth,
+ bool bNWrapOpt ) :
+ m_xContents(std::move(xCnts)),
+ m_nRowSpan( nRSpan ), m_nColSpan( nCSpan ),
+ m_nWidthOption( nWidth ), m_bPercentWidthOption( bPercentWidth ),
+ m_bNoWrapOption( bNWrapOpt )
+{}
+
+SwHTMLTableLayoutColumn::SwHTMLTableLayoutColumn( sal_uInt16 nWidth,
+ bool bRelWidth,
+ bool bLBorder ) :
+ m_nMinNoAlign(MINLAY), m_nMaxNoAlign(MINLAY), m_nAbsMinNoAlign(MINLAY),
+ m_nMin(0), m_nMax(0),
+ m_nAbsColWidth(0), m_nRelColWidth(0),
+ m_nWidthOption( nWidth ), m_bRelWidthOption( bRelWidth ),
+ m_bLeftBorder( bLBorder )
+{}
+
+SwHTMLTableLayoutConstraints::SwHTMLTableLayoutConstraints(sal_uLong nMin, sal_uLong nMax,
+ sal_uInt16 nRw, sal_uInt16 nColumn,
+ sal_uInt16 nColSp)
+ : m_nRow(nRw)
+ , m_nCol(nColumn)
+ , m_nColSpan(nColSp)
+ , m_nMinNoAlign(nMin)
+ , m_nMaxNoAlign(nMax)
+{}
+
+SwHTMLTableLayoutConstraints *SwHTMLTableLayoutConstraints::InsertNext(
+ SwHTMLTableLayoutConstraints *pNxt )
+{
+ SwHTMLTableLayoutConstraints *pPrev = nullptr;
+ SwHTMLTableLayoutConstraints *pConstr = this;
+ while( pConstr )
+ {
+ if (pConstr->m_nRow > pNxt->m_nRow || pConstr->GetColumn() > pNxt->GetColumn())
+ break;
+ pPrev = pConstr;
+ pConstr = pConstr->GetNext();
+ }
+
+ if( pPrev )
+ {
+ pNxt->m_pNext = std::move(pPrev->m_pNext);
+ pPrev->m_pNext.reset(pNxt);
+ pConstr = this;
+ }
+ else
+ {
+ pNxt->m_pNext.reset(this);
+ pConstr = pNxt;
+ }
+
+ return pConstr;
+}
+
+SwHTMLTableLayout::SwHTMLTableLayout( const SwTable * pTable,
+ sal_uInt16 nRws, sal_uInt16 nCls,
+ bool bColsOpt, bool bColTgs,
+ sal_uInt16 nWdth, bool bPercentWdth,
+ sal_uInt16 nBorderOpt, sal_uInt16 nCellPad,
+ sal_uInt16 nCellSp, SvxAdjust eAdjust,
+ sal_uInt16 nLMargin, sal_uInt16 nRMargin,
+ sal_uInt16 nBWidth, sal_uInt16 nLeftBWidth,
+ sal_uInt16 nRightBWidth )
+ : m_aResizeTimer("SwHTMLTableLayout m_aResizeTimer")
+ , m_aColumns( nCls )
+ , m_aCells( static_cast<size_t>(nRws)*nCls )
+ , m_pSwTable( pTable )
+ , m_nMin( 0 )
+ , m_nMax( 0 )
+ , m_nRows( nRws )
+ , m_nCols( nCls )
+ , m_nLeftMargin( nLMargin )
+ , m_nRightMargin( nRMargin )
+ , m_nInhAbsLeftSpace( 0 )
+ , m_nInhAbsRightSpace( 0 )
+ , m_nRelLeftFill( 0 )
+ , m_nRelRightFill( 0 )
+ , m_nRelTabWidth( 0 )
+ , m_nWidthOption( nWdth )
+ , m_nCellPadding( nCellPad )
+ , m_nCellSpacing( nCellSp )
+ , m_nBorder( nBorderOpt )
+ , m_nLeftBorderWidth( nLeftBWidth )
+ , m_nRightBorderWidth( nRightBWidth )
+ , m_nInhLeftBorderWidth( 0 )
+ , m_nInhRightBorderWidth( 0 )
+ , m_nBorderWidth( nBWidth )
+ , m_nDelayedResizeAbsAvail( 0 )
+ , m_nLastResizeAbsAvail( 0 )
+ , m_nPass1Done( 0 )
+ , m_nWidthSet( 0 )
+ , m_eTableAdjust( eAdjust )
+ , m_bColsOption( bColsOpt )
+ , m_bColTags( bColTgs )
+ , m_bPercentWidthOption( bPercentWdth )
+ , m_bUseRelWidth( false )
+ , m_bMustResize( true )
+ , m_bExportable( true )
+ , m_bBordersChanged( false )
+ , m_bMayBeInFlyFrame( false )
+ , m_bDelayedResizeRecalc( false)
+ , m_bMustNotResize( false )
+ , m_bMustNotRecalc( false )
+{
+ m_aResizeTimer.SetInvokeHandler( LINK( this, SwHTMLTableLayout,
+ DelayedResize_Impl ) );
+}
+
+SwHTMLTableLayout::~SwHTMLTableLayout()
+{
+}
+
+/// The border widths are calculated like in Netscape:
+/// Outer border: BORDER + CELLSPACING + CELLPADDING
+/// Inner border: CELLSPACING + CELLPADDING
+/// However, we respect the border widths in SW if bSwBorders is set,
+/// so that we don't wrap wrongly.
+/// We also need to respect the distance to the content. Even if
+/// only the opposite side has a border.
+sal_uInt16 SwHTMLTableLayout::GetLeftCellSpace( sal_uInt16 nCol, sal_uInt16 nColSpan,
+ bool bSwBorders ) const
+{
+ sal_uInt16 nSpace = m_nCellSpacing + m_nCellPadding;
+
+ if( nCol == 0 )
+ {
+ nSpace = nSpace + m_nBorder;
+
+ if( bSwBorders && nSpace < m_nLeftBorderWidth )
+ nSpace = m_nLeftBorderWidth;
+ }
+ else if( bSwBorders )
+ {
+ if( GetColumn(nCol)->HasLeftBorder() )
+ {
+ if( nSpace < m_nBorderWidth )
+ nSpace = m_nBorderWidth;
+ }
+ else if( nCol+nColSpan == m_nCols && m_nRightBorderWidth &&
+ nSpace < MIN_BORDER_DIST )
+ {
+ OSL_ENSURE( !m_nCellPadding, "GetLeftCellSpace: CELLPADDING!=0" );
+ // If the opposite side has a border we need to respect at
+ // least the minimum distance to the content.
+ // Additionally, we could also use nCellPadding for this.
+ nSpace = MIN_BORDER_DIST;
+ }
+ }
+
+ return nSpace;
+}
+
+sal_uInt16 SwHTMLTableLayout::GetRightCellSpace( sal_uInt16 nCol, sal_uInt16 nColSpan,
+ bool bSwBorders ) const
+{
+ sal_uInt16 nSpace = m_nCellPadding;
+
+ if( nCol+nColSpan == m_nCols )
+ {
+ nSpace += m_nBorder + m_nCellSpacing;
+ if( bSwBorders && nSpace < m_nRightBorderWidth )
+ nSpace = m_nRightBorderWidth;
+ }
+ else if( bSwBorders && GetColumn(nCol)->HasLeftBorder() &&
+ nSpace < MIN_BORDER_DIST )
+ {
+ OSL_ENSURE( !m_nCellPadding, "GetRightCellSpace: CELLPADDING!=0" );
+ // If the opposite side has a border we need to respect at
+ // least the minimum distance to the content.
+ // Additionally, we could also use nCellPadding for this.
+ nSpace = MIN_BORDER_DIST;
+ }
+
+ return nSpace;
+}
+
+void SwHTMLTableLayout::AddBorderWidth( sal_uLong &rMin, sal_uLong &rMax,
+ sal_uLong &rAbsMin,
+ sal_uInt16 nCol, sal_uInt16 nColSpan,
+ bool bSwBorders ) const
+{
+ sal_uLong nAdd = GetLeftCellSpace( nCol, nColSpan, bSwBorders ) +
+ GetRightCellSpace( nCol, nColSpan, bSwBorders );
+
+ rMin += nAdd;
+ rMax += nAdd;
+ rAbsMin += nAdd;
+}
+
+void SwHTMLTableLayout::SetBoxWidth( SwTableBox *pBox, sal_uInt16 nCol,
+ sal_uInt16 nColSpan ) const
+{
+ SwFrameFormat *pFrameFormat = pBox->GetFrameFormat();
+
+ // calculate the box's width
+ SwTwips nFrameWidth = 0;
+ while( nColSpan-- )
+ nFrameWidth += GetColumn( nCol++ )->GetRelColWidth();
+
+ // and reset
+ pFrameFormat->SetFormatAttr( SwFormatFrameSize( SwFrameSize::Variable, nFrameWidth, 0 ));
+}
+
+void SwHTMLTableLayout::GetAvail( sal_uInt16 nCol, sal_uInt16 nColSpan,
+ sal_uInt16& rAbsAvail, sal_uInt16& rRelAvail ) const
+{
+ rAbsAvail = 0;
+ rRelAvail = 0;
+ for( sal_uInt16 i=nCol; i<nCol+nColSpan;i++ )
+ {
+ const SwHTMLTableLayoutColumn *pColumn = GetColumn(i);
+ rAbsAvail = rAbsAvail + pColumn->GetAbsColWidth();
+ rRelAvail = rRelAvail + pColumn->GetRelColWidth();
+ }
+}
+
+sal_uInt16 SwHTMLTableLayout::GetBrowseWidthByVisArea( const SwDoc& rDoc )
+{
+ SwViewShell const *pVSh = rDoc.getIDocumentLayoutAccess().GetCurrentViewShell();
+ if( pVSh )
+ {
+ return o3tl::narrowing<sal_uInt16>(pVSh->GetBrowseWidth());
+ }
+
+ return 0;
+}
+
+sal_uInt16 SwHTMLTableLayout::GetBrowseWidth( const SwDoc& rDoc )
+{
+ // If we have a layout, we can get the width from there.
+ const SwRootFrame *pRootFrame = rDoc.getIDocumentLayoutAccess().GetCurrentLayout();
+ if( pRootFrame )
+ {
+ const SwFrame *pPageFrame = pRootFrame->GetLower();
+ if( pPageFrame )
+ return o3tl::narrowing<sal_uInt16>(pPageFrame->getFramePrintArea().Width());
+ }
+
+ // #i91658#
+ // Assertion removed which state that no browse width is available.
+ // Investigation reveals that all calls can handle the case that no browse
+ // width is provided.
+ return GetBrowseWidthByVisArea( rDoc );
+}
+
+sal_uInt16 SwHTMLTableLayout::GetBrowseWidthByTabFrame(
+ const SwTabFrame& rTabFrame ) const
+{
+ SwTwips nWidth = 0;
+
+ const SwFrame *pUpper = rTabFrame.GetUpper();
+ if( MayBeInFlyFrame() && pUpper->IsFlyFrame() &&
+ static_cast<const SwFlyFrame *>(pUpper)->GetAnchorFrame() )
+ {
+ // If the table is located within a self-created frame, the anchor's
+ // width is relevant not the frame's width.
+ // For paragraph-bound frames we don't respect paragraph indents.
+ const SwFrame *pAnchor = static_cast<const SwFlyFrame *>(pUpper)->GetAnchorFrame();
+ if( pAnchor->IsTextFrame() )
+ nWidth = pAnchor->getFrameArea().Width();
+ else
+ nWidth = pAnchor->getFramePrintArea().Width();
+ }
+ else
+ {
+ nWidth = pUpper->getFramePrintArea().Width();
+ }
+
+ SwTwips nUpperDummy = 0;
+ tools::Long nRightOffset = 0,
+ nLeftOffset = 0;
+ rTabFrame.CalcFlyOffsets(nUpperDummy, nLeftOffset, nRightOffset, nullptr);
+ nWidth -= (nLeftOffset + nRightOffset);
+
+ return o3tl::narrowing<sal_uInt16>(std::min(nWidth, SwTwips(SAL_MAX_UINT16)));
+}
+
+sal_uInt16 SwHTMLTableLayout::GetBrowseWidthByTable( const SwDoc& rDoc ) const
+{
+ sal_uInt16 nBrowseWidth = 0;
+ SwTabFrame* pFrame = SwIterator<SwTabFrame,SwFormat>( *m_pSwTable->GetFrameFormat() ).First();
+ if( pFrame )
+ {
+ nBrowseWidth = GetBrowseWidthByTabFrame( *pFrame );
+ }
+ else
+ {
+ nBrowseWidth = SwHTMLTableLayout::GetBrowseWidth( rDoc );
+ }
+
+ return nBrowseWidth;
+}
+
+const SwStartNode *SwHTMLTableLayout::GetAnyBoxStartNode() const
+{
+ const SwStartNode *pBoxSttNd;
+
+ const SwTableBox* pBox = m_pSwTable->GetTabLines()[0]->GetTabBoxes()[0];
+ while( nullptr == (pBoxSttNd = pBox->GetSttNd()) )
+ {
+ OSL_ENSURE( !pBox->GetTabLines().empty(),
+ "Box without start node and lines" );
+ OSL_ENSURE( !pBox->GetTabLines().front()->GetTabBoxes().empty(),
+ "Line without boxes" );
+ pBox = pBox->GetTabLines().front()->GetTabBoxes().front();
+ }
+
+ return pBoxSttNd;
+}
+
+SwFrameFormat *SwHTMLTableLayout::FindFlyFrameFormat() const
+{
+ const SwTableNode *pTableNd = GetAnyBoxStartNode()->FindTableNode();
+ OSL_ENSURE( pTableNd, "No Table-Node?" );
+ return pTableNd->GetFlyFormat();
+}
+
+static void lcl_GetMinMaxSize( sal_uLong& rMinNoAlignCnts, sal_uLong& rMaxNoAlignCnts,
+ sal_uLong& rAbsMinNoAlignCnts,
+ SwTextNode const *pTextNd, SwNodeOffset nIdx, bool bNoBreak )
+{
+ pTextNd->GetMinMaxSize( nIdx, rMinNoAlignCnts, rMaxNoAlignCnts,
+ rAbsMinNoAlignCnts );
+ OSL_ENSURE( rAbsMinNoAlignCnts <= rMinNoAlignCnts,
+ "GetMinMaxSize: absmin > min" );
+ OSL_ENSURE( rMinNoAlignCnts <= rMaxNoAlignCnts,
+ "GetMinMaxSize: max > min" );
+
+ // The maximal width for a <PRE> paragraph is the minimal width
+ const SwFormatColl *pColl = &pTextNd->GetAnyFormatColl();
+ while( pColl && !pColl->IsDefault() &&
+ (USER_FMT & pColl->GetPoolFormatId()) )
+ {
+ pColl = static_cast<const SwFormatColl *>(pColl->DerivedFrom());
+ }
+
+ // <NOBR> in the whole cell apply to text but not to tables.
+ // Netscape only considers this for graphics.
+ if( (pColl && RES_POOLCOLL_HTML_PRE==pColl->GetPoolFormatId()) || bNoBreak )
+ {
+ rMinNoAlignCnts = rMaxNoAlignCnts;
+ rAbsMinNoAlignCnts = rMaxNoAlignCnts;
+ }
+}
+
+void SwHTMLTableLayout::AutoLayoutPass1()
+{
+ m_nPass1Done++;
+
+ m_nMin = m_nMax = 0; // clear pass1 info
+
+ bool bFixRelWidths = false;
+ sal_uInt16 i;
+
+ std::unique_ptr<SwHTMLTableLayoutConstraints> xConstraints;
+
+ for( i=0; i<m_nCols; i++ )
+ {
+ SwHTMLTableLayoutColumn *pColumn = GetColumn( i );
+ pColumn->ClearPass1Info( !HasColTags() );
+ sal_uInt16 nMinColSpan = USHRT_MAX; // Column count to which the calculated width refers to
+ sal_uInt16 nColSkip = USHRT_MAX; // How many columns need to be skipped
+
+ for( sal_uInt16 j=0; j<m_nRows; j++ )
+ {
+ SwHTMLTableLayoutCell *pCell = GetCell(j,i);
+ SwHTMLTableLayoutCnts *pCnts = pCell->GetContents().get();
+
+ // We need to examine all rows in order to
+ // get the column that should be calculated next.
+ sal_uInt16 nColSpan = pCell->GetColSpan();
+ if( nColSpan < nColSkip )
+ nColSkip = nColSpan;
+
+ if( !pCnts || !pCnts->IsPass1Done(m_nPass1Done) )
+ {
+ // The cell is empty or it's content was not edited
+ if( nColSpan < nMinColSpan )
+ nMinColSpan = nColSpan;
+
+ sal_uLong nMinNoAlignCell = 0;
+ sal_uLong nMaxNoAlignCell = 0;
+ sal_uLong nAbsMinNoAlignCell = 0;
+ sal_uLong nMaxTableCell = 0;
+ sal_uLong nAbsMinTableCell = 0;
+
+ while( pCnts )
+ {
+ const SwStartNode *pSttNd = pCnts->GetStartNode();
+ if( pSttNd )
+ {
+ const SwDoc& rDoc = pSttNd->GetDoc();
+ SwNodeOffset nIdx = pSttNd->GetIndex();
+ while (!rDoc.GetNodes()[nIdx]->IsEndNode())
+ {
+ SwTextNode *pTextNd = (rDoc.GetNodes()[nIdx])->GetTextNode();
+ if( pTextNd )
+ {
+ sal_uLong nMinNoAlignCnts = 0;
+ sal_uLong nMaxNoAlignCnts = 0;
+ sal_uLong nAbsMinNoAlignCnts = 0;
+
+ lcl_GetMinMaxSize( nMinNoAlignCnts,
+ nMaxNoAlignCnts,
+ nAbsMinNoAlignCnts,
+ pTextNd, nIdx,
+ pCnts->HasNoBreakTag() );
+
+ if( nMinNoAlignCnts > nMinNoAlignCell )
+ nMinNoAlignCell = nMinNoAlignCnts;
+ if( nMaxNoAlignCnts > nMaxNoAlignCell )
+ nMaxNoAlignCell = nMaxNoAlignCnts;
+ if( nAbsMinNoAlignCnts > nAbsMinNoAlignCell )
+ nAbsMinNoAlignCell = nAbsMinNoAlignCnts;
+ }
+ else
+ {
+ SwTableNode *pTabNd = (rDoc.GetNodes()[nIdx])->GetTableNode();
+ if( pTabNd )
+ {
+ SwHTMLTableLayout *pChild = pTabNd->GetTable().GetHTMLTableLayout();
+ if( pChild )
+ {
+ pChild->AutoLayoutPass1();
+ sal_uLong nMaxTableCnts = pChild->m_nMax;
+ sal_uLong nAbsMinTableCnts = pChild->m_nMin;
+
+ // A fixed table width is taken over as minimum and
+ // maximum at the same time
+ if( !pChild->m_bPercentWidthOption && pChild->m_nWidthOption )
+ {
+ sal_uLong nTabWidth = pChild->m_nWidthOption;
+ if( nTabWidth >= nAbsMinTableCnts )
+ {
+ nMaxTableCnts = nTabWidth;
+ nAbsMinTableCnts = nTabWidth;
+ }
+ else
+ {
+ nMaxTableCnts = nAbsMinTableCnts;
+ }
+ }
+
+ if( nMaxTableCnts > nMaxTableCell )
+ nMaxTableCell = nMaxTableCnts;
+ if( nAbsMinTableCnts > nAbsMinTableCell )
+ nAbsMinTableCell = nAbsMinTableCnts;
+ }
+ nIdx = pTabNd->EndOfSectionNode()->GetIndex();
+ }
+ }
+ nIdx++;
+ }
+ }
+ else if (SwHTMLTableLayout *pChild = pCnts->GetTable())
+ {
+ OSL_ENSURE( false, "Sub tables in HTML import?" );
+ pChild->AutoLayoutPass1();
+ sal_uLong nMaxTableCnts = pChild->m_nMax;
+ sal_uLong nAbsMinTableCnts = pChild->m_nMin;
+
+ // A fixed table width is taken over as minimum and
+ // maximum at the same time
+ if( !pChild->m_bPercentWidthOption && pChild->m_nWidthOption )
+ {
+ sal_uLong nTabWidth = pChild->m_nWidthOption;
+ if( nTabWidth >= nAbsMinTableCnts )
+ {
+ nMaxTableCnts = nTabWidth;
+ nAbsMinTableCnts = nTabWidth;
+ }
+ else
+ {
+ nMaxTableCnts = nAbsMinTableCnts;
+ }
+ }
+
+ if( nMaxTableCnts > nMaxTableCell )
+ nMaxTableCell = nMaxTableCnts;
+ if( nAbsMinTableCnts > nAbsMinTableCell )
+ nAbsMinTableCell = nAbsMinTableCnts;
+ }
+ pCnts->SetPass1Done( m_nPass1Done );
+ pCnts = pCnts->GetNext().get();
+ }
+
+// This code previously came after AddBorderWidth
+ // If a table's width is wider in a cell than what we've calculated
+ // for the other content we need to use the table's width.
+ if( nMaxTableCell > nMaxNoAlignCell )
+ nMaxNoAlignCell = nMaxTableCell;
+ if( nAbsMinTableCell > nAbsMinNoAlignCell )
+ {
+ nAbsMinNoAlignCell = nAbsMinTableCell;
+ if( nMinNoAlignCell < nAbsMinNoAlignCell )
+ nMinNoAlignCell = nAbsMinNoAlignCell;
+ if( nMaxNoAlignCell < nMinNoAlignCell )
+ nMaxNoAlignCell = nMinNoAlignCell;
+ }
+// This code previously came after AddBorderWidth
+
+ bool bRelWidth = pCell->IsPercentWidthOption();
+ sal_uInt16 nWidth = pCell->GetWidthOption();
+
+ // A NOWRAP option applies to text and tables, but is
+ // not applied for fixed cell width.
+ // Instead, the stated cell width behaves like a minimal
+ // width.
+ if( pCell->HasNoWrapOption() )
+ {
+ if( nWidth==0 || bRelWidth )
+ {
+ nMinNoAlignCell = nMaxNoAlignCell;
+ nAbsMinNoAlignCell = nMaxNoAlignCell;
+ }
+ else
+ {
+ if( nWidth>nMinNoAlignCell )
+ nMinNoAlignCell = nWidth;
+ if( nWidth>nAbsMinNoAlignCell )
+ nAbsMinNoAlignCell = nWidth;
+ }
+ }
+
+ // Respect minimum width for content
+ if( nMinNoAlignCell < MINLAY )
+ nMinNoAlignCell = MINLAY;
+ if( nMaxNoAlignCell < MINLAY )
+ nMaxNoAlignCell = MINLAY;
+ if( nAbsMinNoAlignCell < MINLAY )
+ nAbsMinNoAlignCell = MINLAY;
+
+ // Respect the border and distance to the content
+ AddBorderWidth( nMinNoAlignCell, nMaxNoAlignCell,
+ nAbsMinNoAlignCell, i, nColSpan );
+
+ if( 1==nColSpan )
+ {
+ // take over the values directly
+ pColumn->MergeMinMaxNoAlign( nMinNoAlignCell,
+ nMaxNoAlignCell,
+ nAbsMinNoAlignCell );
+
+ // the widest WIDTH wins
+ if( !HasColTags() )
+ pColumn->MergeCellWidthOption( nWidth, bRelWidth );
+ }
+ else
+ {
+ // Process the data line by line from left to right at the end
+
+ // When which values is taken over will be explained further down.
+ if( !HasColTags() && nWidth && !bRelWidth )
+ {
+ sal_uLong nAbsWidth = nWidth, nDummy = 0, nDummy2 = 0;
+ AddBorderWidth( nAbsWidth, nDummy, nDummy2,
+ i, nColSpan, false );
+
+ if( nAbsWidth >= nMinNoAlignCell )
+ {
+ nMaxNoAlignCell = nAbsWidth;
+ if( HasColsOption() )
+ nMinNoAlignCell = nAbsWidth;
+ }
+ else if( nAbsWidth >= nAbsMinNoAlignCell )
+ {
+ nMaxNoAlignCell = nAbsWidth;
+ nMinNoAlignCell = nAbsWidth;
+ }
+ else
+ {
+ nMaxNoAlignCell = nAbsMinNoAlignCell;
+ nMinNoAlignCell = nAbsMinNoAlignCell;
+ }
+ }
+ else if( HasColsOption() || HasColTags() )
+ nMinNoAlignCell = nAbsMinNoAlignCell;
+
+ SwHTMLTableLayoutConstraints *pConstr =
+ new SwHTMLTableLayoutConstraints( nMinNoAlignCell,
+ nMaxNoAlignCell, j, i, nColSpan );
+ if (xConstraints)
+ {
+ SwHTMLTableLayoutConstraints* pConstraints = xConstraints->InsertNext(pConstr);
+ // coverity[leaked_storage] - ownership transferred to pConstraints chain
+ xConstraints.release();
+ xConstraints.reset(pConstraints);
+ }
+ else
+ xConstraints.reset(pConstr);
+ }
+ }
+ }
+
+ OSL_ENSURE( nMinColSpan>0 && nColSkip>0 && nColSkip <= nMinColSpan,
+ "Layout pass 1: Columns are being forgotten!" );
+ OSL_ENSURE( nMinColSpan!=USHRT_MAX,
+ "Layout pass 1: unnecessary pass through the loop or a bug" );
+
+ if( 1==nMinColSpan )
+ {
+ // There are cells with COLSPAN 1 and therefore also useful
+ // values in pColumn
+
+ // Take over values according to the following table (Netscape 4.0 pv 3):
+
+ // WIDTH: no COLS COLS
+
+ // none min = min min = absmin
+ // max = max max = max
+
+ // >= min min = min min = width
+ // max = width max = width
+
+ // >= absmin min = width(*) min = width
+ // max = width max = width
+
+ // < absmin min = absmin min = absmin
+ // max = absmin max = absmin
+
+ // (*) Netscape uses the minimum width without a break before
+ // the last graphic here. We don't have that (yet?),
+ // so we leave it set to width.
+
+ if( pColumn->GetWidthOption() && !pColumn->IsRelWidthOption() )
+ {
+ // Take over absolute widths as minimal and maximal widths.
+ sal_uLong nAbsWidth = pColumn->GetWidthOption();
+ sal_uLong nDummy = 0, nDummy2 = 0;
+ AddBorderWidth( nAbsWidth, nDummy, nDummy2, i, 1, false );
+
+ if( nAbsWidth >= pColumn->GetMinNoAlign() )
+ {
+ pColumn->SetMinMax( HasColsOption() ? nAbsWidth
+ : pColumn->GetMinNoAlign(),
+ nAbsWidth );
+ }
+ else if( nAbsWidth >= pColumn->GetAbsMinNoAlign() )
+ {
+ pColumn->SetMinMax( nAbsWidth, nAbsWidth );
+ }
+ else
+ {
+ pColumn->SetMinMax( pColumn->GetAbsMinNoAlign(),
+ pColumn->GetAbsMinNoAlign() );
+ }
+ }
+ else
+ {
+ pColumn->SetMinMax( HasColsOption() ? pColumn->GetAbsMinNoAlign()
+ : pColumn->GetMinNoAlign(),
+ pColumn->GetMaxNoAlign() );
+ }
+ }
+ else if( USHRT_MAX!=nMinColSpan )
+ {
+ // Can be anything != 0, because it is altered by the constraints.
+ pColumn->SetMinMax( MINLAY, MINLAY );
+
+ // the next columns need not to be processed
+ i += (nColSkip-1);
+ }
+
+ m_nMin += pColumn->GetMin();
+ m_nMax += pColumn->GetMax();
+ if (pColumn->IsRelWidthOption()) bFixRelWidths = true;
+ }
+
+ // Now process the constraints
+ SwHTMLTableLayoutConstraints *pConstr = xConstraints.get();
+ while( pConstr )
+ {
+ // At first we need to process the width in the same way
+ // as the column widths
+ sal_uInt16 nCol = pConstr->GetColumn();
+ sal_uInt16 nColSpan = pConstr->GetColSpan();
+ sal_uLong nConstrMin = pConstr->GetMinNoAlign();
+ sal_uLong nConstrMax = pConstr->GetMaxNoAlign();
+
+ // We get the hitherto width of the spanned columns
+ sal_uLong nColsMin = 0;
+ sal_uLong nColsMax = 0;
+ for( sal_uInt16 j=nCol; j<nCol+nColSpan; j++ )
+ {
+ SwHTMLTableLayoutColumn *pColumn = GetColumn( j );
+ nColsMin += pColumn->GetMin();
+ nColsMax += pColumn->GetMax();
+ }
+
+ if( nColsMin<nConstrMin )
+ {
+ // Proportionately distribute the minimum value to the columns
+ sal_uLong nMinD = nConstrMin-nColsMin;
+
+ if( nConstrMin > nColsMax )
+ {
+ // Proportional according to the minimum widths
+ sal_uInt16 nEndCol = nCol+nColSpan;
+ sal_uLong nDiff = nMinD;
+ for( sal_uInt16 ic=nCol; ic<nEndCol; ic++ )
+ {
+ SwHTMLTableLayoutColumn *pColumn = GetColumn( ic );
+
+ sal_uLong nColMin = pColumn->GetMin();
+ sal_uLong nColMax = pColumn->GetMax();
+
+ m_nMin -= nColMin;
+ sal_uLong nAdd;
+ if (ic < nEndCol-1)
+ {
+ if (nColsMin == 0)
+ throw o3tl::divide_by_zero();
+ nAdd = (nColMin * nMinD) / nColsMin;
+ }
+ else
+ {
+ nAdd = nDiff;
+ }
+ nColMin += nAdd;
+ m_nMin += nColMin;
+ OSL_ENSURE( nDiff >= nAdd, "Ooops: nDiff is not correct anymore" );
+ nDiff -= nAdd;
+
+ if( nColMax < nColMin )
+ {
+ m_nMax -= nColMax;
+ nColsMax -= nColMax;
+ nColMax = nColMin;
+ m_nMax += nColMax;
+ nColsMax += nColMax;
+ }
+
+ pColumn->SetMinMax( nColMin, nColMax );
+ }
+ }
+ else
+ {
+ // Proportional according to the difference of max and min
+ for( sal_uInt16 ic=nCol; ic<nCol+nColSpan; ic++ )
+ {
+ SwHTMLTableLayoutColumn *pColumn = GetColumn( ic );
+
+ sal_uLong nDiff = pColumn->GetMax()-pColumn->GetMin();
+ if( nMinD < nDiff )
+ nDiff = nMinD;
+
+ pColumn->AddToMin( nDiff );
+
+ OSL_ENSURE( pColumn->GetMax() >= pColumn->GetMin(),
+ "Why is the Column suddenly too narrow?" );
+
+ m_nMin += nDiff;
+ nMinD -= nDiff;
+ }
+ }
+ }
+
+ if( !HasColTags() && nColsMax<nConstrMax )
+ {
+ sal_uLong nMaxD = nConstrMax-nColsMax;
+
+ for( sal_uInt16 ic=nCol; ic<nCol+nColSpan; ic++ )
+ {
+ SwHTMLTableLayoutColumn *pColumn = GetColumn( ic );
+
+ m_nMax -= pColumn->GetMax();
+
+ pColumn->AddToMax( (pColumn->GetMax() * nMaxD) / nColsMax );
+
+ m_nMax += pColumn->GetMax();
+ }
+ }
+
+ pConstr = pConstr->GetNext();
+ }
+
+ if( !bFixRelWidths )
+ return;
+
+ if( HasColTags() )
+ {
+ // To adapt the relative widths, in a first step we multiply the
+ // minimum width of all affected cells with the relative width
+ // of the column.
+ // Thus, the width ratio among the columns is correct.
+
+ // Furthermore, a factor is calculated that says by how much the
+ // cell has gotten wider than the minimum width.
+
+ // In the second step the calculated widths are divided by this
+ // factor. Thereby a cell's width is preserved and serves as a
+ // basis for the other cells.
+ // We only change the maximum widths here!
+
+ sal_uLong nAbsMin = 0; // absolute minimum width of all widths with relative width
+ sal_uLong nRel = 0; // sum of all relative widths of all columns
+ for( i=0; i<m_nCols; i++ )
+ {
+ SwHTMLTableLayoutColumn *pColumn = GetColumn( i );
+ if( pColumn->IsRelWidthOption() && pColumn->GetWidthOption() )
+ {
+ nAbsMin += pColumn->GetMin();
+ nRel += pColumn->GetWidthOption();
+ }
+ }
+
+ sal_uLong nQuot = ULONG_MAX;
+ for( i=0; i<m_nCols; i++ )
+ {
+ SwHTMLTableLayoutColumn *pColumn = GetColumn( i );
+ if( pColumn->IsRelWidthOption() )
+ {
+ m_nMax -= pColumn->GetMax();
+ if( pColumn->GetWidthOption() && pColumn->GetMin() )
+ {
+ pColumn->SetMax( nAbsMin * pColumn->GetWidthOption() );
+ sal_uLong nColQuot = pColumn->GetMax() / pColumn->GetMin();
+ if( nColQuot<nQuot )
+ nQuot = nColQuot;
+ }
+ }
+ }
+ OSL_ENSURE( 0==nRel || nQuot!=ULONG_MAX,
+ "Where did the relative columns go?" );
+ for( i=0; i<m_nCols; i++ )
+ {
+ SwHTMLTableLayoutColumn *pColumn = GetColumn( i );
+ if( pColumn->IsRelWidthOption() )
+ {
+ if( pColumn->GetWidthOption() )
+ pColumn->SetMax( pColumn->GetMax() / nQuot );
+ else
+ pColumn->SetMax( pColumn->GetMin() );
+ OSL_ENSURE( pColumn->GetMax() >= pColumn->GetMin(),
+ "Maximum column width is lower than the minimum column width" );
+ m_nMax += pColumn->GetMax();
+ }
+ }
+ }
+ else
+ {
+ sal_uInt16 nRel = 0; // sum of the relative widths of all columns
+ sal_uInt16 nRelCols = 0; // count of the columns with a relative setting
+ sal_uLong nRelMax = 0; // fraction of the maximum of this column
+ for( i=0; i<m_nCols; i++ )
+ {
+ OSL_ENSURE( nRel<=100, "relative width of all columns > 100%" );
+ SwHTMLTableLayoutColumn *pColumn = GetColumn( i );
+ if( pColumn->IsRelWidthOption() && pColumn->GetWidthOption() )
+ {
+ // Make sure that the relative widths don't go above 100%
+ sal_uInt16 nColWidth = pColumn->GetWidthOption();
+ if( nRel+nColWidth > 100 )
+ {
+ nColWidth = 100 - nRel;
+ pColumn->SetWidthOption( nColWidth );
+ }
+ nRelMax += pColumn->GetMax();
+ nRel = nRel + nColWidth;
+ nRelCols++;
+ }
+ else if( !pColumn->GetMin() )
+ {
+ // The column is empty (so it was solely created by
+ // COLSPAN) and therefore must not be assigned a % width.
+ nRelCols++;
+ }
+ }
+
+ // If there are percentages left we distribute them to the columns
+ // that don't have a width setting. Like in Netscape we distribute
+ // the remaining percentages according to the ratio of the maximum
+ // width of the affected columns.
+ // For the maximum widths we also take the fixed-width columns
+ // into account. Is that correct?
+ sal_uLong nFixMax = 0;
+ if( nRel < 100 && nRelCols < m_nCols )
+ {
+ nFixMax = m_nMax - nRelMax;
+ SAL_WARN_IF(!nFixMax, "sw.core", "bad fixed width max");
+ }
+ if (nFixMax)
+ {
+ sal_uInt16 nRelLeft = 100 - nRel;
+ for( i=0; i<m_nCols; i++ )
+ {
+ SwHTMLTableLayoutColumn *pColumn = GetColumn( i );
+ if( !pColumn->IsRelWidthOption() &&
+ !pColumn->GetWidthOption() &&
+ pColumn->GetMin() )
+ {
+ // the next column gets the rest
+ sal_uInt16 nColWidth =
+ o3tl::narrowing<sal_uInt16>((pColumn->GetMax() * nRelLeft) / nFixMax);
+ pColumn->SetWidthOption( nColWidth );
+ }
+ }
+ }
+
+ // adjust the maximum widths now accordingly
+ sal_uLong nQuotMax = ULONG_MAX;
+ sal_uLong nOldMax = m_nMax;
+ m_nMax = 0;
+ for( i=0; i<m_nCols; i++ )
+ {
+ // Columns with a % setting are adapted accordingly.
+ // Columns, that
+ // - do not have a % setting and are located within a tables
+ // with COLS and WIDTH, or
+ // - their width is 0%
+ // get set to the minimum width.
+ SwHTMLTableLayoutColumn *pColumn = GetColumn( i );
+ if( pColumn->IsRelWidthOption() && pColumn->GetWidthOption() )
+ {
+ sal_uLong nNewMax;
+ sal_uLong nColQuotMax;
+ if( !m_nWidthOption )
+ {
+ nNewMax = nOldMax * pColumn->GetWidthOption();
+ nColQuotMax = nNewMax / pColumn->GetMax();
+ }
+ else
+ {
+ nNewMax = m_nMin * pColumn->GetWidthOption();
+ nColQuotMax = nNewMax / pColumn->GetMin();
+ }
+ pColumn->SetMax( nNewMax );
+ if( nColQuotMax < nQuotMax )
+ nQuotMax = nColQuotMax;
+ }
+ else if( HasColsOption() || m_nWidthOption ||
+ (pColumn->IsRelWidthOption() &&
+ !pColumn->GetWidthOption()) )
+ pColumn->SetMax( pColumn->GetMin() );
+ }
+ // and divide by the quotient
+ SAL_WARN_IF(!nQuotMax, "sw.core", "Where did the relative columns go?");
+ for (i = 0; i < m_nCols; ++i)
+ {
+ SwHTMLTableLayoutColumn *pColumn = GetColumn( i );
+ if (pColumn->IsRelWidthOption() && pColumn->GetWidthOption() && nQuotMax)
+ {
+ pColumn->SetMax( pColumn->GetMax() / nQuotMax );
+ OSL_ENSURE( pColumn->GetMax() >= pColumn->GetMin(),
+ "Minimum width is one column bigger than maximum" );
+ if( pColumn->GetMax() < pColumn->GetMin() )
+ pColumn->SetMax( pColumn->GetMin() );
+ }
+ m_nMax += pColumn->GetMax();
+ }
+ }
+}
+
+//TODO: provide documentation
+/**
+
+ @param nAbsAvail available space in TWIPS.
+ @param nRelAvail available space related to USHRT_MAX or 0
+ @param nAbsSpace fraction of nAbsAvail, which is reserved by the surrounding
+ cell for the border and the distance to the paragraph.
+*/
+void SwHTMLTableLayout::AutoLayoutPass2( sal_uInt16 nAbsAvail, sal_uInt16 nRelAvail,
+ sal_uInt16 nAbsLeftSpace,
+ sal_uInt16 nAbsRightSpace,
+ sal_uInt16 nParentInhAbsSpace )
+{
+ // For a start we do a lot of plausibility tests
+
+ // An absolute width always has to be passed
+ OSL_ENSURE( nAbsAvail, "AutoLayout pass 2: No absolute width given" );
+
+ // A relative width must only be passed for tables within tables (?)
+ OSL_ENSURE( IsTopTable() == (nRelAvail==0),
+ "AutoLayout pass 2: Relative width at table in table or the other way around" );
+
+ // The table's minimum width must not be bigger than its maximum width
+ OSL_ENSURE( m_nMin<=m_nMax, "AutoLayout pass 2: nMin > nMax" );
+
+ // Remember the available width for which the table was calculated.
+ // This is a good place as we pass by here for the initial calculation
+ // of the table in the parser and for each Resize_ call.
+ m_nLastResizeAbsAvail = nAbsAvail;
+
+ // Step 1: The available space is readjusted for the left/right border,
+ // possibly existing filler cells and distances.
+
+ // Distance to the content and border
+ sal_uInt16 nAbsLeftFill = 0, nAbsRightFill = 0;
+ if( !IsTopTable() &&
+ GetMin() + nAbsLeftSpace + nAbsRightSpace <= nAbsAvail )
+ {
+ nAbsLeftFill = nAbsLeftSpace;
+ nAbsRightFill = nAbsRightSpace;
+ }
+
+ // Left and right distance
+ if( m_nLeftMargin || m_nRightMargin )
+ {
+ if( IsTopTable() )
+ {
+ // For the top table we always respect the borders, because we
+ // never go below the table's minimum width.
+ nAbsAvail -= (m_nLeftMargin + m_nRightMargin);
+ }
+ else if( GetMin() + m_nLeftMargin + m_nRightMargin <= nAbsAvail )
+ {
+ // Else, we only respect the borders if there's space available
+ // for them (nMin has already been calculated!)
+ nAbsLeftFill = nAbsLeftFill + m_nLeftMargin;
+ nAbsRightFill = nAbsRightFill + m_nRightMargin;
+ }
+ }
+
+ // Read just the available space
+ m_nRelLeftFill = 0;
+ m_nRelRightFill = 0;
+ if( !IsTopTable() && (nAbsLeftFill>0 || nAbsRightFill) )
+ {
+ sal_uLong nAbsLeftFillL = nAbsLeftFill, nAbsRightFillL = nAbsRightFill;
+
+ m_nRelLeftFill = o3tl::narrowing<sal_uInt16>((nAbsLeftFillL * nRelAvail) / nAbsAvail);
+ m_nRelRightFill = o3tl::narrowing<sal_uInt16>((nAbsRightFillL * nRelAvail) / nAbsAvail);
+
+ nAbsAvail -= (nAbsLeftFill + nAbsRightFill);
+ if( nRelAvail )
+ nRelAvail -= (m_nRelLeftFill + m_nRelRightFill);
+ }
+
+ // Step 2: Calculate the absolute table width.
+ sal_uInt16 nAbsTabWidth = 0;
+ m_bUseRelWidth = false;
+ if( m_nWidthOption )
+ {
+ if( m_bPercentWidthOption )
+ {
+ OSL_ENSURE( m_nWidthOption<=100, "Percentage value too high" );
+ if( m_nWidthOption > 100 )
+ m_nWidthOption = 100;
+
+ // The absolute width is equal to the given percentage of
+ // the available width.
+ // Top tables only get a relative width if the available space
+ // is *strictly larger* than the minimum width.
+
+ // CAUTION: We need the "strictly larger" because changing from a
+ // relative width to an absolute width by resizing would lead
+ // to an infinite loop.
+
+ // Because we do not call resize for tables in frames if the
+ // frame has a non-relative width, we cannot play such games.
+
+ // Let's play such games now anyway. We had a graphic in a 1% wide
+ // table and it didn't fit in of course.
+ nAbsTabWidth = o3tl::narrowing<sal_uInt16>( (static_cast<sal_uLong>(nAbsAvail) * m_nWidthOption) / 100 );
+ if( IsTopTable() &&
+ ( /*MayBeInFlyFrame() ||*/ static_cast<sal_uLong>(nAbsTabWidth) > m_nMin ) )
+ {
+ nRelAvail = USHRT_MAX;
+ m_bUseRelWidth = true;
+ }
+ }
+ else
+ {
+ nAbsTabWidth = m_nWidthOption;
+ if( nAbsTabWidth > MAX_TABWIDTH )
+ nAbsTabWidth = MAX_TABWIDTH;
+
+ // Tables within tables must never get wider than the available
+ // space.
+ if( !IsTopTable() && nAbsTabWidth > nAbsAvail )
+ nAbsTabWidth = nAbsAvail;
+ }
+ }
+
+ OSL_ENSURE( IsTopTable() || nAbsTabWidth<=nAbsAvail,
+ "AutoLayout pass 2: nAbsTabWidth > nAbsAvail for table in table" );
+ OSL_ENSURE( !nRelAvail || nAbsTabWidth<=nAbsAvail,
+ "AutoLayout pass 2: nAbsTabWidth > nAbsAvail for relative width" );
+
+ // Catch for the two asserts above (we never know!)
+ if( (!IsTopTable() || nRelAvail>0) && nAbsTabWidth>nAbsAvail )
+ nAbsTabWidth = nAbsAvail;
+
+ // Step 3: Identify the column width and, if applicable, the absolute
+ // and relative table widths.
+ if( (!IsTopTable() && m_nMin > static_cast<sal_uLong>(nAbsAvail)) ||
+ m_nMin > MAX_TABWIDTH )
+ {
+ // If
+ // - an inner table's minimum is larger than the available space, or
+ // - a top table's minimum is larger than USHORT_MAX the table
+ // has to be adapted to the available space or USHORT_MAX.
+ // We preserve the widths' ratio amongst themselves, however.
+
+ nAbsTabWidth = IsTopTable() ? MAX_TABWIDTH : nAbsAvail;
+ m_nRelTabWidth = (nRelAvail ? nRelAvail : nAbsTabWidth );
+
+ // First of all, we check whether we can fit the layout constrains,
+ // which are: Every cell's width excluding the borders must be at least
+ // MINLAY:
+
+ sal_uLong nRealMin = 0;
+ for( sal_uInt16 i=0; i<m_nCols; i++ )
+ {
+ sal_uLong nRealColMin = MINLAY, nDummy1 = 0, nDummy2 = 0;
+ AddBorderWidth( nRealColMin, nDummy1, nDummy2, i, 1 );
+ nRealMin += nRealColMin;
+ }
+ if( (nRealMin >= nAbsTabWidth) || (nRealMin >= m_nMin) )
+ {
+ // "Rien ne va plus": we cannot get the minimum column widths
+ // the layout wants to have.
+
+ sal_uInt16 nAbs = 0, nRel = 0;
+ SwHTMLTableLayoutColumn *pColumn;
+ for( sal_uInt16 i=0; i<m_nCols-1; i++ )
+ {
+ pColumn = GetColumn( i );
+ sal_uLong nColMin = pColumn->GetMin();
+ if( nColMin <= USHRT_MAX )
+ {
+ pColumn->SetAbsColWidth(
+ o3tl::narrowing<sal_uInt16>((nColMin * nAbsTabWidth) / m_nMin) );
+ pColumn->SetRelColWidth(
+ o3tl::narrowing<sal_uInt16>((nColMin * m_nRelTabWidth) / m_nMin) );
+ }
+ else
+ {
+ double nColMinD = nColMin;
+ pColumn->SetAbsColWidth(
+ o3tl::narrowing<sal_uInt16>((nColMinD * nAbsTabWidth) / m_nMin) );
+ pColumn->SetRelColWidth(
+ o3tl::narrowing<sal_uInt16>((nColMinD * m_nRelTabWidth) / m_nMin) );
+ }
+
+ nAbs = nAbs + pColumn->GetAbsColWidth();
+ nRel = nRel + pColumn->GetRelColWidth();
+ }
+ pColumn = GetColumn( m_nCols-1 );
+ pColumn->SetAbsColWidth( nAbsTabWidth - nAbs );
+ pColumn->SetRelColWidth( m_nRelTabWidth - nRel );
+ }
+ else
+ {
+ sal_uLong nDistAbs = nAbsTabWidth - nRealMin;
+ sal_uLong nDistRel = m_nRelTabWidth - nRealMin;
+ sal_uLong nDistMin = m_nMin - nRealMin;
+ sal_uInt16 nAbs = 0, nRel = 0;
+ SwHTMLTableLayoutColumn *pColumn;
+ for( sal_uInt16 i=0; i<m_nCols-1; i++ )
+ {
+ pColumn = GetColumn( i );
+ sal_uLong nColMin = pColumn->GetMin();
+ sal_uLong nRealColMin = MINLAY, nDummy1 = 0, nDummy2 = 0;
+ AddBorderWidth( nRealColMin, nDummy1, nDummy2, i, 1 );
+
+ if( nColMin <= USHRT_MAX )
+ {
+ pColumn->SetAbsColWidth(
+ o3tl::narrowing<sal_uInt16>((((nColMin-nRealColMin) * nDistAbs) / nDistMin) + nRealColMin) );
+ pColumn->SetRelColWidth(
+ o3tl::narrowing<sal_uInt16>((((nColMin-nRealColMin) * nDistRel) / nDistMin) + nRealColMin) );
+ }
+ else
+ {
+ double nColMinD = nColMin;
+ pColumn->SetAbsColWidth(
+ o3tl::narrowing<sal_uInt16>((((nColMinD-nRealColMin) * nDistAbs) / nDistMin) + nRealColMin) );
+ pColumn->SetRelColWidth(
+ o3tl::narrowing<sal_uInt16>((((nColMinD-nRealColMin) * nDistRel) / nDistMin) + nRealColMin) );
+ }
+
+ nAbs = nAbs + pColumn->GetAbsColWidth();
+ nRel = nRel + pColumn->GetRelColWidth();
+ }
+ pColumn = GetColumn( m_nCols-1 );
+ pColumn->SetAbsColWidth( nAbsTabWidth - nAbs );
+ pColumn->SetRelColWidth( m_nRelTabWidth - nRel );
+ }
+ }
+ else if( m_nMax <= static_cast<sal_uLong>(nAbsTabWidth ? nAbsTabWidth : nAbsAvail) )
+ {
+ // If
+ // - the table has a fixed width and the table's maximum is
+ // smaller, or
+ //- the maximum is smaller than the available space,
+ // we can take over the maximum as it is. Respectively
+ // the table can only be adapted to the fixed width by
+ // respecting the maximum.
+
+ // No fixed width, use the maximum.
+ if( !nAbsTabWidth )
+ nAbsTabWidth = o3tl::narrowing<sal_uInt16>(m_nMax);
+
+ // A top table may also get wider then the available space.
+ if( nAbsTabWidth > nAbsAvail )
+ {
+ OSL_ENSURE( IsTopTable(),
+ "Table in table should get wider than the surrounding cell." );
+ nAbsAvail = nAbsTabWidth;
+ }
+
+ // Only use the relative widths' fraction, that is used for the
+ // absolute width.
+ sal_uLong nAbsTabWidthL = nAbsTabWidth;
+ if (nRelAvail)
+ {
+ if (nAbsAvail == 0)
+ throw o3tl::divide_by_zero();
+ m_nRelTabWidth = o3tl::narrowing<sal_uInt16>((nAbsTabWidthL * nRelAvail) / nAbsAvail);
+ }
+ else
+ m_nRelTabWidth = nAbsTabWidth;
+
+ // Are there columns width a percentage setting and some without one?
+ sal_uLong nFixMax = m_nMax;
+ for( sal_uInt16 i=0; i<m_nCols; i++ )
+ {
+ const SwHTMLTableLayoutColumn *pColumn = GetColumn( i );
+ if( pColumn->IsRelWidthOption() && pColumn->GetWidthOption()>0 )
+ nFixMax -= pColumn->GetMax();
+ }
+
+ if( nFixMax > 0 && nFixMax < m_nMax )
+ {
+ // Yes, distribute the to-be-distributed space only to the
+ // columns with a percentage setting.
+
+ // In this case (and in this case only) there are columns
+ // that exactly keep their maximum width, that is they neither
+ // get smaller nor wider. When calculating the absolute width
+ // from the relative width we can get rounding errors.
+ // To correct this, we first make the fixed widths compensate for
+ // this error. We then fix the relative widths the same way.
+
+ sal_uInt16 nAbs = 0, nRel = 0;
+ sal_uInt16 nFixedCols = 0;
+ sal_uInt16 i;
+
+ for( i = 0; i < m_nCols; i++ )
+ {
+ SwHTMLTableLayoutColumn *pColumn = GetColumn( i );
+ if( !pColumn->IsRelWidthOption() || !pColumn->GetWidthOption() )
+ {
+ // The column keeps its width.
+ nFixedCols++;
+ sal_uLong nColMax = pColumn->GetMax();
+ pColumn->SetAbsColWidth( o3tl::narrowing<sal_uInt16>(nColMax) );
+
+ sal_uLong nRelColWidth =
+ (nColMax * m_nRelTabWidth) / nAbsTabWidth;
+ sal_uLong nChkWidth =
+ (nRelColWidth * nAbsTabWidth) / m_nRelTabWidth;
+ if( nChkWidth < nColMax )
+ nRelColWidth++;
+ else if( nChkWidth > nColMax )
+ nRelColWidth--;
+ pColumn->SetRelColWidth( o3tl::narrowing<sal_uInt16>(nRelColWidth) );
+
+ nAbs = nAbs + o3tl::narrowing<sal_uInt16>(nColMax);
+ nRel = nRel + o3tl::narrowing<sal_uInt16>(nRelColWidth);
+ }
+ }
+
+ // The to-be-distributed percentage of the maximum, the
+ // relative and absolute widths. Here, nFixMax corresponds
+ // to nAbs, so that we could've called it nAbs.
+ // The code is, however, more readable like that.
+ OSL_ENSURE( nFixMax == nAbs, "Two loops, two sums?" );
+ sal_uLong nDistMax = m_nMax - nFixMax;
+ sal_uInt16 nDistAbsTabWidth = nAbsTabWidth - nAbs;
+ sal_uInt16 nDistRelTabWidth = m_nRelTabWidth - nRel;
+
+ for( i=0; i<m_nCols; i++ )
+ {
+ SwHTMLTableLayoutColumn *pColumn = GetColumn( i );
+ if( pColumn->IsRelWidthOption() && pColumn->GetWidthOption() > 0 )
+ {
+ // The column gets proportionately wider.
+ nFixedCols++;
+ if( nFixedCols == m_nCols )
+ {
+ pColumn->SetAbsColWidth( nAbsTabWidth-nAbs );
+ pColumn->SetRelColWidth( m_nRelTabWidth-nRel );
+ }
+ else
+ {
+ sal_uLong nColMax = pColumn->GetMax();
+ pColumn->SetAbsColWidth(
+ o3tl::narrowing<sal_uInt16>((nColMax * nDistAbsTabWidth) / nDistMax) );
+ pColumn->SetRelColWidth(
+ o3tl::narrowing<sal_uInt16>((nColMax * nDistRelTabWidth) / nDistMax) );
+ }
+ nAbs = nAbs + pColumn->GetAbsColWidth();
+ nRel = nRel + pColumn->GetRelColWidth();
+ }
+ }
+ OSL_ENSURE( m_nCols==nFixedCols, "Missed a column!" );
+ }
+ else if (m_nCols > 0)
+ {
+ if (m_nMax == 0)
+ throw o3tl::divide_by_zero();
+ // No. So distribute the space regularly among all columns.
+ for (sal_uInt16 i=0; i < m_nCols; ++i)
+ {
+ sal_uLong nColMax = GetColumn( i )->GetMax();
+ GetColumn( i )->SetAbsColWidth(
+ o3tl::narrowing<sal_uInt16>((nColMax * nAbsTabWidth) / m_nMax) );
+ GetColumn( i )->SetRelColWidth(
+ o3tl::narrowing<sal_uInt16>((nColMax * m_nRelTabWidth) / m_nMax) );
+ }
+ }
+ }
+ else
+ {
+ // Proportionately distribute the space that extends over the minimum
+ // width among the columns.
+ if( !nAbsTabWidth )
+ nAbsTabWidth = nAbsAvail;
+ if( nAbsTabWidth < m_nMin )
+ nAbsTabWidth = o3tl::narrowing<sal_uInt16>(m_nMin);
+
+ if( nAbsTabWidth > nAbsAvail )
+ {
+ OSL_ENSURE( IsTopTable(),
+ "A nested table should become wider than the available space." );
+ nAbsAvail = nAbsTabWidth;
+ }
+
+ sal_uLong nAbsTabWidthL = nAbsTabWidth;
+ if (nRelAvail)
+ {
+ if (nAbsAvail == 0)
+ throw o3tl::divide_by_zero();
+ m_nRelTabWidth = o3tl::narrowing<sal_uInt16>((nAbsTabWidthL * nRelAvail) / nAbsAvail);
+ }
+ else
+ m_nRelTabWidth = nAbsTabWidth;
+ double nW = nAbsTabWidth - m_nMin;
+ double nD = (m_nMax==m_nMin ? 1 : m_nMax-m_nMin);
+ sal_uInt16 nAbs = 0, nRel = 0;
+ for( sal_uInt16 i=0; i<m_nCols-1; i++ )
+ {
+ double nd = GetColumn( i )->GetMax() - GetColumn( i )->GetMin();
+ sal_uLong nAbsColWidth = GetColumn( i )->GetMin() + static_cast<sal_uLong>((nd*nW)/nD);
+ sal_uLong nRelColWidth = nRelAvail
+ ? (nAbsColWidth * m_nRelTabWidth) / nAbsTabWidth
+ : nAbsColWidth;
+
+ GetColumn( i )->SetAbsColWidth( o3tl::narrowing<sal_uInt16>(nAbsColWidth) );
+ GetColumn( i )->SetRelColWidth( o3tl::narrowing<sal_uInt16>(nRelColWidth) );
+ nAbs = nAbs + o3tl::narrowing<sal_uInt16>(nAbsColWidth);
+ nRel = nRel + o3tl::narrowing<sal_uInt16>(nRelColWidth);
+ }
+ GetColumn( m_nCols-1 )->SetAbsColWidth( nAbsTabWidth - nAbs );
+ GetColumn( m_nCols-1 )->SetRelColWidth( m_nRelTabWidth - nRel );
+
+ }
+
+ // Step 4: For nested tables we can have balancing cells on the
+ // left or right. Here we calculate their width.
+ m_nInhAbsLeftSpace = 0;
+ m_nInhAbsRightSpace = 0;
+ if( IsTopTable() ||
+ !(m_nRelLeftFill>0 || m_nRelRightFill>0 || nAbsTabWidth<nAbsAvail) )
+ return;
+
+ // Calculate the width of additional cells we use for
+ // aligning inner tables.
+ sal_uInt16 nAbsDist = o3tl::narrowing<sal_uInt16>(nAbsAvail-nAbsTabWidth);
+ sal_uInt16 nRelDist = o3tl::narrowing<sal_uInt16>(nRelAvail-m_nRelTabWidth);
+ sal_uInt16 nParentInhAbsLeftSpace = 0, nParentInhAbsRightSpace = 0;
+
+ // Calculate the size and position of the additional cells.
+ switch( m_eTableAdjust )
+ {
+ case SvxAdjust::Right:
+ nAbsLeftFill = nAbsLeftFill + nAbsDist;
+ m_nRelLeftFill = m_nRelLeftFill + nRelDist;
+ nParentInhAbsLeftSpace = nParentInhAbsSpace;
+ break;
+ case SvxAdjust::Center:
+ {
+ sal_uInt16 nAbsLeftDist = nAbsDist / 2;
+ nAbsLeftFill = nAbsLeftFill + nAbsLeftDist;
+ nAbsRightFill += nAbsDist - nAbsLeftDist;
+ sal_uInt16 nRelLeftDist = nRelDist / 2;
+ m_nRelLeftFill = m_nRelLeftFill + nRelLeftDist;
+ m_nRelRightFill += nRelDist - nRelLeftDist;
+ nParentInhAbsLeftSpace = nParentInhAbsSpace / 2;
+ nParentInhAbsRightSpace = nParentInhAbsSpace -
+ nParentInhAbsLeftSpace;
+ }
+ break;
+ case SvxAdjust::Left:
+ default:
+ nAbsRightFill = nAbsRightFill + nAbsDist;
+ m_nRelRightFill = m_nRelRightFill + nRelDist;
+ nParentInhAbsRightSpace = nParentInhAbsSpace;
+ break;
+ }
+
+ // Filler widths are added to the outer columns, if there are no boxes
+ // for them after the first pass (nWidth>0) or their width would become
+ // too small or if there are COL tags and the filler width corresponds
+ // to the border width.
+ // In the last case we probably exported the table ourselves.
+ if( m_nRelLeftFill &&
+ ( m_nWidthSet>0 || nAbsLeftFill<MINLAY+m_nInhLeftBorderWidth ||
+ (HasColTags() && nAbsLeftFill < nAbsLeftSpace+nParentInhAbsLeftSpace+20) ) )
+ {
+ SwHTMLTableLayoutColumn *pColumn = GetColumn( 0 );
+ pColumn->SetAbsColWidth( pColumn->GetAbsColWidth()+nAbsLeftFill );
+ pColumn->SetRelColWidth( pColumn->GetRelColWidth()+m_nRelLeftFill );
+ m_nRelLeftFill = 0;
+ m_nInhAbsLeftSpace = nAbsLeftSpace + nParentInhAbsLeftSpace;
+ }
+ if( m_nRelRightFill &&
+ ( m_nWidthSet>0 || nAbsRightFill<MINLAY+m_nInhRightBorderWidth ||
+ (HasColTags() && nAbsRightFill < nAbsRightSpace+nParentInhAbsRightSpace+20) ) )
+ {
+ SwHTMLTableLayoutColumn *pColumn = GetColumn( m_nCols-1 );
+ pColumn->SetAbsColWidth( pColumn->GetAbsColWidth()+nAbsRightFill );
+ pColumn->SetRelColWidth( pColumn->GetRelColWidth()+m_nRelRightFill );
+ m_nRelRightFill = 0;
+ m_nInhAbsRightSpace = nAbsRightSpace + nParentInhAbsRightSpace;
+ }
+}
+
+static void lcl_ResizeLine( const SwTableLine* pLine, SwTwips *pWidth );
+
+static void lcl_ResizeBox( const SwTableBox* pBox, SwTwips* pWidth )
+{
+ if( !pBox->GetSttNd() )
+ {
+ SwTwips nWidth = 0;
+ for( const SwTableLine *pLine : pBox->GetTabLines() )
+ lcl_ResizeLine( pLine, &nWidth );
+ pBox->GetFrameFormat()->SetFormatAttr( SwFormatFrameSize( SwFrameSize::Variable, nWidth, 0 ));
+ *pWidth = *pWidth + nWidth;
+ }
+ else
+ {
+ *pWidth = *pWidth + pBox->GetFrameFormat()->GetFrameSize().GetSize().Width();
+ }
+}
+
+static void lcl_ResizeLine( const SwTableLine* pLine, SwTwips *pWidth )
+{
+ SwTwips nOldWidth = *pWidth;
+ *pWidth = 0;
+ for( const SwTableBox* pBox : pLine->GetTabBoxes() )
+ lcl_ResizeBox(pBox, pWidth );
+
+ SAL_WARN_IF( nOldWidth && std::abs(*pWidth-nOldWidth) >= COLFUZZY, "sw.core",
+ "A box's rows have all a different length" );
+}
+
+void SwHTMLTableLayout::SetWidths( bool bCallPass2, sal_uInt16 nAbsAvail,
+ sal_uInt16 nRelAvail, sal_uInt16 nAbsLeftSpace,
+ sal_uInt16 nAbsRightSpace,
+ sal_uInt16 nParentInhAbsSpace )
+{
+ // SetWidth must have been passed through once more for every cell in the
+ // end.
+ m_nWidthSet++;
+
+ // Step 0: If necessary, we call the layout algorithm of Pass2.
+ if( bCallPass2 )
+ AutoLayoutPass2( nAbsAvail, nRelAvail, nAbsLeftSpace, nAbsRightSpace,
+ nParentInhAbsSpace );
+
+ // Step 1: Set the new width in all content boxes.
+ // Because the boxes don't know anything about the HTML table structure,
+ // we iterate over the HTML table structure.
+ // For tables in tables in tables we call SetWidth recursively.
+ for( sal_uInt16 i=0; i<m_nRows; i++ )
+ {
+ for( sal_uInt16 j=0; j<m_nCols; j++ )
+ {
+ SwHTMLTableLayoutCell *pCell = GetCell( i, j );
+
+ SwHTMLTableLayoutCnts* pContents = pCell->GetContents().get();
+ while( pContents && !pContents->IsWidthSet(m_nWidthSet) )
+ {
+ SwTableBox *pBox = pContents->GetTableBox();
+ if( pBox )
+ {
+ SetBoxWidth( pBox, j, pCell->GetColSpan() );
+ }
+ else if (SwHTMLTableLayout *pTable = pContents->GetTable())
+ {
+ sal_uInt16 nAbs = 0, nRel = 0, nLSpace = 0, nRSpace = 0,
+ nInhSpace = 0;
+ if( bCallPass2 )
+ {
+ sal_uInt16 nColSpan = pCell->GetColSpan();
+ GetAvail( j, nColSpan, nAbs, nRel );
+ nLSpace = GetLeftCellSpace( j, nColSpan );
+ nRSpace = GetRightCellSpace( j, nColSpan );
+ nInhSpace = GetInhCellSpace( j, nColSpan );
+ }
+ pTable->SetWidths( bCallPass2, nAbs, nRel,
+ nLSpace, nRSpace,
+ nInhSpace );
+ }
+
+ pContents->SetWidthSet( m_nWidthSet );
+ pContents = pContents->GetNext().get();
+ }
+ }
+ }
+
+ // Step 2: If we have a top table, we adapt the formats of the
+ // non-content-boxes. Because they are not known in the HTML table
+ // due to garbage collection there, we need the iterate over the
+ // whole table.
+ // We also adapt the table frame format. For nested tables we set the
+ // filler cell's width instead.
+ if( !IsTopTable() )
+ return;
+
+ SwTwips nCalcTabWidth = 0;
+ for( const SwTableLine *pLine : m_pSwTable->GetTabLines() )
+ lcl_ResizeLine( pLine, &nCalcTabWidth );
+ SAL_WARN_IF( std::abs( m_nRelTabWidth-nCalcTabWidth ) >= COLFUZZY, "sw.core",
+ "Table width is not equal to the row width" );
+
+ // Lock the table format when altering it, or else the box formats
+ // are altered again.
+ // Also, we need to preserve a percent setting if it exists.
+ SwFrameFormat *pFrameFormat = m_pSwTable->GetFrameFormat();
+ const_cast<SwTable *>(m_pSwTable)->LockModify();
+ SwFormatFrameSize aFrameSize( pFrameFormat->GetFrameSize() );
+ aFrameSize.SetWidth( m_nRelTabWidth );
+ bool bRel = m_bUseRelWidth &&
+ text::HoriOrientation::FULL!=pFrameFormat->GetHoriOrient().GetHoriOrient();
+ aFrameSize.SetWidthPercent( static_cast<sal_uInt8>(bRel ? m_nWidthOption : 0) );
+ pFrameFormat->SetFormatAttr( aFrameSize );
+ const_cast<SwTable *>(m_pSwTable)->UnlockModify();
+
+ // If the table is located in a frame, we also need to adapt the
+ // frame's width.
+ if( MayBeInFlyFrame() )
+ {
+ SwFrameFormat *pFlyFrameFormat = FindFlyFrameFormat();
+ if( pFlyFrameFormat )
+ {
+ SwFormatFrameSize aFlyFrameSize( SwFrameSize::Variable, m_nRelTabWidth, MINLAY );
+
+ if( m_bUseRelWidth )
+ {
+ // For percentage settings we set the width to the minimum.
+ aFlyFrameSize.SetWidth( m_nMin > USHRT_MAX ? USHRT_MAX
+ : m_nMin );
+ aFlyFrameSize.SetWidthPercent( static_cast<sal_uInt8>(m_nWidthOption) );
+ }
+ pFlyFrameFormat->SetFormatAttr( aFlyFrameSize );
+ }
+ }
+
+#ifdef DBG_UTIL
+ {
+ // check if the tables have correct widths
+ SwTwips nSize = m_pSwTable->GetFrameFormat()->GetFrameSize().GetWidth();
+ const SwTableLines& rLines = m_pSwTable->GetTabLines();
+ for (size_t n = 0; n < rLines.size(); ++n)
+ {
+ CheckBoxWidth( *rLines[ n ], nSize );
+ }
+ }
+#endif
+}
+
+void SwHTMLTableLayout::Resize_( sal_uInt16 nAbsAvail, bool bRecalc )
+{
+ // If bRecalc is set, the table's content changed.
+ // We need to execute pass 1 again.
+ if( bRecalc )
+ AutoLayoutPass1();
+
+ SwRootFrame *pRoot = GetDoc().getIDocumentLayoutAccess().GetCurrentViewShell()->GetLayout();
+ if ( pRoot && pRoot->IsCallbackActionEnabled() )
+ pRoot->StartAllAction();
+
+ // Else we can set the widths, in which we have to run Pass 2 in each case.
+ SetWidths( true, nAbsAvail );
+
+ if (pRoot && pRoot->IsCallbackActionEnabled())
+ pRoot->EndAllAction();
+}
+
+IMPL_LINK_NOARG( SwHTMLTableLayout, DelayedResize_Impl, Timer*, void )
+{
+ m_aResizeTimer.Stop();
+ Resize_( m_nDelayedResizeAbsAvail, m_bDelayedResizeRecalc );
+}
+
+bool SwHTMLTableLayout::Resize( sal_uInt16 nAbsAvail, bool bRecalc,
+ bool bForce, sal_uLong nDelay )
+{
+ if( 0 == nAbsAvail )
+ return false;
+ OSL_ENSURE( IsTopTable(), "Resize must only be called for top tables!" );
+
+ // May the table be resized at all? Or is it forced?
+ if( m_bMustNotResize && !bForce )
+ return false;
+
+ // May the table be recalculated? Or is it forced?
+ if( m_bMustNotRecalc && !bForce )
+ bRecalc = false;
+
+ const SwDoc& rDoc = GetDoc();
+
+ // If there is a layout, the root frame's size instead of the
+ // VisArea's size was potentially passed.
+ // If we're not in a frame we need to calculate the table for the VisArea,
+ // because switching from relative to absolute wouldn't work.
+ if( rDoc.getIDocumentLayoutAccess().GetCurrentViewShell() && rDoc.getIDocumentLayoutAccess().GetCurrentViewShell()->GetViewOptions()->getBrowseMode() )
+ {
+ const sal_uInt16 nVisAreaWidth = GetBrowseWidthByVisArea( rDoc );
+ if( nVisAreaWidth < nAbsAvail && !FindFlyFrameFormat() )
+ nAbsAvail = nVisAreaWidth;
+ }
+
+ if( nDelay==0 && m_aResizeTimer.IsActive() )
+ {
+ m_nDelayedResizeAbsAvail = nAbsAvail;
+ return false;
+ }
+
+ // Optimisation:
+ // If the minimum or maximum should not be recalculated and
+ // - the table's width never needs to be recalculated, or
+ // - the table was already calculated for the passed width, or
+ // - the available space is less or equal to the minimum width
+ // and the table already has the minimum width, or
+ // - the available space is larger than the maximum width and
+ // the table already has the maximum width
+ // nothing will happen to the table.
+ if( !bRecalc && ( !m_bMustResize ||
+ (m_nLastResizeAbsAvail==nAbsAvail) ||
+ (nAbsAvail<=m_nMin && m_nRelTabWidth==m_nMin) ||
+ (!m_bPercentWidthOption && nAbsAvail>=m_nMax && m_nRelTabWidth==m_nMax) ) )
+ return false;
+
+ if( nDelay==HTMLTABLE_RESIZE_NOW )
+ {
+ if( m_aResizeTimer.IsActive() )
+ m_aResizeTimer.Stop();
+ Resize_( nAbsAvail, bRecalc );
+ }
+ else if( nDelay > 0 )
+ {
+ m_nDelayedResizeAbsAvail = nAbsAvail;
+ m_bDelayedResizeRecalc = bRecalc;
+ m_aResizeTimer.SetTimeout( nDelay );
+ m_aResizeTimer.Start();
+ }
+ else
+ {
+ Resize_( nAbsAvail, bRecalc );
+ }
+
+ return true;
+}
+
+void SwHTMLTableLayout::BordersChanged( sal_uInt16 nAbsAvail )
+{
+ m_bBordersChanged = true;
+
+ Resize( nAbsAvail, true/*bRecalc*/ );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/lineinfo.cxx b/sw/source/core/doc/lineinfo.cxx
new file mode 100644
index 0000000000..628e90e321
--- /dev/null
+++ b/sw/source/core/doc/lineinfo.cxx
@@ -0,0 +1,132 @@
+/* -*- 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 <doc.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <IDocumentStylePoolAccess.hxx>
+#include <IDocumentState.hxx>
+#include <lineinfo.hxx>
+#include <charfmt.hxx>
+#include <poolfmt.hxx>
+#include <rootfrm.hxx>
+#include <osl/diagnose.h>
+
+void SwDoc::SetLineNumberInfo( const SwLineNumberInfo &rNew )
+{
+ SwRootFrame* pTmpRoot = getIDocumentLayoutAccess().GetCurrentLayout();
+ if ( pTmpRoot &&
+ (rNew.IsCountBlankLines() != mpLineNumberInfo->IsCountBlankLines() ||
+ rNew.IsRestartEachPage() != mpLineNumberInfo->IsRestartEachPage()) )
+ {
+ pTmpRoot->StartAllAction();
+ // FME 2007-08-14 #i80120# Invalidate size, because ChgThisLines()
+ // is only (and may only be) called by the formatting routines
+ //pTmpRoot->InvalidateAllContent( SwInvalidateFlags::LineNum | SwInvalidateFlags::Size );
+ for( auto aLayout : GetAllLayouts() )
+ aLayout->InvalidateAllContent( SwInvalidateFlags::LineNum | SwInvalidateFlags::Size );
+ pTmpRoot->EndAllAction();
+ }
+ *mpLineNumberInfo = rNew;
+ getIDocumentState().SetModified();
+}
+
+const SwLineNumberInfo& SwDoc::GetLineNumberInfo() const
+{
+ return *mpLineNumberInfo;
+}
+
+SwLineNumberInfo::SwLineNumberInfo() :
+ m_nPosFromLeft(o3tl::toTwips(5, o3tl::Length::mm)),
+ m_nCountBy( 5 ),
+ m_nDividerCountBy( 3 ),
+ m_ePos( LINENUMBER_POS_LEFT ),
+ m_bPaintLineNumbers( false ),
+ m_bCountBlankLines( true ),
+ m_bCountInFlys( false ),
+ m_bRestartEachPage( false )
+{
+}
+
+SwLineNumberInfo::SwLineNumberInfo(const SwLineNumberInfo &rCpy ) : SwClient(),
+ m_aType( rCpy.GetNumType() ),
+ m_aDivider( rCpy.GetDivider() ),
+ m_nPosFromLeft( rCpy.GetPosFromLeft() ),
+ m_nCountBy( rCpy.GetCountBy() ),
+ m_nDividerCountBy( rCpy.GetDividerCountBy() ),
+ m_ePos( rCpy.GetPos() ),
+ m_bPaintLineNumbers( rCpy.IsPaintLineNumbers() ),
+ m_bCountBlankLines( rCpy.IsCountBlankLines() ),
+ m_bCountInFlys( rCpy.IsCountInFlys() ),
+ m_bRestartEachPage( rCpy.IsRestartEachPage() )
+{
+ StartListeningToSameModifyAs(rCpy);
+}
+
+SwLineNumberInfo& SwLineNumberInfo::operator=(const SwLineNumberInfo &rCpy)
+{
+ StartListeningToSameModifyAs(rCpy);
+
+ m_aType = rCpy.GetNumType();
+ m_aDivider = rCpy.GetDivider();
+ m_nPosFromLeft = rCpy.GetPosFromLeft();
+ m_nCountBy = rCpy.GetCountBy();
+ m_nDividerCountBy = rCpy.GetDividerCountBy();
+ m_ePos = rCpy.GetPos();
+ m_bPaintLineNumbers = rCpy.IsPaintLineNumbers();
+ m_bCountBlankLines = rCpy.IsCountBlankLines();
+ m_bCountInFlys = rCpy.IsCountInFlys();
+ m_bRestartEachPage = rCpy.IsRestartEachPage();
+
+ return *this;
+}
+
+SwCharFormat* SwLineNumberInfo::GetCharFormat( IDocumentStylePoolAccess& rIDSPA ) const
+{
+ if ( !GetRegisteredIn() )
+ {
+ SwCharFormat* pFormat = rIDSPA.GetCharFormatFromPool( RES_POOLCHR_LINENUM );
+ pFormat->Add( const_cast<SwLineNumberInfo*>(this) );
+ }
+ return const_cast<SwCharFormat*>(static_cast<const SwCharFormat*>(GetRegisteredIn()));
+}
+
+void SwLineNumberInfo::SetCharFormat( SwCharFormat *pChFormat )
+{
+ OSL_ENSURE( pChFormat, "SetCharFormat, 0 is not a valid pointer" );
+ pChFormat->Add( this );
+}
+
+void SwLineNumberInfo::SwClientNotify(const SwModify&, const SfxHint& rHint)
+{
+ if (rHint.GetId() != SfxHintId::SwLegacyModify)
+ return;
+ auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
+ CheckRegistration( pLegacy->m_pOld );
+ SwDoc *pDoc = static_cast<SwCharFormat*>(GetRegisteredIn())->GetDoc();
+ SwRootFrame* pRoot = pDoc->getIDocumentLayoutAccess().GetCurrentLayout();
+ if( pRoot )
+ {
+ pRoot->StartAllAction();
+ for( auto aLayout : pDoc->GetAllLayouts() )
+ aLayout->AllAddPaintRect();
+ pRoot->EndAllAction();
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/list.cxx b/sw/source/core/doc/list.cxx
new file mode 100644
index 0000000000..5095d4e6c9
--- /dev/null
+++ b/sw/source/core/doc/list.cxx
@@ -0,0 +1,190 @@
+/* -*- 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 <list.hxx>
+
+#include <numrule.hxx>
+#include <ndarr.hxx>
+#include <node.hxx>
+#include <utility>
+
+SwList::SwList( OUString sListId,
+ SwNumRule& rDefaultListStyle,
+ const SwNodes& rNodes )
+ : msListId( std::move(sListId) ),
+ msDefaultListStyleName( rDefaultListStyle.GetName() ),
+ mnMarkedListLevel( MAXLEVEL )
+{
+ // create empty list trees for the document ranges
+ const SwNode* pNode = rNodes[SwNodeOffset(0)];
+ std::vector<bool> aVisited(static_cast<sal_Int32>(rNodes.Count()), false);
+ do
+ {
+ SwNodeOffset nIndex = pNode->GetIndex();
+ if (aVisited[static_cast<sal_Int32>(nIndex)])
+ {
+ // crashtesting ooo84576-1.odt, which manages to trigger a broken document structure
+ // in our code. This is just a workaround to prevent an infinite loop leading to OOM.
+ SAL_WARN("sw.core", "corrupt document structure, bailing out of infinite loop");
+ throw css::uno::RuntimeException("corrupt document structure, bailing out of infinite loop");
+ }
+ aVisited[static_cast<sal_Int32>(nIndex)] = true;
+ SwPaM aPam( *pNode, *pNode->EndOfSectionNode() );
+
+ maListTrees.emplace_back(
+ std::make_unique<SwNodeNum>( &rDefaultListStyle ),
+ std::make_unique<SwNodeNum>( &rDefaultListStyle ),
+ std::make_unique<SwNodeNum>( &rDefaultListStyle ),
+ std::make_unique<SwPaM>( *(aPam.Start()), *(aPam.End()) ));
+
+ pNode = pNode->EndOfSectionNode();
+ if (pNode != &rNodes.GetEndOfContent())
+ {
+ nIndex = pNode->GetIndex();
+ nIndex++;
+ pNode = rNodes[nIndex];
+ }
+ }
+ while ( pNode != &rNodes.GetEndOfContent() );
+}
+
+SwList::~SwList() COVERITY_NOEXCEPT_FALSE
+{
+ for ( auto& rNumberTree : maListTrees )
+ {
+ SwNodeNum::HandleNumberTreeRootNodeDelete(*(rNumberTree.pRoot));
+ SwNodeNum::HandleNumberTreeRootNodeDelete(*(rNumberTree.pRootRLHidden));
+ SwNodeNum::HandleNumberTreeRootNodeDelete(*(rNumberTree.pRootOrigText));
+ }
+}
+
+bool SwList::HasNodes() const
+{
+ for (auto const& rNumberTree : maListTrees)
+ {
+ if (rNumberTree.pRoot->GetChildCount() != 0)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+void SwList::InsertListItem(SwNodeNum& rNodeNum, SwListRedlineType const eRedline,
+ const int nLevel, const SwDoc& rDoc)
+{
+ const SwPosition aPosOfNodeNum( rNodeNum.GetPosition() );
+ const SwNodes* pNodesOfNodeNum = &(aPosOfNodeNum.GetNode().GetNodes());
+
+ for ( const auto& rNumberTree : maListTrees )
+ {
+ auto [pStart, pEnd] = rNumberTree.pSection->StartEnd(); // SwPosition*
+ const SwNodes* pRangeNodes = &(pStart->GetNode().GetNodes());
+
+ if ( pRangeNodes == pNodesOfNodeNum &&
+ *pStart <= aPosOfNodeNum && aPosOfNodeNum <= *pEnd)
+ {
+ auto const& pRoot(SwListRedlineType::HIDDEN == eRedline
+ ? rNumberTree.pRootRLHidden
+ : SwListRedlineType::SHOW == eRedline
+ ? rNumberTree.pRoot
+ : rNumberTree.pRootOrigText);
+ pRoot->AddChild(&rNodeNum, nLevel, rDoc);
+ break;
+ }
+ }
+}
+
+void SwList::RemoveListItem(SwNodeNum& rNodeNum, const SwDoc& rDoc)
+{
+ rNodeNum.RemoveMe(rDoc);
+}
+
+void SwList::InvalidateListTree()
+{
+ for ( const auto& rNumberTree : maListTrees )
+ {
+ rNumberTree.pRoot->InvalidateTree();
+ rNumberTree.pRootRLHidden->InvalidateTree();
+ rNumberTree.pRootOrigText->InvalidateTree();
+ }
+}
+
+void SwList::ValidateListTree(const SwDoc& rDoc)
+{
+ for ( auto& rNumberTree : maListTrees )
+ {
+ rNumberTree.pRoot->NotifyInvalidChildren(rDoc);
+ rNumberTree.pRootRLHidden->NotifyInvalidChildren(rDoc);
+ rNumberTree.pRootOrigText->NotifyInvalidChildren(rDoc);
+ }
+}
+
+void SwList::MarkListLevel( const int nListLevel,
+ const bool bValue )
+{
+ if ( bValue )
+ {
+ if ( nListLevel != mnMarkedListLevel )
+ {
+ if ( mnMarkedListLevel != MAXLEVEL )
+ {
+ // notify former marked list nodes
+ NotifyItemsOnListLevel( mnMarkedListLevel );
+ }
+
+ mnMarkedListLevel = nListLevel;
+
+ // notify new marked list nodes
+ NotifyItemsOnListLevel( mnMarkedListLevel );
+ }
+ }
+ else
+ {
+ if ( mnMarkedListLevel != MAXLEVEL )
+ {
+ // notify former marked list nodes
+ NotifyItemsOnListLevel( mnMarkedListLevel );
+ }
+
+ mnMarkedListLevel = MAXLEVEL;
+ }
+}
+
+bool SwList::IsListLevelMarked( const int nListLevel ) const
+{
+ return nListLevel == mnMarkedListLevel;
+}
+
+void SwList::NotifyItemsOnListLevel( const int nLevel )
+{
+ for ( auto& rNumberTree : maListTrees )
+ {
+ rNumberTree.pRoot->NotifyNodesOnListLevel( nLevel );
+ rNumberTree.pRootRLHidden->NotifyNodesOnListLevel( nLevel );
+ rNumberTree.pRootOrigText->NotifyNodesOnListLevel( nLevel );
+ }
+}
+
+void SwList::SetDefaultListStyleName(OUString const& rNew)
+{
+ msDefaultListStyleName = rNew;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/notxtfrm.cxx b/sw/source/core/doc/notxtfrm.cxx
new file mode 100644
index 0000000000..14cb568030
--- /dev/null
+++ b/sw/source/core/doc/notxtfrm.cxx
@@ -0,0 +1,1502 @@
+/* -*- 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 <hintids.hxx>
+#include <tools/urlobj.hxx>
+#include <vcl/imapobj.hxx>
+#include <vcl/imap.hxx>
+#include <svl/urihelper.hxx>
+#include <sfx2/progress.hxx>
+#include <sfx2/printer.hxx>
+#include <editeng/udlnitem.hxx>
+#include <editeng/colritem.hxx>
+#include <editeng/boxitem.hxx>
+#include <fmturl.hxx>
+#include <fmtsrnd.hxx>
+#include <frmfmt.hxx>
+#include <swrect.hxx>
+#include <fesh.hxx>
+#include <doc.hxx>
+#include <IDocumentSettingAccess.hxx>
+#include <IDocumentStylePoolAccess.hxx>
+#include <IDocumentDeviceAccess.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <flyfrm.hxx>
+#include <flyfrms.hxx>
+#include <frmtool.hxx>
+#include <viewopt.hxx>
+#include <viewimp.hxx>
+#include <pam.hxx>
+#include <hints.hxx>
+#include <rootfrm.hxx>
+#include <dflyobj.hxx>
+#include <pagefrm.hxx>
+#include <notxtfrm.hxx>
+#include <grfatr.hxx>
+#include <charatr.hxx>
+#include <ndnotxt.hxx>
+#include <ndgrf.hxx>
+#include <ndole.hxx>
+#include <swregion.hxx>
+#include <poolfmt.hxx>
+#include <strings.hrc>
+#include <accessibilityoptions.hxx>
+#include <com/sun/star/embed/EmbedMisc.hpp>
+#include <com/sun/star/embed/EmbedStates.hpp>
+#include <com/sun/star/embed/XEmbeddedObject.hpp>
+#include <svtools/embedhlp.hxx>
+#include <dview.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <drawinglayer/processor2d/baseprocessor2d.hxx>
+#include <drawinglayer/primitive2d/graphicprimitive2d.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <basegfx/utils/b2dclipstate.hxx>
+#include <drawinglayer/processor2d/processor2dtools.hxx>
+#include <txtfly.hxx>
+#include <drawinglayer/primitive2d/maskprimitive2d.hxx>
+#include <drawinglayer/primitive2d/objectinfoprimitive2d.hxx>
+#include <osl/diagnose.h>
+
+// MM02 needed for VOC mechanism and getting the OC - may be moved to an own file
+#include <svx/sdrpagewindow.hxx>
+#include <svx/svdpagv.hxx>
+#include <svx/sdr/contact/viewcontact.hxx>
+#include <svx/sdr/contact/viewobjectcontact.hxx>
+#include <svx/sdr/contact/objectcontact.hxx>
+#include <svx/sdr/contact/displayinfo.hxx>
+
+using namespace com::sun::star;
+
+static bool GetRealURL( const SwGrfNode& rNd, OUString& rText )
+{
+ bool bRet = rNd.GetFileFilterNms( &rText, nullptr );
+ if( bRet )
+ rText = URIHelper::removePassword( rText, INetURLObject::EncodeMechanism::WasEncoded,
+ INetURLObject::DecodeMechanism::Unambiguous);
+ if (rText.startsWith("data:image")) rText = "inline image";
+
+ return bRet;
+}
+
+static void lcl_PaintReplacement( const SwRect &rRect, const OUString &rText,
+ const SwViewShell &rSh, const SwNoTextFrame *pFrame,
+ bool bDefect )
+{
+ static vcl::Font aFont = []()
+ {
+ vcl::Font tmp;
+ tmp.SetWeight( WEIGHT_BOLD );
+ tmp.SetStyleName( OUString() );
+ tmp.SetFamilyName("Noto Sans");
+ tmp.SetFamily( FAMILY_SWISS );
+ tmp.SetTransparent( true );
+ return tmp;
+ }();
+
+ Color aCol( COL_RED );
+ FontLineStyle eUnderline = LINESTYLE_NONE;
+ const SwFormatURL &rURL = pFrame->FindFlyFrame()->GetFormat()->GetURL();
+ if( !rURL.GetURL().isEmpty() || rURL.GetMap() )
+ {
+ bool bVisited = false;
+ if ( rURL.GetMap() )
+ {
+ ImageMap *pMap = const_cast<ImageMap*>(rURL.GetMap());
+ for( size_t i = 0; i < pMap->GetIMapObjectCount(); ++i )
+ {
+ IMapObject *pObj = pMap->GetIMapObject( i );
+ if( rSh.GetDoc()->IsVisitedURL( pObj->GetURL() ) )
+ {
+ bVisited = true;
+ break;
+ }
+ }
+ }
+ else if ( !rURL.GetURL().isEmpty() )
+ bVisited = rSh.GetDoc()->IsVisitedURL( rURL.GetURL() );
+
+ SwFormat *pFormat = rSh.GetDoc()->getIDocumentStylePoolAccess().GetFormatFromPool( o3tl::narrowing<sal_uInt16>
+ (bVisited ? RES_POOLCHR_INET_VISIT : RES_POOLCHR_INET_NORMAL ) );
+ aCol = pFormat->GetColor().GetValue();
+ eUnderline = pFormat->GetUnderline().GetLineStyle();
+ }
+
+ aFont.SetUnderline( eUnderline );
+ aFont.SetColor( aCol );
+
+ const BitmapEx& rBmp = const_cast<SwViewShell&>(rSh).GetReplacementBitmap(bDefect);
+ Graphic::DrawEx(*rSh.GetOut(), rText, aFont, rBmp, rRect.Pos(), rRect.SSize());
+}
+
+SwNoTextFrame::SwNoTextFrame(SwNoTextNode * const pNode, SwFrame* pSib )
+: SwContentFrame( pNode, pSib )
+{
+ mnFrameType = SwFrameType::NoTxt;
+}
+
+SwContentFrame *SwNoTextNode::MakeFrame( SwFrame* pSib )
+{
+ return new SwNoTextFrame(this, pSib);
+}
+
+void SwNoTextFrame::DestroyImpl()
+{
+ StopAnimation();
+
+ SwContentFrame::DestroyImpl();
+}
+
+SwNoTextFrame::~SwNoTextFrame()
+{
+}
+
+void SetOutDev( SwViewShell *pSh, OutputDevice *pOut )
+{
+ pSh->mpOut = pOut;
+}
+
+static void lcl_ClearArea( const SwFrame &rFrame,
+ vcl::RenderContext &rOut, const SwRect& rPtArea,
+ const SwRect &rGrfArea )
+{
+ SwRegionRects aRegion( rPtArea, 4 );
+ aRegion -= rGrfArea;
+
+ if ( aRegion.empty() )
+ return;
+
+ const SvxBrushItem *pItem;
+ std::optional<Color> xCol;
+ SwRect aOrigRect;
+ drawinglayer::attribute::SdrAllFillAttributesHelperPtr aFillAttributes;
+
+ if ( rFrame.GetBackgroundBrush( aFillAttributes, pItem, xCol, aOrigRect, false, /*bConsiderTextBox=*/false ) )
+ {
+ SwRegionRects const region(rPtArea);
+ basegfx::utils::B2DClipState aClipState;
+ const bool bDone(::DrawFillAttributes(aFillAttributes, aOrigRect, region, aClipState, rOut));
+
+ if(!bDone)
+ {
+ for( const auto &rRegion : aRegion )
+ {
+ ::DrawGraphic(pItem, rOut, aOrigRect, rRegion);
+ }
+ }
+ }
+ else
+ {
+ rOut.Push( vcl::PushFlags::FILLCOLOR|vcl::PushFlags::LINECOLOR );
+ rOut.SetFillColor( rFrame.getRootFrame()->GetCurrShell()->Imp()->GetRetoucheColor());
+ rOut.SetLineColor();
+ for( const auto &rRegion : aRegion )
+ rOut.DrawRect( rRegion.SVRect() );
+ rOut.Pop();
+ }
+}
+
+void SwNoTextFrame::PaintSwFrame(vcl::RenderContext& rRenderContext, SwRect const& rRect, SwPrintData const*const) const
+{
+ if ( getFrameArea().IsEmpty() )
+ return;
+
+ const SwViewShell* pSh = getRootFrame()->GetCurrShell();
+ if( !pSh->GetViewOptions()->IsGraphic() )
+ {
+ StopAnimation();
+ // #i6467# - no paint of placeholder for page preview
+ if ( pSh->GetWin() && !pSh->IsPreview() )
+ {
+ const SwNoTextNode* pNd = GetNode()->GetNoTextNode();
+ OUString aText( pNd->GetTitle() );
+ if ( aText.isEmpty() && pNd->IsGrfNode() )
+ GetRealURL( *static_cast<const SwGrfNode*>(pNd), aText );
+ if( aText.isEmpty() )
+ aText = FindFlyFrame()->GetFormat()->GetName();
+ lcl_PaintReplacement( getFrameArea(), aText, *pSh, this, false );
+ }
+ return;
+ }
+
+ if( pSh->GetAccessibilityOptions()->IsStopAnimatedGraphics() ||
+ // #i9684# Stop animation during printing/pdf export
+ !pSh->GetWin() )
+ StopAnimation();
+
+ SfxProgress::EnterLock(); // No progress reschedules in paint (SwapIn)
+
+ rRenderContext.Push();
+ bool bClip = true;
+ tools::PolyPolygon aPoly;
+
+ SwNoTextNode& rNoTNd = const_cast<SwNoTextNode&>(*static_cast<const SwNoTextNode*>(GetNode()));
+ SwGrfNode* pGrfNd = rNoTNd.GetGrfNode();
+ if( pGrfNd )
+ pGrfNd->SetFrameInPaint( true );
+
+ // #i13147# - add 2nd parameter with value <true> to
+ // method call <FindFlyFrame().GetContour(..)> to indicate that it is called
+ // for paint in order to avoid load of the intrinsic graphic.
+ if ( ( !rRenderContext.GetConnectMetaFile() ||
+ !pSh->GetWin() ) &&
+ FindFlyFrame()->GetContour( aPoly, true )
+ )
+ {
+ rRenderContext.SetClipRegion(vcl::Region(aPoly));
+ bClip = false;
+ }
+
+ SwRect aOrigPaint( rRect );
+ if ( HasAnimation() && pSh->GetWin() )
+ {
+ aOrigPaint = getFrameArea(); aOrigPaint += getFramePrintArea().Pos();
+ }
+
+ SwRect aGrfArea( getFrameArea() );
+ SwRect aPaintArea( aGrfArea );
+
+ // In case the picture fly frm was clipped, render it with the origin
+ // size instead of scaling it
+ if ( pGrfNd && rNoTNd.getIDocumentSettingAccess()->get( DocumentSettingId::CLIPPED_PICTURES ) )
+ {
+ auto pFindFly = FindFlyFrame();
+ if (pFindFly && pFindFly->IsFlyFreeFrame())
+ {
+ const SwFlyFreeFrame *pFly = static_cast< const SwFlyFreeFrame* >( pFindFly );
+ bool bGetUnclippedFrame=true;
+ const SvxBoxItem* pBoxItem;
+ if( pFly->GetFormat() && (pBoxItem = pFly->GetFormat()->GetItemIfSet(RES_BOX, false)) )
+ {
+ if( pBoxItem->HasBorder( /*bTreatPaddingAsBorder*/true) )
+ bGetUnclippedFrame = false;
+ }
+
+ if( bGetUnclippedFrame )
+ aGrfArea = SwRect( getFrameArea().Pos( ), pFly->GetUnclippedFrame( ).SSize( ) );
+ }
+ }
+
+ aPaintArea.Intersection_( aOrigPaint );
+
+ SwRect aNormal( getFrameArea().Pos() + getFramePrintArea().Pos(), getFramePrintArea().SSize() );
+ aNormal.Justify(); // Normalized rectangle for the comparisons
+
+ if( aPaintArea.Overlaps( aNormal ) )
+ {
+ // Calculate the four to-be-deleted rectangles
+ if( pSh->GetWin() )
+ ::lcl_ClearArea( *this, rRenderContext, aPaintArea, aNormal );
+
+ // The intersection of the PaintArea and the Bitmap contains the absolutely visible area of the Frame
+ aPaintArea.Intersection_( aNormal );
+
+ if ( bClip )
+ rRenderContext.IntersectClipRegion( aPaintArea.SVRect() );
+ /// delete unused 3rd parameter
+ PaintPicture( &rRenderContext, aGrfArea );
+ }
+ else
+ // If it's not visible, simply delete the given Area
+ lcl_ClearArea( *this, rRenderContext, aPaintArea, SwRect() );
+ if( pGrfNd )
+ pGrfNd->SetFrameInPaint( false );
+
+ rRenderContext.Pop();
+ SfxProgress::LeaveLock();
+}
+
+/** Calculate the position and the size of the graphic in the Frame,
+ corresponding to the current graphic attributes
+
+ @param Point the position in the Frame (also returned)
+ @param Size the graphic's size (also returned)
+ @param nMirror the current mirror attribute
+*/
+static void lcl_CalcRect( Point& rPt, Size& rDim, MirrorGraph nMirror )
+{
+ if( nMirror == MirrorGraph::Vertical || nMirror == MirrorGraph::Both )
+ {
+ rPt.setX(rPt.getX() + rDim.Width() -1);
+ rDim.setWidth( -rDim.Width() );
+ }
+
+ if( nMirror == MirrorGraph::Horizontal || nMirror == MirrorGraph::Both )
+ {
+ rPt.setY(rPt.getY() + rDim.Height() -1);
+ rDim.setHeight( -rDim.Height() );
+ }
+}
+
+/** Calculate the Bitmap's position and the size within the passed rectangle */
+void SwNoTextFrame::GetGrfArea( SwRect &rRect, SwRect* pOrigRect ) const
+{
+ // Currently only used for scaling, cropping and mirroring the contour of graphics!
+ // Everything else is handled by GraphicObject
+ // We put the graphic's visible rectangle into rRect.
+ // pOrigRect contains position and size of the whole graphic.
+
+ // RotateFlyFrame3: SwFrame may be transformed. Get untransformed
+ // SwRect(s) as base of calculation
+ const TransformableSwFrame* pTransformableSwFrame(getTransformableSwFrame());
+ const SwRect aFrameArea(pTransformableSwFrame ? pTransformableSwFrame->getUntransformedFrameArea() : getFrameArea());
+ const SwRect aFramePrintArea(pTransformableSwFrame ? pTransformableSwFrame->getUntransformedFramePrintArea() : getFramePrintArea());
+
+ const SwAttrSet& rAttrSet = GetNode()->GetSwAttrSet();
+ const SwCropGrf& rCrop = rAttrSet.GetCropGrf();
+ MirrorGraph nMirror = rAttrSet.GetMirrorGrf().GetValue();
+
+ if( rAttrSet.GetMirrorGrf().IsGrfToggle() )
+ {
+ if( !(FindPageFrame()->GetVirtPageNum() % 2) )
+ {
+ switch ( nMirror )
+ {
+ case MirrorGraph::Dont: nMirror = MirrorGraph::Vertical; break;
+ case MirrorGraph::Vertical: nMirror = MirrorGraph::Dont; break;
+ case MirrorGraph::Horizontal: nMirror = MirrorGraph::Both; break;
+ default: nMirror = MirrorGraph::Horizontal; break;
+ }
+ }
+ }
+
+ // We read graphic from the Node, if needed.
+ // It may fail, however.
+ tools::Long nLeftCrop, nRightCrop, nTopCrop, nBottomCrop;
+ Size aOrigSz( static_cast<const SwNoTextNode*>(GetNode())->GetTwipSize() );
+ if ( !aOrigSz.Width() )
+ {
+ aOrigSz.setWidth( aFramePrintArea.Width() );
+ nLeftCrop = -rCrop.GetLeft();
+ nRightCrop = -rCrop.GetRight();
+ }
+ else
+ {
+ nLeftCrop = std::max( aOrigSz.Width() -
+ (rCrop.GetRight() + rCrop.GetLeft()), tools::Long(1) );
+ const double nScale = double(aFramePrintArea.Width()) / double(nLeftCrop);
+ nLeftCrop = tools::Long(nScale * -rCrop.GetLeft() );
+ nRightCrop = tools::Long(nScale * -rCrop.GetRight() );
+ }
+
+ // crop values have to be mirrored too
+ if( nMirror == MirrorGraph::Vertical || nMirror == MirrorGraph::Both )
+ {
+ tools::Long nTmpCrop = nLeftCrop;
+ nLeftCrop = nRightCrop;
+ nRightCrop= nTmpCrop;
+ }
+
+ if( !aOrigSz.Height() )
+ {
+ aOrigSz.setHeight( aFramePrintArea.Height() );
+ nTopCrop = -rCrop.GetTop();
+ nBottomCrop= -rCrop.GetBottom();
+ }
+ else
+ {
+ nTopCrop = std::max( aOrigSz.Height() - (rCrop.GetTop() + rCrop.GetBottom()), tools::Long(1) );
+ const double nScale = double(aFramePrintArea.Height()) / double(nTopCrop);
+ nTopCrop = tools::Long(nScale * -rCrop.GetTop() );
+ nBottomCrop= tools::Long(nScale * -rCrop.GetBottom() );
+ }
+
+ // crop values have to be mirrored too
+ if( nMirror == MirrorGraph::Horizontal || nMirror == MirrorGraph::Both )
+ {
+ tools::Long nTmpCrop = nTopCrop;
+ nTopCrop = nBottomCrop;
+ nBottomCrop= nTmpCrop;
+ }
+
+ Size aVisSz( aFramePrintArea.SSize() );
+ Size aGrfSz( aVisSz );
+ Point aVisPt( aFrameArea.Pos() + aFramePrintArea.Pos() );
+ Point aGrfPt( aVisPt );
+
+ // Set the "visible" rectangle first
+ if ( nLeftCrop > 0 )
+ {
+ aVisPt.setX(aVisPt.getX() + nLeftCrop);
+ aVisSz.AdjustWidth( -nLeftCrop );
+ }
+ if ( nTopCrop > 0 )
+ {
+ aVisPt.setY(aVisPt.getY() + nTopCrop);
+ aVisSz.AdjustHeight( -nTopCrop );
+ }
+ if ( nRightCrop > 0 )
+ aVisSz.AdjustWidth( -nRightCrop );
+ if ( nBottomCrop > 0 )
+ aVisSz.AdjustHeight( -nBottomCrop );
+
+ rRect.Pos ( aVisPt );
+ rRect.SSize( aVisSz );
+
+ // Calculate the whole graphic if needed
+ if ( !pOrigRect )
+ return;
+
+ Size aTmpSz( aGrfSz );
+ aGrfPt.setX(aGrfPt.getX() + nLeftCrop);
+ aTmpSz.AdjustWidth( -(nLeftCrop + nRightCrop) );
+ aGrfPt.setY(aGrfPt.getY() + nTopCrop);
+ aTmpSz.AdjustHeight( -(nTopCrop + nBottomCrop) );
+
+ if( MirrorGraph::Dont != nMirror )
+ lcl_CalcRect( aGrfPt, aTmpSz, nMirror );
+
+ pOrigRect->Pos ( aGrfPt );
+ pOrigRect->SSize( aTmpSz );
+}
+
+/** By returning the surrounding Fly's size which equals the graphic's size */
+const Size& SwNoTextFrame::GetSize() const
+{
+ // Return the Frame's size
+ const SwFrame *pFly = FindFlyFrame();
+ if( !pFly )
+ pFly = this;
+ return pFly->getFramePrintArea().SSize();
+}
+
+void SwNoTextFrame::MakeAll(vcl::RenderContext* pRenderContext)
+{
+ // RotateFlyFrame3 - inner frame. Get rotation and check if used
+ const double fRotation(getLocalFrameRotation());
+ const bool bRotated(!basegfx::fTools::equalZero(fRotation));
+
+ if(bRotated)
+ {
+ SwFlyFreeFrame* pUpperFly(dynamic_cast< SwFlyFreeFrame* >(GetUpper()));
+
+ if(pUpperFly)
+ {
+ if(!pUpperFly->isFrameAreaDefinitionValid())
+ {
+ // RotateFlyFrame3: outer frame *needs* to be layouted first, force this by calling
+ // it's ::Calc directly
+ pUpperFly->Calc(pRenderContext);
+ }
+
+ // Reset outer frame to unrotated state. This is necessary to make the
+ // layouting below work as currently implemented in Writer. As expected
+ // using Transformations allows to do this on the fly due to all information
+ // being included there.
+ // The full solution would be to adapt the whole layouting
+ // process of Writer to take care of Transformations, but that
+ // is currently beyond scope
+ if(pUpperFly->isTransformableSwFrame())
+ {
+ pUpperFly->getTransformableSwFrame()->restoreFrameAreas();
+ }
+ }
+
+ // Re-layout may be partially (see all isFrameAreaDefinitionValid() flags),
+ // so resetting the local SwFrame(s) in the local SwFrameAreaDefinition is also
+ // needed (e.g. for PrintPreview).
+ // Reset to BoundAreas will be done below automatically
+ if(isTransformableSwFrame())
+ {
+ getTransformableSwFrame()->restoreFrameAreas();
+ }
+ }
+
+ SwContentNotify aNotify( this );
+ SwBorderAttrAccess aAccess( SwFrame::GetCache(), this );
+ const SwBorderAttrs &rAttrs = *aAccess.Get();
+
+ while ( !isFrameAreaPositionValid() || !isFrameAreaSizeValid() || !isFramePrintAreaValid() )
+ {
+ MakePos();
+
+ if ( !isFrameAreaSizeValid() )
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aFrm.Width( GetUpper()->getFramePrintArea().Width() );
+ }
+
+ MakePrtArea( rAttrs );
+
+ if ( !isFrameAreaSizeValid() )
+ {
+ setFrameAreaSizeValid(true);
+ Format(getRootFrame()->GetCurrShell()->GetOut());
+ }
+ }
+
+ // RotateFlyFrame3 - inner frame
+ if(bRotated)
+ {
+ SwFlyFreeFrame* pUpperFly(dynamic_cast< SwFlyFreeFrame* >(GetUpper()));
+
+ if(pUpperFly)
+ {
+ // restore outer frame back to Transformed state, that means
+ // set the SwFrameAreaDefinition(s) back to BoundAreas of
+ // the transformed SwFrame. All needed information is part
+ // of the already correctly created Transformations of the
+ // upper frame, so it can be re-created on the fly
+ if(pUpperFly->isTransformableSwFrame())
+ {
+ pUpperFly->getTransformableSwFrame()->adaptFrameAreasToTransformations();
+ }
+ }
+
+ // After the unrotated layout is finished, apply possible set rotation to it
+ // get center from outer frame (layout frame) to be on the safe side
+ const Point aCenter(GetUpper() ? GetUpper()->getFrameArea().Center() : getFrameArea().Center());
+ const basegfx::B2DPoint aB2DCenter(aCenter.X(), aCenter.Y());
+
+ if(!mpTransformableSwFrame)
+ {
+ mpTransformableSwFrame.reset(new TransformableSwFrame(*this));
+ }
+
+ getTransformableSwFrame()->createFrameAreaTransformations(
+ fRotation,
+ aB2DCenter);
+ getTransformableSwFrame()->adaptFrameAreasToTransformations();
+ }
+ else
+ {
+ // reset transformations to show that they are not used
+ mpTransformableSwFrame.reset();
+ }
+}
+
+// RotateFlyFrame3 - Support for Transformations - outer frame
+basegfx::B2DHomMatrix SwNoTextFrame::getFrameAreaTransformation() const
+{
+ if(isTransformableSwFrame())
+ {
+ // use pre-created transformation
+ return getTransformableSwFrame()->getLocalFrameAreaTransformation();
+ }
+
+ // call parent
+ return SwContentFrame::getFrameAreaTransformation();
+}
+
+basegfx::B2DHomMatrix SwNoTextFrame::getFramePrintAreaTransformation() const
+{
+ if(isTransformableSwFrame())
+ {
+ // use pre-created transformation
+ return getTransformableSwFrame()->getLocalFramePrintAreaTransformation();
+ }
+
+ // call parent
+ return SwContentFrame::getFramePrintAreaTransformation();
+}
+
+// RotateFlyFrame3 - Support for Transformations
+void SwNoTextFrame::transform_translate(const Point& rOffset)
+{
+ // call parent - this will do the basic transform for SwRect(s)
+ // in the SwFrameAreaDefinition
+ SwContentFrame::transform_translate(rOffset);
+
+ // check if the Transformations need to be adapted
+ if(isTransformableSwFrame())
+ {
+ const basegfx::B2DHomMatrix aTransform(
+ basegfx::utils::createTranslateB2DHomMatrix(
+ rOffset.X(), rOffset.Y()));
+
+ // transform using TransformableSwFrame
+ getTransformableSwFrame()->transform(aTransform);
+ }
+}
+
+// RotateFlyFrame3 - inner frame
+// Check if we contain a SwGrfNode and get possible rotation from it
+double SwNoTextFrame::getLocalFrameRotation() const
+{
+ const SwNoTextNode* pSwNoTextNode(nullptr != GetNode() ? GetNode()->GetNoTextNode() : nullptr);
+
+ if(nullptr != pSwNoTextNode)
+ {
+ const SwGrfNode* pSwGrfNode(pSwNoTextNode->GetGrfNode());
+
+ if(nullptr != pSwGrfNode)
+ {
+ const SwAttrSet& rSwAttrSet(pSwGrfNode->GetSwAttrSet());
+ const SwRotationGrf& rSwRotationGrf(rSwAttrSet.GetRotationGrf());
+ const double fRotate = -toRadians(rSwRotationGrf.GetValue());
+
+ return basegfx::normalizeToRange(fRotate, 2 * M_PI);
+ }
+ }
+
+ // no rotation
+ return 0.0;
+}
+
+void SwNoTextFrame::dumpAsXml(xmlTextWriterPtr writer) const
+{
+ (void)xmlTextWriterStartElement(writer, reinterpret_cast<const xmlChar*>("notxt"));
+ dumpAsXmlAttributes(writer);
+
+ (void)xmlTextWriterStartElement(writer, BAD_CAST("infos"));
+ dumpInfosAsXml(writer);
+ (void)xmlTextWriterEndElement(writer);
+ dumpChildrenAsXml(writer);
+
+ (void)xmlTextWriterEndElement(writer);
+}
+
+/** Calculate the Bitmap's site, if needed */
+void SwNoTextFrame::Format( vcl::RenderContext* /*pRenderContext*/, const SwBorderAttrs * )
+{
+ const Size aNewSize( GetSize() );
+
+ // Did the height change?
+ SwTwips nChgHght = IsVertical() ?
+ static_cast<SwTwips>(aNewSize.Width() - getFramePrintArea().Width()) :
+ static_cast<SwTwips>(aNewSize.Height() - getFramePrintArea().Height());
+ if( nChgHght > 0)
+ Grow( nChgHght );
+ else if( nChgHght < 0)
+ Shrink( std::min(getFramePrintArea().Height(), tools::Long(-nChgHght)) );
+}
+
+bool SwNoTextFrame::GetCharRect( SwRect &rRect, const SwPosition& rPos,
+ SwCursorMoveState *pCMS, bool /*bAllowFarAway*/ ) const
+{
+ if ( &rPos.GetNode() != static_cast<SwNode const *>(GetNode()) )
+ return false;
+
+ Calc(getRootFrame()->GetCurrShell()->GetOut());
+ SwRect aFrameRect( getFrameArea() );
+ rRect = aFrameRect;
+ rRect.Pos( getFrameArea().Pos() + getFramePrintArea().Pos() );
+ rRect.SSize( getFramePrintArea().SSize() );
+
+ rRect.Justify();
+
+ // Is the Bitmap in the visible area at all?
+ if( !aFrameRect.Overlaps( rRect ) )
+ {
+ // If not, then the Cursor is on the Frame
+ rRect = aFrameRect;
+ rRect.Width( 1 );
+ }
+ else
+ rRect.Intersection_( aFrameRect );
+
+ if ( pCMS && pCMS->m_bRealHeight )
+ {
+ pCMS->m_aRealHeight.setY(rRect.Height());
+ pCMS->m_aRealHeight.setX(0);
+ }
+
+ return true;
+}
+
+bool SwNoTextFrame::GetModelPositionForViewPoint(SwPosition* pPos, Point& ,
+ SwCursorMoveState*, bool ) const
+{
+ pPos->Assign(*GetNode());
+ return true;
+}
+
+void SwNoTextFrame::ClearCache()
+{
+ SwFlyFrame* pFly = FindFlyFrame();
+ if( pFly && pFly->GetFormat()->GetSurround().IsContour() )
+ {
+ ClrContourCache( pFly->GetVirtDrawObj() );
+ pFly->NotifyBackground( FindPageFrame(), getFramePrintArea(), PrepareHint::FlyFrameAttributesChanged );
+ }
+}
+
+void SwNoTextFrame::OnGraphicArrived()
+{
+ if(GetNode()->GetNodeType() != SwNodeType::Grf)
+ {
+ InvalidatePrt();
+ SetCompletePaint();
+ return;
+ }
+ SwGrfNode* pNd = static_cast<SwGrfNode*>(GetNode());
+ ClearCache();
+ auto pVSh = pNd->GetDoc().getIDocumentLayoutAccess().GetCurrentViewShell();
+ if(pVSh)
+ pVSh->OnGraphicArrived(getFrameArea());
+}
+
+void SwNoTextFrame::SwClientNotify(const SwModify& rModify, const SfxHint& rHint)
+{
+ if(dynamic_cast<const sw::GrfRereadAndInCacheHint*>(&rHint))
+ {
+ if(SwNodeType::Grf != GetNode()->GetNodeType())
+ {
+ InvalidatePrt();
+ SetCompletePaint();
+ }
+ return;
+ }
+ if(rHint.GetId() == SfxHintId::SwPreGraphicArrived
+ || rHint.GetId() == SfxHintId::SwGraphicPieceArrived
+ || rHint.GetId() == SfxHintId::SwLinkedGraphicStreamArrived)
+ {
+ OnGraphicArrived();
+ return;
+ }
+ else if (rHint.GetId() != SfxHintId::SwLegacyModify)
+ return;
+ auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
+ sal_uInt16 nWhich = pLegacy->GetWhich();
+
+ SwContentFrame::SwClientNotify(rModify, rHint);
+
+ bool bComplete = true;
+
+ switch( nWhich )
+ {
+ case RES_OBJECTDYING:
+ break;
+
+ case RES_UPDATE_ATTR:
+ if (GetNode()->GetNodeType() != SwNodeType::Grf) {
+ break;
+ }
+ [[fallthrough]];
+ case RES_FMT_CHG:
+ ClearCache();
+ break;
+
+ case RES_ATTRSET_CHG:
+ {
+ sal_uInt16 n;
+ for( n = RES_GRFATR_BEGIN; n < RES_GRFATR_END; ++n )
+ if( SfxItemState::SET == static_cast<const SwAttrSetChg*>(pLegacy->m_pOld)->GetChgSet()->
+ GetItemState( n, false ))
+ {
+ ClearCache();
+
+ if(RES_GRFATR_ROTATION == n)
+ {
+ // RotGrfFlyFrame: Update Handles in view, these may be rotation-dependent
+ // (e.g. crop handles) and need a visualisation update
+ if ( GetNode()->GetNodeType() == SwNodeType::Grf )
+ {
+ SwGrfNode* pNd = static_cast<SwGrfNode*>( GetNode());
+ SwViewShell *pVSh = pNd->GetDoc().getIDocumentLayoutAccess().GetCurrentViewShell();
+
+ if(pVSh)
+ {
+ SdrView* pDrawView = pVSh->GetDrawView();
+
+ if(pDrawView)
+ {
+ pDrawView->AdjustMarkHdl(nullptr);
+ }
+ }
+
+ // RotateFlyFrame3 - invalidate needed for ContentFrame (inner, this)
+ // and LayoutFrame (outer, GetUpper). It is possible to only invalidate
+ // the outer frame, but that leads to an in-between state that gets
+ // potentially painted
+ if(GetUpper())
+ {
+ GetUpper()->InvalidateAll_();
+ }
+
+ InvalidateAll_();
+ }
+ }
+ break;
+ }
+ if( RES_GRFATR_END == n ) // not found
+ return ;
+ }
+ break;
+
+ default:
+ if ( !pLegacy->m_pNew || !isGRFATR(nWhich) )
+ return;
+ }
+
+ if( bComplete )
+ {
+ InvalidatePrt();
+ SetCompletePaint();
+ }
+}
+
+static void lcl_correctlyAlignRect( SwRect& rAlignedGrfArea, const SwRect& rInArea, vcl::RenderContext const * pOut )
+{
+
+ if(!pOut)
+ return;
+ tools::Rectangle aPxRect = pOut->LogicToPixel( rInArea.SVRect() );
+ tools::Rectangle aNewPxRect( aPxRect );
+ while( aNewPxRect.Left() < aPxRect.Left() )
+ {
+ rAlignedGrfArea.AddLeft( 1 );
+ aNewPxRect = pOut->LogicToPixel( rAlignedGrfArea.SVRect() );
+ }
+ while( aNewPxRect.Top() < aPxRect.Top() )
+ {
+ rAlignedGrfArea.AddTop(+1);
+ aNewPxRect = pOut->LogicToPixel( rAlignedGrfArea.SVRect() );
+ }
+ while( aNewPxRect.Bottom() > aPxRect.Bottom() )
+ {
+ rAlignedGrfArea.AddBottom( -1 );
+ aNewPxRect = pOut->LogicToPixel( rAlignedGrfArea.SVRect() );
+ }
+ while( aNewPxRect.Right() > aPxRect.Right() )
+ {
+ rAlignedGrfArea.AddRight(-1);
+ aNewPxRect = pOut->LogicToPixel( rAlignedGrfArea.SVRect() );
+ }
+}
+
+static bool paintUsingPrimitivesHelper(
+ vcl::RenderContext& rOutputDevice,
+ const drawinglayer::primitive2d::Primitive2DContainer& rSequence,
+ const basegfx::B2DRange& rSourceRange,
+ const basegfx::B2DRange& rTargetRange)
+{
+ if(!rSequence.empty() && !basegfx::fTools::equalZero(rSourceRange.getWidth()) && !basegfx::fTools::equalZero(rSourceRange.getHeight()))
+ {
+ if(!basegfx::fTools::equalZero(rTargetRange.getWidth()) && !basegfx::fTools::equalZero(rTargetRange.getHeight()))
+ {
+ // map graphic range to target range. This will e.g. automatically include
+ // the mapping from 1/100th mm content to twips if needed when the target
+ // range is defined in twips
+ const basegfx::B2DHomMatrix aMappingTransform(
+ basegfx::utils::createSourceRangeTargetRangeTransform(
+ rSourceRange,
+ rTargetRange));
+
+ // Fill ViewInformation. Use MappingTransform here, so there is no need to
+ // embed the primitives to it. Use original TargetRange here so there is also
+ // no need to embed the primitives to a MaskPrimitive for cropping. This works
+ // only in this case where the graphic object cannot be rotated, though.
+ drawinglayer::geometry::ViewInformation2D aViewInformation2D;
+ aViewInformation2D.setObjectTransformation(aMappingTransform);
+ aViewInformation2D.setViewTransformation(rOutputDevice.GetViewTransformation());
+ aViewInformation2D.setViewport(rTargetRange);
+
+ // get a primitive processor for rendering
+ std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> pProcessor2D(
+ drawinglayer::processor2d::createProcessor2DFromOutputDevice(
+ rOutputDevice, aViewInformation2D) );
+
+ // render and cleanup
+ pProcessor2D->process(rSequence);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+// MM02 original using fallback to VOC and primitive-based version
+void paintGraphicUsingPrimitivesHelper(
+ vcl::RenderContext & rOutputDevice,
+ GraphicObject const& rGrfObj,
+ GraphicAttr const& rGraphicAttr,
+ const basegfx::B2DHomMatrix& rGraphicTransform,
+ const OUString& rName,
+ const OUString& rTitle,
+ const OUString& rDescription)
+{
+ // RotGrfFlyFrame: unify using GraphicPrimitive2D
+ // -> the primitive handles all crop and mirror stuff
+ // -> the primitive renderer will create the needed pdf export data
+ // -> if bitmap content, it will be cached system-dependent
+ drawinglayer::primitive2d::Primitive2DContainer aContent(1);
+ aContent[0] = new drawinglayer::primitive2d::GraphicPrimitive2D(
+ rGraphicTransform,
+ rGrfObj,
+ rGraphicAttr);
+
+ // MM02 use primitive-based version for visualization
+ paintGraphicUsingPrimitivesHelper(
+ rOutputDevice,
+ aContent,
+ rGraphicTransform,
+ rName,
+ rTitle,
+ rDescription);
+}
+
+// MM02 new VOC and primitive-based version
+void paintGraphicUsingPrimitivesHelper(
+ vcl::RenderContext & rOutputDevice,
+ drawinglayer::primitive2d::Primitive2DContainer& rContent,
+ const basegfx::B2DHomMatrix& rGraphicTransform,
+ const OUString& rName,
+ const OUString& rTitle,
+ const OUString& rDescription)
+{
+ // RotateFlyFrame3: If ClipRegion is set at OutputDevice, we
+ // need to use that. Usually the renderer would be a VCL-based
+ // PrimitiveRenderer, but there are system-specific shortcuts that
+ // will *not* use the VCL-Paint of Bitmap and thus ignore this.
+ // Anyways, indirectly using a CLipRegion set at the target OutDev
+ // when using a PrimitiveRenderer is a non-valid implication.
+ // First tried only to use when HasPolyPolygonOrB2DPolyPolygon(),
+ // but there is an optimization at ClipRegion creation that detects
+ // a single Rectangle in a tools::PolyPolygon and forces to a simple
+ // RegionBand-based implementation, so cannot use it here.
+ if(rOutputDevice.IsClipRegion())
+ {
+ basegfx::B2DPolyPolygon aClip(rOutputDevice.GetClipRegion().GetAsB2DPolyPolygon());
+
+ if(0 != aClip.count())
+ {
+ rContent.resize(1);
+ rContent[0] =
+ new drawinglayer::primitive2d::MaskPrimitive2D(
+ std::move(aClip),
+ drawinglayer::primitive2d::Primitive2DContainer(rContent));
+ }
+ }
+
+ if(!rName.isEmpty() || !rTitle.isEmpty() || !rDescription.isEmpty())
+ {
+ // Embed to ObjectInfoPrimitive2D when we have Name/Title/Description
+ // information available
+ rContent.resize(1);
+ rContent[0] =
+ new drawinglayer::primitive2d::ObjectInfoPrimitive2D(
+ drawinglayer::primitive2d::Primitive2DContainer(rContent),
+ rName,
+ rTitle,
+ rDescription);
+ }
+
+ basegfx::B2DRange aTargetRange(0.0, 0.0, 1.0, 1.0);
+ aTargetRange.transform(rGraphicTransform);
+
+ paintUsingPrimitivesHelper(
+ rOutputDevice,
+ rContent,
+ aTargetRange,
+ aTargetRange);
+}
+
+// DrawContact section
+namespace { // anonymous namespace
+class ViewObjectContactOfSwNoTextFrame : public sdr::contact::ViewObjectContact
+{
+protected:
+ virtual void createPrimitive2DSequence(
+ const sdr::contact::DisplayInfo& rDisplayInfo,
+ drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const override;
+
+ // tdf#155190 disable this so superclass doesn't wrongly produce NonStruct
+ virtual bool isExportPDFTags() const override { return false; }
+
+public:
+ ViewObjectContactOfSwNoTextFrame(
+ sdr::contact::ObjectContact& rObjectContact,
+ sdr::contact::ViewContact& rViewContact);
+};
+
+class ViewContactOfSwNoTextFrame : public sdr::contact::ViewContact
+{
+private:
+ // owner
+ const SwNoTextFrame& mrSwNoTextFrame;
+
+protected:
+ // Create an Object-Specific ViewObjectContact, set ViewContact and
+ // ObjectContact. Always needs to return something.
+ virtual sdr::contact::ViewObjectContact& CreateObjectSpecificViewObjectContact(
+ sdr::contact::ObjectContact& rObjectContact) override;
+
+public:
+ // read-access to owner
+ const SwNoTextFrame& getSwNoTextFrame() const { return mrSwNoTextFrame; }
+
+ // basic constructor, used from SwNoTextFrame.
+ explicit ViewContactOfSwNoTextFrame(const SwNoTextFrame& rSwNoTextFrame);
+};
+
+void ViewObjectContactOfSwNoTextFrame::createPrimitive2DSequence(
+ const sdr::contact::DisplayInfo& /*rDisplayInfo*/,
+ drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const
+{
+ // MM02 get all the parameters formally used in paintGraphicUsingPrimitivesHelper
+ ViewContactOfSwNoTextFrame& rVCOfNTF(static_cast<ViewContactOfSwNoTextFrame&>(GetViewContact()));
+ const SwNoTextFrame& rSwNoTextFrame(rVCOfNTF.getSwNoTextFrame());
+ SwNoTextNode& rNoTNd(const_cast<SwNoTextNode&>(*static_cast<const SwNoTextNode*>(rSwNoTextFrame.GetNode())));
+ SwGrfNode* pGrfNd(rNoTNd.GetGrfNode());
+
+ if(nullptr != pGrfNd)
+ {
+ const bool bPrn(GetObjectContact().isOutputToPrinter() || GetObjectContact().isOutputToRecordingMetaFile());
+ const GraphicObject& rGrfObj(pGrfNd->GetGrfObj(bPrn));
+ GraphicAttr aGraphicAttr;
+ pGrfNd->GetGraphicAttr(aGraphicAttr, &rSwNoTextFrame);
+ const basegfx::B2DHomMatrix aGraphicTransform(rSwNoTextFrame.getFrameAreaTransformation());
+
+ // MM02 this is the right place in the VOC-Mechanism to create
+ // the primitives for visualization - these will be automatically
+ // buffered and reused
+ rVisitor.visit(new drawinglayer::primitive2d::GraphicPrimitive2D(
+ aGraphicTransform,
+ rGrfObj,
+ aGraphicAttr));
+ }
+}
+
+ViewObjectContactOfSwNoTextFrame::ViewObjectContactOfSwNoTextFrame(
+ sdr::contact::ObjectContact& rObjectContact,
+ sdr::contact::ViewContact& rViewContact)
+: sdr::contact::ViewObjectContact(rObjectContact, rViewContact)
+{
+}
+
+sdr::contact::ViewObjectContact& ViewContactOfSwNoTextFrame::CreateObjectSpecificViewObjectContact(
+ sdr::contact::ObjectContact& rObjectContact)
+{
+ sdr::contact::ViewObjectContact* pRetval = new ViewObjectContactOfSwNoTextFrame(rObjectContact, *this);
+ return *pRetval;
+}
+
+ViewContactOfSwNoTextFrame::ViewContactOfSwNoTextFrame(
+ const SwNoTextFrame& rSwNoTextFrame
+)
+: mrSwNoTextFrame(rSwNoTextFrame)
+{
+}
+} // end of anonymous namespace
+
+sdr::contact::ViewContact& SwNoTextFrame::GetViewContact() const
+{
+ if(!mpViewContact)
+ {
+ const_cast< SwNoTextFrame* >(this)->mpViewContact =
+ std::make_unique<ViewContactOfSwNoTextFrame>(*this);
+ }
+
+ return *mpViewContact;
+}
+
+/** Paint the graphic.
+
+ We require either a QuickDraw-Bitmap or a graphic here. If we do not have
+ either, we return a replacement.
+
+ @todo use aligned rectangle for drawing graphic.
+ @todo pixel-align coordinations for drawing graphic. */
+void SwNoTextFrame::PaintPicture( vcl::RenderContext* pOut, const SwRect &rGrfArea ) const
+{
+ SwViewShell* pShell = getRootFrame()->GetCurrShell();
+
+ SwNoTextNode& rNoTNd = const_cast<SwNoTextNode&>(*static_cast<const SwNoTextNode*>(GetNode()));
+ SwGrfNode* pGrfNd = rNoTNd.GetGrfNode();
+ SwOLENode* pOLENd = rNoTNd.GetOLENode();
+
+ const bool bPrn = pOut == rNoTNd.getIDocumentDeviceAccess().getPrinter( false ) ||
+ pOut->GetConnectMetaFile();
+
+ const bool bIsChart = pOLENd && pOLENd->GetOLEObj().GetObject().IsChart();
+
+ // calculate aligned rectangle from parameter <rGrfArea>.
+ // Use aligned rectangle <aAlignedGrfArea> instead of <rGrfArea> in
+ // the following code.
+ SwRect aAlignedGrfArea = rGrfArea;
+ ::SwAlignRect( aAlignedGrfArea, pShell, pOut );
+
+ if( !bIsChart )
+ {
+ // Because for drawing a graphic left-top-corner and size coordinations are
+ // used, these coordinations have to be determined on pixel level.
+ ::SwAlignGrfRect( &aAlignedGrfArea, *pOut );
+ }
+ else //if( bIsChart )
+ {
+ // #i78025# charts own borders are not completely visible
+ // the above pixel correction is not correct - at least not for charts
+ // so a different pixel correction is chosen here
+ // this might be a good idea for all other OLE objects also,
+ // but as I cannot oversee the consequences I fix it only for charts for now
+ lcl_correctlyAlignRect( aAlignedGrfArea, rGrfArea, pOut );
+ }
+
+ if( pGrfNd )
+ {
+ // Fix for bug fdo#33781
+ const AntialiasingFlags nFormerAntialiasingAtOutput( pOut->GetAntialiasing() );
+ if (SwDrawView::IsAntiAliasing())
+ pOut->SetAntialiasing( nFormerAntialiasingAtOutput | AntialiasingFlags::Enable );
+
+ ImplPaintPictureGraphic( pOut, pGrfNd, bPrn, aAlignedGrfArea, pShell, rNoTNd );
+
+ if ( SwDrawView::IsAntiAliasing() )
+ pOut->SetAntialiasing( nFormerAntialiasingAtOutput );
+ }
+ else // bIsChart || pOLENd
+ {
+ // Fix for bug fdo#33781
+ const AntialiasingFlags nFormerAntialiasingAtOutput( pOut->GetAntialiasing() );
+ if (SwDrawView::IsAntiAliasing())
+ {
+ AntialiasingFlags nNewAntialiasingAtOutput = nFormerAntialiasingAtOutput | AntialiasingFlags::Enable;
+
+ // #i99665#
+ // Adjust AntiAliasing mode at output device for chart OLE
+ if ( pOLENd->IsChart() )
+ nNewAntialiasingAtOutput |= AntialiasingFlags::PixelSnapHairline;
+
+ pOut->SetAntialiasing( nNewAntialiasingAtOutput );
+ }
+
+ ImplPaintPictureBitmap( pOut, pOLENd, bIsChart, bPrn, aAlignedGrfArea, pShell );
+
+ // see #i99665#
+ if (SwDrawView::IsAntiAliasing())
+ {
+ pOut->SetAntialiasing( nFormerAntialiasingAtOutput );
+ }
+ }
+}
+
+void SwNoTextFrame::ImplPaintPictureGraphic( vcl::RenderContext* pOut,
+ SwGrfNode* pGrfNd, bool bPrn,
+ const SwRect& rAlignedGrfArea, SwViewShell* pShell,
+ SwNoTextNode& rNoTNd ) const
+{
+ bool bContinue = true;
+ const GraphicObject& rGrfObj = pGrfNd->GetGrfObj(bPrn);
+
+ GraphicAttr aGrfAttr;
+ pGrfNd->GetGraphicAttr( aGrfAttr, this );
+
+ if( !bPrn )
+ {
+ // #i73788#
+ if ( pGrfNd->IsLinkedInputStreamReady() )
+ {
+ pGrfNd->UpdateLinkWithInputStream();
+ }
+ // #i85717#, #i90395# - check, if asynchronous retrieval
+ // if input stream for the graphic is possible
+ else if ( ( rGrfObj.GetType() == GraphicType::Default ||
+ rGrfObj.GetType() == GraphicType::NONE ) &&
+ pGrfNd->IsLinkedFile() &&
+ pGrfNd->IsAsyncRetrieveInputStreamPossible() )
+ {
+ Size aTmpSz;
+ ::sfx2::SvLinkSource* pGrfObj = pGrfNd->GetLink()->GetObj();
+ if( !pGrfObj ||
+ !pGrfObj->IsDataComplete() ||
+ !(aTmpSz = pGrfNd->GetTwipSize()).Width() ||
+ !aTmpSz.Height())
+ {
+ pGrfNd->TriggerAsyncRetrieveInputStream(); // #i73788#
+ }
+ OUString aText( pGrfNd->GetTitle() );
+ if ( aText.isEmpty() )
+ GetRealURL( *pGrfNd, aText );
+ ::lcl_PaintReplacement( rAlignedGrfArea, aText, *pShell, this, false );
+ bContinue = false;
+ }
+ }
+
+ if( !bContinue )
+ return;
+
+ if( !rGrfObj.GetGraphic().IsSupportedGraphic())
+ {
+ ImplPaintPictureReplacement(rGrfObj, pGrfNd, rAlignedGrfArea, pShell);
+ return;
+ }
+
+ const bool bAnimate = rGrfObj.IsAnimated() &&
+ !pShell->IsPreview() &&
+ !pShell->GetAccessibilityOptions()->IsStopAnimatedGraphics() &&
+ // #i9684# Stop animation during printing/pdf export
+ pShell->GetWin();
+ if( bAnimate &&
+ FindFlyFrame() != ::GetFlyFromMarked( nullptr, pShell ))
+ {
+ ImplPaintPictureAnimate(pOut, pShell, pGrfNd, rAlignedGrfArea);
+ return;
+ }
+
+ // MM02 To allow system-dependent buffering of the involved
+ // bitmaps it is necessary to re-use the involved primitives
+ // and their already executed decomposition (also for
+ // performance reasons). This is usually done in DrawingLayer
+ // by using the VOC-Mechanism (see descriptions elsewhere).
+ // To get that here, make the involved SwNoTextFrame (this)
+ // a sdr::contact::ViewContact supplier by supporting
+ // a GetViewContact() - call. For ObjectContact we can use
+ // the already existing ObjectContact from the involved
+ // DrawingLayer. For this, the helper classes
+ // ViewObjectContactOfSwNoTextFrame
+ // ViewContactOfSwNoTextFrame
+ // are created which support the VOC-mechanism in its minimal
+ // form. This allows automatic and view-dependent (multiple edit
+ // windows, print, etc.) re-use of the created primitives.
+ // Also: Will be very useful when completely changing the Writer
+ // repaint to VOC and Primitives, too.
+ static const char* pDisableMM02Goodies(getenv("SAL_DISABLE_MM02_GOODIES"));
+ static bool bUseViewObjectContactMechanism(nullptr == pDisableMM02Goodies);
+ // tdf#130951 for safety reasons use fallback if ViewObjectContactMechanism
+ // fails for some reason - usually could only be not to find the correct
+ // SdrPageWindow
+ bool bSucceeded(false);
+
+ if(bUseViewObjectContactMechanism)
+ {
+ // MM02 use VOC-mechanism and buffer primitives
+ SwViewShellImp* pImp(pShell->Imp());
+ SdrPageView* pPageView(nullptr != pImp
+ ? pImp->GetPageView()
+ : nullptr);
+ // tdf#130951 caution - target may be Window, use the correct OutputDevice
+ OutputDevice* pTarget((pShell->isOutputToWindow() && pShell->GetWin())
+ ? pShell->GetWin()->GetOutDev()
+ : pShell->GetOut());
+ SdrPageWindow* pPageWindow(nullptr != pPageView && nullptr != pTarget
+ ? pPageView->FindPageWindow(*pTarget)
+ : nullptr);
+
+ if(nullptr != pPageWindow)
+ {
+ sdr::contact::ObjectContact& rOC(pPageWindow->GetObjectContact());
+ sdr::contact::ViewContact& rVC(GetViewContact());
+ sdr::contact::ViewObjectContact& rVOC(rVC.GetViewObjectContact(rOC));
+ sdr::contact::DisplayInfo aDisplayInfo;
+
+ drawinglayer::primitive2d::Primitive2DContainer aPrimitives(rVOC.getPrimitive2DSequence(aDisplayInfo));
+ const basegfx::B2DHomMatrix aGraphicTransform(getFrameAreaTransformation());
+
+ paintGraphicUsingPrimitivesHelper(
+ *pOut,
+ aPrimitives,
+ aGraphicTransform,
+ nullptr == pGrfNd->GetFlyFormat() ? OUString() : pGrfNd->GetFlyFormat()->GetName(),
+ rNoTNd.GetTitle(),
+ rNoTNd.GetDescription());
+ bSucceeded = true;
+ }
+ }
+
+ if(!bSucceeded)
+ {
+ // MM02 fallback to direct paint with primitive-recreation
+ // which will block reusage of system-dependent bitmap data
+ const basegfx::B2DHomMatrix aGraphicTransform(getFrameAreaTransformation());
+
+ paintGraphicUsingPrimitivesHelper(
+ *pOut,
+ rGrfObj,
+ aGrfAttr,
+ aGraphicTransform,
+ nullptr == pGrfNd->GetFlyFormat() ? OUString() : pGrfNd->GetFlyFormat()->GetName(),
+ rNoTNd.GetTitle(),
+ rNoTNd.GetDescription());
+ }
+}
+
+void SwNoTextFrame::ImplPaintPictureAnimate(vcl::RenderContext* pOut, SwViewShell* pShell,
+ SwGrfNode* pGrfNd, const SwRect& rAlignedGrfArea) const
+{
+ OutputDevice* pVout;
+ if( pOut == pShell->GetOut() && SwRootFrame::FlushVout() )
+ {
+ pVout = pOut;
+ pOut = pShell->GetOut();
+ }
+ else if( pShell->GetWin() && pOut->IsVirtual() )
+ {
+ pVout = pOut;
+ pOut = pShell->GetWin()->GetOutDev();
+ }
+ else
+ pVout = nullptr;
+
+ OSL_ENSURE( !pOut->IsVirtual() ||
+ pShell->GetViewOptions()->IsPDFExport() || pShell->isOutputToWindow(),
+ "pOut should not be a virtual device" );
+
+ pGrfNd->StartGraphicAnimation(pOut, rAlignedGrfArea.Pos(),
+ rAlignedGrfArea.SSize(), reinterpret_cast<sal_IntPtr>(this),
+ pVout );
+}
+
+void SwNoTextFrame::ImplPaintPictureReplacement(const GraphicObject& rGrfObj, SwGrfNode* pGrfNd,
+ const SwRect& rAlignedGrfArea, SwViewShell* pShell) const
+{
+ TranslateId pResId;
+
+ if( GraphicType::NONE == rGrfObj.GetType() )
+ pResId = STR_COMCORE_READERROR;
+ else if ( !rGrfObj.GetGraphic().IsSupportedGraphic() )
+ pResId = STR_COMCORE_CANT_SHOW;
+
+ OUString aText;
+ if ( !pResId &&
+ (aText = pGrfNd->GetTitle()).isEmpty() &&
+ (!GetRealURL( *pGrfNd, aText ) || aText.isEmpty()))
+ {
+ pResId = STR_COMCORE_READERROR;
+ }
+ if (pResId)
+ aText = SwResId(pResId);
+
+ ::lcl_PaintReplacement( rAlignedGrfArea, aText, *pShell, this, true );
+}
+
+void SwNoTextFrame::ImplPaintPictureBitmap( vcl::RenderContext* pOut,
+ SwOLENode* pOLENd, bool bIsChart, bool bPrn, const SwRect& rAlignedGrfArea,
+ SwViewShell* pShell ) const
+{
+ bool bDone(false);
+
+ if(bIsChart)
+ {
+ basegfx::B2DRange aSourceRange;
+ const drawinglayer::primitive2d::Primitive2DContainer aSequence(
+ pOLENd->GetOLEObj().tryToGetChartContentAsPrimitive2DSequence(
+ aSourceRange,
+ bPrn));
+
+ if(!aSequence.empty() && !aSourceRange.isEmpty())
+ {
+ const basegfx::B2DRange aTargetRange(
+ rAlignedGrfArea.Left(), rAlignedGrfArea.Top(),
+ rAlignedGrfArea.Right(), rAlignedGrfArea.Bottom());
+
+ bDone = paintUsingPrimitivesHelper(
+ *pOut,
+ aSequence,
+ aSourceRange,
+ aTargetRange);
+ }
+ }
+
+ if(bDone || !pOLENd)
+ return;
+
+ // SwOLENode does not have a known GraphicObject, need to
+ // work with Graphic instead
+ const Graphic* pGraphic = pOLENd->GetGraphic();
+ const Point aPosition(rAlignedGrfArea.Pos());
+ const Size aSize(rAlignedGrfArea.SSize());
+
+ if ( pGraphic && pGraphic->GetType() != GraphicType::NONE )
+ {
+ pGraphic->Draw(*pOut, aPosition, aSize);
+
+ // shade the representation if the object is activated outplace
+ uno::Reference < embed::XEmbeddedObject > xObj = pOLENd->GetOLEObj().GetOleRef();
+ if ( xObj.is() && xObj->getCurrentState() == embed::EmbedStates::ACTIVE )
+ {
+
+ ::svt::EmbeddedObjectRef::DrawShading(
+ tools::Rectangle(
+ aPosition,
+ aSize),
+ pOut);
+ }
+ }
+ else
+ {
+ ::svt::EmbeddedObjectRef::DrawPaintReplacement(
+ tools::Rectangle(aPosition, aSize),
+ pOLENd->GetOLEObj().GetCurrentPersistName(),
+ pOut);
+ }
+
+ sal_Int64 nMiscStatus = pOLENd->GetOLEObj().GetOleRef()->getStatus( pOLENd->GetAspect() );
+ if ( !bPrn && dynamic_cast< const SwCursorShell *>( pShell ) != nullptr &&
+ (nMiscStatus & embed::EmbedMisc::MS_EMBED_ACTIVATEWHENVISIBLE))
+ {
+ const SwFlyFrame *pFly = FindFlyFrame();
+ assert( pFly != nullptr );
+ static_cast<SwFEShell*>(pShell)->ConnectObj( pOLENd->GetOLEObj().GetObject(), pFly->getFramePrintArea(), pFly->getFrameArea());
+ }
+}
+
+bool SwNoTextFrame::IsTransparent() const
+{
+ const SwViewShell* pSh = getRootFrame()->GetCurrShell();
+
+ if ( !pSh || !pSh->GetViewOptions()->IsGraphic() )
+ {
+ return true;
+ }
+
+ const SwGrfNode *pNd;
+
+ if( nullptr != (pNd = GetNode()->GetGrfNode()) )
+ {
+ if(pNd->IsTransparent())
+ {
+ return true;
+ }
+ }
+
+ // RotateFlyFrame3: If we are transformed, there are 'free' areas between
+ // the Graphic and the Border/Padding stuff - at least as long as those
+ // (Border and Padding) are not transformed, too
+ if(isTransformableSwFrame())
+ {
+ // we can be more specific - rotations of multiples of
+ // 90 degrees will leave no gaps. Go from [0.0 .. 2PI]
+ // to [0 .. 360] and check modulo 90
+ const tools::Long nRot(static_cast<tools::Long>(basegfx::rad2deg(getLocalFrameRotation())));
+ const bool bMultipleOf90(0 == (nRot % 90));
+
+ if(!bMultipleOf90)
+ {
+ return true;
+ }
+ }
+
+ //#29381# OLE are always transparent
+ if(nullptr != GetNode()->GetOLENode())
+ {
+ return true;
+ }
+
+ // return false by default to avoid background paint
+ return false;
+}
+
+void SwNoTextFrame::StopAnimation( const OutputDevice* pOut ) const
+{
+ // Stop animated graphics
+ const SwGrfNode* pGrfNd = GetNode()->GetGrfNode();
+
+ if( pGrfNd && pGrfNd->IsAnimated() )
+ {
+ const_cast< SwGrfNode* >(pGrfNd)->StopGraphicAnimation( pOut, reinterpret_cast<sal_IntPtr>(this) );
+ }
+}
+
+bool SwNoTextFrame::HasAnimation() const
+{
+ const SwGrfNode* pGrfNd = GetNode()->GetGrfNode();
+ return pGrfNd && pGrfNd->IsAnimated();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/number.cxx b/sw/source/core/doc/number.cxx
new file mode 100644
index 0000000000..9cef97ddc2
--- /dev/null
+++ b/sw/source/core/doc/number.cxx
@@ -0,0 +1,1656 @@
+/* -*- 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 <hintids.hxx>
+
+#include <utility>
+#include <vcl/font.hxx>
+#include <editeng/brushitem.hxx>
+#include <editeng/numitem.hxx>
+#include <svl/grabbagitem.hxx>
+#include <fmtornt.hxx>
+#include <doc.hxx>
+#include <charfmt.hxx>
+#include <ndtxt.hxx>
+#include <docary.hxx>
+#include <SwStyleNameMapper.hxx>
+
+// Needed to load default bullet list configuration
+#include <unotools/configmgr.hxx>
+#include <unotools/configitem.hxx>
+
+#include <numrule.hxx>
+#include <SwNodeNum.hxx>
+
+#include <list.hxx>
+
+#include <algorithm>
+#include <unordered_map>
+#include <libxml/xmlwriter.h>
+
+#include <rtl/ustrbuf.hxx>
+#include <i18nlangtag/languagetag.hxx>
+#include <unotools/saveopt.hxx>
+#include <osl/diagnose.h>
+
+#include <IDocumentListsAccess.hxx>
+#include <IDocumentStylePoolAccess.hxx>
+#include <IDocumentState.hxx>
+
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <wrtsh.hxx>
+
+using namespace ::com::sun::star;
+
+sal_uInt16 SwNumRule::snRefCount = 0;
+SwNumFormat* SwNumRule::saBaseFormats[ RULE_END ][ MAXLEVEL ] = {
+ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr } };
+
+SwNumFormat* SwNumRule::saLabelAlignmentBaseFormats[ RULE_END ][ MAXLEVEL ] = {
+ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr } };
+
+const sal_uInt16 SwNumRule::saDefNumIndents[ MAXLEVEL ] = {
+ o3tl::toTwips(25, o3tl::Length::in100),
+ o3tl::toTwips(50, o3tl::Length::in100),
+ o3tl::toTwips(75, o3tl::Length::in100),
+ o3tl::toTwips(100, o3tl::Length::in100),
+ o3tl::toTwips(125, o3tl::Length::in100),
+ o3tl::toTwips(150, o3tl::Length::in100),
+ o3tl::toTwips(175, o3tl::Length::in100),
+ o3tl::toTwips(200, o3tl::Length::in100),
+ o3tl::toTwips(225, o3tl::Length::in100),
+ o3tl::toTwips(250, o3tl::Length::in100),
+};
+
+OUString SwNumRule::GetOutlineRuleName()
+{
+ return "Outline";
+}
+
+const SwNumFormat& SwNumRule::Get( sal_uInt16 i ) const
+{
+ assert( i < MAXLEVEL && meRuleType < RULE_END );
+ return maFormats[ i ]
+ ? *maFormats[ i ]
+ : ( meDefaultNumberFormatPositionAndSpaceMode == SvxNumberFormat::LABEL_WIDTH_AND_POSITION
+ ? *saBaseFormats[ meRuleType ][ i ]
+ : *saLabelAlignmentBaseFormats[ meRuleType ][ i ] );
+}
+
+const SwNumFormat* SwNumRule::GetNumFormat( sal_uInt16 i ) const
+{
+ const SwNumFormat * pResult = nullptr;
+
+ assert( i < MAXLEVEL && meRuleType < RULE_END );
+ if ( i < MAXLEVEL && meRuleType < RULE_END)
+ {
+ pResult = maFormats[ i ].get();
+ }
+
+ return pResult;
+}
+
+// #i91400#
+void SwNumRule::SetName( const OUString & rName,
+ IDocumentListsAccess& rDocListAccess)
+{
+ if ( msName == rName )
+ return;
+
+ if (mpNumRuleMap)
+ {
+ mpNumRuleMap->erase(msName);
+ (*mpNumRuleMap)[rName] = this;
+
+ if ( !GetDefaultListId().isEmpty() )
+ {
+ rDocListAccess.trackChangeOfListStyleName( msName, rName );
+ }
+ }
+
+ msName = rName;
+}
+
+void SwNumRule::GetTextNodeList( SwNumRule::tTextNodeList& rTextNodeList ) const
+{
+ rTextNodeList = maTextNodeList;
+}
+
+SwNumRule::tTextNodeList::size_type SwNumRule::GetTextNodeListSize() const
+{
+ return maTextNodeList.size();
+}
+
+void SwNumRule::AddTextNode( SwTextNode& rTextNode )
+{
+ tTextNodeList::iterator aIter =
+ std::find( maTextNodeList.begin(), maTextNodeList.end(), &rTextNode );
+
+ if ( aIter == maTextNodeList.end() )
+ {
+ maTextNodeList.push_back( &rTextNode );
+ }
+}
+
+void SwNumRule::RemoveTextNode( SwTextNode& rTextNode )
+{
+ tTextNodeList::iterator aIter =
+ std::find( maTextNodeList.begin(), maTextNodeList.end(), &rTextNode );
+ if ( aIter == maTextNodeList.end() )
+ return;
+
+ maTextNodeList.erase( aIter );
+
+ // Just in case we remove a node after we have marked the rule invalid, but before we have validated the tree
+ if (mbInvalidRuleFlag)
+ {
+ SwList* pList = rTextNode.GetDoc().getIDocumentListsAccess().getListByName( rTextNode.GetListId() );
+ if (pList)
+ pList->InvalidateListTree();
+ }
+}
+
+void SwNumRule::SetNumRuleMap(std::unordered_map<OUString, SwNumRule *> *
+ pNumRuleMap)
+{
+ mpNumRuleMap = pNumRuleMap;
+}
+
+sal_uInt16 SwNumRule::GetNumIndent( sal_uInt8 nLvl )
+{
+ OSL_ENSURE( MAXLEVEL > nLvl, "NumLevel is out of range" );
+ return saDefNumIndents[ nLvl ];
+}
+
+sal_uInt16 SwNumRule::GetBullIndent( sal_uInt8 nLvl )
+{
+ OSL_ENSURE( MAXLEVEL > nLvl, "NumLevel is out of range" );
+ return saDefNumIndents[ nLvl ];
+}
+
+static void lcl_SetRuleChgd( SwTextNode& rNd, sal_uInt8 nLevel )
+{
+ if( rNd.GetActualListLevel() == nLevel )
+ rNd.NumRuleChgd();
+}
+
+SwNumFormat::SwNumFormat() :
+ SvxNumberFormat(SVX_NUM_ARABIC),
+ SwClient( nullptr ),
+ m_aVertOrient( 0, text::VertOrientation::NONE )
+ ,m_cGrfBulletCP(USHRT_MAX)//For i120928,record the cp info of graphic within bullet
+{
+}
+
+SwNumFormat::SwNumFormat( const SwNumFormat& rFormat) :
+ SvxNumberFormat(rFormat),
+ SwClient( rFormat.GetRegisteredInNonConst() ),
+ m_aVertOrient( 0, rFormat.GetVertOrient() )
+ ,m_cGrfBulletCP(rFormat.m_cGrfBulletCP)//For i120928,record the cp info of graphic within bullet
+{
+ sal_Int16 eMyVertOrient = rFormat.GetVertOrient();
+ SetGraphicBrush( rFormat.GetBrush(), &rFormat.GetGraphicSize(),
+ &eMyVertOrient);
+}
+
+SwNumFormat::SwNumFormat(const SvxNumberFormat& rNumFormat, SwDoc* pDoc)
+ : SvxNumberFormat(rNumFormat)
+ , m_aVertOrient( 0, rNumFormat.GetVertOrient() )
+ , m_cGrfBulletCP(USHRT_MAX)
+{
+ sal_Int16 eMyVertOrient = rNumFormat.GetVertOrient();
+ SetGraphicBrush( rNumFormat.GetBrush(), &rNumFormat.GetGraphicSize(),
+ &eMyVertOrient);
+ const OUString rCharStyleName = rNumFormat.SvxNumberFormat::GetCharFormatName();
+ if( !rCharStyleName.isEmpty() )
+ {
+ SwCharFormat* pCFormat = pDoc->FindCharFormatByName( rCharStyleName );
+ if( !pCFormat )
+ {
+ sal_uInt16 nId = SwStyleNameMapper::GetPoolIdFromUIName( rCharStyleName,
+ SwGetPoolIdFromName::ChrFmt );
+ pCFormat = nId != USHRT_MAX
+ ? pDoc->getIDocumentStylePoolAccess().GetCharFormatFromPool( nId )
+ : pDoc->MakeCharFormat( rCharStyleName, nullptr );
+ }
+ pCFormat->Add( this );
+ }
+ else
+ EndListeningAll();
+}
+
+SwNumFormat::~SwNumFormat()
+{
+}
+
+// #i22362#
+bool SwNumFormat::IsEnumeration() const
+{
+ // #i30655# native numbering did not work any longer
+ // using this code. Therefore HBRINKM and I agreed upon defining
+ // IsEnumeration() as !IsItemize()
+ return !IsItemize();
+}
+
+bool SwNumFormat::IsItemize() const
+{
+ bool bResult;
+
+ switch(GetNumberingType())
+ {
+ case SVX_NUM_CHAR_SPECIAL:
+ case SVX_NUM_BITMAP:
+ bResult = true;
+
+ break;
+
+ default:
+ bResult = false;
+ }
+
+ return bResult;
+
+}
+
+SwNumFormat& SwNumFormat::operator=( const SwNumFormat& rNumFormat)
+{
+ SvxNumberFormat::operator=(rNumFormat);
+ StartListeningToSameModifyAs(rNumFormat);
+ //For i120928,record the cp info of graphic within bullet
+ m_cGrfBulletCP = rNumFormat.m_cGrfBulletCP;
+ return *this;
+}
+
+bool SwNumFormat::operator==( const SwNumFormat& rNumFormat) const
+{
+ bool bRet = SvxNumberFormat::operator==(rNumFormat) &&
+ GetRegisteredIn() == rNumFormat.GetRegisteredIn();
+ return bRet;
+}
+
+void SwNumFormat::SetCharFormat( SwCharFormat* pChFormat)
+{
+ if( pChFormat )
+ pChFormat->Add( this );
+ else
+ EndListeningAll();
+}
+
+void SwNumFormat::SwClientNotify(const SwModify&, const SfxHint& rHint)
+{
+ if (rHint.GetId() != SfxHintId::SwLegacyModify)
+ return;
+ auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
+ // Look for the NumRules object in the Doc where this NumFormat is set.
+ // The format does not need to exist!
+ const SwCharFormat* pFormat = nullptr;
+ switch(pLegacy->GetWhich())
+ {
+ case RES_ATTRSET_CHG:
+ case RES_FMT_CHG:
+ pFormat = GetCharFormat();
+ break;
+ }
+
+ if(pFormat && !pFormat->GetDoc()->IsInDtor())
+ UpdateNumNodes(*const_cast<SwDoc*>(pFormat->GetDoc()));
+ else
+ CheckRegistration(pLegacy->m_pOld);
+}
+
+OUString SwNumFormat::GetCharFormatName() const
+{
+ if(static_cast<const SwCharFormat*>(GetRegisteredIn()))
+ return static_cast<const SwCharFormat*>(GetRegisteredIn())->GetName();
+
+ return OUString();
+}
+
+void SwNumFormat::SetGraphicBrush( const SvxBrushItem* pBrushItem, const Size* pSize,
+ const sal_Int16* pOrient)
+{
+ if(pOrient)
+ m_aVertOrient.SetVertOrient( *pOrient );
+ SvxNumberFormat::SetGraphicBrush( pBrushItem, pSize, pOrient);
+}
+
+void SwNumFormat::UpdateNumNodes( SwDoc& rDoc )
+{
+ bool bDocIsModified = rDoc.getIDocumentState().IsModified();
+ bool bFnd = false;
+ for( SwNumRuleTable::size_type n = rDoc.GetNumRuleTable().size(); !bFnd && n; )
+ {
+ const SwNumRule* pRule = rDoc.GetNumRuleTable()[ --n ];
+ for( sal_uInt8 i = 0; i < MAXLEVEL; ++i )
+ if( pRule->GetNumFormat( i ) == this )
+ {
+ SwNumRule::tTextNodeList aTextNodeList;
+ pRule->GetTextNodeList( aTextNodeList );
+ for ( auto& rpTextNode : aTextNodeList )
+ {
+ lcl_SetRuleChgd( *rpTextNode, i );
+ }
+ bFnd = true;
+ break;
+ }
+ }
+
+ if( bFnd && !bDocIsModified )
+ rDoc.getIDocumentState().ResetModified();
+}
+
+const SwFormatVertOrient* SwNumFormat::GetGraphicOrientation() const
+{
+ sal_Int16 eOrient = SvxNumberFormat::GetVertOrient();
+ if(text::VertOrientation::NONE == eOrient)
+ return nullptr;
+ else
+ {
+ const_cast<SwFormatVertOrient&>(m_aVertOrient).SetVertOrient(eOrient);
+ return &m_aVertOrient;
+ }
+}
+
+SwNumRule::SwNumRule( OUString aNm,
+ const SvxNumberFormat::SvxNumPositionAndSpaceMode eDefaultNumberFormatPositionAndSpaceMode,
+ SwNumRuleType eType )
+ : mpNumRuleMap(nullptr),
+ msName( std::move(aNm) ),
+ meRuleType( eType ),
+ mnPoolFormatId( USHRT_MAX ),
+ mnPoolHelpId( USHRT_MAX ),
+ mnPoolHlpFileId( UCHAR_MAX ),
+ mbAutoRuleFlag( true ),
+ mbInvalidRuleFlag( true ),
+ mbContinusNum( false ),
+ mbAbsSpaces( false ),
+ mbHidden( false ),
+ mbCountPhantoms( true ),
+ mbUsedByRedline( false ),
+ meDefaultNumberFormatPositionAndSpaceMode( eDefaultNumberFormatPositionAndSpaceMode )
+{
+ if( !snRefCount++ ) // for the first time, initialize
+ {
+ SwNumFormat* pFormat;
+ sal_uInt8 n;
+
+ // numbering:
+ // position-and-space mode LABEL_WIDTH_AND_POSITION:
+ for( n = 0; n < MAXLEVEL; ++n )
+ {
+ pFormat = new SwNumFormat;
+ pFormat->SetIncludeUpperLevels( 1 );
+ pFormat->SetStart( 1 );
+ pFormat->SetAbsLSpace( lNumberIndent + SwNumRule::GetNumIndent( n ) );
+ pFormat->SetFirstLineOffset( lNumberFirstLineOffset );
+ pFormat->SetListFormat("%" + OUString::number(n + 1) + "%.");
+ pFormat->SetBulletChar(numfunc::GetBulletChar(n));
+ SwNumRule::saBaseFormats[ NUM_RULE ][ n ] = pFormat;
+ }
+ // position-and-space mode LABEL_ALIGNMENT
+ // first line indent of general numbering in inch: -0,25 inch
+ const tools::Long cFirstLineIndent = o3tl::toTwips(-0.25, o3tl::Length::in);
+ // indent values of general numbering in inch:
+ const tools::Long cIndentAt[ MAXLEVEL ] = {
+ o3tl::toTwips(50, o3tl::Length::in100),
+ o3tl::toTwips(75, o3tl::Length::in100),
+ o3tl::toTwips(100, o3tl::Length::in100),
+ o3tl::toTwips(125, o3tl::Length::in100),
+ o3tl::toTwips(150, o3tl::Length::in100),
+ o3tl::toTwips(175, o3tl::Length::in100),
+ o3tl::toTwips(200, o3tl::Length::in100),
+ o3tl::toTwips(225, o3tl::Length::in100),
+ o3tl::toTwips(250, o3tl::Length::in100),
+ o3tl::toTwips(275, o3tl::Length::in100),
+ };
+ for( n = 0; n < MAXLEVEL; ++n )
+ {
+ pFormat = new SwNumFormat;
+ pFormat->SetIncludeUpperLevels( 1 );
+ pFormat->SetStart( 1 );
+ pFormat->SetPositionAndSpaceMode( SvxNumberFormat::LABEL_ALIGNMENT );
+ pFormat->SetLabelFollowedBy( SvxNumberFormat::LISTTAB );
+ pFormat->SetListtabPos( cIndentAt[ n ] );
+ pFormat->SetFirstLineIndent( cFirstLineIndent );
+ pFormat->SetIndentAt( cIndentAt[ n ] );
+ pFormat->SetListFormat( "%" + OUString::number(n + 1) + "%.");
+ pFormat->SetBulletChar( numfunc::GetBulletChar(n));
+ SwNumRule::saLabelAlignmentBaseFormats[ NUM_RULE ][ n ] = pFormat;
+ }
+
+ // outline:
+ // position-and-space mode LABEL_WIDTH_AND_POSITION:
+ for( n = 0; n < MAXLEVEL; ++n )
+ {
+ pFormat = new SwNumFormat;
+ pFormat->SetNumberingType(SVX_NUM_NUMBER_NONE);
+ pFormat->SetIncludeUpperLevels( MAXLEVEL );
+ pFormat->SetStart( 1 );
+ pFormat->SetCharTextDistance( lOutlineMinTextDistance );
+ pFormat->SetBulletChar( numfunc::GetBulletChar(n));
+ SwNumRule::saBaseFormats[ OUTLINE_RULE ][ n ] = pFormat;
+ }
+ // position-and-space mode LABEL_ALIGNMENT:
+ for( n = 0; n < MAXLEVEL; ++n )
+ {
+ pFormat = new SwNumFormat;
+ pFormat->SetNumberingType(SVX_NUM_NUMBER_NONE);
+ pFormat->SetIncludeUpperLevels( MAXLEVEL );
+ pFormat->SetStart( 1 );
+ pFormat->SetPositionAndSpaceMode( SvxNumberFormat::LABEL_ALIGNMENT );
+ pFormat->SetBulletChar( numfunc::GetBulletChar(n));
+ SwNumRule::saLabelAlignmentBaseFormats[ OUTLINE_RULE ][ n ] = pFormat;
+ }
+ }
+ OSL_ENSURE( !msName.isEmpty(), "NumRule without a name!" );
+}
+
+SwNumRule::SwNumRule( const SwNumRule& rNumRule )
+ : mpNumRuleMap(nullptr),
+ msName( rNumRule.msName ),
+ meRuleType( rNumRule.meRuleType ),
+ mnPoolFormatId( rNumRule.GetPoolFormatId() ),
+ mnPoolHelpId( rNumRule.GetPoolHelpId() ),
+ mnPoolHlpFileId( rNumRule.GetPoolHlpFileId() ),
+ mbAutoRuleFlag( rNumRule.mbAutoRuleFlag ),
+ mbInvalidRuleFlag( true ),
+ mbContinusNum( rNumRule.mbContinusNum ),
+ mbAbsSpaces( rNumRule.mbAbsSpaces ),
+ mbHidden( rNumRule.mbHidden ),
+ mbCountPhantoms( true ),
+ mbUsedByRedline( false ),
+ meDefaultNumberFormatPositionAndSpaceMode( rNumRule.meDefaultNumberFormatPositionAndSpaceMode ),
+ msDefaultListId( rNumRule.msDefaultListId )
+{
+ ++snRefCount;
+ for( sal_uInt16 n = 0; n < MAXLEVEL; ++n )
+ if( rNumRule.maFormats[ n ] )
+ Set( n, *rNumRule.maFormats[ n ] );
+}
+
+SwNumRule::~SwNumRule()
+{
+ for (auto & i : maFormats)
+ i.reset();
+
+ if (mpNumRuleMap)
+ {
+ mpNumRuleMap->erase(GetName());
+ }
+
+ if( !--snRefCount ) // the last one closes the door (?)
+ {
+ // Numbering:
+ SwNumFormat** ppFormats = &SwNumRule::saBaseFormats[0][0];
+ int n;
+
+ for( n = 0; n < MAXLEVEL; ++n, ++ppFormats )
+ {
+ delete *ppFormats;
+ *ppFormats = nullptr;
+ }
+
+ // Outline:
+ for( n = 0; n < MAXLEVEL; ++n, ++ppFormats )
+ {
+ delete *ppFormats;
+ *ppFormats = nullptr;
+ }
+
+ ppFormats = &SwNumRule::saLabelAlignmentBaseFormats[0][0];
+ for( n = 0; n < MAXLEVEL; ++n, ++ppFormats )
+ {
+ delete *ppFormats;
+ *ppFormats = nullptr;
+ }
+ for( n = 0; n < MAXLEVEL; ++n, ++ppFormats )
+ {
+ delete *ppFormats;
+ *ppFormats = nullptr;
+ }
+ }
+
+ maTextNodeList.clear();
+ maParagraphStyleList.clear();
+}
+
+void SwNumRule::CheckCharFormats( SwDoc& rDoc )
+{
+ for(auto& rpNumFormat : maFormats)
+ {
+ if( rpNumFormat )
+ {
+ SwCharFormat* pFormat = rpNumFormat->GetCharFormat();
+ if( pFormat && pFormat->GetDoc() != &rDoc )
+ {
+ // copy
+ SwNumFormat* pNew = new SwNumFormat( *rpNumFormat );
+ pNew->SetCharFormat( rDoc.CopyCharFormat( *pFormat ) );
+ rpNumFormat.reset(pNew);
+ }
+ }
+ }
+}
+
+SwNumRule& SwNumRule::operator=( const SwNumRule& rNumRule )
+{
+ if( this != &rNumRule )
+ {
+ for( sal_uInt16 n = 0; n < MAXLEVEL; ++n )
+ Set( n, rNumRule.maFormats[ n ].get() );
+
+ meRuleType = rNumRule.meRuleType;
+ msName = rNumRule.msName;
+ mbAutoRuleFlag = rNumRule.mbAutoRuleFlag;
+ mbInvalidRuleFlag = true;
+ mbContinusNum = rNumRule.mbContinusNum;
+ mbAbsSpaces = rNumRule.mbAbsSpaces;
+ mbHidden = rNumRule.mbHidden;
+ mnPoolFormatId = rNumRule.GetPoolFormatId();
+ mnPoolHelpId = rNumRule.GetPoolHelpId();
+ mnPoolHlpFileId = rNumRule.GetPoolHlpFileId();
+ }
+ return *this;
+}
+
+void SwNumRule::Reset( const OUString& rName )
+{
+ for( sal_uInt16 n = 0; n < MAXLEVEL; ++n )
+ Set( n, nullptr);
+
+ meRuleType = NUM_RULE;
+ msName = rName;
+ mbAutoRuleFlag = true;
+ mbInvalidRuleFlag = true;
+ mbContinusNum = false;
+ mbAbsSpaces = false;
+ mbHidden = false;
+ mnPoolFormatId = USHRT_MAX;
+ mnPoolHelpId = USHRT_MAX;
+ mnPoolHlpFileId = UCHAR_MAX;
+}
+
+bool SwNumRule::operator==( const SwNumRule& rRule ) const
+{
+ bool bRet = meRuleType == rRule.meRuleType &&
+ msName == rRule.msName &&
+ mbAutoRuleFlag == rRule.mbAutoRuleFlag &&
+ mbContinusNum == rRule.mbContinusNum &&
+ mbAbsSpaces == rRule.mbAbsSpaces &&
+ mnPoolFormatId == rRule.GetPoolFormatId() &&
+ mnPoolHelpId == rRule.GetPoolHelpId() &&
+ mnPoolHlpFileId == rRule.GetPoolHlpFileId();
+ if( bRet )
+ {
+ for( sal_uInt8 n = 0; n < MAXLEVEL; ++n )
+ if( rRule.Get( n ) != Get( n ) )
+ {
+ bRet = false;
+ break;
+ }
+ }
+ return bRet;
+}
+
+void SwNumRule::Set( sal_uInt16 i, const SwNumFormat& rNumFormat )
+{
+ OSL_ENSURE( i < MAXLEVEL, "Serious defect" );
+ if( i < MAXLEVEL )
+ {
+ if( !maFormats[ i ] || (rNumFormat != Get( i )) )
+ {
+ maFormats[ i ].reset(new SwNumFormat( rNumFormat ));
+ mbInvalidRuleFlag = true;
+ }
+ }
+}
+
+void SwNumRule::Set( sal_uInt16 i, const SwNumFormat* pNumFormat )
+{
+ OSL_ENSURE( i < MAXLEVEL, "Serious defect" );
+ if( i >= MAXLEVEL )
+ return;
+ if( !maFormats[ i ] )
+ {
+ if( pNumFormat )
+ {
+ maFormats[ i ].reset(new SwNumFormat( *pNumFormat ));
+ mbInvalidRuleFlag = true;
+ }
+ }
+ else if( !pNumFormat )
+ {
+ maFormats[ i ].reset();
+ mbInvalidRuleFlag = true;
+ }
+ else if( *maFormats[i] != *pNumFormat )
+ {
+ *maFormats[ i ] = *pNumFormat;
+ mbInvalidRuleFlag = true;
+ }
+}
+
+OUString SwNumRule::MakeNumString( const SwNodeNum& rNum, bool bInclStrings ) const
+{
+ if (rNum.IsCounted())
+ return MakeNumString(rNum.GetNumberVector(), bInclStrings);
+
+ return OUString();
+}
+
+namespace {
+/// Strip out text that is not a delimiter. Used in STYLEREF for when you
+/// have chapters labelled "Chapter X.Y" and want to just keep the "X.Y"
+/// Only used on the prefix/infix/suffix, so the numbers are not modified
+void StripNonDelimiter(OUString& rText)
+{
+ std::vector<sal_Unicode> charactersToKeep;
+
+ for (int i = 0; i < rText.getLength(); i++) {
+ auto character = rText[i];
+
+ // tdf#86790# for Word compatibility: I haven't found any better way to determine whether a
+ // character is a delimiter than testing in Word and listing them out. Furthermore, I haven't
+ // found a list so I can't be certain this is the complete set- if there's a compatibility issue
+ // with this in the future, here's the first place to look...
+ if (
+ character == '.'
+ || character == ','
+ || character == ':'
+ || character == ';'
+ || character == '-'
+ || character == '('
+ || character == ')'
+ || character == '['
+ || character == ']'
+ || character == '{'
+ || character == '}'
+ || character == '/'
+ || character == '\\'
+ || character == '|'
+ )
+ charactersToKeep.push_back(character);
+ }
+
+ if (charactersToKeep.size())
+ rText = OUString(charactersToKeep.data(), charactersToKeep.size());
+ else
+ rText = OUString();
+}
+}
+
+OUString SwNumRule::MakeNumString( const SwNumberTree::tNumberVector & rNumVector,
+ const bool bInclStrings,
+ const unsigned int _nRestrictToThisLevel,
+ const bool bHideNonNumerical,
+ SwNumRule::Extremities* pExtremities,
+ LanguageType nLang ) const
+{
+ OUStringBuffer aStr;
+
+ SwNumberTree::tNumberVector::size_type nLevel = rNumVector.size() - 1;
+
+ if ( pExtremities )
+ pExtremities->nPrefixChars = pExtremities->nSuffixChars = 0;
+
+ if ( nLevel > _nRestrictToThisLevel )
+ {
+ nLevel = _nRestrictToThisLevel;
+ }
+
+ assert(nLevel < MAXLEVEL);
+
+ const SwNumFormat& rMyNFormat = Get( o3tl::narrowing<sal_uInt16>(nLevel) );
+
+ if (rMyNFormat.GetNumberingType() == SVX_NUM_NUMBER_NONE)
+ {
+ if (!rMyNFormat.HasListFormat()) {
+ OUString sRet = bInclStrings ? rMyNFormat.GetPrefix() + rMyNFormat.GetSuffix() : OUString();
+ StripNonDelimiter(sRet);
+ return sRet;
+ }
+
+ // If numbering is disabled for this level we should emit just prefix/suffix
+ // Remove everything between first %1% and last %n% (including markers)
+ OUString sLevelFormat = rMyNFormat.GetListFormat(bInclStrings && !bHideNonNumerical);
+
+ if (bInclStrings && bHideNonNumerical) {
+ // If hiding non numerical text, we need to strip the prefix and suffix properly, so let's add them manually
+ OUString sPrefix = rMyNFormat.GetPrefix();
+ OUString sSuffix = rMyNFormat.GetSuffix();
+
+ StripNonDelimiter(sPrefix);
+ StripNonDelimiter(sSuffix);
+
+ sLevelFormat = sPrefix + sLevelFormat + sSuffix;
+ }
+
+ sal_Int32 nFirstPosition = sLevelFormat.indexOf("%");
+ sal_Int32 nLastPosition = sLevelFormat.lastIndexOf("%");
+ if (nFirstPosition >= 0 && nLastPosition >= nFirstPosition)
+ sLevelFormat = sLevelFormat.replaceAt(nFirstPosition, nLastPosition - nFirstPosition + 1, u"");
+ return sLevelFormat;
+ }
+
+ css::lang::Locale aLocale( LanguageTag::convertToLocale(nLang));
+
+ if (rMyNFormat.HasListFormat())
+ {
+ OUString sLevelFormat = rMyNFormat.GetListFormat(bInclStrings && !bHideNonNumerical);
+
+ if (bInclStrings && bHideNonNumerical) {
+ OUString sPrefix = rMyNFormat.GetPrefix();
+ OUString sSuffix = rMyNFormat.GetSuffix();
+
+ StripNonDelimiter(sPrefix);
+ StripNonDelimiter(sSuffix);
+
+ sLevelFormat = sPrefix + sLevelFormat + sSuffix;
+ }
+
+ // In this case we are ignoring GetIncludeUpperLevels: we put all
+ // level numbers requested by level format
+ for (SwNumberTree::tNumberVector::size_type i=0; i <= nLevel; ++i)
+ {
+ OUString sReplacement;
+ const SwNumFormat& rNFormat = Get(i);
+
+ OUString sFind("%" + OUString::number(i + 1) + "%");
+ sal_Int32 nPosition = sLevelFormat.indexOf(sFind);
+
+ if (rNFormat.GetNumberingType() == SVX_NUM_NUMBER_NONE)
+ {
+ // Numbering disabled - replacement is empty
+ // And we should skip all level string content until next level marker:
+ // so %1%.%2%.%3% with second level as NONE will result 1.1, not 1..1
+ sal_Int32 nPositionNext = sLevelFormat.indexOf('%', nPosition + sFind.getLength());
+ if (nPosition >= 0 && nPositionNext >= nPosition)
+ {
+ sLevelFormat = sLevelFormat.replaceAt(nPosition, nPositionNext - nPosition, u"");
+ }
+ continue;
+ }
+ else if (rNumVector[i])
+ sReplacement = Get(i).GetNumStr(rNumVector[i], aLocale, rMyNFormat.GetIsLegal());
+ else
+ sReplacement = "0"; // all 0 level are a 0
+
+ if (nPosition >= 0)
+ {
+ if (bHideNonNumerical)
+ {
+ sal_Int32 nPositionNext = sLevelFormat.indexOf('%', nPosition + sFind.getLength());
+
+ if (nPositionNext >= nPosition) {
+ sal_Int32 nReplaceStart = nPosition + sFind.getLength();
+ sal_Int32 nReplaceCount = nPositionNext - nReplaceStart;
+
+ OUString sSeparator = sLevelFormat.copy(nReplaceStart, nReplaceCount);
+ StripNonDelimiter(sSeparator);
+
+ sLevelFormat = sLevelFormat.replaceAt(nReplaceStart, nReplaceCount, sSeparator);
+ }
+ }
+
+ sLevelFormat = sLevelFormat.replaceAt(nPosition, sFind.getLength(), sReplacement);
+ }
+ }
+
+ aStr = sLevelFormat;
+ }
+ else
+ {
+ // Fallback case: level format is not defined
+ // So use old way with levels joining by dot "."
+ SwNumberTree::tNumberVector::size_type i = nLevel;
+
+ if (!IsContinusNum() &&
+ // - do not include upper levels, if level isn't numbered.
+ rMyNFormat.GetNumberingType() != SVX_NUM_NUMBER_NONE &&
+ rMyNFormat.GetIncludeUpperLevels()) // Just the own level?
+ {
+ sal_uInt8 n = rMyNFormat.GetIncludeUpperLevels();
+ if (1 < n)
+ {
+ if (i + 1 >= n)
+ i -= n - 1;
+ else
+ i = 0;
+ }
+ }
+
+ for (; i <= nLevel; ++i)
+ {
+ const SwNumFormat& rNFormat = Get(i);
+ if (SVX_NUM_NUMBER_NONE == rNFormat.GetNumberingType())
+ {
+ // Should 1.1.1 --> 2. NoNum --> 1..1 or 1.1 ??
+ // if( i != rNum.nMyLevel )
+ // aStr += ".";
+ continue;
+ }
+
+ if (rNumVector[i])
+ aStr.append(rNFormat.GetNumStr(rNumVector[i], aLocale, rMyNFormat.GetIsLegal()));
+ else
+ aStr.append("0"); // all 0 level are a 0
+ if (i != nLevel && !aStr.isEmpty())
+ aStr.append(".");
+ }
+
+ // The type doesn't have any number, so don't append
+ // the post-/prefix string
+ if (bInclStrings &&
+ SVX_NUM_CHAR_SPECIAL != rMyNFormat.GetNumberingType() &&
+ SVX_NUM_BITMAP != rMyNFormat.GetNumberingType())
+ {
+ OUString sPrefix = rMyNFormat.GetPrefix();
+ OUString sSuffix = rMyNFormat.GetSuffix();
+
+ if (bHideNonNumerical) {
+ StripNonDelimiter(sPrefix);
+ StripNonDelimiter(sSuffix);
+ }
+
+ aStr.insert(0, sPrefix);
+ aStr.append(sSuffix);
+ if (pExtremities)
+ {
+ pExtremities->nPrefixChars = sPrefix.getLength();
+ pExtremities->nSuffixChars = sSuffix.getLength();
+ }
+ }
+ }
+
+ return aStr.makeStringAndClear();
+}
+
+OUString SwNumRule::MakeRefNumString( const SwNodeNum& rNodeNum,
+ const bool bInclSuperiorNumLabels,
+ const int nRestrictInclToThisLevel,
+ const bool bHideNonNumerical ) const
+{
+ OUString aRefNumStr;
+
+ if ( rNodeNum.GetLevelInListTree() >= 0 )
+ {
+ bool bOldHadPrefix = true;
+
+ const SwNodeNum* pWorkingNodeNum( &rNodeNum );
+ do
+ {
+ bool bMakeNumStringForPhantom( false );
+ if ( pWorkingNodeNum->IsPhantom() )
+ {
+ int nListLevel = pWorkingNodeNum->GetLevelInListTree();
+
+ if (nListLevel < 0)
+ nListLevel = 0;
+
+ if (nListLevel >= MAXLEVEL)
+ nListLevel = MAXLEVEL - 1;
+
+ SwNumFormat aFormat( Get( o3tl::narrowing<sal_uInt16>(nListLevel) ) );
+ bMakeNumStringForPhantom = aFormat.IsEnumeration() &&
+ SVX_NUM_NUMBER_NONE != aFormat.GetNumberingType();
+
+ }
+ if ( bMakeNumStringForPhantom ||
+ ( !pWorkingNodeNum->IsPhantom() &&
+ pWorkingNodeNum->GetTextNode() &&
+ pWorkingNodeNum->GetTextNode()->HasNumber() ) )
+ {
+ Extremities aExtremities;
+ OUString aPrevStr = MakeNumString( pWorkingNodeNum->GetNumberVector(),
+ true, MAXLEVEL,
+ bHideNonNumerical, &aExtremities);
+ sal_Int32 nStrip = 0;
+ while ( nStrip < aExtremities.nPrefixChars )
+ {
+ const sal_Unicode c = aPrevStr[nStrip];
+ if ( c!='\t' && c!=' ')
+ break;
+ ++nStrip;
+ }
+
+ if (nStrip)
+ {
+ aPrevStr = aPrevStr.copy( nStrip );
+ aExtremities.nPrefixChars -= nStrip;
+ }
+
+ if (bOldHadPrefix &&
+ aExtremities.nSuffixChars &&
+ !aExtremities.nPrefixChars
+ )
+ {
+ aPrevStr = aPrevStr.copy(0,
+ aPrevStr.getLength() - aExtremities.nSuffixChars);
+ }
+
+ bOldHadPrefix = ( aExtremities.nPrefixChars > 0);
+
+ aRefNumStr = aPrevStr + aRefNumStr;
+ }
+
+ if ( bInclSuperiorNumLabels && pWorkingNodeNum->GetLevelInListTree() > 0 )
+ {
+ sal_uInt8 n = Get( o3tl::narrowing<sal_uInt16>(pWorkingNodeNum->GetLevelInListTree()) ).GetIncludeUpperLevels();
+ pWorkingNodeNum = dynamic_cast<SwNodeNum*>(pWorkingNodeNum->GetParent());
+ // skip parents, whose list label is already contained in the actual list label.
+ while ( pWorkingNodeNum && n > 1 )
+ {
+ pWorkingNodeNum = dynamic_cast<SwNodeNum*>(pWorkingNodeNum->GetParent());
+ --n;
+ }
+ }
+ else
+ {
+ break;
+ }
+ } while ( pWorkingNodeNum &&
+ pWorkingNodeNum->GetLevelInListTree() >= 0 &&
+ pWorkingNodeNum->GetLevelInListTree() >= nRestrictInclToThisLevel );
+ }
+
+ if (aRefNumStr.endsWith("."))
+ {
+ // tdf#144563: looks like a special case for refs by MS Word: if numbering is ending with dot, this dot is removed
+ aRefNumStr = aRefNumStr.copy(0, aRefNumStr.getLength() - 1);
+ }
+
+ return aRefNumStr;
+}
+
+OUString SwNumRule::MakeParagraphStyleListString() const
+{
+ OUString aParagraphStyleListString;
+ for (const auto& rParagraphStyle : maParagraphStyleList)
+ {
+ if (!aParagraphStyleListString.isEmpty())
+ aParagraphStyleListString += ", ";
+ aParagraphStyleListString += rParagraphStyle->GetName();
+ }
+ return aParagraphStyleListString;
+}
+
+/** Copy method of SwNumRule
+
+ A kind of copy constructor, so that the num formats are attached to the
+ right CharFormats of a Document.
+ Copies the NumFormats and returns itself. */
+SwNumRule& SwNumRule::CopyNumRule( SwDoc& rDoc, const SwNumRule& rNumRule )
+{
+ for( sal_uInt16 n = 0; n < MAXLEVEL; ++n )
+ {
+ Set( n, rNumRule.maFormats[ n ].get() );
+ if( maFormats[ n ] && maFormats[ n ]->GetCharFormat() &&
+ !rDoc.GetCharFormats()->ContainsFormat(maFormats[n]->GetCharFormat()))
+ {
+ // If we copy across different Documents, then copy the
+ // corresponding CharFormat into the new Document.
+ maFormats[n]->SetCharFormat( rDoc.CopyCharFormat( *maFormats[n]->
+ GetCharFormat() ) );
+ }
+ }
+ meRuleType = rNumRule.meRuleType;
+ msName = rNumRule.msName;
+ mbAutoRuleFlag = rNumRule.mbAutoRuleFlag;
+ mnPoolFormatId = rNumRule.GetPoolFormatId();
+ mnPoolHelpId = rNumRule.GetPoolHelpId();
+ mnPoolHlpFileId = rNumRule.GetPoolHlpFileId();
+ mbInvalidRuleFlag = true;
+ return *this;
+}
+
+void SwNumRule::SetSvxRule(const SvxNumRule& rNumRule, SwDoc* pDoc)
+{
+ for( sal_uInt16 n = 0; n < MAXLEVEL; ++n )
+ {
+ const SvxNumberFormat* pSvxFormat = rNumRule.Get(n);
+ maFormats[n].reset( pSvxFormat ? new SwNumFormat(*pSvxFormat, pDoc) : nullptr );
+ }
+
+ mbInvalidRuleFlag = true;
+ mbContinusNum = rNumRule.IsContinuousNumbering();
+}
+
+SvxNumRule SwNumRule::MakeSvxNumRule() const
+{
+ SvxNumRule aRule(SvxNumRuleFlags::CONTINUOUS | SvxNumRuleFlags::CHAR_STYLE |
+ SvxNumRuleFlags::ENABLE_LINKED_BMP | SvxNumRuleFlags::ENABLE_EMBEDDED_BMP,
+ MAXLEVEL, mbContinusNum,
+ meRuleType == NUM_RULE ? SvxNumRuleType::NUMBERING : SvxNumRuleType::OUTLINE_NUMBERING );
+ for( sal_uInt16 n = 0; n < MAXLEVEL; ++n )
+ {
+ const SwNumFormat & rNumFormat = Get(n);
+ if(rNumFormat.GetCharFormat())
+ {
+ SwNumFormat aNewFormat = rNumFormat;
+ aNewFormat.SetCharFormatName(rNumFormat.GetCharFormat()->GetName());
+ aRule.SetLevel(n, aNewFormat, maFormats[n] != nullptr);
+ }
+ else
+ aRule.SetLevel(n, rNumFormat, maFormats[n] != nullptr);
+ }
+ return aRule;
+}
+
+void SwNumRule::SetInvalidRule(bool bFlag)
+{
+ if (mbInvalidRuleFlag == bFlag)
+ return;
+
+ mbInvalidRuleFlag = bFlag;
+}
+
+/// change indent of all list levels by given difference
+void SwNumRule::ChangeIndent( const sal_Int32 nDiff )
+{
+ for ( sal_uInt16 i = 0; i < MAXLEVEL; ++i )
+ {
+ SwNumFormat aTmpNumFormat( Get(i) );
+
+ const SvxNumberFormat::SvxNumPositionAndSpaceMode ePosAndSpaceMode(
+ aTmpNumFormat.GetPositionAndSpaceMode() );
+ if ( ePosAndSpaceMode == SvxNumberFormat::LABEL_WIDTH_AND_POSITION )
+ {
+ auto nNewIndent = nDiff +
+ aTmpNumFormat.GetAbsLSpace();
+ if ( nNewIndent < 0 )
+ {
+ nNewIndent = 0;
+ }
+ aTmpNumFormat.SetAbsLSpace( nNewIndent );
+ }
+ else if ( ePosAndSpaceMode == SvxNumberFormat::LABEL_ALIGNMENT )
+ {
+ // adjust also the list tab position, if a list tab stop is applied
+ if ( aTmpNumFormat.GetLabelFollowedBy() == SvxNumberFormat::LISTTAB )
+ {
+ const tools::Long nNewListTab = aTmpNumFormat.GetListtabPos() + nDiff;
+ aTmpNumFormat.SetListtabPos( nNewListTab );
+ }
+
+ const tools::Long nNewIndent = nDiff +
+ aTmpNumFormat.GetIndentAt();
+ aTmpNumFormat.SetIndentAt( nNewIndent );
+ }
+
+ Set( i, aTmpNumFormat );
+ }
+
+ SetInvalidRule( true );
+}
+
+/// set indent of certain list level to given value
+void SwNumRule::SetIndent( const short nNewIndent,
+ const sal_uInt16 nListLevel )
+{
+ SwNumFormat aTmpNumFormat( Get(nListLevel) );
+
+ const SvxNumberFormat::SvxNumPositionAndSpaceMode ePosAndSpaceMode(
+ aTmpNumFormat.GetPositionAndSpaceMode() );
+ if ( ePosAndSpaceMode == SvxNumberFormat::LABEL_WIDTH_AND_POSITION )
+ {
+ aTmpNumFormat.SetAbsLSpace( nNewIndent );
+ }
+ else if ( ePosAndSpaceMode == SvxNumberFormat::LABEL_ALIGNMENT )
+ {
+ // adjust also the list tab position, if a list tab stop is applied
+ if ( aTmpNumFormat.GetLabelFollowedBy() == SvxNumberFormat::LISTTAB )
+ {
+ const tools::Long nNewListTab = aTmpNumFormat.GetListtabPos() +
+ ( nNewIndent - aTmpNumFormat.GetIndentAt() );
+ aTmpNumFormat.SetListtabPos( nNewListTab );
+ }
+
+ aTmpNumFormat.SetIndentAt( nNewIndent );
+ }
+
+ SetInvalidRule( true );
+}
+
+/// set indent of first list level to given value and change other list level's
+/// indents accordingly
+void SwNumRule::SetIndentOfFirstListLevelAndChangeOthers( const short nNewIndent )
+{
+ SwNumFormat aTmpNumFormat( Get(0) );
+
+ sal_Int32 nDiff( 0 );
+ const SvxNumberFormat::SvxNumPositionAndSpaceMode ePosAndSpaceMode(
+ aTmpNumFormat.GetPositionAndSpaceMode() );
+ if ( ePosAndSpaceMode == SvxNumberFormat::LABEL_WIDTH_AND_POSITION )
+ {
+ nDiff = nNewIndent
+ - aTmpNumFormat.GetFirstLineOffset()
+ - aTmpNumFormat.GetAbsLSpace();
+ }
+ else if ( ePosAndSpaceMode == SvxNumberFormat::LABEL_ALIGNMENT )
+ {
+ nDiff = nNewIndent - aTmpNumFormat.GetIndentAt();
+ }
+ if ( nDiff != 0 )
+ {
+ ChangeIndent( nDiff );
+ }
+}
+
+void SwNumRule::Validate(const SwDoc& rDoc)
+{
+ o3tl::sorted_vector< SwList* > aLists;
+ for ( const SwTextNode* pTextNode : maTextNodeList )
+ {
+ SwList* pList = pTextNode->GetDoc().getIDocumentListsAccess().getListByName( pTextNode->GetListId() );
+ aLists.insert( pList );
+ }
+
+ for ( auto aList : aLists )
+ aList->InvalidateListTree();
+
+ for ( auto aList : aLists )
+ aList->ValidateListTree(rDoc);
+
+ SetInvalidRule(false);
+}
+
+void SwNumRule::SetCountPhantoms(bool bCountPhantoms)
+{
+ mbCountPhantoms = bCountPhantoms;
+}
+
+SwNumRule::tParagraphStyleList::size_type SwNumRule::GetParagraphStyleListSize() const
+{
+ return maParagraphStyleList.size();
+}
+
+void SwNumRule::AddParagraphStyle( SwTextFormatColl& rTextFormatColl )
+{
+ tParagraphStyleList::iterator aIter =
+ std::find( maParagraphStyleList.begin(), maParagraphStyleList.end(), &rTextFormatColl );
+
+ if ( aIter == maParagraphStyleList.end() )
+ {
+ maParagraphStyleList.push_back( &rTextFormatColl );
+ }
+}
+
+void SwNumRule::RemoveParagraphStyle( SwTextFormatColl& rTextFormatColl )
+{
+ tParagraphStyleList::iterator aIter =
+ std::find( maParagraphStyleList.begin(), maParagraphStyleList.end(), &rTextFormatColl );
+
+ if ( aIter != maParagraphStyleList.end() )
+ {
+ maParagraphStyleList.erase( aIter );
+ }
+}
+
+void SwNumRule::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwNumRule"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("msName"), BAD_CAST(msName.toUtf8().getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("mnPoolFormatId"), BAD_CAST(OString::number(mnPoolFormatId).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("mbAutoRuleFlag"), BAD_CAST(OString::boolean(mbAutoRuleFlag).getStr()));
+
+ for (const auto& pFormat : maFormats)
+ {
+ if (!pFormat)
+ {
+ continue;
+ }
+
+ pFormat->dumpAsXml(pWriter);
+ }
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+void SwNumRule::GetGrabBagItem(uno::Any& rVal) const
+{
+ if (mpGrabBagItem)
+ mpGrabBagItem->QueryValue(rVal);
+ else
+ rVal <<= uno::Sequence<beans::PropertyValue>();
+}
+
+void SwNumRule::SetGrabBagItem(const uno::Any& rVal)
+{
+ if (!mpGrabBagItem)
+ mpGrabBagItem = std::make_shared<SfxGrabBagItem>();
+
+ mpGrabBagItem->PutValue(rVal, 0);
+}
+
+namespace numfunc
+{
+ namespace {
+
+ /** class containing default bullet list configuration data */
+ class SwDefBulletConfig : private utl::ConfigItem
+ {
+ public:
+ static SwDefBulletConfig& getInstance();
+
+ const OUString& GetFontname() const
+ {
+ return msFontname;
+ }
+
+ bool IsFontnameUserDefined() const
+ {
+ return mbUserDefinedFontname;
+ }
+
+ const vcl::Font& GetFont() const
+ {
+ return *mpFont;
+ }
+
+ sal_Unicode GetChar( sal_uInt8 p_nListLevel ) const
+ {
+ if (p_nListLevel >= MAXLEVEL)
+ {
+ p_nListLevel = MAXLEVEL - 1;
+ }
+
+ return mnLevelChars[p_nListLevel];
+ }
+
+ SwDefBulletConfig();
+
+ private:
+ /** sets internal default bullet configuration data to default values */
+ void SetToDefault();
+
+ /** returns sequence of default bullet configuration property names */
+ static uno::Sequence<OUString> GetPropNames();
+
+ /** loads default bullet configuration properties and applies
+ values to internal data */
+ void LoadConfig();
+
+ /** initialize font instance for default bullet list */
+ void InitFont();
+
+ /** catches notification about changed default bullet configuration data */
+ virtual void Notify( const uno::Sequence<OUString>& aPropertyNames ) override;
+ virtual void ImplCommit() override;
+
+ // default bullet list configuration data
+ OUString msFontname;
+ bool mbUserDefinedFontname;
+ FontWeight meFontWeight;
+ FontItalic meFontItalic;
+ sal_Unicode mnLevelChars[MAXLEVEL];
+
+ // default bullet list font instance
+ std::optional<vcl::Font> mpFont;
+ };
+
+ }
+
+ SwDefBulletConfig& SwDefBulletConfig::getInstance()
+ {
+ static SwDefBulletConfig theSwDefBulletConfig;
+ return theSwDefBulletConfig;
+ }
+
+ SwDefBulletConfig::SwDefBulletConfig()
+ : ConfigItem( "Office.Writer/Numbering/DefaultBulletList" ),
+ // default bullet font is now OpenSymbol
+ msFontname( OUString("OpenSymbol") ),
+ mbUserDefinedFontname( false ),
+ meFontWeight( WEIGHT_DONTKNOW ),
+ meFontItalic( ITALIC_NONE )
+ {
+ SetToDefault();
+ LoadConfig();
+ InitFont();
+
+ // enable notification for changes on default bullet configuration change
+ EnableNotification( GetPropNames() );
+ }
+
+ void SwDefBulletConfig::SetToDefault()
+ {
+ msFontname = "OpenSymbol";
+ mbUserDefinedFontname = false;
+ meFontWeight = WEIGHT_DONTKNOW;
+ meFontItalic = ITALIC_NONE;
+
+ mnLevelChars[0] = 0x2022;
+ mnLevelChars[1] = 0x25e6;
+ mnLevelChars[2] = 0x25aa;
+ mnLevelChars[3] = 0x2022;
+ mnLevelChars[4] = 0x25e6;
+ mnLevelChars[5] = 0x25aa;
+ mnLevelChars[6] = 0x2022;
+ mnLevelChars[7] = 0x25e6;
+ mnLevelChars[8] = 0x25aa;
+ mnLevelChars[9] = 0x2022;
+ }
+
+ uno::Sequence<OUString> SwDefBulletConfig::GetPropNames()
+ {
+ uno::Sequence<OUString> aPropNames(13);
+ OUString* pNames = aPropNames.getArray();
+ pNames[0] = "BulletFont/FontFamilyname";
+ pNames[1] = "BulletFont/FontWeight";
+ pNames[2] = "BulletFont/FontItalic";
+ pNames[3] = "BulletCharLvl1";
+ pNames[4] = "BulletCharLvl2";
+ pNames[5] = "BulletCharLvl3";
+ pNames[6] = "BulletCharLvl4";
+ pNames[7] = "BulletCharLvl5";
+ pNames[8] = "BulletCharLvl6";
+ pNames[9] = "BulletCharLvl7";
+ pNames[10] = "BulletCharLvl8";
+ pNames[11] = "BulletCharLvl9";
+ pNames[12] = "BulletCharLvl10";
+
+ return aPropNames;
+ }
+
+ void SwDefBulletConfig::LoadConfig()
+ {
+ uno::Sequence<OUString> aPropNames = GetPropNames();
+ uno::Sequence<uno::Any> aValues = GetProperties( aPropNames );
+ const uno::Any* pValues = aValues.getConstArray();
+ OSL_ENSURE( aValues.getLength() == aPropNames.getLength(),
+ "<SwDefBulletConfig::SwDefBulletConfig()> - GetProperties failed");
+ if ( aValues.getLength() != aPropNames.getLength() )
+ return;
+
+ for ( int nProp = 0; nProp < aPropNames.getLength(); ++nProp )
+ {
+ if ( pValues[nProp].hasValue() )
+ {
+ switch ( nProp )
+ {
+ case 0:
+ {
+ OUString aStr;
+ pValues[nProp] >>= aStr;
+ msFontname = aStr;
+ mbUserDefinedFontname = true;
+ }
+ break;
+ case 1:
+ case 2:
+ {
+ sal_Int16 nTmp = 0;
+ pValues[nProp] >>= nTmp;
+ if ( nProp == 1 )
+ meFontWeight = static_cast<FontWeight>(nTmp);
+ else if ( nProp == 2 )
+ meFontItalic = static_cast<FontItalic>(nTmp);
+ }
+ break;
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ case 9:
+ case 10:
+ case 11:
+ case 12:
+ {
+ sal_Unicode cChar = sal_Unicode();
+ pValues[nProp] >>= cChar;
+ mnLevelChars[nProp-3] = cChar;
+ }
+ break;
+ }
+ }
+ }
+
+ }
+
+ void SwDefBulletConfig::InitFont()
+ {
+ mpFont.emplace( msFontname, OUString(), Size( 0, 14 ) );
+ mpFont->SetWeight( meFontWeight );
+ mpFont->SetItalic( meFontItalic );
+ mpFont->SetCharSet( RTL_TEXTENCODING_SYMBOL );
+ }
+
+ void SwDefBulletConfig::Notify( const uno::Sequence<OUString>& )
+ {
+ SetToDefault();
+ LoadConfig();
+ InitFont();
+ }
+
+ void SwDefBulletConfig::ImplCommit()
+ {
+ }
+
+ OUString const & GetDefBulletFontname()
+ {
+ return SwDefBulletConfig::getInstance().GetFontname();
+ }
+
+ bool IsDefBulletFontUserDefined()
+ {
+ return SwDefBulletConfig::getInstance().IsFontnameUserDefined();
+ }
+
+ const vcl::Font& GetDefBulletFont()
+ {
+ return SwDefBulletConfig::getInstance().GetFont();
+ }
+
+ sal_Unicode GetBulletChar( sal_uInt8 nLevel )
+ {
+ return SwDefBulletConfig::getInstance().GetChar( nLevel );
+ }
+
+ namespace {
+
+ /** class containing configuration data about user interface behavior
+ regarding lists and list items.
+ configuration item about behavior of <TAB>/<SHIFT-TAB>-key at first
+ position of first list item
+ */
+ class SwNumberingUIBehaviorConfig : private utl::ConfigItem
+ {
+ public:
+ static SwNumberingUIBehaviorConfig& getInstance();
+
+ bool ChangeIndentOnTabAtFirstPosOfFirstListItem() const
+ {
+ return mbChangeIndentOnTabAtFirstPosOfFirstListItem;
+ }
+
+ SwNumberingUIBehaviorConfig();
+
+ private:
+
+ /** sets internal configuration data to default values */
+ void SetToDefault();
+
+ /** returns sequence of configuration property names */
+ static css::uno::Sequence<OUString> GetPropNames();
+
+ /** loads configuration properties and applies values to internal data */
+ void LoadConfig();
+
+ /** catches notification about changed configuration data */
+ virtual void Notify( const css::uno::Sequence<OUString>& aPropertyNames ) override;
+ virtual void ImplCommit() override;
+
+ // configuration data
+ bool mbChangeIndentOnTabAtFirstPosOfFirstListItem;
+ };
+
+ }
+
+ SwNumberingUIBehaviorConfig& SwNumberingUIBehaviorConfig::getInstance()
+ {
+ static SwNumberingUIBehaviorConfig theSwNumberingUIBehaviorConfig;
+ return theSwNumberingUIBehaviorConfig;
+ }
+
+ SwNumberingUIBehaviorConfig::SwNumberingUIBehaviorConfig()
+ : ConfigItem( "Office.Writer/Numbering/UserInterfaceBehavior" ),
+ mbChangeIndentOnTabAtFirstPosOfFirstListItem( true )
+ {
+ SetToDefault();
+ LoadConfig();
+
+ // enable notification for changes on configuration change
+ EnableNotification( GetPropNames() );
+ }
+
+ void SwNumberingUIBehaviorConfig::SetToDefault()
+ {
+ mbChangeIndentOnTabAtFirstPosOfFirstListItem = true;
+ }
+
+ css::uno::Sequence<OUString> SwNumberingUIBehaviorConfig::GetPropNames()
+ {
+ css::uno::Sequence<OUString> aPropNames { "ChangeIndentOnTabAtFirstPosOfFirstListItem" };
+
+ return aPropNames;
+ }
+
+ void SwNumberingUIBehaviorConfig::ImplCommit() {}
+
+ void SwNumberingUIBehaviorConfig::LoadConfig()
+ {
+ css::uno::Sequence<OUString> aPropNames = GetPropNames();
+ css::uno::Sequence<css::uno::Any> aValues = GetProperties( aPropNames );
+ const css::uno::Any* pValues = aValues.getConstArray();
+ OSL_ENSURE( aValues.getLength() == aPropNames.getLength(),
+ "<SwNumberingUIBehaviorConfig::LoadConfig()> - GetProperties failed");
+ if ( aValues.getLength() != aPropNames.getLength() )
+ return;
+
+ for ( int nProp = 0; nProp < aPropNames.getLength(); ++nProp )
+ {
+ if ( pValues[nProp].hasValue() )
+ {
+ switch ( nProp )
+ {
+ case 0:
+ {
+ pValues[nProp] >>= mbChangeIndentOnTabAtFirstPosOfFirstListItem;
+ }
+ break;
+ default:
+ {
+ OSL_FAIL( "<SwNumberingUIBehaviorConfig::LoadConfig()> - unknown configuration property");
+ }
+ }
+ }
+ }
+ }
+
+ void SwNumberingUIBehaviorConfig::Notify( const css::uno::Sequence<OUString>& )
+ {
+ SetToDefault();
+ LoadConfig();
+ }
+
+ bool ChangeIndentOnTabAtFirstPosOfFirstListItem()
+ {
+ return SwNumberingUIBehaviorConfig::getInstance().ChangeIndentOnTabAtFirstPosOfFirstListItem();
+ }
+
+ bool NumDownChangesIndent(const SwWrtShell& rShell)
+ {
+ SwPaM* pCursor = rShell.GetCursor();
+ if (!pCursor)
+ {
+ return true;
+ }
+
+ SwTextNode* pTextNode = pCursor->GetPointNode().GetTextNode();
+ if (!pTextNode)
+ {
+ return true;
+ }
+
+ const SwNumRule* pNumRule = pTextNode->GetNumRule();
+ if (!pNumRule)
+ {
+ return true;
+ }
+
+ int nOldLevel = pTextNode->GetActualListLevel();
+ int nNewLevel = nOldLevel + 1;
+ if (nNewLevel >= MAXLEVEL)
+ {
+ return true;
+ }
+
+ const SwNumFormat& rOldFormat = pNumRule->Get(nOldLevel);
+ if (rOldFormat.GetNumberingType() != SVX_NUM_NUMBER_NONE)
+ {
+ return true;
+ }
+
+ const SwNumFormat& rNewFormat = pNumRule->Get(nNewLevel);
+ if (rNewFormat.GetNumberingType() != SVX_NUM_NUMBER_NONE)
+ {
+ return true;
+ }
+
+ // This is the case when the numbering levels don't differ, so changing between them is not
+ // a better alternative to inserting a tab character.
+ return rOldFormat.GetIndentAt() != rNewFormat.GetIndentAt();
+ }
+
+ SvxNumberFormat::SvxNumPositionAndSpaceMode GetDefaultPositionAndSpaceMode()
+ {
+ if (utl::ConfigManager::IsFuzzing())
+ return SvxNumberFormat::LABEL_ALIGNMENT;
+
+ SvxNumberFormat::SvxNumPositionAndSpaceMode ePosAndSpaceMode;
+ switch (GetODFSaneDefaultVersion())
+ {
+ case SvtSaveOptions::ODFSVER_010:
+ case SvtSaveOptions::ODFSVER_011:
+ {
+ ePosAndSpaceMode = SvxNumberFormat::LABEL_WIDTH_AND_POSITION;
+ }
+ break;
+ default: // >= ODFSVER_012
+ {
+ ePosAndSpaceMode = SvxNumberFormat::LABEL_ALIGNMENT;
+ }
+ }
+
+ return ePosAndSpaceMode;
+ }
+}
+
+void SwNumRuleTable::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwNumRuleTable"));
+ for (SwNumRule* pNumRule : *this)
+ pNumRule->dumpAsXml(pWriter);
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/poolfmt.cxx b/sw/source/core/doc/poolfmt.cxx
new file mode 100644
index 0000000000..97b4e4cce9
--- /dev/null
+++ b/sw/source/core/doc/poolfmt.cxx
@@ -0,0 +1,311 @@
+/* -*- 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 <hintids.hxx>
+#include <i18nlangtag/mslangid.hxx>
+#include <editeng/boxitem.hxx>
+#include <editeng/frmdiritem.hxx>
+#include <osl/diagnose.h>
+#include <doc.hxx>
+#include <IDocumentState.hxx>
+#include <IDocumentStylePoolAccess.hxx>
+#include <IDocumentListsAccess.hxx>
+#include <list.hxx>
+#include <poolfmt.hxx>
+#include <pagedesc.hxx>
+#include <fmtcol.hxx>
+#include <numrule.hxx>
+#include <swtable.hxx>
+#include <tblafmt.hxx>
+#include <hints.hxx>
+
+using namespace ::editeng;
+using namespace ::com::sun::star;
+
+void SetAllScriptItem( SfxItemSet& rSet, const SfxPoolItem& rItem )
+{
+ rSet.Put( rItem );
+ sal_uInt16 nWhCJK = 0, nWhCTL = 0;
+ switch( rItem.Which() )
+ {
+ case RES_CHRATR_FONTSIZE:
+ nWhCJK = RES_CHRATR_CJK_FONTSIZE;
+ nWhCTL = RES_CHRATR_CTL_FONTSIZE;
+ break;
+ case RES_CHRATR_FONT:
+ nWhCJK = RES_CHRATR_CJK_FONT;
+ nWhCTL = RES_CHRATR_CTL_FONT;
+ break;
+ case RES_CHRATR_LANGUAGE:
+ nWhCJK = RES_CHRATR_CJK_LANGUAGE;
+ nWhCTL = RES_CHRATR_CTL_LANGUAGE;
+ break;
+ case RES_CHRATR_POSTURE:
+ nWhCJK = RES_CHRATR_CJK_POSTURE;
+ nWhCTL = RES_CHRATR_CTL_POSTURE;
+ break;
+ case RES_CHRATR_WEIGHT:
+ nWhCJK = RES_CHRATR_CJK_WEIGHT;
+ nWhCTL = RES_CHRATR_CTL_WEIGHT;
+ break;
+ }
+
+ if( nWhCJK )
+ rSet.Put( rItem.CloneSetWhich(nWhCJK) );
+ if( nWhCTL )
+ rSet.Put( rItem.CloneSetWhich(nWhCTL) );
+}
+
+/// Return the AutoCollection by its Id. If it doesn't
+/// exist yet, create it.
+/// If the String pointer is defined, then only query for
+/// the Attribute descriptions. It doesn't create a style!
+SvxFrameDirection GetDefaultFrameDirection(LanguageType nLanguage)
+{
+ return MsLangId::isRightToLeft(nLanguage) ?
+ SvxFrameDirection::Horizontal_RL_TB : SvxFrameDirection::Horizontal_LR_TB;
+}
+
+// See if the Paragraph/Character/Frame/Page style is in use
+bool SwDoc::IsUsed( const sw::BroadcastingModify& rModify ) const
+{
+ // Check if we have dependent ContentNodes in the Nodes array
+ // (also indirect ones for derived Formats)
+ bool isUsed = false;
+ sw::AutoFormatUsedHint aHint(isUsed, GetNodes());
+ rModify.CallSwClientNotify(aHint);
+ return isUsed;
+}
+
+// See if Table style is in use
+bool SwDoc::IsUsed( const SwTableAutoFormat& rTableAutoFormat) const
+{
+ size_t nTableCount = GetTableFrameFormatCount(true);
+ for (size_t i=0; i < nTableCount; ++i)
+ {
+ SwFrameFormat* pFrameFormat = &GetTableFrameFormat(i, true);
+ SwTable* pTable = SwTable::FindTable(pFrameFormat);
+ if (pTable->GetTableStyleName() == rTableAutoFormat.GetName())
+ return true;
+ }
+ return false;
+}
+
+// See if the NumRule is used
+bool SwDoc::IsUsed( const SwNumRule& rRule ) const
+{
+ SwList const*const pList(getIDocumentListsAccess().getListByName(rRule.GetDefaultListId()));
+ bool bUsed = rRule.GetTextNodeListSize() > 0 ||
+ rRule.GetParagraphStyleListSize() > 0 ||
+ rRule.IsUsedByRedline()
+ // tdf#135014 default num rule is used if any associated num rule is used
+ || (pList
+ && pList->GetDefaultListStyleName() == rRule.GetName()
+ && pList->HasNodes());
+
+ return bUsed;
+}
+
+const OUString* SwDoc::GetDocPattern(size_t const nPos) const
+{
+ if (nPos >= m_PatternNames.size())
+ return nullptr;
+ return &m_PatternNames[nPos];
+}
+
+// Look for the style name's position. If it doesn't exist,
+// insert an anew
+size_t SwDoc::SetDocPattern(const OUString& rPatternName)
+{
+ OSL_ENSURE( !rPatternName.isEmpty(), "no Document style name" );
+
+ auto const iter(
+ std::find(m_PatternNames.begin(), m_PatternNames.end(), rPatternName));
+ if (iter != m_PatternNames.end())
+ {
+ return std::distance(m_PatternNames.begin(), iter);
+ }
+ else
+ {
+ m_PatternNames.push_back(rPatternName);
+ getIDocumentState().SetModified();
+ return m_PatternNames.size() - 1;
+ }
+}
+
+sal_uInt16 GetPoolParent( sal_uInt16 nId )
+{
+ sal_uInt16 nRet = USHRT_MAX;
+ if( POOLGRP_NOCOLLID & nId ) // 1 == Formats / 0 == Collections
+ {
+ switch( ( COLL_GET_RANGE_BITS | POOLGRP_NOCOLLID ) & nId )
+ {
+ case POOLGRP_CHARFMT:
+ case POOLGRP_FRAMEFMT:
+ nRet = 0; // derived from the default
+ break;
+ case POOLGRP_PAGEDESC:
+ case POOLGRP_NUMRULE:
+ break; // there are no derivations
+ }
+ }
+ else
+ {
+ switch( COLL_GET_RANGE_BITS & nId )
+ {
+ case COLL_TEXT_BITS:
+ switch( nId )
+ {
+ case RES_POOLCOLL_STANDARD:
+ nRet = 0; break;
+ case RES_POOLCOLL_TEXT_IDENT:
+ case RES_POOLCOLL_TEXT_NEGIDENT:
+ case RES_POOLCOLL_TEXT_MOVE:
+ case RES_POOLCOLL_CONFRONTATION:
+ case RES_POOLCOLL_MARGINAL:
+ nRet = RES_POOLCOLL_TEXT; break;
+
+ case RES_POOLCOLL_TEXT:
+ case RES_POOLCOLL_GREETING:
+ case RES_POOLCOLL_SIGNATURE:
+ case RES_POOLCOLL_HEADLINE_BASE:
+ nRet = RES_POOLCOLL_STANDARD; break;
+
+ case RES_POOLCOLL_HEADLINE1:
+ case RES_POOLCOLL_HEADLINE2:
+ case RES_POOLCOLL_HEADLINE3:
+ case RES_POOLCOLL_HEADLINE4:
+ case RES_POOLCOLL_HEADLINE5:
+ case RES_POOLCOLL_HEADLINE6:
+ case RES_POOLCOLL_HEADLINE7:
+ case RES_POOLCOLL_HEADLINE8:
+ case RES_POOLCOLL_HEADLINE9:
+ case RES_POOLCOLL_HEADLINE10:
+ nRet = RES_POOLCOLL_HEADLINE_BASE; break;
+ }
+ break;
+
+ case COLL_LISTS_BITS:
+ switch( nId )
+ {
+ case RES_POOLCOLL_NUMBER_BULLET_BASE:
+ nRet = RES_POOLCOLL_TEXT; break;
+
+ default:
+ nRet = RES_POOLCOLL_NUMBER_BULLET_BASE; break;
+ }
+ break;
+
+ case COLL_EXTRA_BITS:
+ switch( nId )
+ {
+ case RES_POOLCOLL_TABLE_HDLN:
+ nRet = RES_POOLCOLL_TABLE; break;
+
+ case RES_POOLCOLL_FRAME:
+ case RES_POOLCOLL_TABLE:
+ case RES_POOLCOLL_FOOTNOTE:
+ case RES_POOLCOLL_ENDNOTE:
+ case RES_POOLCOLL_ENVELOPE_ADDRESS:
+ case RES_POOLCOLL_SEND_ADDRESS:
+ case RES_POOLCOLL_HEADERFOOTER:
+ case RES_POOLCOLL_LABEL:
+ case RES_POOLCOLL_COMMENT:
+ nRet = RES_POOLCOLL_STANDARD; break;
+ case RES_POOLCOLL_HEADER:
+ nRet = RES_POOLCOLL_HEADERFOOTER; break;
+ case RES_POOLCOLL_HEADERL:
+ case RES_POOLCOLL_HEADERR:
+ nRet = RES_POOLCOLL_HEADER; break;
+ case RES_POOLCOLL_FOOTER:
+ nRet = RES_POOLCOLL_HEADERFOOTER; break;
+ case RES_POOLCOLL_FOOTERL:
+ case RES_POOLCOLL_FOOTERR:
+ nRet = RES_POOLCOLL_FOOTER; break;
+
+ case RES_POOLCOLL_LABEL_ABB:
+ case RES_POOLCOLL_LABEL_TABLE:
+ case RES_POOLCOLL_LABEL_FRAME:
+ case RES_POOLCOLL_LABEL_DRAWING:
+ case RES_POOLCOLL_LABEL_FIGURE:
+ nRet = RES_POOLCOLL_LABEL; break;
+ }
+ break;
+
+ case COLL_REGISTER_BITS:
+ switch( nId )
+ {
+ case RES_POOLCOLL_REGISTER_BASE:
+ nRet = RES_POOLCOLL_STANDARD; break;
+
+ case RES_POOLCOLL_TOX_IDXH:
+ nRet = RES_POOLCOLL_HEADLINE_BASE; break;
+
+ case RES_POOLCOLL_TOX_USERH:
+ case RES_POOLCOLL_TOX_CNTNTH:
+ case RES_POOLCOLL_TOX_ILLUSH:
+ case RES_POOLCOLL_TOX_OBJECTH:
+ case RES_POOLCOLL_TOX_TABLESH:
+ case RES_POOLCOLL_TOX_AUTHORITIESH:
+ nRet = RES_POOLCOLL_TOX_IDXH; break;
+
+ default:
+ nRet = RES_POOLCOLL_REGISTER_BASE; break;
+ }
+ break;
+
+ case COLL_DOC_BITS:
+ nRet = RES_POOLCOLL_HEADLINE_BASE;
+ break;
+
+ case COLL_HTML_BITS:
+ nRet = RES_POOLCOLL_STANDARD;
+ break;
+ }
+ }
+
+ return nRet;
+}
+
+void SwDoc::RemoveAllFormatLanguageDependencies()
+{
+ /* Restore the language independent pool defaults and styles. */
+ GetAttrPool().ResetPoolDefaultItem( RES_PARATR_ADJUST );
+
+ SwTextFormatColl * pTextFormatColl = getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_STANDARD );
+
+ pTextFormatColl->ResetFormatAttr( RES_PARATR_ADJUST );
+ /* koreans do not like SvxScriptItem(TRUE) */
+ pTextFormatColl->ResetFormatAttr( RES_PARATR_SCRIPTSPACE );
+
+ SvxFrameDirectionItem aFrameDir( SvxFrameDirection::Horizontal_LR_TB, RES_FRAMEDIR );
+
+ size_t nCount = GetPageDescCnt();
+ for( size_t i=0; i<nCount; ++i )
+ {
+ SwPageDesc& rDesc = GetPageDesc( i );
+ rDesc.GetMaster().SetFormatAttr( aFrameDir );
+ rDesc.GetLeft().SetFormatAttr( aFrameDir );
+ }
+
+ //#i16874# AutoKerning as default for new documents
+ GetAttrPool().ResetPoolDefaultItem( RES_CHRATR_AUTOKERN );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/rdfhelper.cxx b/sw/source/core/doc/rdfhelper.cxx
new file mode 100644
index 0000000000..d4e5d9da2b
--- /dev/null
+++ b/sw/source/core/doc/rdfhelper.cxx
@@ -0,0 +1,265 @@
+/* -*- 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/.
+ */
+
+#include <rdfhelper.hxx>
+
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/rdf/Literal.hpp>
+#include <com/sun/star/rdf/Statement.hpp>
+#include <com/sun/star/rdf/URI.hpp>
+#include <com/sun/star/rdf/XDocumentMetadataAccess.hpp>
+
+#include <comphelper/processfactory.hxx>
+
+#include <doc.hxx>
+#include <docsh.hxx>
+#include <ndtxt.hxx>
+#include <unoparagraph.hxx>
+#include <unotext.hxx>
+
+using namespace com::sun::star;
+
+css::uno::Sequence<css::uno::Reference<css::rdf::XURI>> SwRDFHelper::getGraphNames(
+ const css::uno::Reference<rdf::XDocumentMetadataAccess>& xDocumentMetadataAccess,
+ const css::uno::Reference<rdf::XURI>& xType)
+{
+ try
+ {
+ return xDocumentMetadataAccess->getMetadataGraphsWithType(xType);
+ }
+ catch (const uno::RuntimeException&)
+ {
+ return uno::Sequence<uno::Reference<rdf::XURI>>();
+ }
+}
+
+css::uno::Sequence<uno::Reference<css::rdf::XURI>>
+SwRDFHelper::getGraphNames(const css::uno::Reference<css::frame::XModel>& xModel,
+ const OUString& rType)
+{
+ try
+ {
+ uno::Reference<uno::XComponentContext> xComponentContext(
+ comphelper::getProcessComponentContext());
+ // rdf::URI::create may fail with type: com.sun.star.uno.DeploymentException
+ // message: component context fails to supply service com.sun.star.rdf.URI of type com.sun.star.rdf.XURI
+ // context: cppu::ComponentContext
+ uno::Reference<rdf::XURI> xType = rdf::URI::create(xComponentContext, rType);
+ uno::Reference<rdf::XDocumentMetadataAccess> xDocumentMetadataAccess(xModel,
+ uno::UNO_QUERY);
+ return getGraphNames(xDocumentMetadataAccess, xType);
+ }
+ catch (const ::css::uno::Exception&)
+ {
+ return uno::Sequence<uno::Reference<rdf::XURI>>();
+ }
+}
+
+std::map<OUString, OUString>
+SwRDFHelper::getStatements(const css::uno::Reference<css::frame::XModel>& xModel,
+ const uno::Sequence<uno::Reference<css::rdf::XURI>>& rGraphNames,
+ const css::uno::Reference<css::rdf::XResource>& xSubject)
+{
+ std::map<OUString, OUString> aRet;
+ if (!rGraphNames.hasElements())
+ return aRet;
+
+ uno::Reference<rdf::XDocumentMetadataAccess> xDocumentMetadataAccess(xModel, uno::UNO_QUERY);
+ const uno::Reference<rdf::XRepository>& xRepo = xDocumentMetadataAccess->getRDFRepository();
+ for (const uno::Reference<rdf::XURI>& xGraphName : rGraphNames)
+ {
+ uno::Reference<rdf::XNamedGraph> xGraph = xRepo->getGraph(xGraphName);
+ if (!xGraph.is())
+ continue;
+
+ uno::Reference<container::XEnumeration> xStatements = xGraph->getStatements(
+ xSubject, uno::Reference<rdf::XURI>(), uno::Reference<rdf::XURI>());
+ while (xStatements->hasMoreElements())
+ {
+ const rdf::Statement aStatement = xStatements->nextElement().get<rdf::Statement>();
+ aRet[aStatement.Predicate->getStringValue()] = aStatement.Object->getStringValue();
+ }
+ }
+
+ return aRet;
+}
+
+std::map<OUString, OUString>
+SwRDFHelper::getStatements(const css::uno::Reference<css::frame::XModel>& xModel,
+ const OUString& rType,
+ const css::uno::Reference<css::rdf::XResource>& xSubject)
+{
+ return getStatements(xModel, getGraphNames(xModel, rType), xSubject);
+}
+
+void SwRDFHelper::addStatement(const css::uno::Reference<css::frame::XModel>& xModel,
+ const OUString& rType, const OUString& rPath,
+ const css::uno::Reference<css::rdf::XResource>& xSubject,
+ const OUString& rKey, const OUString& rValue)
+{
+ uno::Reference<uno::XComponentContext> xComponentContext(comphelper::getProcessComponentContext());
+ uno::Reference<rdf::XURI> xType = rdf::URI::create(xComponentContext, rType);
+ uno::Reference<rdf::XDocumentMetadataAccess> xDocumentMetadataAccess(xModel, uno::UNO_QUERY);
+ const uno::Sequence< uno::Reference<rdf::XURI> > aGraphNames = getGraphNames(xDocumentMetadataAccess, xType);
+ uno::Reference<rdf::XURI> xGraphName;
+ if (aGraphNames.hasElements())
+ xGraphName = aGraphNames[0];
+ else
+ {
+ uno::Sequence< uno::Reference<rdf::XURI> > xTypes = { xType };
+ xGraphName = xDocumentMetadataAccess->addMetadataFile(rPath, xTypes);
+ }
+ uno::Reference<rdf::XNamedGraph> xGraph = xDocumentMetadataAccess->getRDFRepository()->getGraph(xGraphName);
+ uno::Reference<rdf::XURI> xKey = rdf::URI::create(xComponentContext, rKey);
+ uno::Reference<rdf::XLiteral> xValue = rdf::Literal::create(xComponentContext, rValue);
+ xGraph->addStatement(xSubject, xKey, xValue);
+}
+
+bool SwRDFHelper::hasMetadataGraph(const css::uno::Reference<css::frame::XModel>& xModel, const OUString& rType)
+{
+ uno::Reference<uno::XComponentContext> xComponentContext(comphelper::getProcessComponentContext());
+ uno::Reference<rdf::XURI> xType = rdf::URI::create(xComponentContext, rType);
+ uno::Reference<rdf::XDocumentMetadataAccess> xDocumentMetadataAccess(xModel, uno::UNO_QUERY);
+ return getGraphNames(xDocumentMetadataAccess, xType).hasElements();
+}
+
+void SwRDFHelper::removeStatement(const css::uno::Reference<css::frame::XModel>& xModel,
+ const OUString& rType,
+ const css::uno::Reference<css::rdf::XResource>& xSubject,
+ const OUString& rKey, const OUString& rValue)
+{
+ uno::Reference<uno::XComponentContext> xComponentContext(comphelper::getProcessComponentContext());
+ uno::Reference<rdf::XURI> xType = rdf::URI::create(xComponentContext, rType);
+ uno::Reference<rdf::XDocumentMetadataAccess> xDocumentMetadataAccess(xModel, uno::UNO_QUERY);
+ const uno::Sequence< uno::Reference<rdf::XURI> > aGraphNames = getGraphNames(xDocumentMetadataAccess, xType);
+ if (!aGraphNames.hasElements())
+ return;
+
+ uno::Reference<rdf::XNamedGraph> xGraph = xDocumentMetadataAccess->getRDFRepository()->getGraph(aGraphNames[0]);
+ uno::Reference<rdf::XURI> xKey = rdf::URI::create(xComponentContext, rKey);
+ uno::Reference<rdf::XLiteral> xValue = rdf::Literal::create(xComponentContext, rValue);
+ xGraph->removeStatements(xSubject, xKey, xValue);
+}
+
+void SwRDFHelper::clearStatements(const css::uno::Reference<css::frame::XModel>& xModel,
+ const OUString& rType,
+ const css::uno::Reference<css::rdf::XResource>& xSubject)
+{
+ uno::Reference<uno::XComponentContext> xComponentContext(comphelper::getProcessComponentContext());
+ uno::Reference<rdf::XURI> xType = rdf::URI::create(xComponentContext, rType);
+ uno::Reference<rdf::XDocumentMetadataAccess> xDocumentMetadataAccess(xModel, uno::UNO_QUERY);
+ const uno::Sequence< uno::Reference<rdf::XURI> > aGraphNames = getGraphNames(xDocumentMetadataAccess, xType);
+ if (!aGraphNames.hasElements())
+ return;
+
+ for (const uno::Reference<rdf::XURI>& xGraphName : aGraphNames)
+ {
+ uno::Reference<rdf::XNamedGraph> xGraph = xDocumentMetadataAccess->getRDFRepository()->getGraph(xGraphName);
+ uno::Reference<container::XEnumeration> xStatements = xGraph->getStatements(xSubject, uno::Reference<rdf::XURI>(), uno::Reference<rdf::XURI>());
+ while (xStatements->hasMoreElements())
+ {
+ rdf::Statement aStatement = xStatements->nextElement().get<rdf::Statement>();
+ uno::Reference<rdf::XURI> xKey = rdf::URI::create(xComponentContext, aStatement.Predicate->getStringValue());
+ uno::Reference<rdf::XLiteral> xValue = rdf::Literal::create(xComponentContext, aStatement.Object->getStringValue());
+ xGraph->removeStatements(xSubject, xKey, xValue);
+ }
+ }
+}
+
+void SwRDFHelper::cloneStatements(const css::uno::Reference<css::frame::XModel>& xSrcModel,
+ const css::uno::Reference<css::frame::XModel>& xDstModel,
+ const OUString& rType,
+ const css::uno::Reference<css::rdf::XResource>& xSrcSubject,
+ const css::uno::Reference<css::rdf::XResource>& xDstSubject)
+{
+ uno::Reference<uno::XComponentContext> xComponentContext(comphelper::getProcessComponentContext());
+ uno::Reference<rdf::XURI> xType = rdf::URI::create(xComponentContext, rType);
+ uno::Reference<rdf::XDocumentMetadataAccess> xDocumentMetadataAccess(xSrcModel, uno::UNO_QUERY);
+ const uno::Sequence< uno::Reference<rdf::XURI> > aGraphNames = getGraphNames(xDocumentMetadataAccess, xType);
+ if (!aGraphNames.hasElements())
+ return;
+
+ for (const uno::Reference<rdf::XURI>& xGraphName : aGraphNames)
+ {
+ uno::Reference<rdf::XNamedGraph> xGraph = xDocumentMetadataAccess->getRDFRepository()->getGraph(xGraphName);
+ uno::Reference<container::XEnumeration> xStatements = xGraph->getStatements(xSrcSubject, uno::Reference<rdf::XURI>(), uno::Reference<rdf::XURI>());
+ while (xStatements->hasMoreElements())
+ {
+ const rdf::Statement aStatement = xStatements->nextElement().get<rdf::Statement>();
+
+ const OUString sKey = aStatement.Predicate->getStringValue();
+ const OUString sValue = aStatement.Object->getStringValue();
+ addStatement(xDstModel, rType, xGraphName->getLocalName(), xDstSubject, sKey, sValue);
+ }
+ }
+}
+
+std::map<OUString, OUString> SwRDFHelper::getTextNodeStatements(const OUString& rType, SwTextNode& rTextNode)
+{
+ uno::Reference<rdf::XResource> xTextNode(SwXParagraph::CreateXParagraph(rTextNode.GetDoc(), &rTextNode, nullptr));
+ return getStatements(rTextNode.GetDoc().GetDocShell()->GetBaseModel(), rType, xTextNode);
+}
+
+void SwRDFHelper::addTextNodeStatement(const OUString& rType, const OUString& rPath, SwTextNode& rTextNode, const OUString& rKey, const OUString& rValue)
+{
+ uno::Reference<rdf::XResource> xSubject(SwXParagraph::CreateXParagraph(rTextNode.GetDoc(), &rTextNode, nullptr));
+ addStatement(rTextNode.GetDoc().GetDocShell()->GetBaseModel(), rType, rPath, xSubject, rKey, rValue);
+}
+
+void SwRDFHelper::removeTextNodeStatement(const OUString& rType, SwTextNode& rTextNode, const OUString& rKey, const OUString& rValue)
+{
+ uno::Reference<uno::XComponentContext> xComponentContext(comphelper::getProcessComponentContext());
+ uno::Reference<rdf::XURI> xType = rdf::URI::create(xComponentContext, rType);
+ uno::Reference<rdf::XDocumentMetadataAccess> xDocumentMetadataAccess(rTextNode.GetDoc().GetDocShell()->GetBaseModel(), uno::UNO_QUERY);
+ const uno::Sequence< uno::Reference<rdf::XURI> > aGraphNames = getGraphNames(xDocumentMetadataAccess, xType);
+ if (!aGraphNames.hasElements())
+ return;
+
+ uno::Reference<rdf::XURI> xGraphName = aGraphNames[0];
+ uno::Reference<rdf::XNamedGraph> xGraph = xDocumentMetadataAccess->getRDFRepository()->getGraph(xGraphName);
+ uno::Reference<rdf::XResource> xSubject(SwXParagraph::CreateXParagraph(rTextNode.GetDoc(), &rTextNode, nullptr));
+ uno::Reference<rdf::XURI> xKey = rdf::URI::create(xComponentContext, rKey);
+ uno::Reference<rdf::XLiteral> xValue = rdf::Literal::create(xComponentContext, rValue);
+ xGraph->removeStatements(xSubject, xKey, xValue);
+}
+
+void SwRDFHelper::updateTextNodeStatement(const OUString& rType, const OUString& rPath, SwTextNode& rTextNode, const OUString& rKey, const OUString& rOldValue, const OUString& rNewValue)
+{
+ uno::Reference<uno::XComponentContext> xComponentContext(comphelper::getProcessComponentContext());
+ uno::Reference<rdf::XURI> xType = rdf::URI::create(xComponentContext, rType);
+ uno::Reference<rdf::XDocumentMetadataAccess> xDocumentMetadataAccess(rTextNode.GetDoc().GetDocShell()->GetBaseModel(), uno::UNO_QUERY);
+ const uno::Sequence< uno::Reference<rdf::XURI> > aGraphNames = getGraphNames(xDocumentMetadataAccess, xType);
+ uno::Reference<rdf::XURI> xGraphName;
+ if (aGraphNames.hasElements())
+ {
+ xGraphName = aGraphNames[0];
+ }
+ else
+ {
+ uno::Sequence< uno::Reference<rdf::XURI> > xTypes = { xType };
+ xGraphName = xDocumentMetadataAccess->addMetadataFile(rPath, xTypes);
+ }
+
+ uno::Reference<rdf::XNamedGraph> xGraph = xDocumentMetadataAccess->getRDFRepository()->getGraph(xGraphName);
+ uno::Reference<rdf::XResource> xSubject(SwXParagraph::CreateXParagraph(rTextNode.GetDoc(), &rTextNode, nullptr));
+ uno::Reference<rdf::XURI> xKey = rdf::URI::create(xComponentContext, rKey);
+
+ if (aGraphNames.hasElements())
+ {
+ // Remove the old value.
+ uno::Reference<rdf::XLiteral> xOldValue = rdf::Literal::create(xComponentContext, rOldValue);
+ xGraph->removeStatements(xSubject, xKey, xOldValue);
+ }
+
+ // Now add it with new value.
+ uno::Reference<rdf::XLiteral> xNewValue = rdf::Literal::create(xComponentContext, rNewValue);
+ xGraph->addStatement(xSubject, xKey, xNewValue);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/sortopt.cxx b/sw/source/core/doc/sortopt.cxx
new file mode 100644
index 0000000000..b5fc62ba84
--- /dev/null
+++ b/sw/source/core/doc/sortopt.cxx
@@ -0,0 +1,61 @@
+/* -*- 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 <i18nlangtag/lang.h>
+#include <sortopt.hxx>
+
+SwSortKey::SwSortKey() :
+ eSortOrder( SwSortOrder::Ascending ),
+ nColumnId( 0 ),
+ bIsNumeric( true )
+{
+}
+
+SwSortKey::SwSortKey(sal_uInt16 nId, const OUString& rSrtType, SwSortOrder eOrder) :
+ sSortType( rSrtType ),
+ eSortOrder( eOrder ),
+ nColumnId( nId ),
+ bIsNumeric( rSrtType.isEmpty() )
+{
+}
+
+SwSortOptions::SwSortOptions()
+ : eDirection( SwSortDirection::Rows ),
+ cDeli( 9 ),
+ nLanguage( LANGUAGE_SYSTEM ),
+ bTable( false ),
+ bIgnoreCase( false )
+{
+}
+
+SwSortOptions::SwSortOptions(const SwSortOptions& rOpt) :
+ aKeys( rOpt.aKeys ),
+ eDirection( rOpt.eDirection ),
+ cDeli( rOpt.cDeli ),
+ nLanguage( rOpt.nLanguage ),
+ bTable( rOpt.bTable ),
+ bIgnoreCase( rOpt.bIgnoreCase )
+{
+}
+
+SwSortOptions::~SwSortOptions()
+{
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/swserv.cxx b/sw/source/core/doc/swserv.cxx
new file mode 100644
index 0000000000..b12ab6b6c1
--- /dev/null
+++ b/sw/source/core/doc/swserv.cxx
@@ -0,0 +1,320 @@
+/* -*- 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 <sot/exchange.hxx>
+#include <sfx2/linkmgr.hxx>
+#include <com/sun/star/uno/Sequence.h>
+#include <doc.hxx>
+#include <IDocumentLinksAdministration.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <swserv.hxx>
+#include <swbaslnk.hxx>
+#include <mvsave.hxx>
+#include <IMark.hxx>
+#include <bookmark.hxx>
+#include <pam.hxx>
+#include <shellio.hxx>
+
+using namespace ::com::sun::star;
+
+SwServerObject::~SwServerObject()
+{
+}
+
+bool SwServerObject::GetData( uno::Any & rData,
+ const OUString & rMimeType, bool )
+{
+ bool bRet = false;
+ WriterRef xWrt;
+ switch( SotExchange::GetFormatIdFromMimeType( rMimeType ) )
+ {
+ case SotClipboardFormatId::STRING:
+ ::GetASCWriter( std::u16string_view(), OUString(), xWrt );
+ break;
+
+ case SotClipboardFormatId::RTF:
+ case SotClipboardFormatId::RICHTEXT:
+ // mba: no BaseURL for data exchange
+ ::GetRTFWriter( std::u16string_view(), OUString(), xWrt );
+ break;
+ default: break;
+ }
+
+ if( xWrt.is() )
+ {
+ SwPaM* pPam = nullptr;
+ switch( m_eType )
+ {
+ case BOOKMARK_SERVER:
+ if( m_CNTNT_TYPE.pBkmk->IsExpanded() )
+ {
+ // Span area
+ pPam = new SwPaM( m_CNTNT_TYPE.pBkmk->GetMarkPos(),
+ m_CNTNT_TYPE.pBkmk->GetOtherMarkPos() );
+ }
+ break;
+
+ case TABLE_SERVER:
+ pPam = new SwPaM( *m_CNTNT_TYPE.pTableNd,
+ *m_CNTNT_TYPE.pTableNd->EndOfSectionNode() );
+ break;
+
+ case SECTION_SERVER:
+ pPam = new SwPaM( SwPosition( *m_CNTNT_TYPE.pSectNd ) );
+ pPam->Move( fnMoveForward );
+ pPam->SetMark();
+ pPam->GetPoint()->Assign( *m_CNTNT_TYPE.pSectNd->EndOfSectionNode() );
+ pPam->Move( fnMoveBackward );
+ break;
+ case NONE_SERVER: break;
+ }
+
+ if( pPam )
+ {
+ // Create stream
+ SvMemoryStream aMemStm( 65535, 65535 );
+ SwWriter aWrt( aMemStm, *pPam, false );
+ if( !aWrt.Write( xWrt ).IsError() )
+ {
+ aMemStm.WriteChar( '\0' ); // append a zero char
+ rData <<= uno::Sequence< sal_Int8 >(
+ static_cast<sal_Int8 const *>(aMemStm.GetData()),
+ aMemStm.Tell() );
+ bRet = true;
+ }
+ delete pPam;
+ }
+ }
+ return bRet;
+}
+
+void SwServerObject::SendDataChanged( const SwPosition& rPos )
+{
+ // Is someone interested in our changes?
+ if( !HasDataLinks() )
+ return;
+
+ bool bCall = false;
+ const SwStartNode* pNd = nullptr;
+ switch( m_eType )
+ {
+ case BOOKMARK_SERVER:
+ if( m_CNTNT_TYPE.pBkmk->IsExpanded() )
+ {
+ bCall = m_CNTNT_TYPE.pBkmk->GetMarkStart() <= rPos
+ && rPos < m_CNTNT_TYPE.pBkmk->GetMarkEnd();
+ }
+ break;
+
+ case TABLE_SERVER: pNd = m_CNTNT_TYPE.pTableNd; break;
+ case SECTION_SERVER: pNd = m_CNTNT_TYPE.pSectNd; break;
+ case NONE_SERVER: break;
+ }
+ if( pNd )
+ {
+ SwNodeOffset nNd = rPos.GetNodeIndex();
+ bCall = pNd->GetIndex() < nNd && nNd < pNd->EndOfSectionIndex();
+ }
+
+ if( bCall )
+ {
+ // Recognize recursions and flag them
+ IsLinkInServer( nullptr );
+ SvLinkSource::NotifyDataChanged();
+ }
+}
+
+void SwServerObject::SendDataChanged( const SwPaM& rRange )
+{
+ // Is someone interested in our changes?
+ if( !HasDataLinks() )
+ return;
+
+ bool bCall = false;
+ const SwStartNode* pNd = nullptr;
+ auto [pStt, pEnd] = rRange.StartEnd(); // SwPosition*
+ switch( m_eType )
+ {
+ case BOOKMARK_SERVER:
+ if(m_CNTNT_TYPE.pBkmk->IsExpanded())
+ {
+ bCall = *pStt <= m_CNTNT_TYPE.pBkmk->GetMarkEnd()
+ && *pEnd > m_CNTNT_TYPE.pBkmk->GetMarkStart();
+ }
+ break;
+
+ case TABLE_SERVER: pNd = m_CNTNT_TYPE.pTableNd; break;
+ case SECTION_SERVER: pNd = m_CNTNT_TYPE.pSectNd; break;
+ case NONE_SERVER: break;
+ }
+ if( pNd )
+ {
+ // Is the start area within the node area?
+ bCall = pStt->GetNodeIndex() < pNd->EndOfSectionIndex() &&
+ pEnd->GetNodeIndex() >= pNd->GetIndex();
+ }
+
+ if( bCall )
+ {
+ // Recognize recursions and flag them
+ IsLinkInServer( nullptr );
+ SvLinkSource::NotifyDataChanged();
+ }
+}
+
+bool SwServerObject::IsLinkInServer( const SwBaseLink* pChkLnk ) const
+{
+ SwNodeOffset nSttNd(0), nEndNd(0);
+ const SwNode* pNd = nullptr;
+ const SwNodes* pNds = nullptr;
+
+ switch( m_eType )
+ {
+ case BOOKMARK_SERVER:
+ if( m_CNTNT_TYPE.pBkmk->IsExpanded() )
+ {
+ const SwPosition* pStt = &m_CNTNT_TYPE.pBkmk->GetMarkStart(),
+ * pEnd = &m_CNTNT_TYPE.pBkmk->GetMarkEnd();
+
+ nSttNd = pStt->GetNodeIndex();
+ nEndNd = pEnd->GetNodeIndex();
+ pNds = &pStt->GetNodes();
+ }
+ break;
+
+ case TABLE_SERVER: pNd = m_CNTNT_TYPE.pTableNd; break;
+ case SECTION_SERVER: pNd = m_CNTNT_TYPE.pSectNd; break;
+
+ case SECTION_SERVER+1:
+ return true;
+ }
+
+ if( pNd )
+ {
+ nSttNd = pNd->GetIndex();
+ nEndNd = pNd->EndOfSectionIndex();
+ pNds = &pNd->GetNodes();
+ }
+
+ if( nSttNd && nEndNd )
+ {
+ // Get LinkManager
+ const ::sfx2::SvBaseLinks& rLnks = pNds->GetDoc().getIDocumentLinksAdministration().GetLinkManager().GetLinks();
+
+ // To avoid recursions: convert ServerType!
+ SwServerObject::ServerModes eSave = m_eType;
+ if( !pChkLnk )
+ const_cast<SwServerObject*>(this)->m_eType = NONE_SERVER;
+ for( size_t n = rLnks.size(); n; )
+ {
+ const ::sfx2::SvBaseLink* pLnk = &(*rLnks[ --n ]);
+ if (sfx2::SvBaseLinkObjectType::ClientGraphic != pLnk->GetObjType() &&
+ dynamic_cast<const SwBaseLink*>( pLnk) != nullptr &&
+ !static_cast<const SwBaseLink*>(pLnk)->IsNoDataFlag() &&
+ static_cast<const SwBaseLink*>(pLnk)->IsInRange( nSttNd, nEndNd ))
+ {
+ if( pChkLnk )
+ {
+ if( pLnk == pChkLnk ||
+ static_cast<const SwBaseLink*>(pLnk)->IsRecursion( pChkLnk ) )
+ return true;
+ }
+ else if( static_cast<const SwBaseLink*>(pLnk)->IsRecursion( static_cast<const SwBaseLink*>(pLnk) ) )
+ const_cast<SwBaseLink*>(static_cast<const SwBaseLink*>(pLnk))->SetNoDataFlag();
+ }
+ }
+ if( !pChkLnk )
+ const_cast<SwServerObject*>(this)->m_eType = eSave;
+ }
+
+ return false;
+}
+
+void SwServerObject::SetNoServer()
+{
+ if(m_eType == BOOKMARK_SERVER && m_CNTNT_TYPE.pBkmk)
+ {
+ ::sw::mark::DdeBookmark* const pDdeBookmark = dynamic_cast< ::sw::mark::DdeBookmark* >(m_CNTNT_TYPE.pBkmk);
+ if(pDdeBookmark)
+ {
+ m_CNTNT_TYPE.pBkmk = nullptr;
+ m_eType = NONE_SERVER;
+ pDdeBookmark->SetRefObject(nullptr);
+ }
+ }
+}
+
+void SwServerObject::SetDdeBookmark( ::sw::mark::IMark& rBookmark)
+{
+ ::sw::mark::DdeBookmark* const pDdeBookmark = dynamic_cast< ::sw::mark::DdeBookmark* >(&rBookmark);
+ if(pDdeBookmark)
+ {
+ m_eType = BOOKMARK_SERVER;
+ m_CNTNT_TYPE.pBkmk = &rBookmark;
+ pDdeBookmark->SetRefObject(this);
+ }
+ else
+ OSL_FAIL("SwServerObject::SetNoServer(..)"
+ " - setting a bookmark that is not DDE-capable");
+}
+
+SwDataChanged::SwDataChanged( const SwPaM& rPam )
+ : m_pPam( &rPam ), m_pPos( nullptr ), m_rDoc( rPam.GetDoc() )
+{
+ m_nContent = rPam.GetPoint()->GetContentIndex();
+}
+
+SwDataChanged::SwDataChanged( SwDoc& rDc, const SwPosition& rPos )
+ : m_pPam( nullptr ), m_pPos( &rPos ), m_rDoc( rDc )
+{
+ m_nContent = rPos.GetContentIndex();
+}
+
+SwDataChanged::~SwDataChanged()
+{
+ // JP 09.04.96: Only if the Layout is available (thus during input)
+ if( !m_rDoc.getIDocumentLayoutAccess().GetCurrentViewShell() )
+ return;
+
+ const ::sfx2::SvLinkSources& rServers = m_rDoc.getIDocumentLinksAdministration().GetLinkManager().GetServers();
+
+ ::sfx2::SvLinkSources aTemp(rServers);
+ for( const auto& rpLinkSrc : aTemp )
+ {
+ ::sfx2::SvLinkSourceRef refObj( rpLinkSrc );
+ // Anyone else interested in the Object?
+ if( refObj->HasDataLinks())
+ if (auto pServerObj = dynamic_cast<SwServerObject*>( refObj.get() ))
+ {
+ if( m_pPos )
+ pServerObj->SendDataChanged( *m_pPos );
+ else
+ pServerObj->SendDataChanged( *m_pPam );
+ }
+
+ // We shouldn't have a connection anymore
+ if( !refObj->HasDataLinks() )
+ {
+ // Then remove from the list
+ m_rDoc.getIDocumentLinksAdministration().GetLinkManager().RemoveServer( rpLinkSrc );
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/swstylemanager.cxx b/sw/source/core/doc/swstylemanager.cxx
new file mode 100644
index 0000000000..6372ab10c8
--- /dev/null
+++ b/sw/source/core/doc/swstylemanager.cxx
@@ -0,0 +1,176 @@
+/* -*- 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 "swstylemanager.hxx"
+#include <svl/stylepool.hxx>
+#include <istyleaccess.hxx>
+#include <swatrset.hxx>
+#include <unordered_map>
+#include <osl/diagnose.h>
+
+typedef std::unordered_map< OUString,
+ std::shared_ptr<SfxItemSet> > SwStyleNameCache;
+
+namespace {
+
+class SwStyleCache
+{
+ SwStyleNameCache mMap;
+public:
+ SwStyleCache() {}
+ void addStyleName( const std::shared_ptr<SfxItemSet>& pStyle )
+ { mMap[ StylePool::nameOf(pStyle) ] = pStyle; }
+ void addCompletePool( StylePool& rPool );
+ std::shared_ptr<SfxItemSet> getByName( const OUString& rName ) { return mMap[rName]; }
+ void clear() { mMap.clear(); }
+};
+
+}
+
+void SwStyleCache::addCompletePool( StylePool& rPool )
+{
+ std::unique_ptr<IStylePoolIteratorAccess> pIter = rPool.createIterator();
+ std::shared_ptr<SfxItemSet> pStyle = pIter->getNext();
+ while( pStyle )
+ {
+ OUString aName( StylePool::nameOf(pStyle) );
+ mMap[ aName ] = pStyle;
+ pStyle = pIter->getNext();
+ }
+}
+
+namespace {
+
+class SwStyleManager : public IStyleAccess
+{
+ StylePool m_aAutoCharPool;
+ StylePool m_aAutoParaPool;
+ SwStyleCache maCharCache;
+ SwStyleCache maParaCache;
+
+public:
+ // accept empty item set for ignorable paragraph items.
+ explicit SwStyleManager(SfxItemSet const* pIgnorableParagraphItems)
+ : m_aAutoParaPool(pIgnorableParagraphItems)
+ {}
+ virtual std::shared_ptr<SfxItemSet> getAutomaticStyle( const SfxItemSet& rSet,
+ IStyleAccess::SwAutoStyleFamily eFamily,
+ const OUString* pParentName = nullptr ) override;
+ virtual std::shared_ptr<SwAttrSet> getAutomaticStyle( const SwAttrSet& rSet,
+ IStyleAccess::SwAutoStyleFamily eFamily,
+ const OUString* pParentName = nullptr ) override;
+ virtual std::shared_ptr<SfxItemSet> getByName( const OUString& rName,
+ IStyleAccess::SwAutoStyleFamily eFamily ) override;
+ virtual void getAllStyles( std::vector<std::shared_ptr<SfxItemSet>> &rStyles,
+ IStyleAccess::SwAutoStyleFamily eFamily ) override;
+ virtual std::shared_ptr<SfxItemSet> cacheAutomaticStyle( const SfxItemSet& rSet,
+ SwAutoStyleFamily eFamily ) override;
+ virtual void clearCaches() override;
+};
+
+}
+
+std::unique_ptr<IStyleAccess> createStyleManager( SfxItemSet const * pIgnorableParagraphItems )
+{
+ return std::make_unique<SwStyleManager>( pIgnorableParagraphItems );
+}
+
+void SwStyleManager::clearCaches()
+{
+ maCharCache.clear();
+ maParaCache.clear();
+}
+
+std::shared_ptr<SfxItemSet> SwStyleManager::getAutomaticStyle( const SfxItemSet& rSet,
+ IStyleAccess::SwAutoStyleFamily eFamily,
+ const OUString* pParentName )
+{
+ assert(eFamily != IStyleAccess::AUTO_STYLE_PARA || dynamic_cast<const SwAttrSet*>(&rSet));
+ StylePool& rAutoPool
+ = eFamily == IStyleAccess::AUTO_STYLE_CHAR ? m_aAutoCharPool : m_aAutoParaPool;
+ return rAutoPool.insertItemSet( rSet, pParentName );
+}
+
+std::shared_ptr<SwAttrSet> SwStyleManager::getAutomaticStyle( const SwAttrSet& rSet,
+ IStyleAccess::SwAutoStyleFamily eFamily,
+ const OUString* pParentName )
+{
+ StylePool& rAutoPool
+ = eFamily == IStyleAccess::AUTO_STYLE_CHAR ? m_aAutoCharPool : m_aAutoParaPool;
+ std::shared_ptr<SfxItemSet> pItemSet = rAutoPool.insertItemSet( rSet, pParentName );
+ std::shared_ptr<SwAttrSet> pAttrSet = std::dynamic_pointer_cast<SwAttrSet>(pItemSet);
+ assert(bool(pItemSet) == bool(pAttrSet) && "types do not match");
+ return pAttrSet;
+}
+
+std::shared_ptr<SfxItemSet> SwStyleManager::cacheAutomaticStyle( const SfxItemSet& rSet,
+ IStyleAccess::SwAutoStyleFamily eFamily )
+{
+ assert(eFamily != IStyleAccess::AUTO_STYLE_PARA || dynamic_cast<const SwAttrSet*>(&rSet));
+ StylePool& rAutoPool
+ = eFamily == IStyleAccess::AUTO_STYLE_CHAR ? m_aAutoCharPool : m_aAutoParaPool;
+ std::shared_ptr<SfxItemSet> pStyle = rAutoPool.insertItemSet( rSet );
+ if (eFamily == IStyleAccess::AUTO_STYLE_CHAR)
+ {
+ maCharCache.addStyleName( pStyle );
+ }
+ else
+ {
+ maParaCache.addStyleName( pStyle );
+ }
+ return pStyle;
+}
+
+std::shared_ptr<SfxItemSet> SwStyleManager::getByName( const OUString& rName,
+ IStyleAccess::SwAutoStyleFamily eFamily )
+{
+ StylePool& rAutoPool
+ = eFamily == IStyleAccess::AUTO_STYLE_CHAR ? m_aAutoCharPool : m_aAutoParaPool;
+ SwStyleCache &rCache = eFamily == IStyleAccess::AUTO_STYLE_CHAR ? maCharCache : maParaCache;
+ std::shared_ptr<SfxItemSet> pStyle = rCache.getByName( rName );
+ if( !pStyle )
+ {
+ // Ok, ok, it's allowed to ask for uncached styles (from UNO) but it should not be done
+ // during loading a document
+ OSL_FAIL( "Don't ask for uncached styles" );
+ rCache.addCompletePool( rAutoPool );
+ pStyle = rCache.getByName( rName );
+ }
+ assert(!pStyle || eFamily != IStyleAccess::AUTO_STYLE_PARA || dynamic_cast<SwAttrSet*>(pStyle.get()));
+ return pStyle;
+}
+
+void SwStyleManager::getAllStyles( std::vector<std::shared_ptr<SfxItemSet>> &rStyles,
+ IStyleAccess::SwAutoStyleFamily eFamily )
+{
+ StylePool& rAutoPool
+ = eFamily == IStyleAccess::AUTO_STYLE_CHAR ? m_aAutoCharPool : m_aAutoParaPool;
+ // setup <StylePool> iterator, which skips unused styles and ignorable items
+ std::unique_ptr<IStylePoolIteratorAccess> pIter = rAutoPool.createIterator( true, true );
+ std::shared_ptr<SfxItemSet> pStyle = pIter->getNext();
+ while( pStyle )
+ {
+ assert(eFamily != IStyleAccess::AUTO_STYLE_PARA || dynamic_cast<SwAttrSet*>(pStyle.get()));
+ rStyles.push_back( pStyle );
+
+ pStyle = pIter->getNext();
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/swstylemanager.hxx b/sw/source/core/doc/swstylemanager.hxx
new file mode 100644
index 0000000000..4b27b02746
--- /dev/null
+++ b/sw/source/core/doc/swstylemanager.hxx
@@ -0,0 +1,32 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_DOC_SWSTYLEMANAGER_HXX
+#define INCLUDED_SW_SOURCE_CORE_DOC_SWSTYLEMANAGER_HXX
+
+#include <memory>
+
+class IStyleAccess;
+class SfxItemSet;
+
+std::unique_ptr<IStyleAccess> createStyleManager(SfxItemSet const* pIgnorableParagraphItems);
+
+#endif // INCLUDED_SW_SOURCE_CORE_DOC_SWSTYLEMANAGER_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/tblafmt.cxx b/sw/source/core/doc/tblafmt.cxx
new file mode 100644
index 0000000000..65bff647eb
--- /dev/null
+++ b/sw/source/core/doc/tblafmt.cxx
@@ -0,0 +1,1256 @@
+/* -*- 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/fileformat.h>
+#include <tools/stream.hxx>
+#include <sfx2/docfile.hxx>
+#include <svl/numformat.hxx>
+#include <svl/zforlist.hxx>
+#include <svl/zformat.hxx>
+#include <unotools/configmgr.hxx>
+#include <unotools/pathoptions.hxx>
+#include <swtable.hxx>
+#include <swtblfmt.hxx>
+#include <com/sun/star/text/VertOrientation.hpp>
+#include <swtypes.hxx>
+#include <doc.hxx>
+#include <poolfmt.hxx>
+#include <tblafmt.hxx>
+#include <cellatr.hxx>
+#include <SwStyleNameMapper.hxx>
+#include <hintids.hxx>
+#include <fmtornt.hxx>
+#include <editsh.hxx>
+#include <fmtlsplt.hxx>
+#include <fmtrowsplt.hxx>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+#include <osl/thread.h>
+
+#include <editeng/adjustitem.hxx>
+#include <editeng/boxitem.hxx>
+#include <editeng/brushitem.hxx>
+#include <editeng/colritem.hxx>
+#include <editeng/contouritem.hxx>
+#include <editeng/crossedoutitem.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/formatbreakitem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/justifyitem.hxx>
+#include <editeng/legacyitem.hxx>
+#include <editeng/lineitem.hxx>
+#include <editeng/postitem.hxx>
+#include <editeng/shdditem.hxx>
+#include <editeng/udlnitem.hxx>
+#include <editeng/wghtitem.hxx>
+#include <svx/algitem.hxx>
+#include <svx/rotmodit.hxx>
+#include <legacyitem.hxx>
+#include <unostyle.hxx>
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+/*
+ * XXX: BIG RED NOTICE! Changes MUST be binary file format compatible and MUST
+ * be synchronized with Calc's ScAutoFormat sc/source/core/tool/autoform.cxx
+ */
+
+using ::editeng::SvxBorderLine;
+
+// until SO5PF
+const sal_uInt16 AUTOFORMAT_ID_X = 9501;
+const sal_uInt16 AUTOFORMAT_ID_358 = 9601;
+const sal_uInt16 AUTOFORMAT_DATA_ID_X = 9502;
+
+// from SO5
+//! In follow-up versions these IDs' values need to increase
+const sal_uInt16 AUTOFORMAT_ID_504 = 9801;
+const sal_uInt16 AUTOFORMAT_DATA_ID_504 = 9802;
+
+const sal_uInt16 AUTOFORMAT_DATA_ID_552 = 9902;
+
+// --- from 680/dr25 on: store strings as UTF-8
+const sal_uInt16 AUTOFORMAT_ID_680DR25 = 10021;
+
+// --- Bug fix to fdo#31005: Table Autoformats does not save/apply all properties (Writer and Calc)
+const sal_uInt16 AUTOFORMAT_ID_31005 = 10041;
+const sal_uInt16 AUTOFORMAT_DATA_ID_31005 = 10042;
+
+// current version
+const sal_uInt16 AUTOFORMAT_ID = AUTOFORMAT_ID_31005;
+const sal_uInt16 AUTOFORMAT_DATA_ID = AUTOFORMAT_DATA_ID_31005;
+const sal_uInt16 AUTOFORMAT_FILE_VERSION= SOFFICE_FILEFORMAT_50;
+
+SwBoxAutoFormat* SwTableAutoFormat::s_pDefaultBoxAutoFormat = nullptr;
+
+constexpr OUString AUTOTABLE_FORMAT_NAME = u"autotbl.fmt"_ustr;
+
+namespace
+{
+ /// Begins a writer-specific data block. Call before serializing any writer-specific properties.
+ sal_uInt64 BeginSwBlock(SvStream& rStream)
+ {
+ // We need to write down the offset of the end of the writer-specific data, so that
+ // calc can skip it. We'll only have that value after writing the data, so we
+ // write a placeholder value first, write the data, then jump back and write the
+ // real offset.
+
+ // Note that we explicitly use sal_uInt64 instead of sal_Size (which can be 32
+ // or 64 depending on platform) to ensure 64-bit portability on this front. I don't
+ // actually know if autotbl.fmt as a whole is portable, since that requires all serialization
+ // logic to be written with portability in mind.
+ sal_uInt64 whereToWriteEndOfSwBlock = rStream.Tell();
+
+ rStream.WriteUInt64( 0 ); // endOfSwBlock
+
+ return whereToWriteEndOfSwBlock;
+ }
+
+ /// Ends a writer-specific data block. Call after serializing writer-specific properties.
+ /// Closes a corresponding BeginSwBlock call.
+ void EndSwBlock(SvStream& rStream, sal_uInt64 whereToWriteEndOfSwBlock)
+ {
+ sal_uInt64 endOfSwBlock = rStream.Tell();
+ rStream.Seek(whereToWriteEndOfSwBlock);
+ rStream.WriteUInt64( endOfSwBlock );
+ rStream.Seek(endOfSwBlock);
+ }
+
+ /**
+ Helper class for writer-specific blocks. Begins a writer-specific block on construction,
+ and closes it on destruction.
+
+ See also: BeginSwBlock and EndSwBlock.
+ */
+ class WriterSpecificAutoFormatBlock
+ {
+ public:
+ explicit WriterSpecificAutoFormatBlock(SvStream& rStream)
+ : mrStream(rStream)
+ , mnWhereToWriteEndOfBlock(BeginSwBlock(rStream))
+ {
+ }
+
+ ~WriterSpecificAutoFormatBlock() { EndSwBlock(mrStream, mnWhereToWriteEndOfBlock); }
+
+ private:
+ WriterSpecificAutoFormatBlock(WriterSpecificAutoFormatBlock const&) = delete;
+ WriterSpecificAutoFormatBlock& operator=(WriterSpecificAutoFormatBlock const&) = delete;
+
+ SvStream& mrStream;
+ sal_uInt64 mnWhereToWriteEndOfBlock;
+ };
+
+ /// Checks whether a writer-specific block exists (i.e. size is not zero)
+ sal_Int64 WriterSpecificBlockExists(SvStream &stream)
+ {
+ sal_uInt64 endOfSwBlock = 0;
+ stream.ReadUInt64( endOfSwBlock );
+
+ // end-of-block pointing to itself indicates a zero-size block.
+ return endOfSwBlock - stream.Tell();
+ }
+}
+
+// Struct with version numbers of the Items
+
+struct SwAfVersions : public AutoFormatVersions
+{
+public:
+ sal_uInt16 m_nTextOrientationVersion;
+ sal_uInt16 m_nVerticalAlignmentVersion;
+
+ SwAfVersions();
+ void Load( SvStream& rStream, sal_uInt16 nVer );
+ static void Write(SvStream& rStream, sal_uInt16 fileVersion);
+};
+
+SwAfVersions::SwAfVersions()
+: m_nTextOrientationVersion(0),
+ m_nVerticalAlignmentVersion(0)
+{
+}
+
+void SwAfVersions::Load( SvStream& rStream, sal_uInt16 nVer )
+{
+ LoadBlockA(rStream, nVer);
+ if (nVer >= AUTOFORMAT_ID_31005 && WriterSpecificBlockExists(rStream))
+ {
+ rStream.ReadUInt16( m_nTextOrientationVersion );
+ rStream.ReadUInt16( m_nVerticalAlignmentVersion );
+ }
+ LoadBlockB(rStream, nVer);
+}
+
+void SwAfVersions::Write(SvStream& rStream, sal_uInt16 fileVersion)
+{
+ AutoFormatVersions::WriteBlockA(rStream, fileVersion);
+
+ if (fileVersion >= SOFFICE_FILEFORMAT_50)
+ {
+ WriterSpecificAutoFormatBlock block(rStream);
+
+ rStream.WriteUInt16(legacy::SvxFrameDirection::GetVersion(fileVersion));
+ rStream.WriteUInt16(legacy::SwFormatVert::GetVersion(fileVersion));
+ }
+
+ AutoFormatVersions::WriteBlockB(rStream, fileVersion);
+}
+
+
+
+SwBoxAutoFormat::SwBoxAutoFormat()
+: m_aTextOrientation(std::make_unique<SvxFrameDirectionItem>(SvxFrameDirection::Environment, RES_FRAMEDIR)),
+ m_aVerticalAlignment(std::make_unique<SwFormatVertOrient>(0, css::text::VertOrientation::NONE, css::text::RelOrientation::FRAME)),
+ m_eSysLanguage(::GetAppLanguage()),
+ m_eNumFormatLanguage(::GetAppLanguage())
+{
+ // need to set default instances for base class AutoFormatBase here
+ // due to resource defines (e.g. RES_CHRATR_FONT) which are not available
+ // in svx and different in the different usages of derivations
+ m_aFont = std::make_unique<SvxFontItem>(*GetDfltAttr( RES_CHRATR_FONT ) );
+ m_aHeight = std::make_unique<SvxFontHeightItem>(240, 100, RES_CHRATR_FONTSIZE );
+ m_aWeight = std::make_unique<SvxWeightItem>(WEIGHT_NORMAL, RES_CHRATR_WEIGHT );
+ m_aPosture = std::make_unique<SvxPostureItem>(ITALIC_NONE, RES_CHRATR_POSTURE );
+ m_aCJKFont = std::make_unique<SvxFontItem>(*GetDfltAttr( RES_CHRATR_CJK_FONT ) );
+ m_aCJKHeight = std::make_unique<SvxFontHeightItem>(240, 100, RES_CHRATR_CJK_FONTSIZE );
+ m_aCJKWeight = std::make_unique<SvxWeightItem>(WEIGHT_NORMAL, RES_CHRATR_CJK_WEIGHT );
+ m_aCJKPosture = std::make_unique<SvxPostureItem>(ITALIC_NONE, RES_CHRATR_CJK_POSTURE );
+ m_aCTLFont = std::make_unique<SvxFontItem>(*GetDfltAttr( RES_CHRATR_CTL_FONT ) );
+ m_aCTLHeight = std::make_unique<SvxFontHeightItem>(240, 100, RES_CHRATR_CTL_FONTSIZE );
+ m_aCTLWeight = std::make_unique<SvxWeightItem>(WEIGHT_NORMAL, RES_CHRATR_CTL_WEIGHT );
+ m_aCTLPosture = std::make_unique<SvxPostureItem>(ITALIC_NONE, RES_CHRATR_CTL_POSTURE );
+ m_aUnderline = std::make_unique<SvxUnderlineItem>(LINESTYLE_NONE, RES_CHRATR_UNDERLINE );
+ m_aOverline = std::make_unique<SvxOverlineItem>(LINESTYLE_NONE, RES_CHRATR_OVERLINE );
+ m_aCrossedOut = std::make_unique<SvxCrossedOutItem>(STRIKEOUT_NONE, RES_CHRATR_CROSSEDOUT );
+ m_aContour = std::make_unique<SvxContourItem>(false, RES_CHRATR_CONTOUR );
+ m_aShadowed = std::make_unique<SvxShadowedItem>(false, RES_CHRATR_SHADOWED );
+ m_aColor = std::make_unique<SvxColorItem>(RES_CHRATR_COLOR );
+ m_aBox = std::make_unique<SvxBoxItem>(RES_BOX );
+ m_aTLBR = std::make_unique<SvxLineItem>(0 );
+ m_aBLTR = std::make_unique<SvxLineItem>(0 );
+ m_aBackground = std::make_unique<SvxBrushItem>(RES_BACKGROUND );
+ m_aAdjust = std::make_unique<SvxAdjustItem>(SvxAdjust::Left, RES_PARATR_ADJUST );
+ m_aHorJustify = std::make_unique<SvxHorJustifyItem>(SvxCellHorJustify::Standard, 0);
+ m_aVerJustify = std::make_unique<SvxVerJustifyItem>(SvxCellVerJustify::Standard, 0);
+ m_aStacked = std::make_unique<SfxBoolItem>(0 );
+ m_aMargin = std::make_unique<SvxMarginItem>( TypedWhichId<SvxMarginItem>(0) );
+ m_aLinebreak = std::make_unique<SfxBoolItem>(0 );
+ m_aRotateAngle = std::make_unique<SfxInt32Item>(0 );
+ m_aRotateMode = std::make_unique<SvxRotateModeItem>(SVX_ROTATE_MODE_STANDARD, TypedWhichId<SvxRotateModeItem>(0) );
+
+// FIXME - add attribute IDs for the diagonal line items
+// aTLBR( RES_... ),
+// aBLTR( RES_... ),
+ m_aBox->SetAllDistances(55);
+}
+
+SwBoxAutoFormat::SwBoxAutoFormat( const SwBoxAutoFormat& rNew )
+: AutoFormatBase(rNew),
+ m_aTextOrientation(rNew.m_aTextOrientation->Clone()),
+ m_aVerticalAlignment(rNew.m_aVerticalAlignment->Clone()),
+ m_sNumFormatString( rNew.m_sNumFormatString ),
+ m_eSysLanguage( rNew.m_eSysLanguage ),
+ m_eNumFormatLanguage( rNew.m_eNumFormatLanguage )
+{
+}
+
+SwBoxAutoFormat::~SwBoxAutoFormat()
+{
+}
+
+SwBoxAutoFormat& SwBoxAutoFormat::operator=(const SwBoxAutoFormat& rRef)
+{
+ // check self-assignment
+ if(this == &rRef)
+ {
+ return *this;
+ }
+
+ // call baseclass implementation
+ AutoFormatBase::operator=(rRef);
+
+ // copy local members - this will use ::Clone() on all involved Items
+ SetTextOrientation(rRef.GetTextOrientation());
+ SetVerticalAlignment(rRef.GetVerticalAlignment());
+ SetNumFormatString(rRef.GetNumFormatString());
+ SetSysLanguage(rRef.GetSysLanguage());
+ SetNumFormatLanguage(rRef.GetNumFormatLanguage());
+
+ // m_wXObject used to not be copied before 1e2682235cded9a7cd90e55f0bfc60a1285e9a46
+ // "WIP: Further preparations for deeper Item changes" by this operator, so do not do it now, too
+ // rRef.SetXObject(GetXObject());
+
+ return *this;
+}
+
+bool SwBoxAutoFormat::operator==(const SwBoxAutoFormat& rRight) const
+{
+ return GetBackground().GetColor() == rRight.GetBackground().GetColor();
+}
+
+bool SwBoxAutoFormat::Load( SvStream& rStream, const SwAfVersions& rVersions, sal_uInt16 nVer )
+{
+ LoadBlockA( rStream, rVersions, nVer );
+
+ if (nVer >= AUTOFORMAT_DATA_ID_31005)
+ {
+ sal_Int64 const nSize(WriterSpecificBlockExists(rStream));
+ if (0 < nSize && nSize < std::numeric_limits<sal_uInt16>::max())
+ {
+ legacy::SvxFrameDirection::Create(*m_aTextOrientation, rStream, rVersions.m_nTextOrientationVersion);
+ // HORRIBLE HACK to read both 32-bit and 64-bit "long": abuse nSize
+ legacy::SwFormatVert::Create(*m_aVerticalAlignment, rStream, /*rVersions.m_nVerticalAlignmentVersion*/ nSize);
+ }
+ }
+
+ LoadBlockB( rStream, rVersions, nVer );
+
+ if( 0 == rVersions.nNumFormatVersion )
+ {
+ sal_uInt16 eSys, eLge;
+ // --- from 680/dr25 on: store strings as UTF-8
+ rtl_TextEncoding eCharSet = (nVer >= AUTOFORMAT_ID_680DR25) ? RTL_TEXTENCODING_UTF8 : rStream.GetStreamCharSet();
+ m_sNumFormatString = rStream.ReadUniOrByteString( eCharSet );
+ rStream.ReadUInt16( eSys ).ReadUInt16( eLge );
+ m_eSysLanguage = LanguageType(eSys);
+ m_eNumFormatLanguage = LanguageType(eLge);
+ if ( m_eSysLanguage == LANGUAGE_SYSTEM ) // from old versions (Calc)
+ m_eSysLanguage = ::GetAppLanguage();
+ }
+
+ return ERRCODE_NONE == rStream.GetError();
+}
+
+bool SwBoxAutoFormat::Save( SvStream& rStream, sal_uInt16 fileVersion ) const
+{
+ SaveBlockA( rStream, fileVersion );
+
+ if (fileVersion >= SOFFICE_FILEFORMAT_50)
+ {
+ WriterSpecificAutoFormatBlock block(rStream);
+
+ legacy::SvxFrameDirection::Store(*m_aTextOrientation, rStream, legacy::SvxFrameDirection::GetVersion(fileVersion));
+ legacy::SwFormatVert::Store(*m_aVerticalAlignment, rStream, legacy::SwFormatVert::GetVersion(fileVersion));
+ }
+
+ SaveBlockB( rStream, fileVersion );
+
+ // --- from 680/dr25 on: store strings as UTF-8
+ write_uInt16_lenPrefixed_uInt8s_FromOUString(rStream, m_sNumFormatString,
+ RTL_TEXTENCODING_UTF8);
+ rStream.WriteUInt16( static_cast<sal_uInt16>(m_eSysLanguage) ).WriteUInt16( static_cast<sal_uInt16>(m_eNumFormatLanguage) );
+
+ return ERRCODE_NONE == rStream.GetError();
+}
+
+void SwBoxAutoFormat::SetXObject(rtl::Reference<SwXTextCellStyle> const& xObject)
+{
+ m_xAutoFormatUnoObject = xObject.get();
+}
+
+SwTableAutoFormat::SwTableAutoFormat( OUString aName )
+ : m_aName( std::move(aName) )
+ , m_nStrResId( USHRT_MAX )
+ , m_aKeepWithNextPara(std::make_shared<SvxFormatKeepItem>(false, RES_KEEP))
+ , m_aRepeatHeading( 0 )
+ , m_bLayoutSplit( true )
+ , m_bRowSplit( true )
+ , m_bCollapsingBorders(true)
+ , m_aShadow(std::make_shared<SvxShadowItem>(RES_SHADOW))
+ , m_bHidden( false )
+ , m_bUserDefined( true )
+{
+ m_bInclFont = true;
+ m_bInclJustify = true;
+ m_bInclFrame = true;
+ m_bInclBackground = true;
+ m_bInclValueFormat = true;
+ m_bInclWidthHeight = true;
+}
+
+SwTableAutoFormat::SwTableAutoFormat( const SwTableAutoFormat& rNew )
+ : m_aShadow(std::make_shared<SvxShadowItem>(RES_SHADOW))
+{
+ for(SwBoxAutoFormat* & rp : m_aBoxAutoFormat)
+ rp = nullptr;
+ *this = rNew;
+}
+
+SwTableAutoFormat& SwTableAutoFormat::operator=( const SwTableAutoFormat& rNew )
+{
+ if (&rNew == this)
+ return *this;
+
+ for( sal_uInt8 n = 0; n < 16; ++n )
+ {
+ if( m_aBoxAutoFormat[ n ] )
+ delete m_aBoxAutoFormat[ n ];
+
+ SwBoxAutoFormat* pFormat = rNew.m_aBoxAutoFormat[ n ];
+ if( pFormat ) // if is set -> copy
+ m_aBoxAutoFormat[ n ] = new SwBoxAutoFormat( *pFormat );
+ else // else default
+ m_aBoxAutoFormat[ n ] = nullptr;
+ }
+
+ m_aName = rNew.m_aName;
+ m_nStrResId = rNew.m_nStrResId;
+ m_bInclFont = rNew.m_bInclFont;
+ m_bInclJustify = rNew.m_bInclJustify;
+ m_bInclFrame = rNew.m_bInclFrame;
+ m_bInclBackground = rNew.m_bInclBackground;
+ m_bInclValueFormat = rNew.m_bInclValueFormat;
+ m_bInclWidthHeight = rNew.m_bInclWidthHeight;
+
+ m_aKeepWithNextPara.reset(rNew.m_aKeepWithNextPara->Clone());
+ m_aRepeatHeading = rNew.m_aRepeatHeading;
+ m_bLayoutSplit = rNew.m_bLayoutSplit;
+ m_bRowSplit = rNew.m_bRowSplit;
+ m_bCollapsingBorders = rNew.m_bCollapsingBorders;
+ m_aShadow.reset(rNew.m_aShadow->Clone());
+ m_bHidden = rNew.m_bHidden;
+ m_bUserDefined = rNew.m_bUserDefined;
+
+ return *this;
+}
+
+SwTableAutoFormat::~SwTableAutoFormat()
+{
+ SwBoxAutoFormat** ppFormat = m_aBoxAutoFormat;
+ for( sal_uInt8 n = 0; n < 16; ++n, ++ppFormat )
+ if( *ppFormat )
+ delete *ppFormat;
+}
+
+void SwTableAutoFormat::SetBoxFormat( const SwBoxAutoFormat& rNew, sal_uInt8 nPos )
+{
+ OSL_ENSURE( nPos < 16, "wrong area" );
+
+ SwBoxAutoFormat* pFormat = m_aBoxAutoFormat[ nPos ];
+ if( pFormat ) // if is set -> copy
+ *m_aBoxAutoFormat[ nPos ] = rNew;
+ else // else set anew
+ m_aBoxAutoFormat[ nPos ] = new SwBoxAutoFormat( rNew );
+}
+
+const SwBoxAutoFormat& SwTableAutoFormat::GetBoxFormat( sal_uInt8 nPos ) const
+{
+ OSL_ENSURE( nPos < 16, "wrong area" );
+
+ SwBoxAutoFormat* pFormat = m_aBoxAutoFormat[ nPos ];
+ if( pFormat ) // if is set -> copy
+ return *pFormat;
+ else // else return the default
+ {
+ // If it doesn't exist yet:
+ if( !s_pDefaultBoxAutoFormat )
+ s_pDefaultBoxAutoFormat = new SwBoxAutoFormat;
+ return *s_pDefaultBoxAutoFormat;
+ }
+}
+
+SwBoxAutoFormat& SwTableAutoFormat::GetBoxFormat( sal_uInt8 nPos )
+{
+ SAL_WARN_IF(!(nPos < 16), "sw.core", "GetBoxFormat wrong area");
+
+ SwBoxAutoFormat** pFormat = &m_aBoxAutoFormat[ nPos ];
+ if( !*pFormat )
+ {
+ // If default doesn't exist yet:
+ if( !s_pDefaultBoxAutoFormat )
+ s_pDefaultBoxAutoFormat = new SwBoxAutoFormat();
+ *pFormat = new SwBoxAutoFormat(*s_pDefaultBoxAutoFormat);
+ }
+ return **pFormat;
+}
+
+const SwBoxAutoFormat& SwTableAutoFormat::GetDefaultBoxFormat()
+{
+ if(!s_pDefaultBoxAutoFormat)
+ s_pDefaultBoxAutoFormat = new SwBoxAutoFormat();
+
+ return *s_pDefaultBoxAutoFormat;
+}
+
+void SwTableAutoFormat::UpdateFromSet( sal_uInt8 nPos,
+ const SfxItemSet& rSet,
+ SwTableAutoFormatUpdateFlags eFlags,
+ SvNumberFormatter const * pNFormatr)
+{
+ OSL_ENSURE( nPos < 16, "wrong area" );
+
+ SwBoxAutoFormat* pFormat = m_aBoxAutoFormat[ nPos ];
+ if( !pFormat ) // if is set -> copy
+ {
+ pFormat = new SwBoxAutoFormat;
+ m_aBoxAutoFormat[ nPos ] = pFormat;
+ }
+
+ if( SwTableAutoFormatUpdateFlags::Char & eFlags )
+ {
+ pFormat->SetFont( rSet.Get( RES_CHRATR_FONT ) );
+ pFormat->SetHeight( rSet.Get( RES_CHRATR_FONTSIZE ) );
+ pFormat->SetWeight( rSet.Get( RES_CHRATR_WEIGHT ) );
+ pFormat->SetPosture( rSet.Get( RES_CHRATR_POSTURE ) );
+ pFormat->SetCJKFont( rSet.Get( RES_CHRATR_CJK_FONT ) );
+ pFormat->SetCJKHeight( rSet.Get( RES_CHRATR_CJK_FONTSIZE ) );
+ pFormat->SetCJKWeight( rSet.Get( RES_CHRATR_CJK_WEIGHT ) );
+ pFormat->SetCJKPosture( rSet.Get( RES_CHRATR_CJK_POSTURE ) );
+ pFormat->SetCTLFont( rSet.Get( RES_CHRATR_CTL_FONT ) );
+ pFormat->SetCTLHeight( rSet.Get( RES_CHRATR_CTL_FONTSIZE ) );
+ pFormat->SetCTLWeight( rSet.Get( RES_CHRATR_CTL_WEIGHT ) );
+ pFormat->SetCTLPosture( rSet.Get( RES_CHRATR_CTL_POSTURE ) );
+ pFormat->SetUnderline( rSet.Get( RES_CHRATR_UNDERLINE ) );
+ pFormat->SetOverline( rSet.Get( RES_CHRATR_OVERLINE ) );
+ pFormat->SetCrossedOut( rSet.Get( RES_CHRATR_CROSSEDOUT ) );
+ pFormat->SetContour( rSet.Get( RES_CHRATR_CONTOUR ) );
+ pFormat->SetShadowed( rSet.Get( RES_CHRATR_SHADOWED ) );
+ pFormat->SetColor( rSet.Get( RES_CHRATR_COLOR ) );
+ pFormat->SetAdjust( rSet.Get( RES_PARATR_ADJUST ) );
+ }
+ if( !(SwTableAutoFormatUpdateFlags::Box & eFlags) )
+ return;
+
+ pFormat->SetBox( rSet.Get( RES_BOX ) );
+// FIXME - add attribute IDs for the diagonal line items
+// pFormat->SetTLBR( (SvxLineItem&)rSet.Get( RES_... ) );
+// pFormat->SetBLTR( (SvxLineItem&)rSet.Get( RES_... ) );
+ pFormat->SetBackground( rSet.Get( RES_BACKGROUND ) );
+ pFormat->SetTextOrientation(rSet.Get(RES_FRAMEDIR));
+ pFormat->SetVerticalAlignment(rSet.Get(RES_VERT_ORIENT));
+
+ const SwTableBoxNumFormat* pNumFormatItem;
+ const SvNumberformat* pNumFormat = nullptr;
+ if( pNFormatr && (pNumFormatItem = rSet.GetItemIfSet( RES_BOXATR_FORMAT )) &&
+ nullptr != (pNumFormat = pNFormatr->GetEntry( pNumFormatItem->GetValue() )) )
+ pFormat->SetValueFormat( pNumFormat->GetFormatstring(),
+ pNumFormat->GetLanguage(),
+ ::GetAppLanguage());
+ else
+ {
+ // default
+ pFormat->SetValueFormat( OUString(), LANGUAGE_SYSTEM,
+ ::GetAppLanguage() );
+ }
+
+ // we cannot handle the rest, that's specific to StarCalc
+}
+
+void SwTableAutoFormat::UpdateToSet(const sal_uInt8 nPos, const bool bSingleRowTable, const bool bSingleColTable, SfxItemSet& rSet,
+ SwTableAutoFormatUpdateFlags eFlags, SvNumberFormatter* pNFormatr) const
+{
+ const SwBoxAutoFormat& rChg = GetBoxFormat( nPos );
+
+ if( SwTableAutoFormatUpdateFlags::Char & eFlags )
+ {
+ if( IsFont() )
+ {
+ rSet.Put( rChg.GetFont() );
+ rSet.Put( rChg.GetHeight() );
+ rSet.Put( rChg.GetWeight() );
+ rSet.Put( rChg.GetPosture() );
+ // do not insert empty CJK font
+ const SvxFontItem& rCJKFont = rChg.GetCJKFont();
+ if (!rCJKFont.GetStyleName().isEmpty())
+ {
+ rSet.Put( rChg.GetCJKFont() );
+ rSet.Put( rChg.GetCJKHeight() );
+ rSet.Put( rChg.GetCJKWeight() );
+ rSet.Put( rChg.GetCJKPosture() );
+ }
+ else
+ {
+ rSet.Put( rChg.GetHeight().CloneSetWhich(RES_CHRATR_CJK_FONTSIZE) );
+ rSet.Put( rChg.GetWeight().CloneSetWhich(RES_CHRATR_CJK_WEIGHT) );
+ rSet.Put( rChg.GetPosture().CloneSetWhich(RES_CHRATR_CJK_POSTURE) );
+ }
+ // do not insert empty CTL font
+ const SvxFontItem& rCTLFont = rChg.GetCTLFont();
+ if (!rCTLFont.GetStyleName().isEmpty())
+ {
+ rSet.Put( rChg.GetCTLFont() );
+ rSet.Put( rChg.GetCTLHeight() );
+ rSet.Put( rChg.GetCTLWeight() );
+ rSet.Put( rChg.GetCTLPosture() );
+ }
+ else
+ {
+ rSet.Put( rChg.GetHeight().CloneSetWhich(RES_CHRATR_CTL_FONTSIZE) );
+ rSet.Put( rChg.GetWeight().CloneSetWhich(RES_CHRATR_CTL_WEIGHT) );
+ rSet.Put( rChg.GetPosture().CloneSetWhich(RES_CHRATR_CTL_POSTURE) );
+ }
+ rSet.Put( rChg.GetUnderline() );
+ rSet.Put( rChg.GetOverline() );
+ rSet.Put( rChg.GetCrossedOut() );
+ rSet.Put( rChg.GetContour() );
+ rSet.Put( rChg.GetShadowed() );
+ rSet.Put( rChg.GetColor() );
+ }
+ if( IsJustify() )
+ rSet.Put( rChg.GetAdjust() );
+ }
+
+ if( !(SwTableAutoFormatUpdateFlags::Box & eFlags) )
+ return;
+
+ if( IsFrame() )
+ {
+ SvxBoxItem aAutoFormatBox = rChg.GetBox();
+
+ // No format box is adequate to specify the borders of single column/row tables, so combine first/last.
+ if ( bSingleRowTable || bSingleColTable )
+ {
+ sal_uInt8 nSingleRowOrColumnId = 15; //LAST_ROW_END_COLUMN
+ if ( !bSingleRowTable )
+ nSingleRowOrColumnId = nPos + 3; //LAST COLUMN (3, 7, 11, 15)
+ else if ( !bSingleColTable )
+ nSingleRowOrColumnId = nPos + 12; //LAST ROW (12, 13, 14, 15)
+
+ assert( nSingleRowOrColumnId < 16 );
+ const SvxBoxItem aLastAutoFormatBox( GetBoxFormat(nSingleRowOrColumnId).GetBox() );
+ if ( bSingleRowTable )
+ aAutoFormatBox.SetLine( aLastAutoFormatBox.GetLine(SvxBoxItemLine::BOTTOM), SvxBoxItemLine::BOTTOM );
+ if ( bSingleColTable )
+ aAutoFormatBox.SetLine( aLastAutoFormatBox.GetLine(SvxBoxItemLine::RIGHT), SvxBoxItemLine::RIGHT );
+ }
+
+ rSet.Put( aAutoFormatBox );
+// FIXME - uncomment the lines to put the diagonal line items
+// rSet.Put( rChg.GetTLBR() );
+// rSet.Put( rChg.GetBLTR() );
+ }
+ if( IsBackground() )
+ rSet.Put( rChg.GetBackground() );
+
+ rSet.Put(rChg.GetTextOrientation());
+
+ // Do not put a VertAlign when it has default value.
+ // It prevents the export of default value by automatic cell-styles export.
+ if (rChg.GetVerticalAlignment().GetVertOrient() != GetDefaultBoxFormat().GetVerticalAlignment().GetVertOrient())
+ rSet.Put(rChg.GetVerticalAlignment());
+
+ if( !(IsValueFormat() && pNFormatr) )
+ return;
+
+ OUString sFormat;
+ LanguageType eLng, eSys;
+ rChg.GetValueFormat( sFormat, eLng, eSys );
+ if( !sFormat.isEmpty() )
+ {
+ SvNumFormatType nType;
+ bool bNew;
+ sal_Int32 nCheckPos;
+ sal_uInt32 nKey = pNFormatr->GetIndexPuttingAndConverting( sFormat, eLng,
+ eSys, nType, bNew, nCheckPos);
+ rSet.Put( SwTableBoxNumFormat( nKey ));
+ }
+ else
+ rSet.ClearItem( RES_BOXATR_FORMAT );
+
+ // we cannot handle the rest, that's specific to StarCalc
+}
+
+void SwTableAutoFormat::RestoreTableProperties(SwTable &table) const
+{
+ SwTableFormat* pFormat = table.GetFrameFormat();
+ if (!pFormat)
+ return;
+
+ SwDoc *pDoc = pFormat->GetDoc();
+ if (!pDoc)
+ return;
+
+ SfxItemSet rSet(pDoc->GetAttrPool(), aTableSetRange);
+
+ rSet.Put(SwFormatLayoutSplit(m_bLayoutSplit));
+ rSet.Put(SfxBoolItem(RES_COLLAPSING_BORDERS, m_bCollapsingBorders));
+ if ( m_aKeepWithNextPara->GetValue() )
+ rSet.Put(*m_aKeepWithNextPara);
+ rSet.Put(*m_aShadow);
+
+ pFormat->SetFormatAttr(rSet);
+
+ if (SwEditShell *pShell = pDoc->GetEditShell())
+ pDoc->SetRowSplit(*pShell->getShellCursor(false), SwFormatRowSplit(m_bRowSplit));
+
+ table.SetRowsToRepeat(m_aRepeatHeading);
+}
+
+void SwTableAutoFormat::StoreTableProperties(const SwTable &table)
+{
+ SwTableFormat* pFormat = table.GetFrameFormat();
+ if (!pFormat)
+ return;
+
+ SwDoc *pDoc = pFormat->GetDoc();
+ if (!pDoc)
+ return;
+
+ SwEditShell *pShell = pDoc->GetEditShell();
+ std::unique_ptr<SwFormatRowSplit> pRowSplit(pShell ? SwDoc::GetRowSplit(*pShell->getShellCursor(false)) : nullptr);
+ m_bRowSplit = pRowSplit && pRowSplit->GetValue();
+ pRowSplit.reset();
+
+ const SfxItemSet &rSet = pFormat->GetAttrSet();
+
+ const SwFormatLayoutSplit &layoutSplit = rSet.Get(RES_LAYOUT_SPLIT);
+ m_bLayoutSplit = layoutSplit.GetValue();
+ m_bCollapsingBorders = rSet.Get(RES_COLLAPSING_BORDERS).GetValue();
+
+ m_aKeepWithNextPara.reset(rSet.Get(RES_KEEP).Clone());
+ m_aRepeatHeading = table.GetRowsToRepeat();
+ m_aShadow.reset(rSet.Get(RES_SHADOW).Clone());
+}
+
+bool SwTableAutoFormat::FirstRowEndColumnIsRow()
+{
+ return GetBoxFormat(3) == GetBoxFormat(2);
+}
+bool SwTableAutoFormat::FirstRowStartColumnIsRow()
+{
+ return GetBoxFormat(0) == GetBoxFormat(1);
+}
+bool SwTableAutoFormat::LastRowEndColumnIsRow()
+{
+ return GetBoxFormat(14) == GetBoxFormat(15);
+}
+bool SwTableAutoFormat::LastRowStartColumnIsRow()
+{
+ return GetBoxFormat(12) == GetBoxFormat(13);
+}
+bool SwTableAutoFormat::HasHeaderRow() const
+{ // Wild guessing for PDF export: is header different from odd or body?
+ // It would be vastly better to do like SdrTableObj and have flags that
+ // determine which "special" styles apply, instead of horrible guessing.
+ return !(GetBoxFormat(1) == GetBoxFormat(5))
+ || !(GetBoxFormat(1) == GetBoxFormat(10));
+}
+
+bool SwTableAutoFormat::Load( SvStream& rStream, const SwAfVersions& rVersions )
+{
+ sal_uInt16 nVal = 0;
+ rStream.ReadUInt16( nVal );
+ bool bRet = ERRCODE_NONE == rStream.GetError();
+
+ if( bRet && (nVal == AUTOFORMAT_DATA_ID_X ||
+ (AUTOFORMAT_DATA_ID_504 <= nVal && nVal <= AUTOFORMAT_DATA_ID)) )
+ {
+ bool b;
+ // --- from 680/dr25 on: store strings as UTF-8
+ rtl_TextEncoding eCharSet = (nVal >= AUTOFORMAT_ID_680DR25) ? RTL_TEXTENCODING_UTF8 : rStream.GetStreamCharSet();
+ m_aName = rStream.ReadUniOrByteString( eCharSet );
+ if( AUTOFORMAT_DATA_ID_552 <= nVal )
+ {
+ rStream.ReadUInt16( m_nStrResId );
+ // start from 3d because default is added via constructor
+ if( m_nStrResId < RES_POOLTABLESTYLE_END - RES_POOLTABLESTYLE_3D )
+ {
+ m_aName = SwStyleNameMapper::GetUIName(RES_POOLTABLESTYLE_3D + m_nStrResId, m_aName);
+ }
+ else
+ m_nStrResId = USHRT_MAX;
+ }
+ rStream.ReadCharAsBool( b ); m_bInclFont = b;
+ rStream.ReadCharAsBool( b ); m_bInclJustify = b;
+ rStream.ReadCharAsBool( b ); m_bInclFrame = b;
+ rStream.ReadCharAsBool( b ); m_bInclBackground = b;
+ rStream.ReadCharAsBool( b ); m_bInclValueFormat = b;
+ rStream.ReadCharAsBool( b ); m_bInclWidthHeight = b;
+
+ if (nVal >= AUTOFORMAT_DATA_ID_31005 && WriterSpecificBlockExists(rStream))
+ {
+ //this only exists for file format compat
+ SvxFormatBreakItem aBreak(SvxBreak::NONE, RES_BREAK);
+ legacy::SvxFormatBreak::Create(aBreak, rStream, AUTOFORMAT_FILE_VERSION);
+ legacy::SvxFormatKeep::Create(*m_aKeepWithNextPara, rStream, AUTOFORMAT_FILE_VERSION);
+
+ rStream.ReadUInt16( m_aRepeatHeading ).ReadCharAsBool( m_bLayoutSplit ).ReadCharAsBool( m_bRowSplit ).ReadCharAsBool( m_bCollapsingBorders );
+
+ legacy::SvxShadow::Create(*m_aShadow, rStream, AUTOFORMAT_FILE_VERSION);
+ }
+
+ bRet = ERRCODE_NONE== rStream.GetError();
+
+ for( sal_uInt8 i = 0; bRet && i < 16; ++i )
+ {
+ SwBoxAutoFormat* pFormat = new SwBoxAutoFormat;
+ bRet = pFormat->Load( rStream, rVersions, nVal );
+ if( bRet )
+ m_aBoxAutoFormat[ i ] = pFormat;
+ else
+ {
+ delete pFormat;
+ break;
+ }
+ }
+ }
+ m_bUserDefined = false;
+ return bRet;
+}
+
+bool SwTableAutoFormat::Save( SvStream& rStream, sal_uInt16 fileVersion ) const
+{
+ rStream.WriteUInt16( AUTOFORMAT_DATA_ID );
+ // --- from 680/dr25 on: store strings as UTF-8
+ write_uInt16_lenPrefixed_uInt8s_FromOUString(rStream, m_aName,
+ RTL_TEXTENCODING_UTF8 );
+ rStream.WriteUInt16( m_nStrResId );
+ rStream.WriteBool( m_bInclFont );
+ rStream.WriteBool( m_bInclJustify );
+ rStream.WriteBool( m_bInclFrame );
+ rStream.WriteBool( m_bInclBackground );
+ rStream.WriteBool( m_bInclValueFormat );
+ rStream.WriteBool( m_bInclWidthHeight );
+
+ {
+ WriterSpecificAutoFormatBlock block(rStream);
+ //this only exists for file format compat
+ SvxFormatBreakItem aBreak(SvxBreak::NONE, RES_BREAK);
+ legacy::SvxFormatBreak::Store(aBreak, rStream, legacy::SvxFormatBreak::GetVersion(fileVersion));
+ legacy::SvxFormatKeep::Store(*m_aKeepWithNextPara, rStream, legacy::SvxFormatKeep::GetVersion(fileVersion));
+ rStream.WriteUInt16( m_aRepeatHeading ).WriteBool( m_bLayoutSplit ).WriteBool( m_bRowSplit ).WriteBool( m_bCollapsingBorders );
+ legacy::SvxShadow::Store(*m_aShadow, rStream, legacy::SvxShadow::GetVersion(fileVersion));
+ }
+
+ bool bRet = ERRCODE_NONE == rStream.GetError();
+
+ for( int i = 0; bRet && i < 16; ++i )
+ {
+ SwBoxAutoFormat* pFormat = m_aBoxAutoFormat[ i ];
+ if( !pFormat ) // if not set -> write default
+ {
+ // If it doesn't exist yet:
+ if( !s_pDefaultBoxAutoFormat )
+ s_pDefaultBoxAutoFormat = new SwBoxAutoFormat;
+ pFormat = s_pDefaultBoxAutoFormat;
+ }
+ bRet = pFormat->Save( rStream, fileVersion );
+ }
+ return bRet;
+}
+
+OUString SwTableAutoFormat::GetTableTemplateCellSubName(const SwBoxAutoFormat& rBoxFormat) const
+{
+ sal_Int32 nIndex = 0;
+ for (; nIndex < 16; ++nIndex)
+ if (m_aBoxAutoFormat[nIndex] == &rBoxFormat) break;
+
+ // box format doesn't belong to this table format
+ if (16 <= nIndex)
+ return OUString();
+
+ const std::vector<sal_Int32> aTableTemplateMap = GetTableTemplateMap();
+ for (size_t i=0; i < aTableTemplateMap.size(); ++i)
+ {
+ if (aTableTemplateMap[i] == nIndex)
+ return "." + OUString::number(i + 1);
+ }
+
+ // box format doesn't belong to a table template
+ return OUString();
+}
+
+/*
+ * Mapping schema
+ * 0 1 2 3 4 5
+ * +-----------------------------------------------------------------------+
+ * 0 | FRSC | FR | FREC | | | FRENC |
+ * +-----------------------------------------------------------------------+
+ * 1 | FC | ER | EC | | | LC |
+ * +-----------------------------------------------------------------------+
+ * 2 | OR | OC | BODY | | | BCKG |
+ * +-----------------------------------------------------------------------+
+ * 3 | | | | | | |
+ * +-----------------------------------------------------------------------+
+ * 4 | | | | | | |
+ * +-----------------------------------------------------------------------+
+ * 5 | LRSC | LR | LREC | | | LRENC |
+ * +-----------+-----------+-----------+-----------+-----------+-----------+
+ * ODD = 1, 3, 5, ...
+ * EVEN = 2, 4, 6, ...
+ */
+const std::vector<sal_Int32> & SwTableAutoFormat::GetTableTemplateMap()
+{
+ static std::vector<sal_Int32> const aTableTemplateMap
+ {
+ 1 , // FIRST_ROW // FR
+ 13, // LAST_ROW // LR
+ 4 , // FIRST_COLUMN // FC
+ 7 , // LAST_COLUMN // LC
+ 5 , // EVEN_ROWS // ER
+ 8 , // ODD_ROWS // OR
+ 6 , // EVEN_COLUMNS // EC
+ 9 , // ODD_COLUMNS // OC
+ 10, // BODY
+ 11, // BACKGROUND // BCKG
+ 0 , // FIRST_ROW_START_COLUMN // FRSC
+ 3 , // FIRST_ROW_END_COLUMN // FRENC
+ 12, // LAST_ROW_START_COLUMN // LRSC
+ 15, // LAST_ROW_END_COLUMN // LRENC
+ 2 , // FIRST_ROW_EVEN_COLUMN // FREC
+ 14, // LAST_ROW_EVEN_COLUMN // LREC
+ };
+ return aTableTemplateMap;
+}
+
+sal_uInt8 SwTableAutoFormat::CountPos(sal_uInt32 nCol, sal_uInt32 nCols, sal_uInt32 nRow,
+ sal_uInt32 nRows)
+{
+ sal_uInt8 nRet = static_cast<sal_uInt8>(
+ !nRow ? 0 : ((nRow + 1 == nRows) ? 12 : (4 * (1 + ((nRow - 1) & 1)))));
+ nRet = nRet
+ + static_cast<sal_uInt8>(!nCol ? 0 : (nCol + 1 == nCols ? 3 : (1 + ((nCol - 1) & 1))));
+ return nRet;
+}
+
+void SwTableAutoFormat::SetXObject(rtl::Reference<SwXTextTableStyle> const& xObject)
+{
+ m_xUnoTextTableStyle = xObject.get();
+}
+
+struct SwTableAutoFormatTable::Impl
+{
+ std::vector<std::unique_ptr<SwTableAutoFormat>> m_AutoFormats;
+};
+
+size_t SwTableAutoFormatTable::size() const
+{
+ return m_pImpl->m_AutoFormats.size();
+}
+
+SwTableAutoFormat const& SwTableAutoFormatTable::operator[](size_t const i) const
+{
+ return *m_pImpl->m_AutoFormats[i];
+}
+SwTableAutoFormat & SwTableAutoFormatTable::operator[](size_t const i)
+{
+ return *m_pImpl->m_AutoFormats[i];
+}
+
+void SwTableAutoFormatTable::AddAutoFormat(const SwTableAutoFormat& rTableStyle)
+{
+ // don't insert when we already have style of this name
+ if (FindAutoFormat(rTableStyle.GetName()))
+ return;
+
+ InsertAutoFormat(size(), std::make_unique<SwTableAutoFormat>(rTableStyle));
+}
+
+void SwTableAutoFormatTable::InsertAutoFormat(size_t const i, std::unique_ptr<SwTableAutoFormat> pFormat)
+{
+ m_pImpl->m_AutoFormats.insert(m_pImpl->m_AutoFormats.begin() + i, std::move(pFormat));
+}
+
+void SwTableAutoFormatTable::EraseAutoFormat(size_t const i)
+{
+ m_pImpl->m_AutoFormats.erase(m_pImpl->m_AutoFormats.begin() + i);
+}
+
+void SwTableAutoFormatTable::EraseAutoFormat(const OUString& rName)
+{
+ auto iter = std::find_if(m_pImpl->m_AutoFormats.begin(), m_pImpl->m_AutoFormats.end(),
+ [&rName](const std::unique_ptr<SwTableAutoFormat>& rpFormat) { return rpFormat->GetName() == rName; });
+ if (iter != m_pImpl->m_AutoFormats.end())
+ {
+ m_pImpl->m_AutoFormats.erase(iter);
+ return;
+ }
+ SAL_INFO("sw.core", "SwTableAutoFormatTable::EraseAutoFormat, SwTableAutoFormat with given name not found");
+}
+
+std::unique_ptr<SwTableAutoFormat> SwTableAutoFormatTable::ReleaseAutoFormat(size_t const i)
+{
+ auto const iter(m_pImpl->m_AutoFormats.begin() + i);
+ std::unique_ptr<SwTableAutoFormat> pRet(std::move(*iter));
+ m_pImpl->m_AutoFormats.erase(iter);
+ return pRet;
+}
+
+std::unique_ptr<SwTableAutoFormat> SwTableAutoFormatTable::ReleaseAutoFormat(const OUString& rName)
+{
+ std::unique_ptr<SwTableAutoFormat> pRet;
+ auto iter = std::find_if(m_pImpl->m_AutoFormats.begin(), m_pImpl->m_AutoFormats.end(),
+ [&rName](const std::unique_ptr<SwTableAutoFormat>& rpFormat) { return rpFormat->GetName() == rName; });
+ if (iter != m_pImpl->m_AutoFormats.end())
+ {
+ pRet = std::move(*iter);
+ m_pImpl->m_AutoFormats.erase(iter);
+ }
+ return pRet;
+}
+
+SwTableAutoFormat* SwTableAutoFormatTable::FindAutoFormat(std::u16string_view rName) const
+{
+ for (const auto &rFormat : m_pImpl->m_AutoFormats)
+ {
+ if (rFormat->GetName() == rName)
+ return rFormat.get();
+ }
+
+ return nullptr;
+}
+
+SwTableAutoFormatTable::~SwTableAutoFormatTable()
+{
+}
+
+SwTableAutoFormatTable::SwTableAutoFormatTable()
+ : m_pImpl(new Impl)
+{
+ std::unique_ptr<SwTableAutoFormat> pNew(new SwTableAutoFormat(
+ SwStyleNameMapper::GetUIName(RES_POOLTABLESTYLE_DEFAULT, OUString())));
+
+ sal_uInt8 i;
+
+ Color aColor( COL_BLACK );
+ SvxBoxItem aBox( RES_BOX );
+
+ aBox.SetAllDistances(55);
+ SvxBorderLine aLn( &aColor, SvxBorderLineWidth::VeryThin );
+ aBox.SetLine( &aLn, SvxBoxItemLine::LEFT );
+ aBox.SetLine( &aLn, SvxBoxItemLine::BOTTOM );
+
+ for( i = 0; i <= 15; ++i )
+ {
+ aBox.SetLine( i <= 3 ? &aLn : nullptr, SvxBoxItemLine::TOP );
+ aBox.SetLine( (3 == ( i & 3 )) ? &aLn : nullptr, SvxBoxItemLine::RIGHT );
+ pNew->GetBoxFormat( i ).SetBox( aBox );
+ }
+
+ pNew->SetUserDefined(false);
+ m_pImpl->m_AutoFormats.push_back(std::move(pNew));
+}
+
+void SwTableAutoFormatTable::Load()
+{
+ if (utl::ConfigManager::IsFuzzing())
+ return;
+ OUString sNm(AUTOTABLE_FORMAT_NAME);
+ SvtPathOptions aOpt;
+ if( aOpt.SearchFile( sNm ))
+ {
+ SfxMedium aStream( sNm, StreamMode::STD_READ );
+ Load( *aStream.GetInStream() );
+ }
+}
+
+bool SwTableAutoFormatTable::Save() const
+{
+ if (utl::ConfigManager::IsFuzzing())
+ return false;
+ SvtPathOptions aPathOpt;
+ const OUString sNm( aPathOpt.GetUserConfigPath() + "/" + AUTOTABLE_FORMAT_NAME );
+ SfxMedium aStream(sNm, StreamMode::STD_WRITE );
+ return Save( *aStream.GetOutStream() ) && aStream.Commit();
+}
+
+bool SwTableAutoFormatTable::Load( SvStream& rStream )
+{
+ bool bRet = ERRCODE_NONE == rStream.GetError();
+ if (bRet)
+ {
+ // Attention: We need to read a general Header here
+ sal_uInt16 nVal = 0;
+ rStream.ReadUInt16( nVal );
+ bRet = ERRCODE_NONE == rStream.GetError();
+
+ if( bRet )
+ {
+ SwAfVersions aVersions;
+
+ // Default version is 5.0, unless we detect an old format ID.
+ sal_uInt16 nFileVers = SOFFICE_FILEFORMAT_50;
+ if(nVal < AUTOFORMAT_ID_31005)
+ nFileVers = SOFFICE_FILEFORMAT_40;
+
+ if( nVal == AUTOFORMAT_ID_358 ||
+ (AUTOFORMAT_ID_504 <= nVal && nVal <= AUTOFORMAT_ID) )
+ {
+ sal_uInt8 nChrSet, nCnt;
+ sal_uInt64 nPos = rStream.Tell();
+ rStream.ReadUChar( nCnt ).ReadUChar( nChrSet );
+ if( rStream.Tell() != nPos + nCnt )
+ {
+ OSL_ENSURE( false, "The Header contains more or newer Data" );
+ rStream.Seek( nPos + nCnt );
+ }
+ rStream.SetStreamCharSet( static_cast<rtl_TextEncoding>(nChrSet) );
+ rStream.SetVersion( nFileVers );
+ }
+
+ if( nVal == AUTOFORMAT_ID_358 || nVal == AUTOFORMAT_ID_X ||
+ (AUTOFORMAT_ID_504 <= nVal && nVal <= AUTOFORMAT_ID) )
+ {
+ aVersions.Load( rStream, nVal ); // Item versions
+
+ sal_uInt16 nCount = 0;
+ rStream.ReadUInt16( nCount );
+
+ bRet = ERRCODE_NONE== rStream.GetError();
+ if (bRet)
+ {
+ const size_t nMinRecordSize = sizeof(sal_uInt16);
+ const size_t nMaxRecords = rStream.remainingSize() / nMinRecordSize;
+ if (nCount > nMaxRecords)
+ {
+ SAL_WARN("sw.core", "Parsing error: " << nMaxRecords <<
+ " max possible entries, but " << nCount << " claimed, truncating");
+ nCount = nMaxRecords;
+ }
+ for (sal_uInt16 i = 0; i < nCount; ++i)
+ {
+ std::unique_ptr<SwTableAutoFormat> pNew(
+ new SwTableAutoFormat( OUString() ));
+ bRet = pNew->Load( rStream, aVersions );
+ if( bRet )
+ {
+ m_pImpl->m_AutoFormats.push_back(std::move(pNew));
+ }
+ else
+ {
+ break;
+ }
+ }
+ }
+ }
+ else
+ {
+ bRet = false;
+ }
+ }
+ }
+ return bRet;
+}
+
+bool SwTableAutoFormatTable::Save( SvStream& rStream ) const
+{
+ bool bRet = ERRCODE_NONE == rStream.GetError();
+ if (bRet)
+ {
+ rStream.SetVersion(AUTOFORMAT_FILE_VERSION);
+
+ // Attention: We need to save a general Header here
+ rStream.WriteUInt16( AUTOFORMAT_ID )
+ .WriteUChar( 2 ) // Character count of the Header including this value
+ .WriteUChar( GetStoreCharSet( ::osl_getThreadTextEncoding() ) );
+
+ bRet = ERRCODE_NONE == rStream.GetError();
+ if (!bRet)
+ return false;
+
+ // Write this version number for all attributes
+ SwAfVersions::Write(rStream, AUTOFORMAT_FILE_VERSION);
+
+ rStream.WriteUInt16( m_pImpl->m_AutoFormats.size() - 1 );
+ bRet = ERRCODE_NONE == rStream.GetError();
+
+ for (size_t i = 1; bRet && i < m_pImpl->m_AutoFormats.size(); ++i)
+ {
+ SwTableAutoFormat const& rFormat = *m_pImpl->m_AutoFormats[i];
+ bRet = rFormat.Save(rStream, AUTOFORMAT_FILE_VERSION);
+ }
+ }
+ rStream.FlushBuffer();
+ return bRet;
+}
+
+SwCellStyleTable::SwCellStyleTable()
+{ }
+
+SwCellStyleTable::~SwCellStyleTable()
+{
+}
+
+size_t SwCellStyleTable::size() const
+{
+ return m_aCellStyles.size();
+}
+
+void SwCellStyleTable::clear()
+{
+ m_aCellStyles.clear();
+}
+
+SwCellStyleDescriptor SwCellStyleTable::operator[](size_t i) const
+{
+ return SwCellStyleDescriptor(m_aCellStyles[i]);
+}
+
+void SwCellStyleTable::AddBoxFormat(const SwBoxAutoFormat& rBoxFormat, const OUString& sName)
+{
+ m_aCellStyles.emplace_back(sName, std::make_unique<SwBoxAutoFormat>(rBoxFormat));
+}
+
+void SwCellStyleTable::RemoveBoxFormat(const OUString& sName)
+{
+ auto iter = std::find_if(m_aCellStyles.begin(), m_aCellStyles.end(),
+ [&sName](const std::pair<OUString, std::unique_ptr<SwBoxAutoFormat>>& rStyle) { return rStyle.first == sName; });
+ if (iter != m_aCellStyles.end())
+ {
+ m_aCellStyles.erase(iter);
+ return;
+ }
+ SAL_INFO("sw.core", "SwCellStyleTable::RemoveBoxFormat, format with given name doesn't exists");
+}
+
+OUString SwCellStyleTable::GetBoxFormatName(const SwBoxAutoFormat& rBoxFormat) const
+{
+ for (size_t i=0; i < m_aCellStyles.size(); ++i)
+ {
+ if (m_aCellStyles[i].second.get() == &rBoxFormat)
+ return m_aCellStyles[i].first;
+ }
+
+ // box format not found
+ return OUString();
+}
+
+SwBoxAutoFormat* SwCellStyleTable::GetBoxFormat(std::u16string_view sName) const
+{
+ for (size_t i=0; i < m_aCellStyles.size(); ++i)
+ {
+ if (m_aCellStyles[i].first == sName)
+ return m_aCellStyles[i].second.get();
+ }
+
+ return nullptr;
+}
+
+void SwCellStyleTable::ChangeBoxFormatName(std::u16string_view sFromName, const OUString& sToName)
+{
+ if (!GetBoxFormat(sToName))
+ {
+ SAL_INFO("sw.core", "SwCellStyleTable::ChangeBoxName, box with given name already exists");
+ return;
+ }
+ for (size_t i=0; i < m_aCellStyles.size(); ++i)
+ {
+ if (m_aCellStyles[i].first == sFromName)
+ {
+ m_aCellStyles[i].first = sToName;
+ // changed successfully
+ return;
+ }
+ }
+ SAL_INFO("sw.core", "SwCellStyleTable::ChangeBoxName, box with given name not found");
+}
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/tblcpy.cxx b/sw/source/core/doc/tblcpy.cxx
new file mode 100644
index 0000000000..0b11ea6c80
--- /dev/null
+++ b/sw/source/core/doc/tblcpy.cxx
@@ -0,0 +1,1032 @@
+/* -*- 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 <hintids.hxx>
+
+#include <osl/diagnose.h>
+#include <svl/numformat.hxx>
+#include <frmfmt.hxx>
+#include <doc.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <DocumentContentOperationsManager.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <IDocumentFieldsAccess.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <IDocumentStylePoolAccess.hxx>
+#include <pam.hxx>
+#include <swtable.hxx>
+#include <ndtxt.hxx>
+#include <tblsel.hxx>
+#include <poolfmt.hxx>
+#include <cellatr.hxx>
+#include <mvsave.hxx>
+#include <fmtanchr.hxx>
+#include <hints.hxx>
+#include <UndoTable.hxx>
+#include <fmtfsize.hxx>
+#include <frameformats.hxx>
+#include <deque>
+#include <memory>
+#include <numeric>
+
+static void lcl_CpyBox( const SwTable& rCpyTable, const SwTableBox* pCpyBox,
+ SwTable& rDstTable, SwTableBox* pDstBox,
+ bool bDelContent, SwUndoTableCpyTable* pUndo );
+
+// The following type will be used by table copy functions to describe
+// the structure of tables (or parts of tables).
+// It's for new table model only.
+
+namespace
+{
+ struct BoxSpanInfo
+ {
+ SwTableBox* mpBox;
+ SwTableBox* mpCopy;
+ sal_uInt16 mnColSpan;
+ bool mbSelected;
+ };
+
+ typedef std::vector< BoxSpanInfo > BoxStructure;
+ typedef std::vector< BoxStructure > LineStructure;
+ typedef std::deque< sal_uLong > ColumnStructure;
+
+ struct SubBox
+ {
+ SwTableBox *mpBox;
+ bool mbCovered;
+ };
+
+ typedef std::vector< SubBox > SubLine;
+ typedef std::vector< SubLine > SubTable;
+
+ class TableStructure
+ {
+ public:
+ LineStructure maLines;
+ ColumnStructure maCols;
+ sal_uInt16 mnStartCol;
+ sal_uInt16 mnAddLine;
+ void addLine( sal_uInt16 &rLine, const SwTableBoxes&, const SwSelBoxes*,
+ bool bNewModel );
+ void addBox( sal_uInt16 nLine, const SwSelBoxes*, SwTableBox *pBox,
+ sal_uLong &rnB, sal_uInt16 &rnC, ColumnStructure::iterator& rpCl,
+ BoxStructure::iterator& rpSel, bool &rbSel, bool bCover );
+ void incColSpan( sal_uInt16 nLine, sal_uInt16 nCol );
+ explicit TableStructure( const SwTable& rTable );
+ TableStructure( const SwTable& rTable, FndBox_ &rFndBox,
+ const SwSelBoxes& rSelBoxes,
+ LineStructure::size_type nMinSize );
+ LineStructure::size_type getLineCount() const
+ { return maLines.size(); }
+ void moreLines( const SwTable& rTable );
+ void assignBoxes( const TableStructure &rSource );
+ void copyBoxes( const SwTable& rSource, SwTable& rDstTable,
+ SwUndoTableCpyTable* pUndo ) const;
+ };
+
+ SubTable::iterator insertSubLine( SubTable& rSubTable, SwTableLine& rLine,
+ const SubTable::iterator& pStartLn );
+
+ SubTable::iterator insertSubBox( SubTable& rSubTable, SwTableBox& rBox,
+ SubTable::iterator pStartLn, const SubTable::iterator& pEndLn )
+ {
+ if( !rBox.GetTabLines().empty() )
+ {
+ SubTable::size_type nSize = static_cast<SubTable::size_type>(std::distance( pStartLn, pEndLn ));
+ if( nSize < rBox.GetTabLines().size() )
+ {
+ SubLine aSubLine;
+ for( const auto& rSubBox : *pStartLn )
+ {
+ SubBox aSub;
+ aSub.mpBox = rSubBox.mpBox;
+ aSub.mbCovered = true;
+ aSubLine.push_back( aSub );
+ }
+ do
+ {
+ rSubTable.insert( pEndLn, aSubLine );
+ } while( ++nSize < rBox.GetTabLines().size() );
+ }
+ for( auto pLine : rBox.GetTabLines() )
+ pStartLn = insertSubLine( rSubTable, *pLine, pStartLn );
+ OSL_ENSURE( pStartLn == pEndLn, "Sub line confusion" );
+ }
+ else
+ {
+ SubBox aSub;
+ aSub.mpBox = &rBox;
+ aSub.mbCovered = false;
+ while( pStartLn != pEndLn )
+ {
+ pStartLn->push_back( aSub );
+ aSub.mbCovered = true;
+ ++pStartLn;
+ }
+ }
+ return pStartLn;
+ }
+
+ SubTable::iterator insertSubLine( SubTable& rSubTable, SwTableLine& rLine,
+ const SubTable::iterator& pStartLn )
+ {
+ SubTable::iterator pMax = pStartLn;
+ ++pMax;
+ SubTable::difference_type nMax = 1;
+ for( auto pBox : rLine.GetTabBoxes() )
+ {
+ SubTable::iterator pTmp = insertSubBox( rSubTable, *pBox, pStartLn, pMax );
+ SubTable::difference_type nTmp = std::distance( pStartLn, pTmp );
+ if( nTmp > nMax )
+ {
+ pMax = pTmp;
+ nMax = nTmp;
+ }
+ }
+ return pMax;
+ }
+
+ TableStructure::TableStructure( const SwTable& rTable ) :
+ maLines( rTable.GetTabLines().size() ), mnStartCol(USHRT_MAX),
+ mnAddLine(0)
+ {
+ maCols.push_front(0);
+ sal_uInt16 nCnt = 0;
+ for( auto pLine : rTable.GetTabLines() )
+ addLine( nCnt, pLine->GetTabBoxes(), nullptr, rTable.IsNewModel() );
+ }
+
+ TableStructure::TableStructure( const SwTable& rTable,
+ FndBox_ &rFndBox, const SwSelBoxes& rSelBoxes,
+ LineStructure::size_type nMinSize )
+ : mnStartCol(USHRT_MAX), mnAddLine(0)
+ {
+ if( rFndBox.GetLines().empty() )
+ return;
+
+ bool bNoSelection = rSelBoxes.size() < 2;
+ FndLines_t &rFndLines = rFndBox.GetLines();
+ maCols.push_front(0);
+ const SwTableLine* pLine = rFndLines.front()->GetLine();
+ const sal_uInt16 nStartLn = rTable.GetTabLines().GetPos( pLine );
+ SwTableLines::size_type nEndLn = nStartLn;
+ if( rFndLines.size() > 1 )
+ {
+ pLine = rFndLines.back()->GetLine();
+ nEndLn = rTable.GetTabLines().GetPos( pLine );
+ }
+ if( nStartLn < USHRT_MAX && nEndLn < USHRT_MAX )
+ {
+ const SwTableLines &rLines = rTable.GetTabLines();
+ if( bNoSelection && nMinSize > nEndLn - nStartLn + 1 )
+ {
+ SwTableLines::size_type nNewEndLn = nStartLn + nMinSize - 1;
+ if( nNewEndLn >= rLines.size() )
+ {
+ mnAddLine = nNewEndLn - rLines.size() + 1;
+ nNewEndLn = rLines.size() - 1;
+ }
+ while( nEndLn < nNewEndLn )
+ {
+ SwTableLine *pLine2 = rLines[ ++nEndLn ];
+ SwTableBox *pTmpBox = pLine2->GetTabBoxes()[0];
+ FndLine_ *pInsLine = new FndLine_( pLine2, &rFndBox );
+ pInsLine->GetBoxes().insert(pInsLine->GetBoxes().begin(), std::make_unique<FndBox_>(pTmpBox, pInsLine));
+ rFndLines.push_back(std::unique_ptr<FndLine_>(pInsLine));
+ }
+ }
+ maLines.resize( nEndLn - nStartLn + 1 );
+ const SwSelBoxes* pSelBoxes = &rSelBoxes;
+ sal_uInt16 nCnt = 0;
+ for( SwTableLines::size_type nLine = nStartLn; nLine <= nEndLn; ++nLine )
+ {
+ addLine( nCnt, rLines[nLine]->GetTabBoxes(),
+ pSelBoxes, rTable.IsNewModel() );
+ if( bNoSelection )
+ pSelBoxes = nullptr;
+ }
+ }
+ if( bNoSelection && mnStartCol < USHRT_MAX )
+ {
+ sal_uInt16 nIdx = std::min(mnStartCol, o3tl::narrowing<sal_uInt16>(maLines[0].size()));
+ mnStartCol = std::accumulate(maLines[0].begin(), maLines[0].begin() + nIdx, sal_uInt16(0),
+ [](sal_uInt16 sum, const BoxSpanInfo& rInfo) { return sum + rInfo.mnColSpan; });
+ }
+ else
+ mnStartCol = USHRT_MAX;
+ }
+
+ void TableStructure::addLine( sal_uInt16 &rLine, const SwTableBoxes& rBoxes,
+ const SwSelBoxes* pSelBoxes, bool bNewModel )
+ {
+ bool bComplex = false;
+ if( !bNewModel )
+ for( SwTableBoxes::size_type nBox = 0; !bComplex && nBox < rBoxes.size(); ++nBox )
+ bComplex = !rBoxes[nBox]->GetTabLines().empty();
+ if( bComplex )
+ {
+ SubTable aSubTable;
+ SubLine aSubLine;
+ aSubTable.push_back( aSubLine );
+ SubTable::iterator pStartLn = aSubTable.begin();
+ SubTable::iterator pEndLn = aSubTable.end();
+ for( auto pBox : rBoxes )
+ insertSubBox( aSubTable, *pBox, pStartLn, pEndLn );
+ SubTable::size_type nSize = aSubTable.size();
+ if( nSize )
+ {
+ maLines.resize( maLines.size() + nSize - 1 );
+ while( pStartLn != pEndLn )
+ {
+ bool bSelected = false;
+ sal_uLong nBorder = 0;
+ sal_uInt16 nCol = 0;
+ maLines[rLine].reserve( pStartLn->size() );
+ BoxStructure::iterator pSel = maLines[rLine].end();
+ ColumnStructure::iterator pCol = maCols.begin();
+ for( const auto& rBox : *pStartLn )
+ {
+ addBox( rLine, pSelBoxes, rBox.mpBox, nBorder, nCol,
+ pCol, pSel, bSelected, rBox.mbCovered );
+ }
+ ++rLine;
+ ++pStartLn;
+ }
+ }
+ }
+ else
+ {
+ bool bSelected = false;
+ sal_uLong nBorder = 0;
+ sal_uInt16 nCol = 0;
+ maLines[rLine].reserve( rBoxes.size() );
+ ColumnStructure::iterator pCol = maCols.begin();
+ BoxStructure::iterator pSel = maLines[rLine].end();
+ for( auto pBox : rBoxes )
+ addBox( rLine, pSelBoxes, pBox, nBorder, nCol,
+ pCol, pSel, bSelected, false );
+ ++rLine;
+ }
+ }
+
+ void TableStructure::addBox( sal_uInt16 nLine, const SwSelBoxes* pSelBoxes,
+ SwTableBox *pBox, sal_uLong &rnBorder, sal_uInt16 &rnCol,
+ ColumnStructure::iterator& rpCol, BoxStructure::iterator& rpSel,
+ bool &rbSelected, bool bCovered )
+ {
+ BoxSpanInfo aInfo;
+ if( pSelBoxes &&
+ pSelBoxes->end() != pSelBoxes->find( pBox ) )
+ {
+ aInfo.mbSelected = true;
+ if( mnStartCol == USHRT_MAX )
+ {
+ mnStartCol = o3tl::narrowing<sal_uInt16>(maLines[nLine].size());
+ if( pSelBoxes->size() < 2 )
+ {
+ pSelBoxes = nullptr;
+ aInfo.mbSelected = false;
+ }
+ }
+ }
+ else
+ aInfo.mbSelected = false;
+ rnBorder += pBox->GetFrameFormat()->GetFrameSize().GetWidth();
+ const sal_uInt16 nLeftCol = rnCol;
+ while( rpCol != maCols.end() && *rpCol < rnBorder )
+ {
+ ++rnCol;
+ ++rpCol;
+ }
+ if( rpCol == maCols.end() || *rpCol > rnBorder )
+ {
+ rpCol = maCols.insert( rpCol, rnBorder );
+ incColSpan( nLine, rnCol );
+ }
+ aInfo.mnColSpan = rnCol - nLeftCol;
+ aInfo.mpCopy = nullptr;
+ aInfo.mpBox = bCovered ? nullptr : pBox;
+ maLines[nLine].push_back( aInfo );
+ if( !aInfo.mbSelected )
+ return;
+
+ if( rbSelected )
+ {
+ while( rpSel != maLines[nLine].end() )
+ {
+ rpSel->mbSelected = true;
+ ++rpSel;
+ }
+ }
+ else
+ {
+ rpSel = maLines[nLine].end();
+ rbSelected = true;
+ }
+ --rpSel;
+ }
+
+ void TableStructure::moreLines( const SwTable& rTable )
+ {
+ if( !mnAddLine )
+ return;
+
+ const SwTableLines &rLines = rTable.GetTabLines();
+ const sal_uInt16 nLineCount = rLines.size();
+ if( nLineCount < mnAddLine )
+ mnAddLine = nLineCount;
+ sal_uInt16 nLine = o3tl::narrowing<sal_uInt16>(maLines.size());
+ maLines.resize( nLine + mnAddLine );
+ while( mnAddLine )
+ {
+ SwTableLine *pLine = rLines[ nLineCount - mnAddLine ];
+ addLine( nLine, pLine->GetTabBoxes(), nullptr, rTable.IsNewModel() );
+ --mnAddLine;
+ }
+ }
+
+ void TableStructure::incColSpan( sal_uInt16 nLineMax, sal_uInt16 nNewCol )
+ {
+ for( sal_uInt16 nLine = 0; nLine < nLineMax; ++nLine )
+ {
+ BoxStructure::iterator pInfo = maLines[nLine].begin();
+ BoxStructure::iterator pEnd = maLines[nLine].end();
+ tools::Long nCol = pInfo->mnColSpan;
+ while( nNewCol > nCol && ++pInfo != pEnd )
+ nCol += pInfo->mnColSpan;
+ if( pInfo != pEnd )
+ ++(pInfo->mnColSpan);
+ }
+ }
+
+ void TableStructure::assignBoxes( const TableStructure &rSource )
+ {
+ LineStructure::const_iterator pFirstLine = rSource.maLines.begin();
+ LineStructure::const_iterator pLastLine = rSource.maLines.end();
+ if( pFirstLine == pLastLine )
+ return;
+ LineStructure::const_iterator pCurrLine = pFirstLine;
+ LineStructure::size_type nLineCount = maLines.size();
+ sal_uInt16 nFirstStartCol = 0;
+ {
+ BoxStructure::const_iterator pFirstBox = pFirstLine->begin();
+ if( pFirstBox != pFirstLine->end() && pFirstBox->mpBox &&
+ pFirstBox->mpBox->getDummyFlag() )
+ nFirstStartCol = pFirstBox->mnColSpan;
+ }
+ for( LineStructure::size_type nLine = 0; nLine < nLineCount; ++nLine )
+ {
+ BoxStructure::const_iterator pFirstBox = pCurrLine->begin();
+ BoxStructure::const_iterator pLastBox = pCurrLine->end();
+ sal_uInt16 nCurrStartCol = mnStartCol;
+ if( pFirstBox != pLastBox )
+ {
+ BoxStructure::const_iterator pTmpBox = pLastBox;
+ --pTmpBox;
+ if( pTmpBox->mpBox && pTmpBox->mpBox->getDummyFlag() )
+ --pLastBox;
+ if( pFirstBox != pLastBox && pFirstBox->mpBox &&
+ pFirstBox->mpBox->getDummyFlag() )
+ {
+ if( nCurrStartCol < USHRT_MAX )
+ {
+ if( pFirstBox->mnColSpan > nFirstStartCol )
+ nCurrStartCol += pFirstBox->mnColSpan - nFirstStartCol;
+ }
+ ++pFirstBox;
+ }
+ }
+ if( pFirstBox != pLastBox )
+ {
+ BoxStructure::const_iterator pCurrBox = pFirstBox;
+ BoxStructure &rBox = maLines[nLine];
+ BoxStructure::size_type nBoxCount = rBox.size();
+ sal_uInt16 nCol = 0;
+ for( BoxStructure::size_type nBox = 0; nBox < nBoxCount; ++nBox )
+ {
+ BoxSpanInfo& rInfo = rBox[nBox];
+ nCol += rInfo.mnColSpan;
+ if( rInfo.mbSelected || nCol > nCurrStartCol )
+ {
+ rInfo.mpCopy = pCurrBox->mpBox;
+ if( rInfo.mbSelected && rInfo.mpCopy->getDummyFlag() )
+ {
+ ++pCurrBox;
+ if( pCurrBox == pLastBox )
+ {
+ pCurrBox = pFirstBox;
+ if( pCurrBox->mpBox->getDummyFlag() )
+ ++pCurrBox;
+ }
+ rInfo.mpCopy = pCurrBox->mpBox;
+ }
+ ++pCurrBox;
+ if( pCurrBox == pLastBox )
+ {
+ if( rInfo.mbSelected )
+ pCurrBox = pFirstBox;
+ else
+ {
+ rInfo.mbSelected = rInfo.mpCopy == nullptr;
+ break;
+ }
+ }
+ rInfo.mbSelected = rInfo.mpCopy == nullptr;
+ }
+ }
+ }
+ ++pCurrLine;
+ if( pCurrLine == pLastLine )
+ pCurrLine = pFirstLine;
+ }
+ }
+
+ void TableStructure::copyBoxes( const SwTable& rSource, SwTable& rDstTable,
+ SwUndoTableCpyTable* pUndo ) const
+ {
+ LineStructure::size_type nLineCount = maLines.size();
+ for( LineStructure::size_type nLine = 0; nLine < nLineCount; ++nLine )
+ {
+ const BoxStructure &rBox = maLines[nLine];
+ BoxStructure::size_type nBoxCount = rBox.size();
+ for( BoxStructure::size_type nBox = 0; nBox < nBoxCount; ++nBox )
+ {
+ const BoxSpanInfo& rInfo = rBox[nBox];
+ if( ( rInfo.mpCopy && !rInfo.mpCopy->getDummyFlag() )
+ || rInfo.mbSelected )
+ {
+ SwTableBox *pBox = rInfo.mpBox;
+ if( pBox && pBox->getRowSpan() > 0 )
+ lcl_CpyBox( rSource, rInfo.mpCopy, rDstTable, pBox,
+ true, pUndo );
+ }
+ }
+ }
+ }
+}
+
+/** Copy Table into this Box.
+ Copy all Boxes of a Line into the corresponding Boxes. The old content
+ is deleted by doing this.
+ If no Box is left the remaining content goes to the Box of a "BaseLine".
+ If there's no Line anymore, put it also into the last Box of a "BaseLine". */
+static void lcl_CpyBox( const SwTable& rCpyTable, const SwTableBox* pCpyBox,
+ SwTable& rDstTable, SwTableBox* pDstBox,
+ bool bDelContent, SwUndoTableCpyTable* pUndo )
+{
+ OSL_ENSURE( ( !pCpyBox || pCpyBox->GetSttNd() ) && pDstBox->GetSttNd(),
+ "No content in this Box" );
+
+ SwDoc* pCpyDoc = rCpyTable.GetFrameFormat()->GetDoc();
+ SwDoc* pDoc = rDstTable.GetFrameFormat()->GetDoc();
+
+ // First copy the new content and then delete the old one.
+ // Do not create empty Sections, otherwise they will be deleted!
+ std::unique_ptr< SwNodeRange > pRg( pCpyBox ?
+ new SwNodeRange ( *pCpyBox->GetSttNd(), SwNodeOffset(1),
+ *pCpyBox->GetSttNd()->EndOfSectionNode() ) : nullptr );
+
+ SwNodeIndex aInsIdx( *pDstBox->GetSttNd(), bDelContent ? SwNodeOffset(1) :
+ pDstBox->GetSttNd()->EndOfSectionIndex() -
+ pDstBox->GetSttIdx() );
+
+ if( pUndo )
+ pUndo->AddBoxBefore( *pDstBox, bDelContent );
+
+ bool bUndoRedline = pUndo && pDoc->getIDocumentRedlineAccess().IsRedlineOn();
+ ::sw::UndoGuard const undoGuard(pDoc->GetIDocumentUndoRedo());
+
+ SwNodeIndex aSavePos( aInsIdx, -1 );
+ if (pRg)
+ pCpyDoc->GetDocumentContentOperationsManager().CopyWithFlyInFly(*pRg, aInsIdx.GetNode(), nullptr, false);
+ else
+ pDoc->GetNodes().MakeTextNode( aInsIdx.GetNode(), pDoc->GetDfltTextFormatColl() );
+ ++aSavePos;
+
+ SwTableLine* pLine = pDstBox->GetUpper();
+ while( pLine->GetUpper() )
+ pLine = pLine->GetUpper()->GetUpper();
+
+ bool bReplaceColl = true;
+ if( bDelContent && !bUndoRedline )
+ {
+ // Delete the Fly first, then the corresponding Nodes
+ SwNodeIndex aEndNdIdx( *aInsIdx.GetNode().EndOfSectionNode() );
+
+ // Move Bookmarks
+ {
+ SwPosition aMvPos( aInsIdx );
+ SwContentNode* pCNd = SwNodes::GoPrevious( &aMvPos );
+ assert(pCNd); // keep coverity happy
+ aMvPos.SetContent( pCNd->Len() );
+ SwDoc::CorrAbs( aInsIdx, aEndNdIdx, aMvPos );
+ }
+
+ // If we still have FlyFrames hanging around, delete them too
+ for(sw::SpzFrameFormat* pFly: *pDoc->GetSpzFrameFormats())
+ {
+ SwFormatAnchor const*const pAnchor = &pFly->GetAnchor();
+ SwNode const*const pAnchorNode = pAnchor->GetAnchorNode();
+ if (pAnchorNode &&
+ ((RndStdIds::FLY_AT_PARA == pAnchor->GetAnchorId()) ||
+ (RndStdIds::FLY_AT_CHAR == pAnchor->GetAnchorId())) &&
+ aInsIdx <= *pAnchorNode && *pAnchorNode <= aEndNdIdx.GetNode() )
+ {
+ pDoc->getIDocumentLayoutAccess().DelLayoutFormat( pFly );
+ }
+ }
+
+ // If DestBox is a Headline Box and has Table style set, then
+ // DO NOT automatically set the TableHeadline style!
+ if( 1 < rDstTable.GetTabLines().size() &&
+ pLine == rDstTable.GetTabLines().front() )
+ {
+ SwContentNode* pCNd = aInsIdx.GetNode().GetContentNode();
+ if( !pCNd )
+ {
+ SwNodeIndex aTmp( aInsIdx );
+ pCNd = pDoc->GetNodes().GoNext( &aTmp );
+ }
+
+ if( pCNd &&
+ RES_POOLCOLL_TABLE_HDLN !=
+ pCNd->GetFormatColl()->GetPoolFormatId() )
+ bReplaceColl = false;
+ }
+
+ pDoc->GetNodes().Delete( aInsIdx, aEndNdIdx.GetIndex() - aInsIdx.GetIndex() );
+ }
+
+ //b6341295: Table copy redlining will be managed by AddBoxAfter()
+ if( pUndo )
+ pUndo->AddBoxAfter( *pDstBox, aInsIdx, bDelContent );
+
+ // heading
+ SwTextNode *const pTextNd = aSavePos.GetNode().GetTextNode();
+ if( !pTextNd )
+ return;
+
+ const sal_uInt16 nPoolId = pTextNd->GetTextColl()->GetPoolFormatId();
+ if( bReplaceColl &&
+ (( 1 < rDstTable.GetTabLines().size() &&
+ pLine == rDstTable.GetTabLines().front() )
+ // Is the Table's content still valid?
+ ? RES_POOLCOLL_TABLE == nPoolId
+ : RES_POOLCOLL_TABLE_HDLN == nPoolId ) )
+ {
+ SwTextFormatColl* pColl = pDoc->getIDocumentStylePoolAccess().GetTextCollFromPool(
+ o3tl::narrowing<sal_uInt16>(
+ RES_POOLCOLL_TABLE == nPoolId
+ ? RES_POOLCOLL_TABLE_HDLN
+ : RES_POOLCOLL_TABLE ) );
+ if( pColl ) // Apply style
+ {
+ SwPaM aPam( aSavePos );
+ aPam.SetMark();
+ aPam.Move( fnMoveForward, GoInSection );
+ pDoc->SetTextFormatColl( aPam, pColl );
+ }
+ }
+
+ // Delete the current Formula/Format/Value values
+ if( SfxItemState::SET == pDstBox->GetFrameFormat()->GetItemState( RES_BOXATR_FORMAT ) ||
+ SfxItemState::SET == pDstBox->GetFrameFormat()->GetItemState( RES_BOXATR_FORMULA ) ||
+ SfxItemState::SET == pDstBox->GetFrameFormat()->GetItemState( RES_BOXATR_VALUE ) )
+ {
+ pDstBox->ClaimFrameFormat()->ResetFormatAttr( RES_BOXATR_FORMAT,
+ RES_BOXATR_VALUE );
+ }
+
+ // Copy the TableBoxAttributes - Formula/Format/Value
+ if( !pCpyBox )
+ return;
+
+ SfxItemSetFixed<RES_BOXATR_FORMAT, RES_BOXATR_VALUE> aBoxAttrSet( pCpyDoc->GetAttrPool() );
+ aBoxAttrSet.Put( pCpyBox->GetFrameFormat()->GetAttrSet() );
+ if( !aBoxAttrSet.Count() )
+ return;
+
+ const SwTableBoxNumFormat* pItem;
+ SvNumberFormatter* pN = pDoc->GetNumberFormatter( false );
+ if( pN && pN->HasMergeFormatTable() &&
+ (pItem = aBoxAttrSet.GetItemIfSet( RES_BOXATR_FORMAT, false )) )
+ {
+ sal_uLong nOldIdx = pItem->GetValue();
+ sal_uLong nNewIdx = pN->GetMergeFormatIndex( nOldIdx );
+ if( nNewIdx != nOldIdx )
+ aBoxAttrSet.Put( SwTableBoxNumFormat( nNewIdx ));
+ }
+ pDstBox->ClaimFrameFormat()->SetFormatAttr( aBoxAttrSet );
+}
+
+bool SwTable::InsNewTable( const SwTable& rCpyTable, const SwSelBoxes& rSelBoxes,
+ SwUndoTableCpyTable* pUndo )
+{
+ SwDoc* pDoc = GetFrameFormat()->GetDoc();
+ SwDoc* pCpyDoc = rCpyTable.GetFrameFormat()->GetDoc();
+
+ SwTableNumFormatMerge aTNFM( *pCpyDoc, *pDoc );
+
+ // Analyze source structure
+ TableStructure aCopyStruct( rCpyTable );
+
+ // Analyze target structure (from start box) and selected substructure
+ FndBox_ aFndBox( nullptr, nullptr );
+ { // get all boxes/lines
+ FndPara aPara( rSelBoxes, &aFndBox );
+ ForEach_FndLineCopyCol( GetTabLines(), &aPara );
+ }
+ TableStructure aTarget( *this, aFndBox, rSelBoxes, aCopyStruct.getLineCount() );
+
+ bool bClear = false;
+ if( aTarget.mnAddLine && IsNewModel() )
+ {
+ SwSelBoxes aBoxes;
+ aBoxes.insert( GetTabLines().back()->GetTabBoxes().front() );
+ if( pUndo )
+ pUndo->InsertRow( *this, aBoxes, aTarget.mnAddLine );
+ else
+ InsertRow( pDoc, aBoxes, aTarget.mnAddLine, /*bBehind*/true );
+
+ aTarget.moreLines( *this );
+ bClear = true;
+ }
+
+ // Find mapping, if needed extend target table and/or selection
+ aTarget.assignBoxes( aCopyStruct );
+
+ {
+ const_cast<SwTable*>(&rCpyTable)->SwitchFormulasToRelativeRepresentation();
+ }
+
+ // delete frames
+ aFndBox.SetTableLines( *this );
+ if( bClear )
+ aFndBox.ClearLineBehind();
+ aFndBox.DelFrames( *this );
+
+ // copy boxes
+ aTarget.copyBoxes( rCpyTable, *this, pUndo );
+
+ // adjust row span attributes accordingly
+
+ // make frames
+ aFndBox.MakeFrames( *this );
+
+ return true;
+}
+
+/** Copy Table into this Box.
+ Copy all Boxes of a Line into the corresponding Boxes. The old content is
+ deleted by doing this.
+ If no Box is left the remaining content goes to the Box of a "BaseLine".
+ If there's no Line anymore, put it also into the last Box of a "BaseLine". */
+bool SwTable::InsTable( const SwTable& rCpyTable, const SwNodeIndex& rSttBox,
+ SwUndoTableCpyTable* pUndo )
+{
+ SetHTMLTableLayout(std::shared_ptr<SwHTMLTableLayout>()); // Delete HTML Layout
+
+ SwDoc* pDoc = GetFrameFormat()->GetDoc();
+
+ SwTableNode* pTableNd = SwDoc::IsIdxInTable( rSttBox );
+
+ // Find the Box, to which should be copied:
+ SwTableBox* pMyBox = GetTableBox(
+ rSttBox.GetNode().FindTableBoxStartNode()->GetIndex() );
+
+ OSL_ENSURE( pMyBox, "Index is not in a Box in this Table" );
+
+ // First delete the Table's Frames
+ FndBox_ aFndBox( nullptr, nullptr );
+ aFndBox.DelFrames( pTableNd->GetTable() );
+
+ SwDoc* pCpyDoc = rCpyTable.GetFrameFormat()->GetDoc();
+
+ const_cast<SwTable*>(&rCpyTable)->SwitchFormulasToRelativeRepresentation();
+
+ SwTableNumFormatMerge aTNFM( *pCpyDoc, *pDoc );
+
+ bool bDelContent = true;
+ const SwTableBox* pTmp;
+
+ for( auto pLine : rCpyTable.GetTabLines() )
+ {
+ // Get the first from the CopyLine
+ const SwTableBox* pCpyBox = pLine->GetTabBoxes().front();
+ while( !pCpyBox->GetTabLines().empty() )
+ pCpyBox = pCpyBox->GetTabLines().front()->GetTabBoxes().front();
+
+ do {
+ // First copy the new content and then delete the old one.
+ // Do not create empty Sections, otherwise they will be deleted!
+ lcl_CpyBox( rCpyTable, pCpyBox, *this, pMyBox, bDelContent, pUndo );
+
+ pTmp = pCpyBox->FindNextBox( rCpyTable, pCpyBox, false );
+ if( !pTmp )
+ break; // no more Boxes
+ pCpyBox = pTmp;
+
+ pTmp = pMyBox->FindNextBox( *this, pMyBox, false );
+ if( !pTmp )
+ bDelContent = false; // No space left?
+ else
+ pMyBox = const_cast<SwTableBox*>(pTmp);
+
+ } while( true );
+
+ // Find the topmost Line
+ SwTableLine* pNxtLine = pMyBox->GetUpper();
+ while( pNxtLine->GetUpper() )
+ pNxtLine = pNxtLine->GetUpper()->GetUpper();
+ const SwTableLines::size_type nPos = GetTabLines().GetPos( pNxtLine ) + 1;
+ // Is there a next?
+ if( nPos >= GetTabLines().size() )
+ bDelContent = false; // there is none, all goes into the last Box
+ else
+ {
+ // Find the next Box with content
+ pNxtLine = GetTabLines()[ nPos ];
+ pMyBox = pNxtLine->GetTabBoxes().front();
+ while( !pMyBox->GetTabLines().empty() )
+ pMyBox = pMyBox->GetTabLines().front()->GetTabBoxes().front();
+ bDelContent = true;
+ }
+ }
+
+ aFndBox.MakeFrames( pTableNd->GetTable() ); // Create the Frames anew
+ return true;
+}
+
+bool SwTable::InsTable( const SwTable& rCpyTable, const SwSelBoxes& rSelBoxes,
+ SwUndoTableCpyTable* pUndo )
+{
+ OSL_ENSURE( !rSelBoxes.empty(), "Missing selection" );
+
+ SetHTMLTableLayout(std::shared_ptr<SwHTMLTableLayout>()); // Delete HTML Layout
+
+ if( IsNewModel() || rCpyTable.IsNewModel() )
+ return InsNewTable( rCpyTable, rSelBoxes, pUndo );
+
+ OSL_ENSURE( !rCpyTable.IsTableComplex(), "Table too complex" );
+
+ SwDoc* pDoc = GetFrameFormat()->GetDoc();
+ SwDoc* pCpyDoc = rCpyTable.GetFrameFormat()->GetDoc();
+
+ SwTableNumFormatMerge aTNFM( *pCpyDoc, *pDoc );
+
+ FndLine_ *pFLine;
+ FndBox_ aFndBox( nullptr, nullptr );
+ // Find all Boxes/Lines
+ {
+ FndPara aPara( rSelBoxes, &aFndBox );
+ ForEach_FndLineCopyCol( GetTabLines(), &aPara );
+ }
+
+ // Special case: If a Box is located in a Table, copy it to all selected
+ // Boxes!
+ if( 1 != rCpyTable.GetTabSortBoxes().size() )
+ {
+ FndBox_* pFndBox;
+
+ const FndLines_t::size_type nFndCnt = aFndBox.GetLines().size();
+ if( !nFndCnt )
+ return false;
+
+ // Check if we have enough space for all Lines and Boxes
+ SwTableLines::size_type nTstLns = 0;
+ pFLine = aFndBox.GetLines().front().get();
+ sal_uInt16 nSttLine = GetTabLines().GetPos( pFLine->GetLine() );
+ // Do we have as many rows, actually?
+ if( 1 == nFndCnt )
+ {
+ // Is there still enough space in the Table?
+ if( (GetTabLines().size() - nSttLine ) <
+ rCpyTable.GetTabLines().size() )
+ {
+ // If we don't have enough Lines, then see if we can insert
+ // new ones to reach our goal. But only if the SSelection
+ // contains a Box!
+ if( 1 < rSelBoxes.size() )
+ return false;
+
+ const sal_uInt16 nNewLns = rCpyTable.GetTabLines().size() -
+ (GetTabLines().size() - nSttLine );
+
+ // See if the Box count is high enough for the Lines
+ SwTableLine* pLastLn = GetTabLines().back();
+
+ SwTableBox* pSttBox = pFLine->GetBoxes()[0]->GetBox();
+ const SwTableBoxes::size_type nSttBox = pFLine->GetLine()->GetBoxPos( pSttBox );
+ for( SwTableLines::size_type n = rCpyTable.GetTabLines().size() - nNewLns;
+ n < rCpyTable.GetTabLines().size(); ++n )
+ {
+ SwTableLine* pCpyLn = rCpyTable.GetTabLines()[ n ];
+
+ if( pLastLn->GetTabBoxes().size() < nSttBox ||
+ ( pLastLn->GetTabBoxes().size() - nSttBox ) <
+ pCpyLn->GetTabBoxes().size() )
+ return false;
+
+ // Test for nesting
+ for( SwTableBoxes::size_type nBx = 0; nBx < pCpyLn->GetTabBoxes().size(); ++nBx )
+ if( !pLastLn->GetTabBoxes()[ nSttBox + nBx ]->GetSttNd() )
+ return false;
+ }
+ // We have enough space for the to-be-copied, so insert new
+ // rows accordingly.
+ SwTableBox* pInsBox = pLastLn->GetTabBoxes()[ nSttBox ];
+ OSL_ENSURE( pInsBox && pInsBox->GetSttNd(),
+ "no ContentBox or it's not in this Table" );
+ SwSelBoxes aBoxes;
+
+ if( pUndo
+ ? !pUndo->InsertRow( *this, SelLineFromBox( pInsBox,
+ aBoxes ), nNewLns )
+ : !InsertRow( pDoc, SelLineFromBox( pInsBox,
+ aBoxes ), nNewLns, /*bBehind*/true ) )
+ return false;
+ }
+
+ nTstLns = rCpyTable.GetTabLines().size(); // copy this many
+ }
+ else if( 0 == (nFndCnt % rCpyTable.GetTabLines().size()) )
+ nTstLns = nFndCnt;
+ else
+ return false; // not enough space for the rows
+
+ for( SwTableLines::size_type nLn = 0; nLn < nTstLns; ++nLn )
+ {
+ // We have enough rows, so check the Boxes per row
+ pFLine = aFndBox.GetLines()[ nLn % nFndCnt ].get();
+ SwTableLine* pLine = pFLine->GetLine();
+ SwTableBox* pSttBox = pFLine->GetBoxes()[0]->GetBox();
+ const SwTableBoxes::size_type nSttBox = pLine->GetBoxPos( pSttBox );
+ std::unique_ptr<FndLine_> pInsFLine;
+ if( nLn >= nFndCnt )
+ {
+ // We have more rows in the ClipBoard than we have selected
+ pInsFLine.reset(new FndLine_( GetTabLines()[ nSttLine + nLn ],
+ &aFndBox ));
+ pLine = pInsFLine->GetLine();
+ }
+ SwTableLine* pCpyLn = rCpyTable.GetTabLines()[ nLn %
+ rCpyTable.GetTabLines().size() ];
+
+ // Selected too few rows?
+ if( pInsFLine )
+ {
+ // We insert a new row into the FndBox
+ if( pLine->GetTabBoxes().size() < nSttBox ||
+ pLine->GetTabBoxes().size() - nSttBox < pFLine->GetBoxes().size() )
+ {
+ return false;
+ }
+
+ // Test for nesting
+ for (FndBoxes_t::size_type nBx = 0; nBx < pFLine->GetBoxes().size(); ++nBx)
+ {
+ SwTableBox *pTmpBox = pLine->GetTabBoxes()[ nSttBox + nBx ];
+ if( !pTmpBox->GetSttNd() )
+ {
+ return false;
+ }
+ // if Ok, insert the Box into the FndLine
+ pFndBox = new FndBox_( pTmpBox, pInsFLine.get() );
+ pInsFLine->GetBoxes().insert( pInsFLine->GetBoxes().begin() + nBx,
+ std::unique_ptr<FndBox_>(pFndBox));
+ }
+ aFndBox.GetLines().insert( aFndBox.GetLines().begin() + nLn, std::move(pInsFLine));
+ }
+ else if( pFLine->GetBoxes().size() == 1 )
+ {
+ if( pLine->GetTabBoxes().size() < nSttBox ||
+ ( pLine->GetTabBoxes().size() - nSttBox ) <
+ pCpyLn->GetTabBoxes().size() )
+ return false;
+
+ // Test for nesting
+ for( SwTableBoxes::size_type nBx = 0; nBx < pCpyLn->GetTabBoxes().size(); ++nBx )
+ {
+ SwTableBox *pTmpBox = pLine->GetTabBoxes()[ nSttBox + nBx ];
+ if( !pTmpBox->GetSttNd() )
+ return false;
+ // if Ok, insert the Box into the FndLine
+ if( nBx == pFLine->GetBoxes().size() )
+ {
+ pFndBox = new FndBox_( pTmpBox, pFLine );
+ pFLine->GetBoxes().insert(pFLine->GetBoxes().begin() + nBx,
+ std::unique_ptr<FndBox_>(pFndBox));
+ }
+ }
+ }
+ else
+ {
+ // Match the selected Boxes with the ones in the Clipboard
+ // (n times)
+ if( 0 != ( pFLine->GetBoxes().size() %
+ pCpyLn->GetTabBoxes().size() ))
+ return false;
+
+ // Test for nesting
+ for (auto &rpBox : pFLine->GetBoxes())
+ {
+ if (!rpBox->GetBox()->GetSttNd())
+ return false;
+ }
+ }
+ }
+
+ if( aFndBox.GetLines().empty() )
+ return false;
+ }
+
+ {
+ const_cast<SwTable*>(&rCpyTable)->SwitchFormulasToRelativeRepresentation();
+ }
+
+ // Delete the Frames
+ aFndBox.SetTableLines( *this );
+ //Not dispose accessible table
+ aFndBox.DelFrames( *this );
+
+ if( 1 == rCpyTable.GetTabSortBoxes().size() )
+ {
+ SwTableBox *pTmpBx = rCpyTable.GetTabSortBoxes()[0];
+ for (size_t n = 0; n < rSelBoxes.size(); ++n)
+ {
+ lcl_CpyBox( rCpyTable, pTmpBx, *this,
+ rSelBoxes[n], true, pUndo );
+ }
+ }
+ else
+ for (FndLines_t::size_type nLn = 0; nLn < aFndBox.GetLines().size(); ++nLn)
+ {
+ pFLine = aFndBox.GetLines()[ nLn ].get();
+ SwTableLine* pCpyLn = rCpyTable.GetTabLines()[
+ nLn % rCpyTable.GetTabLines().size() ];
+ for (FndBoxes_t::size_type nBx = 0; nBx < pFLine->GetBoxes().size(); ++nBx)
+ {
+ // Copy the pCpyBox into pMyBox
+ lcl_CpyBox( rCpyTable, pCpyLn->GetTabBoxes()[
+ nBx % pCpyLn->GetTabBoxes().size() ],
+ *this, pFLine->GetBoxes()[nBx]->GetBox(), true, pUndo );
+ }
+ }
+
+ aFndBox.MakeFrames( *this );
+ return true;
+}
+
+static void FndContentLine( const SwTableLine* pLine, SwSelBoxes* pPara );
+
+static void FndContentBox( const SwTableBox* pBox, SwSelBoxes* pPara )
+{
+ if( !pBox->GetTabLines().empty() )
+ {
+ for( const SwTableLine* pLine : pBox->GetTabLines() )
+ FndContentLine( pLine, pPara );
+ }
+ else
+ pPara->insert( const_cast<SwTableBox*>(pBox) );
+}
+
+static void FndContentLine( const SwTableLine* pLine, SwSelBoxes* pPara )
+{
+ for( const SwTableBox* pBox : pLine->GetTabBoxes() )
+ FndContentBox(pBox, pPara );
+}
+
+// Find all Boxes with content in this Box
+SwSelBoxes& SwTable::SelLineFromBox( const SwTableBox* pBox,
+ SwSelBoxes& rBoxes, bool bToTop )
+{
+ SwTableLine* pLine = const_cast<SwTableLine*>(pBox->GetUpper());
+ if( bToTop )
+ while( pLine->GetUpper() )
+ pLine = pLine->GetUpper()->GetUpper();
+
+ // Delete all old ones
+ rBoxes.clear();
+ for( const auto& rpBox : pLine->GetTabBoxes() )
+ FndContentBox(rpBox, &rBoxes );
+ return rBoxes;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/tblrwcl.cxx b/sw/source/core/doc/tblrwcl.cxx
new file mode 100644
index 0000000000..ef4b023d4f
--- /dev/null
+++ b/sw/source/core/doc/tblrwcl.cxx
@@ -0,0 +1,3380 @@
+/* -*- 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/text/HoriOrientation.hpp>
+#include <osl/diagnose.h>
+#include <svl/numformat.hxx>
+#include <hintids.hxx>
+
+#include <editeng/lrspitem.hxx>
+#include <editeng/boxitem.hxx>
+#include <tools/fract.hxx>
+#include <fmtfsize.hxx>
+#include <fmtornt.hxx>
+#include <doc.hxx>
+#include <IDocumentSettingAccess.hxx>
+#include <IDocumentChartDataProviderAccess.hxx>
+#include <DocumentContentOperationsManager.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <IDocumentStylePoolAccess.hxx>
+#include <IDocumentFieldsAccess.hxx>
+#include <docsh.hxx>
+#include <fesh.hxx>
+#include <tabfrm.hxx>
+#include <frmatr.hxx>
+#include <frmtool.hxx>
+#include <pam.hxx>
+#include <swtable.hxx>
+#include <tblsel.hxx>
+#include <fldbas.hxx>
+#include <rowfrm.hxx>
+#include <ddefld.hxx>
+#include <hints.hxx>
+#include <UndoTable.hxx>
+#include <cellatr.hxx>
+#include <mvsave.hxx>
+#include <swtblfmt.hxx>
+#include <swddetbl.hxx>
+#include <poolfmt.hxx>
+#include <tblrwcl.hxx>
+#include <unochart.hxx>
+#include <o3tl/numeric.hxx>
+#include <calbck.hxx>
+#include <docary.hxx>
+
+using namespace com::sun::star;
+using namespace com::sun::star::uno;
+
+#define COLFUZZY 20
+#define ROWFUZZY 10
+
+#ifdef DBG_UTIL
+#define CHECK_TABLE(t) (t).CheckConsistency();
+#else
+#define CHECK_TABLE(t)
+#endif
+
+namespace {
+
+// In order to set the Frame Formats for the Boxes, it's enough to look
+// up the current one in the array. If it's already there return the new one.
+struct CpyTabFrame
+{
+ SwFrameFormat* pFrameFormat;
+ SwTableBoxFormat *pNewFrameFormat;
+
+ explicit CpyTabFrame(SwFrameFormat* pCurrentFrameFormat) : pNewFrameFormat( nullptr )
+ { pFrameFormat = pCurrentFrameFormat; }
+
+ bool operator==( const CpyTabFrame& rCpyTabFrame ) const
+ { return pFrameFormat == rCpyTabFrame.pFrameFormat; }
+ bool operator<( const CpyTabFrame& rCpyTabFrame ) const
+ { return pFrameFormat < rCpyTabFrame.pFrameFormat; }
+};
+
+struct CR_SetBoxWidth
+{
+ SwShareBoxFormats aShareFormats;
+ SwTableNode* pTableNd;
+ SwTwips nDiff, nSide, nMaxSize, nLowerDiff;
+ TableChgMode nMode;
+ bool bBigger, bLeft;
+
+ CR_SetBoxWidth( TableChgWidthHeightType eType, SwTwips nDif, SwTwips nSid,
+ SwTwips nMax, SwTableNode* pTNd )
+ : pTableNd( pTNd ),
+ nDiff( nDif ), nSide( nSid ), nMaxSize( nMax ), nLowerDiff( 0 )
+ {
+ bLeft = TableChgWidthHeightType::ColLeft == extractPosition( eType ) ||
+ TableChgWidthHeightType::CellLeft == extractPosition( eType );
+ bBigger = bool(eType & TableChgWidthHeightType::BiggerMode );
+ nMode = pTableNd->GetTable().GetTableChgMode();
+ }
+ CR_SetBoxWidth( const CR_SetBoxWidth& rCpy )
+ : pTableNd( rCpy.pTableNd ),
+ nDiff( rCpy.nDiff ), nSide( rCpy.nSide ),
+ nMaxSize( rCpy.nMaxSize ), nLowerDiff( 0 ),
+ nMode( rCpy.nMode ),
+ bBigger( rCpy.bBigger ), bLeft( rCpy.bLeft )
+ {
+ }
+
+ void LoopClear()
+ {
+ nLowerDiff = 0;
+ }
+};
+
+}
+
+static bool lcl_SetSelBoxWidth( SwTableLine* pLine, CR_SetBoxWidth& rParam,
+ SwTwips nDist, bool bCheck );
+static bool lcl_SetOtherBoxWidth( SwTableLine* pLine, CR_SetBoxWidth& rParam,
+ SwTwips nDist, bool bCheck );
+
+typedef bool (*FN_lcl_SetBoxWidth)(SwTableLine*, CR_SetBoxWidth&, SwTwips, bool );
+
+#ifdef DBG_UTIL
+
+#define CHECKBOXWIDTH \
+ { \
+ SwTwips nSize = GetFrameFormat()->GetFrameSize().GetWidth(); \
+ for (size_t nTmp = 0; nTmp < m_aLines.size(); ++nTmp) \
+ ::CheckBoxWidth( *m_aLines[ nTmp ], nSize ); \
+ }
+
+#define CHECKTABLELAYOUT \
+ { \
+ for ( size_t i = 0; i < GetTabLines().size(); ++i ) \
+ { \
+ SwFrameFormat* pFormat = GetTabLines()[i]->GetFrameFormat(); \
+ SwIterator<SwRowFrame,SwFormat> aIter( *pFormat ); \
+ for (SwRowFrame* pFrame=aIter.First(); pFrame; pFrame=aIter.Next())\
+ { \
+ if ( pFrame->GetTabLine() == GetTabLines()[i] ) \
+ { \
+ OSL_ENSURE( pFrame->GetUpper()->IsTabFrame(), \
+ "Table layout does not match table structure" ); \
+ } \
+ } \
+ } \
+ }
+
+#else
+
+#define CHECKBOXWIDTH
+#define CHECKTABLELAYOUT
+
+#endif // DBG_UTIL
+
+namespace {
+
+struct CR_SetLineHeight
+{
+ SwTableNode* pTableNd;
+ SwTwips nMaxSpace, nMaxHeight;
+ TableChgMode nMode;
+ bool bBigger;
+
+ CR_SetLineHeight( TableChgWidthHeightType eType, SwTableNode* pTNd )
+ : pTableNd( pTNd ),
+ nMaxSpace( 0 ), nMaxHeight( 0 )
+ {
+ bBigger = bool(eType & TableChgWidthHeightType::BiggerMode );
+ nMode = pTableNd->GetTable().GetTableChgMode();
+ }
+ CR_SetLineHeight( const CR_SetLineHeight& rCpy )
+ : pTableNd( rCpy.pTableNd ),
+ nMaxSpace( rCpy.nMaxSpace ), nMaxHeight( rCpy.nMaxHeight ),
+ nMode( rCpy.nMode ),
+ bBigger( rCpy.bBigger )
+ {}
+};
+
+}
+
+static bool lcl_SetSelLineHeight( SwTableLine* pLine, const CR_SetLineHeight& rParam,
+ SwTwips nDist, bool bCheck );
+static bool lcl_SetOtherLineHeight( SwTableLine* pLine, const CR_SetLineHeight& rParam,
+ SwTwips nDist, bool bCheck );
+
+typedef bool (*FN_lcl_SetLineHeight)(SwTableLine*, CR_SetLineHeight&, SwTwips, bool );
+
+typedef o3tl::sorted_vector<CpyTabFrame> CpyTabFrames;
+
+namespace {
+
+struct CpyPara
+{
+ std::shared_ptr< std::vector< std::vector< sal_uLong > > > pWidths;
+ SwDoc& rDoc;
+ SwTableNode* pTableNd;
+ CpyTabFrames& rTabFrameArr;
+ SwTableLine* pInsLine;
+ SwTableBox* pInsBox;
+ sal_uLong nOldSize, nNewSize; // in order to correct the size attributes
+ sal_uLong nMinLeft, nMaxRight;
+ sal_uInt16 nCpyCnt, nInsPos;
+ sal_uInt16 nLnIdx, nBoxIdx;
+ sal_uInt8 nDelBorderFlag;
+ bool bCpyContent;
+
+ CpyPara( SwTableNode* pNd, sal_uInt16 nCopies, CpyTabFrames& rFrameArr )
+ : rDoc( pNd->GetDoc() ), pTableNd( pNd ), rTabFrameArr(rFrameArr),
+ pInsLine(nullptr), pInsBox(nullptr), nOldSize(0), nNewSize(0),
+ nMinLeft(ULONG_MAX), nMaxRight(0),
+ nCpyCnt(nCopies), nInsPos(0),
+ nLnIdx(0), nBoxIdx(0),
+ nDelBorderFlag(0), bCpyContent( true )
+ {}
+ CpyPara( const CpyPara& rPara, SwTableLine* pLine )
+ : pWidths( rPara.pWidths ), rDoc(rPara.rDoc), pTableNd(rPara.pTableNd),
+ rTabFrameArr(rPara.rTabFrameArr), pInsLine(pLine), pInsBox(rPara.pInsBox),
+ nOldSize(0), nNewSize(rPara.nNewSize), nMinLeft( rPara.nMinLeft ),
+ nMaxRight( rPara.nMaxRight ), nCpyCnt(rPara.nCpyCnt), nInsPos(0),
+ nLnIdx( rPara.nLnIdx), nBoxIdx( rPara.nBoxIdx ),
+ nDelBorderFlag( rPara.nDelBorderFlag ), bCpyContent( rPara.bCpyContent )
+ {}
+ CpyPara( const CpyPara& rPara, SwTableBox* pBox )
+ : pWidths( rPara.pWidths ), rDoc(rPara.rDoc), pTableNd(rPara.pTableNd),
+ rTabFrameArr(rPara.rTabFrameArr), pInsLine(rPara.pInsLine), pInsBox(pBox),
+ nOldSize(rPara.nOldSize), nNewSize(rPara.nNewSize),
+ nMinLeft( rPara.nMinLeft ), nMaxRight( rPara.nMaxRight ),
+ nCpyCnt(rPara.nCpyCnt), nInsPos(0), nLnIdx(rPara.nLnIdx), nBoxIdx(rPara.nBoxIdx),
+ nDelBorderFlag( rPara.nDelBorderFlag ), bCpyContent( rPara.bCpyContent )
+ {}
+};
+
+}
+
+static SwTableLine* lcl_CopyRow(FndLine_ & rFndLine, CpyPara *const pCpyPara);
+
+static void lcl_CopyCol( FndBox_ & rFndBox, CpyPara *const pCpyPara)
+{
+ // Look up the Frame Format in the Frame Format Array
+ SwTableBox* pBox = rFndBox.GetBox();
+ CpyTabFrame aFindFrame(pBox->GetFrameFormat());
+
+ if( pCpyPara->nCpyCnt )
+ {
+ sal_uInt16 nFndPos;
+ CpyTabFrames::const_iterator itFind = pCpyPara->rTabFrameArr.lower_bound( aFindFrame );
+ nFndPos = itFind - pCpyPara->rTabFrameArr.begin();
+ if( itFind == pCpyPara->rTabFrameArr.end() || !(*itFind == aFindFrame) )
+ {
+ // For nested copying, also save the new Format as an old one.
+ SwTableBoxFormat* pNewFormat = static_cast<SwTableBoxFormat*>(pBox->ClaimFrameFormat());
+
+ // Find the selected Boxes in the Line:
+ FndLine_ const* pCmpLine = nullptr;
+ SwFormatFrameSize aFrameSz( pNewFormat->GetFrameSize() );
+
+ bool bDiffCount = false;
+ if( !pBox->GetTabLines().empty() )
+ {
+ pCmpLine = rFndBox.GetLines().front().get();
+ if ( pCmpLine->GetBoxes().size() != pCmpLine->GetLine()->GetTabBoxes().size() )
+ bDiffCount = true;
+ }
+
+ if( bDiffCount )
+ {
+ // The first Line should be enough
+ FndBoxes_t const& rFndBoxes = pCmpLine->GetBoxes();
+ tools::Long nSz = 0;
+ for( auto n = rFndBoxes.size(); n; )
+ {
+ nSz += rFndBoxes[--n]->GetBox()->
+ GetFrameFormat()->GetFrameSize().GetWidth();
+ }
+ aFrameSz.SetWidth( aFrameSz.GetWidth() -
+ nSz / ( pCpyPara->nCpyCnt + 1 ) );
+ pNewFormat->SetFormatAttr( aFrameSz );
+ aFrameSz.SetWidth( nSz / ( pCpyPara->nCpyCnt + 1 ) );
+
+ // Create a new Format for the new Box, specifying its size.
+ aFindFrame.pNewFrameFormat = reinterpret_cast<SwTableBoxFormat*>(pNewFormat->GetDoc()->
+ MakeTableLineFormat());
+ *aFindFrame.pNewFrameFormat = *pNewFormat;
+ aFindFrame.pNewFrameFormat->SetFormatAttr( aFrameSz );
+ }
+ else
+ {
+ aFrameSz.SetWidth( aFrameSz.GetWidth() / ( pCpyPara->nCpyCnt + 1 ) );
+ pNewFormat->SetFormatAttr( aFrameSz );
+
+ aFindFrame.pNewFrameFormat = pNewFormat;
+ pCpyPara->rTabFrameArr.insert( aFindFrame );
+ aFindFrame.pFrameFormat = pNewFormat;
+ pCpyPara->rTabFrameArr.insert( aFindFrame );
+ }
+ }
+ else
+ {
+ aFindFrame = pCpyPara->rTabFrameArr[ nFndPos ];
+ pBox->ChgFrameFormat( aFindFrame.pNewFrameFormat );
+ }
+ }
+ else
+ {
+ CpyTabFrames::const_iterator itFind = pCpyPara->rTabFrameArr.find( aFindFrame );
+ if( pCpyPara->nDelBorderFlag &&
+ itFind != pCpyPara->rTabFrameArr.end() )
+ aFindFrame = *itFind;
+ else
+ aFindFrame.pNewFrameFormat = static_cast<SwTableBoxFormat*>(pBox->GetFrameFormat());
+ }
+
+ if (!rFndBox.GetLines().empty())
+ {
+ pBox = new SwTableBox( aFindFrame.pNewFrameFormat,
+ rFndBox.GetLines().size(), pCpyPara->pInsLine );
+ pCpyPara->pInsLine->GetTabBoxes().insert( pCpyPara->pInsLine->GetTabBoxes().begin() + pCpyPara->nInsPos++, pBox );
+ CpyPara aPara( *pCpyPara, pBox );
+ aPara.nDelBorderFlag &= 7;
+
+ for (auto const& pFndLine : rFndBox.GetLines())
+ {
+ lcl_CopyRow(*pFndLine, &aPara);
+ }
+ }
+ else
+ {
+ ::InsTableBox( pCpyPara->rDoc, pCpyPara->pTableNd, pCpyPara->pInsLine,
+ aFindFrame.pNewFrameFormat, pBox, pCpyPara->nInsPos++ );
+
+ const FndBoxes_t& rFndBxs = rFndBox.GetUpper()->GetBoxes();
+ if( 8 > pCpyPara->nDelBorderFlag
+ ? pCpyPara->nDelBorderFlag != 0
+ : &rFndBox == rFndBxs[rFndBxs.size() - 1].get())
+ {
+ const SvxBoxItem& rBoxItem = pBox->GetFrameFormat()->GetBox();
+ if( 8 > pCpyPara->nDelBorderFlag
+ ? rBoxItem.GetTop()
+ : rBoxItem.GetRight() )
+ {
+ aFindFrame.pFrameFormat = pBox->GetFrameFormat();
+
+ SvxBoxItem aNew( rBoxItem );
+ if( 8 > pCpyPara->nDelBorderFlag )
+ aNew.SetLine( nullptr, SvxBoxItemLine::TOP );
+ else
+ aNew.SetLine( nullptr, SvxBoxItemLine::RIGHT );
+
+ if( 1 == pCpyPara->nDelBorderFlag ||
+ 8 == pCpyPara->nDelBorderFlag )
+ {
+ // For all Boxes that delete TopBorderLine, we copy after that
+ pBox = pCpyPara->pInsLine->GetTabBoxes()[
+ pCpyPara->nInsPos - 1 ];
+ }
+
+ aFindFrame.pNewFrameFormat = static_cast<SwTableBoxFormat*>(pBox->GetFrameFormat());
+
+ // Else we copy before that and the first Line keeps the TopLine
+ // and we remove it at the original
+ pBox->ClaimFrameFormat()->SetFormatAttr( aNew );
+
+ if( !pCpyPara->nCpyCnt )
+ pCpyPara->rTabFrameArr.insert( aFindFrame );
+ }
+ }
+ }
+}
+
+static SwTableLine* lcl_CopyRow(FndLine_& rFndLine, CpyPara *const pCpyPara)
+{
+ SwTableLine* pNewLine = new SwTableLine(
+ static_cast<SwTableLineFormat*>(rFndLine.GetLine()->GetFrameFormat()),
+ rFndLine.GetBoxes().size(), pCpyPara->pInsBox );
+ if( pCpyPara->pInsBox )
+ {
+ SwTableLines& rLines = pCpyPara->pInsBox->GetTabLines();
+ rLines.insert( rLines.begin() + pCpyPara->nInsPos++, pNewLine );
+ }
+ else
+ {
+ SwTableLines& rLines = pCpyPara->pTableNd->GetTable().GetTabLines();
+ rLines.insert( rLines.begin() + pCpyPara->nInsPos++, pNewLine );
+ }
+
+ CpyPara aPara( *pCpyPara, pNewLine );
+ for (auto const& it : rFndLine.GetBoxes())
+ {
+ lcl_CopyCol(*it, &aPara);
+ }
+
+ pCpyPara->nDelBorderFlag &= 0xf8;
+
+ return pNewLine;
+}
+
+static void lcl_InsCol( FndLine_* pFndLn, CpyPara& rCpyPara, sal_uInt16 nCpyCnt,
+ bool bBehind )
+{
+ // Bug 29124: Not only copy in the BaseLines. If possible, we go down as far as possible
+ FndBox_* pFBox;
+ if( 1 == pFndLn->GetBoxes().size() &&
+ !( pFBox = pFndLn->GetBoxes()[0].get() )->GetBox()->GetSttNd() )
+ {
+ // A Box with multiple Lines, so insert into these Lines
+ for (auto &rpLine : pFBox->GetLines())
+ {
+ lcl_InsCol( rpLine.get(), rCpyPara, nCpyCnt, bBehind );
+ }
+ }
+ else
+ {
+ rCpyPara.pInsLine = pFndLn->GetLine();
+ SwTableBox* pBox = pFndLn->GetBoxes()[ bBehind ?
+ pFndLn->GetBoxes().size()-1 : 0 ]->GetBox();
+ rCpyPara.nInsPos = pFndLn->GetLine()->GetBoxPos( pBox );
+ if( bBehind )
+ ++rCpyPara.nInsPos;
+
+ for( sal_uInt16 n = 0; n < nCpyCnt; ++n )
+ {
+ if( n + 1 == nCpyCnt && bBehind )
+ rCpyPara.nDelBorderFlag = 9;
+ else
+ rCpyPara.nDelBorderFlag = 8;
+ for (auto const& it : pFndLn->GetBoxes())
+ {
+ lcl_CopyCol(*it, &rCpyPara);
+ }
+ }
+ }
+}
+
+static SwRowFrame* GetRowFrame( SwTableLine& rLine )
+{
+ SwIterator<SwRowFrame,SwFormat> aIter( *rLine.GetFrameFormat() );
+ for( SwRowFrame* pFrame = aIter.First(); pFrame; pFrame = aIter.Next() )
+ if( pFrame->GetTabLine() == &rLine )
+ return pFrame;
+ return nullptr;
+}
+
+bool SwTable::InsertCol( SwDoc& rDoc, const SwSelBoxes& rBoxes, sal_uInt16 nCnt,
+ bool bBehind, bool bInsertDummy )
+{
+ OSL_ENSURE( !rBoxes.empty() && nCnt, "No valid Box List" );
+ SwTableNode* pTableNd = const_cast<SwTableNode*>(rBoxes[0]->GetSttNd()->FindTableNode());
+ if( !pTableNd )
+ return false;
+
+ bool bRes = true;
+ if( IsNewModel() )
+ bRes = NewInsertCol( rDoc, rBoxes, nCnt, bBehind, bInsertDummy );
+ else
+ {
+ // Find all Boxes/Lines
+ FndBox_ aFndBox( nullptr, nullptr );
+ {
+ FndPara aPara( rBoxes, &aFndBox );
+ ForEach_FndLineCopyCol( GetTabLines(), &aPara );
+ }
+ if( aFndBox.GetLines().empty() )
+ return false;
+
+ SetHTMLTableLayout(std::shared_ptr<SwHTMLTableLayout>()); // Delete HTML Layout
+
+ // Find Lines for the layout update
+ aFndBox.SetTableLines( *this );
+ aFndBox.DelFrames( *this );
+
+ // TL_CHART2: nothing to be done since chart2 currently does not want to
+ // get notified about new rows/cols.
+
+ CpyTabFrames aTabFrameArr;
+ CpyPara aCpyPara( pTableNd, nCnt, aTabFrameArr );
+
+ for (auto & rpLine : aFndBox.GetLines())
+ {
+ lcl_InsCol( rpLine.get(), aCpyPara, nCnt, bBehind );
+ }
+
+ // clean up this Line's structure once again, generally all of them
+ GCLines();
+
+ // Update Layout
+ aFndBox.MakeFrames( *this );
+
+ CHECKBOXWIDTH;
+ CHECKTABLELAYOUT;
+ bRes = true;
+ }
+
+ SwChartDataProvider *pPCD = rDoc.getIDocumentChartDataProviderAccess().GetChartDataProvider();
+ if (pPCD && nCnt)
+ pPCD->AddRowCols( *this, rBoxes, nCnt, bBehind );
+ rDoc.UpdateCharts( GetFrameFormat()->GetName() );
+
+ if (SwFEShell* pFEShell = rDoc.GetDocShell()->GetFEShell())
+ pFEShell->UpdateTableStyleFormatting();
+
+ return bRes;
+}
+
+bool SwTable::InsertRow_( SwDoc* pDoc, const SwSelBoxes& rBoxes,
+ sal_uInt16 nCnt, bool bBehind, bool bInsertDummy )
+{
+ OSL_ENSURE( pDoc && !rBoxes.empty() && nCnt, "No valid Box List" );
+ SwTableNode* pTableNd = const_cast<SwTableNode*>(rBoxes[0]->GetSttNd()->FindTableNode());
+ if( !pTableNd )
+ return false;
+
+ // Find all Boxes/Lines
+ FndBox_ aFndBox( nullptr, nullptr );
+ {
+ FndPara aPara( rBoxes, &aFndBox );
+ ForEach_FndLineCopyCol( GetTabLines(), &aPara );
+ }
+ if( aFndBox.GetLines().empty() )
+ return false;
+
+ SetHTMLTableLayout(std::shared_ptr<SwHTMLTableLayout>()); // Delete HTML Layout
+
+ FndBox_* pFndBox = &aFndBox;
+ {
+ FndLine_* pFndLine;
+ while( 1 == pFndBox->GetLines().size() )
+ {
+ pFndLine = pFndBox->GetLines()[0].get();
+ if( 1 != pFndLine->GetBoxes().size() )
+ break;
+ // Don't go down too far! One Line with Box needs to remain!
+ FndBox_ *const pTmpBox = pFndLine->GetBoxes().front().get();
+ if( !pTmpBox->GetLines().empty() )
+ pFndBox = pTmpBox;
+ else
+ break;
+ }
+ }
+
+ // Find Lines for the layout update
+ const bool bLayout = !IsNewModel() &&
+ nullptr != SwIterator<SwTabFrame,SwFormat>( *GetFrameFormat() ).First();
+
+ if ( bLayout )
+ {
+ aFndBox.SetTableLines( *this );
+ if( pFndBox != &aFndBox )
+ aFndBox.DelFrames( *this );
+ // TL_CHART2: nothing to be done since chart2 currently does not want to
+ // get notified about new rows/cols.
+ }
+
+ CpyTabFrames aTabFrameArr;
+ CpyPara aCpyPara( pTableNd, 0, aTabFrameArr );
+
+ SwTableLine* pLine = pFndBox->GetLines()[ bBehind ?
+ pFndBox->GetLines().size()-1 : 0 ]->GetLine();
+ if( &aFndBox == pFndBox )
+ aCpyPara.nInsPos = GetTabLines().GetPos( pLine );
+ else
+ {
+ aCpyPara.pInsBox = pFndBox->GetBox();
+ aCpyPara.nInsPos = pFndBox->GetBox()->GetTabLines().GetPos( pLine );
+ }
+
+ if( bBehind )
+ {
+ ++aCpyPara.nInsPos;
+ aCpyPara.nDelBorderFlag = 1;
+ }
+ else
+ aCpyPara.nDelBorderFlag = 2;
+
+ for( sal_uInt16 nCpyCnt = 0; nCpyCnt < nCnt; ++nCpyCnt )
+ {
+ if( bBehind )
+ aCpyPara.nDelBorderFlag = 1;
+ for (auto & rpFndLine : pFndBox->GetLines())
+ {
+ SwTableLine* pNewTableLine = lcl_CopyRow( *rpFndLine, &aCpyPara );
+
+ // tracked insertion of empty table line
+ if ( pDoc->getIDocumentRedlineAccess().IsRedlineOn() )
+ {
+ SvxPrintItem aSetTracking(RES_PRINT, false);
+ SwPosition aPos(*pNewTableLine->GetTabBoxes()[0]->GetSttNd());
+ SwCursor aCursor( aPos, nullptr );
+ if ( bInsertDummy )
+ {
+ SwPaM aPaM(*pNewTableLine->GetTabBoxes()[0]->GetSttNd(), SwNodeOffset(1));
+ pDoc->getIDocumentContentOperations().InsertString( aPaM,
+ OUStringChar(CH_TXT_TRACKED_DUMMY_CHAR) );
+ }
+ pDoc->SetRowNotTracked( aCursor, aSetTracking, /*bAll=*/false, /*bIns=*/true );
+ }
+ }
+ }
+
+ // clean up this Line's structure once again, generally all of them
+ if( !pDoc->IsInReading() )
+ GCLines();
+
+ // Update Layout
+ if ( bLayout )
+ {
+ if( pFndBox != &aFndBox )
+ aFndBox.MakeFrames( *this );
+ else
+ aFndBox.MakeNewFrames( *this, nCnt, bBehind );
+ }
+
+ CHECKBOXWIDTH;
+ CHECKTABLELAYOUT;
+
+ SwChartDataProvider *pPCD = pDoc->getIDocumentChartDataProviderAccess().GetChartDataProvider();
+ if (pPCD && nCnt)
+ pPCD->AddRowCols( *this, rBoxes, nCnt, bBehind );
+ pDoc->UpdateCharts( GetFrameFormat()->GetName() );
+
+ if (SwFEShell* pFEShell = pDoc->GetDocShell()->GetFEShell())
+ pFEShell->UpdateTableStyleFormatting(pTableNd);
+
+ return true;
+}
+
+static void lcl_LastBoxSetWidth( SwTableBoxes &rBoxes, const tools::Long nOffset,
+ bool bFirst, SwShareBoxFormats& rShareFormats );
+
+static void lcl_LastBoxSetWidthLine( SwTableLines &rLines, const tools::Long nOffset,
+ bool bFirst, SwShareBoxFormats& rShareFormats )
+{
+ for ( auto pLine : rLines )
+ ::lcl_LastBoxSetWidth( pLine->GetTabBoxes(), nOffset, bFirst, rShareFormats );
+}
+
+static void lcl_LastBoxSetWidth( SwTableBoxes &rBoxes, const tools::Long nOffset,
+ bool bFirst, SwShareBoxFormats& rShareFormats )
+{
+ SwTableBox& rBox = *(bFirst ? rBoxes.front() : rBoxes.back());
+ if( !rBox.GetSttNd() )
+ ::lcl_LastBoxSetWidthLine( rBox.GetTabLines(), nOffset,
+ bFirst, rShareFormats );
+
+ // Adapt the Box
+ const SwFrameFormat *pBoxFormat = rBox.GetFrameFormat();
+ SwFormatFrameSize aNew( pBoxFormat->GetFrameSize() );
+ aNew.SetWidth( aNew.GetWidth() + nOffset );
+ SwFrameFormat *pFormat = rShareFormats.GetFormat( *pBoxFormat, aNew );
+ if( pFormat )
+ rBox.ChgFrameFormat( static_cast<SwTableBoxFormat*>(pFormat) );
+ else
+ {
+ pFormat = rBox.ClaimFrameFormat();
+
+ pFormat->LockModify();
+ pFormat->SetFormatAttr( aNew );
+ pFormat->UnlockModify();
+
+ rShareFormats.AddFormat( *pBoxFormat, *pFormat );
+ }
+}
+
+void DeleteBox_( SwTable& rTable, SwTableBox* pBox, SwUndo* pUndo,
+ bool bCalcNewSize, const bool bCorrBorder,
+ SwShareBoxFormats* pShareFormats )
+{
+ do {
+ SwTwips nBoxSz = bCalcNewSize ?
+ pBox->GetFrameFormat()->GetFrameSize().GetWidth() : 0;
+ SwTableLine* pLine = pBox->GetUpper();
+ SwTableBoxes& rTableBoxes = pLine->GetTabBoxes();
+ sal_uInt16 nDelPos = pLine->GetBoxPos( pBox );
+ SwTableBox* pUpperBox = pBox->GetUpper()->GetUpper();
+
+ // Special treatment for the border:
+ if( bCorrBorder && 1 < rTableBoxes.size() )
+ {
+ const SvxBoxItem& rBoxItem = pBox->GetFrameFormat()->GetBox();
+
+ if( rBoxItem.GetLeft() || rBoxItem.GetRight() )
+ {
+ bool bChgd = false;
+
+ // JP 02.04.97: 1st part for Bug 36271
+ // First the left/right edges
+ if( nDelPos + 1 < o3tl::narrowing<sal_uInt16>(rTableBoxes.size()) )
+ {
+ SwTableBox* pNxtBox = rTableBoxes[ nDelPos + 1 ];
+ const SvxBoxItem& rNxtBoxItem = pNxtBox->GetFrameFormat()->GetBox();
+
+ SwTableBox* pPrvBox = nDelPos ? rTableBoxes[ nDelPos - 1 ] : nullptr;
+
+ if( pNxtBox->GetSttNd() && !rNxtBoxItem.GetLeft() &&
+ ( !pPrvBox || !pPrvBox->GetFrameFormat()->GetBox().GetRight()) )
+ {
+ SvxBoxItem aTmp( rNxtBoxItem );
+ aTmp.SetLine( rBoxItem.GetLeft() ? rBoxItem.GetLeft()
+ : rBoxItem.GetRight(),
+ SvxBoxItemLine::LEFT );
+ if( pShareFormats )
+ pShareFormats->SetAttr( *pNxtBox, aTmp );
+ else
+ pNxtBox->ClaimFrameFormat()->SetFormatAttr( aTmp );
+ bChgd = true;
+ }
+ }
+ if( !bChgd && nDelPos )
+ {
+ SwTableBox* pPrvBox = rTableBoxes[ nDelPos - 1 ];
+ const SvxBoxItem& rPrvBoxItem = pPrvBox->GetFrameFormat()->GetBox();
+
+ SwTableBox* pNxtBox = nDelPos + 1 < o3tl::narrowing<sal_uInt16>(rTableBoxes.size())
+ ? rTableBoxes[ nDelPos + 1 ] : nullptr;
+
+ if( pPrvBox->GetSttNd() && !rPrvBoxItem.GetRight() &&
+ ( !pNxtBox || !pNxtBox->GetFrameFormat()->GetBox().GetLeft()) )
+ {
+ SvxBoxItem aTmp( rPrvBoxItem );
+ aTmp.SetLine( rBoxItem.GetLeft() ? rBoxItem.GetLeft()
+ : rBoxItem.GetRight(),
+ SvxBoxItemLine::RIGHT );
+ if( pShareFormats )
+ pShareFormats->SetAttr( *pPrvBox, aTmp );
+ else
+ pPrvBox->ClaimFrameFormat()->SetFormatAttr( aTmp );
+ }
+ }
+ }
+ }
+
+ // Delete the Box first, then the Nodes!
+ SwStartNode* pSttNd = const_cast<SwStartNode*>(pBox->GetSttNd());
+ if( pShareFormats )
+ pShareFormats->RemoveFormat( *rTableBoxes[ nDelPos ]->GetFrameFormat() );
+
+ // Before deleting the 'Table Box' from memory - delete any redlines attached to it
+ rTable.GetFrameFormat()->GetDoc()->getIDocumentRedlineAccess().GetExtraRedlineTable().DeleteTableCellRedline( rTable.GetFrameFormat()->GetDoc(), *(rTableBoxes[nDelPos]), true, RedlineType::Any );
+ delete rTableBoxes[nDelPos];
+ rTableBoxes.erase( rTableBoxes.begin() + nDelPos );
+
+ if( pSttNd )
+ {
+ // Has the UndoObject been prepared to save the Section?
+ if( pUndo && pUndo->IsDelBox() )
+ static_cast<SwUndoTableNdsChg*>(pUndo)->SaveSection( pSttNd );
+ else
+ pSttNd->GetDoc().getIDocumentContentOperations().DeleteSection( pSttNd );
+ }
+
+ // Also delete the Line?
+ if( !rTableBoxes.empty() )
+ {
+ // Then adapt the Frame-SSize
+ bool bLastBox = nDelPos == rTableBoxes.size();
+ if( bLastBox )
+ --nDelPos;
+ pBox = rTableBoxes[nDelPos];
+ if( bCalcNewSize )
+ {
+ SwFormatFrameSize aNew( pBox->GetFrameFormat()->GetFrameSize() );
+ aNew.SetWidth( aNew.GetWidth() + nBoxSz );
+ if( pShareFormats )
+ pShareFormats->SetSize( *pBox, aNew );
+ else
+ pBox->ClaimFrameFormat()->SetFormatAttr( aNew );
+
+ if( !pBox->GetSttNd() )
+ {
+ // We need to this recursively in all Lines in all Cells!
+ SwShareBoxFormats aShareFormats;
+ ::lcl_LastBoxSetWidthLine( pBox->GetTabLines(), nBoxSz,
+ !bLastBox,
+ pShareFormats ? *pShareFormats
+ : aShareFormats );
+ }
+ }
+ break; // Stop deleting
+ }
+ // Delete the Line from the Table/Box
+ if( !pUpperBox )
+ {
+ // Also delete the Line from the Table
+ nDelPos = rTable.GetTabLines().GetPos( pLine );
+ if( pShareFormats )
+ pShareFormats->RemoveFormat( *rTable.GetTabLines()[ nDelPos ]->GetFrameFormat() );
+
+ SwTableLine* pTabLineToDelete = rTable.GetTabLines()[ nDelPos ];
+ // Before deleting the 'Table Line' from memory - delete any redlines attached to it
+ rTable.GetFrameFormat()->GetDoc()->getIDocumentRedlineAccess().GetExtraRedlineTable().DeleteTableRowRedline( rTable.GetFrameFormat()->GetDoc(), *pTabLineToDelete, true, RedlineType::Any );
+ delete pTabLineToDelete;
+ rTable.GetTabLines().erase( rTable.GetTabLines().begin() + nDelPos );
+ break; // we cannot delete more
+ }
+
+ // finally also delete the Line
+ pBox = pUpperBox;
+ nDelPos = pBox->GetTabLines().GetPos( pLine );
+ if( pShareFormats )
+ pShareFormats->RemoveFormat( *pBox->GetTabLines()[ nDelPos ]->GetFrameFormat() );
+
+ SwTableLine* pTabLineToDelete = pBox->GetTabLines()[ nDelPos ];
+ // Before deleting the 'Table Line' from memory - delete any redlines attached to it
+ rTable.GetFrameFormat()->GetDoc()->getIDocumentRedlineAccess().GetExtraRedlineTable().DeleteTableRowRedline( rTable.GetFrameFormat()->GetDoc(), *pTabLineToDelete, true, RedlineType::Any );
+ delete pTabLineToDelete;
+ pBox->GetTabLines().erase( pBox->GetTabLines().begin() + nDelPos );
+ } while( pBox->GetTabLines().empty() );
+}
+
+static SwTableBox*
+lcl_FndNxtPrvDelBox( const SwTableLines& rTableLns,
+ SwTwips nBoxStt, SwTwips nBoxWidth,
+ sal_uInt16 nLinePos, bool bNxt,
+ SwSelBoxes* pAllDelBoxes, size_t *const pCurPos)
+{
+ SwTableBox* pFndBox = nullptr;
+ do {
+ if( bNxt )
+ ++nLinePos;
+ else
+ --nLinePos;
+ SwTableLine* pLine = rTableLns[ nLinePos ];
+ SwTwips nFndBoxWidth = 0;
+ SwTwips nFndWidth = nBoxStt + nBoxWidth;
+
+ pFndBox = pLine->GetTabBoxes()[ 0 ];
+ for( auto pBox : pLine->GetTabBoxes() )
+ {
+ if ( nFndWidth <= 0 )
+ {
+ break;
+ }
+ pFndBox = pBox;
+ nFndBoxWidth = pFndBox->GetFrameFormat()->GetFrameSize().GetWidth();
+ nFndWidth -= nFndBoxWidth;
+ }
+
+ // Find the first ContentBox
+ while( !pFndBox->GetSttNd() )
+ {
+ const SwTableLines& rLowLns = pFndBox->GetTabLines();
+ if( bNxt )
+ pFndBox = rLowLns.front()->GetTabBoxes().front();
+ else
+ pFndBox = rLowLns.back()->GetTabBoxes().front();
+ }
+
+ if( std::abs( nFndWidth ) > COLFUZZY ||
+ std::abs( nBoxWidth - nFndBoxWidth ) > COLFUZZY )
+ pFndBox = nullptr;
+ else if( pAllDelBoxes )
+ {
+ // If the predecessor will also be deleted, there's nothing to do
+ SwSelBoxes::const_iterator aFndIt = pAllDelBoxes->find( pFndBox);
+ if( aFndIt == pAllDelBoxes->end() )
+ break;
+ size_t const nFndPos = aFndIt - pAllDelBoxes->begin() ;
+
+ // else, we keep on searching.
+ // We do not need to recheck the Box, however
+ pFndBox = nullptr;
+ if( nFndPos <= *pCurPos )
+ --*pCurPos;
+ pAllDelBoxes->erase( pAllDelBoxes->begin() + nFndPos );
+ }
+ } while( bNxt ? ( nLinePos + 1 < o3tl::narrowing<sal_uInt16>(rTableLns.size()) ) : nLinePos != 0 );
+ return pFndBox;
+}
+
+static void
+lcl_SaveUpperLowerBorder( SwTable& rTable, const SwTableBox& rBox,
+ SwShareBoxFormats& rShareFormats,
+ SwSelBoxes* pAllDelBoxes = nullptr,
+ size_t *const pCurPos = nullptr )
+{
+//JP 16.04.97: 2. part for Bug 36271
+ const SwTableLine* pLine = rBox.GetUpper();
+ const SwTableBoxes& rTableBoxes = pLine->GetTabBoxes();
+ const SwTableBox* pUpperBox = &rBox;
+ sal_uInt16 nDelPos = pLine->GetBoxPos( pUpperBox );
+ pUpperBox = rBox.GetUpper()->GetUpper();
+ const SvxBoxItem& rBoxItem = rBox.GetFrameFormat()->GetBox();
+
+ // then the top/bottom edges
+ if( !rBoxItem.GetTop() && !rBoxItem.GetBottom() )
+ return;
+
+ bool bChgd = false;
+ const SwTableLines* pTableLns;
+ if( pUpperBox )
+ pTableLns = &pUpperBox->GetTabLines();
+ else
+ pTableLns = &rTable.GetTabLines();
+
+ sal_uInt16 nLnPos = pTableLns->GetPos( pLine );
+
+ // Calculate the attribute position of the top-be-deleted Box and then
+ // search in the top/bottom Line of the respective counterparts.
+ SwTwips nBoxStt = 0;
+ for( sal_uInt16 n = 0; n < nDelPos; ++n )
+ nBoxStt += rTableBoxes[ n ]->GetFrameFormat()->GetFrameSize().GetWidth();
+ SwTwips nBoxWidth = rBox.GetFrameFormat()->GetFrameSize().GetWidth();
+
+ SwTableBox *pPrvBox = nullptr, *pNxtBox = nullptr;
+ if( nLnPos ) // Predecessor?
+ pPrvBox = ::lcl_FndNxtPrvDelBox( *pTableLns, nBoxStt, nBoxWidth,
+ nLnPos, false, pAllDelBoxes, pCurPos );
+
+ if( nLnPos + 1 < o3tl::narrowing<sal_uInt16>(pTableLns->size()) ) // Successor?
+ pNxtBox = ::lcl_FndNxtPrvDelBox( *pTableLns, nBoxStt, nBoxWidth,
+ nLnPos, true, pAllDelBoxes, pCurPos );
+
+ if( pNxtBox && pNxtBox->GetSttNd() )
+ {
+ const SvxBoxItem& rNxtBoxItem = pNxtBox->GetFrameFormat()->GetBox();
+ if( !rNxtBoxItem.GetTop() && ( !pPrvBox ||
+ !pPrvBox->GetFrameFormat()->GetBox().GetBottom()) )
+ {
+ SvxBoxItem aTmp( rNxtBoxItem );
+ aTmp.SetLine( rBoxItem.GetTop() ? rBoxItem.GetTop()
+ : rBoxItem.GetBottom(),
+ SvxBoxItemLine::TOP );
+ rShareFormats.SetAttr( *pNxtBox, aTmp );
+ bChgd = true;
+ }
+ }
+ if( !(!bChgd && pPrvBox && pPrvBox->GetSttNd()) )
+ return;
+
+ const SvxBoxItem& rPrvBoxItem = pPrvBox->GetFrameFormat()->GetBox();
+ if( !rPrvBoxItem.GetTop() && ( !pNxtBox ||
+ !pNxtBox->GetFrameFormat()->GetBox().GetTop()) )
+ {
+ SvxBoxItem aTmp( rPrvBoxItem );
+ aTmp.SetLine( rBoxItem.GetTop() ? rBoxItem.GetTop()
+ : rBoxItem.GetBottom(),
+ SvxBoxItemLine::BOTTOM );
+ rShareFormats.SetAttr( *pPrvBox, aTmp );
+ }
+
+}
+
+bool SwTable::DeleteSel(
+ SwDoc* pDoc
+ ,
+ const SwSelBoxes& rBoxes,
+ const SwSelBoxes* pMerged, SwUndo* pUndo,
+ const bool bDelMakeFrames, const bool bCorrBorder )
+{
+ OSL_ENSURE( pDoc, "No doc?" );
+ SwTableNode* pTableNd = nullptr;
+ if( !rBoxes.empty() )
+ {
+ pTableNd = const_cast<SwTableNode*>(rBoxes[0]->GetSttNd()->FindTableNode());
+ if( !pTableNd )
+ return false;
+ }
+
+ SetHTMLTableLayout(std::shared_ptr<SwHTMLTableLayout>()); // Delete HTML Layout
+
+ // Find Lines for the Layout update
+ FndBox_ aFndBox( nullptr, nullptr );
+ if ( bDelMakeFrames )
+ {
+ if( pMerged && !pMerged->empty() )
+ aFndBox.SetTableLines( *pMerged, *this );
+ else if( !rBoxes.empty() )
+ aFndBox.SetTableLines( rBoxes, *this );
+ aFndBox.DelFrames( *this );
+ }
+
+ SwShareBoxFormats aShareFormats;
+
+ // First switch the Border, then delete
+ if( bCorrBorder )
+ {
+ SwSelBoxes aBoxes( rBoxes );
+ for (size_t n = 0; n < aBoxes.size(); ++n)
+ {
+ ::lcl_SaveUpperLowerBorder( *this, *rBoxes[ n ], aShareFormats,
+ &aBoxes, &n );
+ }
+ }
+
+ PrepareDelBoxes( rBoxes );
+
+ SwChartDataProvider *pPCD = pDoc->getIDocumentChartDataProviderAccess().GetChartDataProvider();
+ // Delete boxes from last to first
+ for (size_t n = 0; n < rBoxes.size(); ++n)
+ {
+ size_t const nIdx = rBoxes.size() - 1 - n;
+
+ // First adapt the data-sequence for chart if necessary
+ // (needed to move the implementation cursor properly to its new
+ // position which can't be done properly if the cell is already gone)
+ if (pPCD && pTableNd)
+ pPCD->DeleteBox( &pTableNd->GetTable(), *rBoxes[nIdx] );
+
+ // ... then delete the boxes
+ DeleteBox_( *this, rBoxes[nIdx], pUndo, true, bCorrBorder, &aShareFormats );
+ }
+
+ // then clean up the structure of all Lines
+ GCLines();
+
+ if( bDelMakeFrames && aFndBox.AreLinesToRestore( *this ) )
+ aFndBox.MakeFrames( *this );
+
+ // TL_CHART2: now inform chart that sth has changed
+ pDoc->UpdateCharts( GetFrameFormat()->GetName() );
+
+ CHECKTABLELAYOUT;
+ CHECK_TABLE( *this );
+
+ return true;
+}
+
+bool SwTable::OldSplitRow( SwDoc& rDoc, const SwSelBoxes& rBoxes, sal_uInt16 nCnt,
+ bool bSameHeight )
+{
+ OSL_ENSURE( !rBoxes.empty() && nCnt, "No valid values" );
+ SwTableNode* pTableNd = const_cast<SwTableNode*>(rBoxes[0]->GetSttNd()->FindTableNode());
+ if( !pTableNd )
+ return false;
+
+ // TL_CHART2: splitting/merging of a number of cells or rows will usually make
+ // the table too complex to be handled with chart.
+ // Thus we tell the charts to use their own data provider and forget about this table
+ rDoc.getIDocumentChartDataProviderAccess().CreateChartInternalDataProviders( this );
+
+ SetHTMLTableLayout(std::shared_ptr<SwHTMLTableLayout>()); // Delete HTML Layout
+
+ // If the rows should get the same (min) height, we first have
+ // to store the old row heights before deleting the frames
+ std::unique_ptr<tools::Long[]> pRowHeights;
+ if ( bSameHeight )
+ {
+ pRowHeights.reset(new tools::Long[ rBoxes.size() ]);
+ for (size_t n = 0; n < rBoxes.size(); ++n)
+ {
+ SwTableBox* pSelBox = rBoxes[n];
+ const SwRowFrame* pRow = GetRowFrame( *pSelBox->GetUpper() );
+ OSL_ENSURE( pRow, "Where is the SwTableLine's Frame?" );
+ SwRectFnSet aRectFnSet(pRow);
+ pRowHeights[ n ] = aRectFnSet.GetHeight(pRow->getFrameArea());
+ }
+ }
+
+ // Find Lines for the Layout update
+ FndBox_ aFndBox( nullptr, nullptr );
+ aFndBox.SetTableLines( rBoxes, *this );
+ aFndBox.DelFrames( *this );
+
+ for (size_t n = 0; n < rBoxes.size(); ++n)
+ {
+ SwTableBox* pSelBox = rBoxes[n];
+ OSL_ENSURE( pSelBox, "Box is not within the Table" );
+
+ // Insert nCnt new Lines into the Box
+ SwTableLine* pInsLine = pSelBox->GetUpper();
+ SwTableBoxFormat* pFrameFormat = static_cast<SwTableBoxFormat*>(pSelBox->GetFrameFormat());
+
+ // Respect the Line's height, reset if needed
+ SwFormatFrameSize aFSz( pInsLine->GetFrameFormat()->GetFrameSize() );
+ if ( bSameHeight && SwFrameSize::Variable == aFSz.GetHeightSizeType() )
+ aFSz.SetHeightSizeType( SwFrameSize::Minimum );
+
+ bool bChgLineSz = 0 != aFSz.GetHeight() || bSameHeight;
+ if ( bChgLineSz )
+ aFSz.SetHeight( ( bSameHeight ? pRowHeights[ n ] : aFSz.GetHeight() ) /
+ (nCnt + 1) );
+
+ SwTableBox* pNewBox = new SwTableBox( pFrameFormat, nCnt, pInsLine );
+ sal_uInt16 nBoxPos = pInsLine->GetBoxPos( pSelBox );
+ pInsLine->GetTabBoxes()[nBoxPos] = pNewBox; // overwrite old one
+
+ // Delete background/border attribute
+ SwTableBox* pLastBox = pSelBox; // To distribute the TextNodes!
+ // If Areas are contained in the Box, it stays as is
+ // !! If this is changed we need to adapt the Undo, too !!!
+ bool bMoveNodes = true;
+ {
+ SwNodeOffset nSttNd = pLastBox->GetSttIdx() + 1,
+ nEndNd = pLastBox->GetSttNd()->EndOfSectionIndex();
+ while( nSttNd < nEndNd )
+ if( !rDoc.GetNodes()[ nSttNd++ ]->IsTextNode() )
+ {
+ bMoveNodes = false;
+ break;
+ }
+ }
+
+ SwTableBoxFormat* pCpyBoxFrameFormat = static_cast<SwTableBoxFormat*>(pSelBox->GetFrameFormat());
+ bool bChkBorder = nullptr != pCpyBoxFrameFormat->GetBox().GetTop();
+ if( bChkBorder )
+ pCpyBoxFrameFormat = static_cast<SwTableBoxFormat*>(pSelBox->ClaimFrameFormat());
+
+ for( sal_uInt16 i = 0; i <= nCnt; ++i )
+ {
+ // Create a new Line in the new Box
+ SwTableLine* pNewLine = new SwTableLine(
+ static_cast<SwTableLineFormat*>(pInsLine->GetFrameFormat()), 1, pNewBox );
+ if( bChgLineSz )
+ {
+ pNewLine->ClaimFrameFormat()->SetFormatAttr( aFSz );
+ }
+
+ pNewBox->GetTabLines().insert( pNewBox->GetTabLines().begin() + i, pNewLine );
+ // then a new Box in the Line
+ if( !i ) // hang up the original Box
+ {
+ pSelBox->SetUpper( pNewLine );
+ pNewLine->GetTabBoxes().insert( pNewLine->GetTabBoxes().begin(), pSelBox );
+ }
+ else
+ {
+ ::InsTableBox( rDoc, pTableNd, pNewLine, pCpyBoxFrameFormat,
+ pLastBox, 0 );
+
+ if( bChkBorder )
+ {
+ pCpyBoxFrameFormat = static_cast<SwTableBoxFormat*>(pNewLine->GetTabBoxes()[ 0 ]->ClaimFrameFormat());
+ SvxBoxItem aTmp( pCpyBoxFrameFormat->GetBox() );
+ aTmp.SetLine( nullptr, SvxBoxItemLine::TOP );
+ pCpyBoxFrameFormat->SetFormatAttr( aTmp );
+ bChkBorder = false;
+ }
+
+ if( bMoveNodes )
+ {
+ const SwNode* pEndNd = pLastBox->GetSttNd()->EndOfSectionNode();
+ if( pLastBox->GetSttIdx()+SwNodeOffset(2) != pEndNd->GetIndex() )
+ {
+ // Move TextNodes
+ SwNodeRange aRg( *pLastBox->GetSttNd(), SwNodeOffset(+2), *pEndNd );
+ pLastBox = pNewLine->GetTabBoxes()[0]; // reset
+ SwNodeIndex aInsPos( *pLastBox->GetSttNd(), 1 );
+ rDoc.GetNodes().MoveNodes(aRg, rDoc.GetNodes(), aInsPos.GetNode(), false);
+ rDoc.GetNodes().Delete( aInsPos ); // delete the empty one
+ }
+ }
+ }
+ }
+ // In Boxes with Lines, we can only have Size/Fillorder
+ pFrameFormat = static_cast<SwTableBoxFormat*>(pNewBox->ClaimFrameFormat());
+ pFrameFormat->ResetFormatAttr( RES_LR_SPACE, RES_FRMATR_END - 1 );
+ pFrameFormat->ResetFormatAttr( RES_BOXATR_BEGIN, RES_BOXATR_END - 1 );
+ }
+
+ pRowHeights.reset();
+
+ GCLines();
+
+ aFndBox.MakeFrames( *this );
+
+ CHECKBOXWIDTH
+ CHECKTABLELAYOUT
+ return true;
+}
+
+bool SwTable::SplitCol(SwDoc& rDoc, const SwSelBoxes& rBoxes, sal_uInt16 nCnt)
+{
+ OSL_ENSURE( !rBoxes.empty() && nCnt, "No valid values" );
+ SwTableNode* pTableNd = const_cast<SwTableNode*>(rBoxes[0]->GetSttNd()->FindTableNode());
+ if( !pTableNd )
+ return false;
+
+ // TL_CHART2: splitting/merging of a number of cells or rows will usually make
+ // the table too complex to be handled with chart.
+ // Thus we tell the charts to use their own data provider and forget about this table
+ rDoc.getIDocumentChartDataProviderAccess().CreateChartInternalDataProviders( this );
+
+ SetHTMLTableLayout(std::shared_ptr<SwHTMLTableLayout>()); // Delete HTML Layout
+ SwSelBoxes aSelBoxes(rBoxes);
+ ExpandSelection( aSelBoxes );
+
+ // Find Lines for the Layout update
+ FndBox_ aFndBox( nullptr, nullptr );
+ aFndBox.SetTableLines( aSelBoxes, *this );
+ aFndBox.DelFrames( *this );
+
+ CpyTabFrames aFrameArr;
+ std::vector<SwTableBoxFormat*> aLastBoxArr;
+ for (size_t n = 0; n < aSelBoxes.size(); ++n)
+ {
+ SwTableBox* pSelBox = aSelBoxes[n];
+ OSL_ENSURE( pSelBox, "Box is not in the table" );
+
+ // We don't want to split small table cells into very very small cells
+ if( pSelBox->GetFrameFormat()->GetFrameSize().GetWidth()/( nCnt + 1 ) < 10 )
+ continue;
+
+ // Then split the nCnt Box up into nCnt Boxes
+ SwTableLine* pInsLine = pSelBox->GetUpper();
+ sal_uInt16 nBoxPos = pInsLine->GetBoxPos( pSelBox );
+
+ // Find the Frame Format in the Frame Format Array
+ SwTableBoxFormat* pLastBoxFormat;
+ CpyTabFrame aFindFrame( static_cast<SwTableBoxFormat*>(pSelBox->GetFrameFormat()) );
+ CpyTabFrames::const_iterator itFind = aFrameArr.lower_bound( aFindFrame );
+ const size_t nFndPos = itFind - aFrameArr.begin();
+ if( itFind == aFrameArr.end() || !(*itFind == aFindFrame) )
+ {
+ // Change the FrameFormat
+ aFindFrame.pNewFrameFormat = static_cast<SwTableBoxFormat*>(pSelBox->ClaimFrameFormat());
+ SwTwips nBoxSz = aFindFrame.pNewFrameFormat->GetFrameSize().GetWidth();
+ SwTwips nNewBoxSz = nBoxSz / ( nCnt + 1 );
+ aFindFrame.pNewFrameFormat->SetFormatAttr( SwFormatFrameSize( SwFrameSize::Variable,
+ nNewBoxSz, 0 ) );
+ aFrameArr.insert( aFindFrame );
+
+ pLastBoxFormat = aFindFrame.pNewFrameFormat;
+ if( nBoxSz != ( nNewBoxSz * (nCnt + 1)))
+ {
+ // We have a remainder, so we need to define an own Format
+ // for the last Box.
+ pLastBoxFormat = new SwTableBoxFormat( *aFindFrame.pNewFrameFormat );
+ pLastBoxFormat->SetFormatAttr( SwFormatFrameSize( SwFrameSize::Variable,
+ nBoxSz - ( nNewBoxSz * nCnt ), 0 ) );
+ }
+ aLastBoxArr.insert( aLastBoxArr.begin() + nFndPos, pLastBoxFormat );
+ }
+ else
+ {
+ aFindFrame = aFrameArr[ nFndPos ];
+ pSelBox->ChgFrameFormat( aFindFrame.pNewFrameFormat );
+ pLastBoxFormat = aLastBoxArr[ nFndPos ];
+ }
+
+ // Insert the Boxes at the Position
+ for( sal_uInt16 i = 1; i < nCnt; ++i )
+ ::InsTableBox( rDoc, pTableNd, pInsLine, aFindFrame.pNewFrameFormat,
+ pSelBox, nBoxPos + i ); // insert after
+
+ ::InsTableBox( rDoc, pTableNd, pInsLine, pLastBoxFormat,
+ pSelBox, nBoxPos + nCnt ); // insert after
+
+ // Special treatment for the Border:
+ const SvxBoxItem& aSelBoxItem = aFindFrame.pNewFrameFormat->GetBox();
+ if( aSelBoxItem.GetRight() )
+ {
+ pInsLine->GetTabBoxes()[ nBoxPos + nCnt ]->ClaimFrameFormat();
+
+ SvxBoxItem aTmp( aSelBoxItem );
+ aTmp.SetLine( nullptr, SvxBoxItemLine::RIGHT );
+ aFindFrame.pNewFrameFormat->SetFormatAttr( aTmp );
+
+ // Remove the Format from the "cache"
+ for( auto i = aFrameArr.size(); i; )
+ {
+ const CpyTabFrame& rCTF = aFrameArr[ --i ];
+ if( rCTF.pNewFrameFormat == aFindFrame.pNewFrameFormat ||
+ rCTF.pFrameFormat == aFindFrame.pNewFrameFormat )
+ {
+ aFrameArr.erase( aFrameArr.begin() + i );
+ aLastBoxArr.erase( aLastBoxArr.begin() + i );
+ }
+ }
+ }
+ }
+
+ // Update Layout
+ aFndBox.MakeFrames( *this );
+
+ CHECKBOXWIDTH
+ CHECKTABLELAYOUT
+ return true;
+}
+
+/*
+ * >> MERGE <<
+ * Algorithm:
+ * If we only have one Line in the FndBox_, take this Line and test
+ * the Box count:
+ * If we have more than one Box, we merge on Box level, meaning
+ * the new Box will be as wide as the old ones.
+ * All Lines that are above/under the Area, are inserted into
+ * the Box as Line + Box.
+ * All Lines that come before/after the Area, are inserted into
+ * the Boxes Left/Right.
+ *
+ * >> MERGE <<
+ */
+static void lcl_CpyLines( sal_uInt16 nStt, sal_uInt16 nEnd,
+ SwTableLines& rLines,
+ SwTableBox* pInsBox,
+ sal_uInt16 nPos = USHRT_MAX )
+{
+ for( sal_uInt16 n = nStt; n < nEnd; ++n )
+ rLines[n]->SetUpper( pInsBox );
+ if( USHRT_MAX == nPos )
+ nPos = pInsBox->GetTabLines().size();
+ pInsBox->GetTabLines().insert( pInsBox->GetTabLines().begin() + nPos,
+ rLines.begin() + nStt, rLines.begin() + nEnd );
+ rLines.erase( rLines.begin() + nStt, rLines.begin() + nEnd );
+}
+
+static void lcl_CpyBoxes( sal_uInt16 nStt, sal_uInt16 nEnd,
+ SwTableBoxes& rBoxes,
+ SwTableLine* pInsLine )
+{
+ for( sal_uInt16 n = nStt; n < nEnd; ++n )
+ rBoxes[n]->SetUpper( pInsLine );
+ sal_uInt16 nPos = pInsLine->GetTabBoxes().size();
+ pInsLine->GetTabBoxes().insert( pInsLine->GetTabBoxes().begin() + nPos,
+ rBoxes.begin() + nStt, rBoxes.begin() + nEnd );
+ rBoxes.erase( rBoxes.begin() + nStt, rBoxes.begin() + nEnd );
+}
+
+static void lcl_CalcWidth( SwTableBox* pBox )
+{
+ // Assertion: Every Line in the Box is as large
+ SwFrameFormat* pFormat = pBox->ClaimFrameFormat();
+ OSL_ENSURE( pBox->GetTabLines().size(), "Box does not have any Lines" );
+
+ SwTableLine* pLine = pBox->GetTabLines()[0];
+ OSL_ENSURE( pLine, "Box is not within a Line" );
+
+ tools::Long nWidth = 0;
+ for( auto pTabBox : pLine->GetTabBoxes() )
+ nWidth += pTabBox->GetFrameFormat()->GetFrameSize().GetWidth();
+
+ pFormat->SetFormatAttr( SwFormatFrameSize( SwFrameSize::Variable, nWidth, 0 ));
+
+ // Boxes with Lines can only have Size/Fillorder
+ pFormat->ResetFormatAttr( RES_LR_SPACE, RES_FRMATR_END - 1 );
+ pFormat->ResetFormatAttr( RES_BOXATR_BEGIN, RES_BOXATR_END - 1 );
+}
+
+namespace {
+
+struct InsULPara
+{
+ SwTableNode* pTableNd;
+ SwTableLine* pInsLine;
+ SwTableBox* pInsBox;
+ bool bUL_LR : 1; // Upper-Lower(true) or Left-Right(false) ?
+ bool bUL : 1; // Upper-Left(true) or Lower-Right(false) ?
+
+ SwTableBox* pLeftBox;
+
+ InsULPara( SwTableNode* pTNd,
+ SwTableBox* pLeft,
+ SwTableLine* pLine )
+ : pTableNd( pTNd ), pInsLine( pLine ), pInsBox( nullptr ),
+ pLeftBox( pLeft )
+ { bUL_LR = true; bUL = true; }
+
+ void SetLeft( SwTableBox* pBox )
+ { bUL_LR = false; bUL = true; if( pBox ) pInsBox = pBox; }
+ void SetRight( SwTableBox* pBox )
+ { bUL_LR = false; bUL = false; if( pBox ) pInsBox = pBox; }
+ void SetLower( SwTableLine* pLine )
+ { bUL_LR = true; bUL = false; if( pLine ) pInsLine = pLine; }
+};
+
+}
+
+static void lcl_Merge_MoveLine(FndLine_ & rFndLine, InsULPara *const pULPara);
+
+static void lcl_Merge_MoveBox(FndBox_ & rFndBox, InsULPara *const pULPara)
+{
+ SwTableBoxes* pBoxes;
+
+ sal_uInt16 nStt = 0, nEnd = rFndBox.GetLines().size();
+ sal_uInt16 nInsPos = USHRT_MAX;
+ if( !pULPara->bUL_LR ) // Left/Right
+ {
+ sal_uInt16 nPos;
+ SwTableBox* pFndTableBox = rFndBox.GetBox();
+ pBoxes = &pFndTableBox->GetUpper()->GetTabBoxes();
+ if( pULPara->bUL ) // Left ?
+ {
+ // if there are Boxes before it, move them
+ nPos = pFndTableBox->GetUpper()->GetBoxPos( pFndTableBox );
+ if( 0 != nPos )
+ lcl_CpyBoxes( 0, nPos, *pBoxes, pULPara->pInsLine );
+ }
+ else // Right
+ {
+ // if there are Boxes behind it, move them
+ nPos = pFndTableBox->GetUpper()->GetBoxPos( pFndTableBox );
+ if( nPos +1 < o3tl::narrowing<sal_uInt16>(pBoxes->size()) )
+ {
+ nInsPos = pULPara->pInsLine->GetTabBoxes().size();
+ lcl_CpyBoxes( nPos+1, pBoxes->size(),
+ *pBoxes, pULPara->pInsLine );
+ }
+ }
+ }
+ // Upper/Lower and still deeper?
+ else if (!rFndBox.GetLines().empty())
+ {
+ // Only search the Line from which we need to move
+ nStt = pULPara->bUL ? 0 : rFndBox.GetLines().size()-1;
+ nEnd = nStt+1;
+ }
+
+ pBoxes = &pULPara->pInsLine->GetTabBoxes();
+
+ // Is there still a level to step down to?
+ if (rFndBox.GetBox()->GetTabLines().empty())
+ return;
+
+ SwTableBox* pBox = new SwTableBox(
+ static_cast<SwTableBoxFormat*>(rFndBox.GetBox()->GetFrameFormat()),
+ 0, pULPara->pInsLine );
+ InsULPara aPara( *pULPara );
+ aPara.pInsBox = pBox;
+ for (FndLines_t::iterator it = rFndBox.GetLines().begin() + nStt;
+ it != rFndBox.GetLines().begin() + nEnd; ++it )
+ {
+ lcl_Merge_MoveLine(**it, &aPara);
+ }
+ if( !pBox->GetTabLines().empty() )
+ {
+ if( USHRT_MAX == nInsPos )
+ nInsPos = pBoxes->size();
+ pBoxes->insert( pBoxes->begin() + nInsPos, pBox );
+ lcl_CalcWidth( pBox ); // calculate the Box's width
+ }
+ else
+ delete pBox;
+}
+
+static void lcl_Merge_MoveLine(FndLine_& rFndLine, InsULPara *const pULPara)
+{
+ SwTableLines* pLines;
+
+ sal_uInt16 nStt = 0, nEnd = rFndLine.GetBoxes().size();
+ sal_uInt16 nInsPos = USHRT_MAX;
+ if( pULPara->bUL_LR ) // UpperLower ?
+ {
+ sal_uInt16 nPos;
+ SwTableLine* pFndLn = rFndLine.GetLine();
+ pLines = pFndLn->GetUpper() ?
+ &pFndLn->GetUpper()->GetTabLines() :
+ &pULPara->pTableNd->GetTable().GetTabLines();
+
+ SwTableBox* pLBx = rFndLine.GetBoxes().front()->GetBox();
+ SwTableBox* pRBx = rFndLine.GetBoxes().back()->GetBox();
+ sal_uInt16 nLeft = pFndLn->GetBoxPos( pLBx );
+ sal_uInt16 nRight = pFndLn->GetBoxPos( pRBx );
+
+ if( !nLeft || nRight == pFndLn->GetTabBoxes().size() )
+ {
+ if( pULPara->bUL ) // Upper ?
+ {
+ // If there are Lines before it, move them
+ nPos = pLines->GetPos( pFndLn );
+ if( 0 != nPos )
+ lcl_CpyLines( 0, nPos, *pLines, pULPara->pInsBox );
+ }
+ else
+ // If there are Lines after it, move them
+ if( (nPos = pLines->GetPos( pFndLn )) + 1 < o3tl::narrowing<sal_uInt16>(pLines->size()) )
+ {
+ nInsPos = pULPara->pInsBox->GetTabLines().size();
+ lcl_CpyLines( nPos+1, pLines->size(), *pLines,
+ pULPara->pInsBox );
+ }
+ }
+ else
+ {
+ // There are still Boxes on the left side, so put the Left-
+ // and Merge-Box into one Box and Line, insert before/after
+ // a Line with a Box, into which the upper/lower Lines are
+ // inserted
+ SwTableLine* pInsLine = pULPara->pLeftBox->GetUpper();
+ SwTableBox* pLMBox = new SwTableBox(
+ static_cast<SwTableBoxFormat*>(pULPara->pLeftBox->GetFrameFormat()), 0, pInsLine );
+ SwTableLine* pLMLn = new SwTableLine(
+ static_cast<SwTableLineFormat*>(pInsLine->GetFrameFormat()), 2, pLMBox );
+ pLMLn->ClaimFrameFormat()->ResetFormatAttr( RES_FRM_SIZE );
+
+ pLMBox->GetTabLines().insert( pLMBox->GetTabLines().begin(), pLMLn );
+
+ lcl_CpyBoxes( 0, 2, pInsLine->GetTabBoxes(), pLMLn );
+
+ pInsLine->GetTabBoxes().insert( pInsLine->GetTabBoxes().begin(), pLMBox );
+
+ if( pULPara->bUL ) // Upper ?
+ {
+ // If there are Lines before it, move them
+ nPos = pLines->GetPos( pFndLn );
+ if( 0 != nPos )
+ lcl_CpyLines( 0, nPos, *pLines, pLMBox, 0 );
+ }
+ else
+ // If there are Lines after it, move them
+ if( (nPos = pLines->GetPos( pFndLn )) + 1 < o3tl::narrowing<sal_uInt16>(pLines->size()) )
+ lcl_CpyLines( nPos+1, pLines->size(), *pLines,
+ pLMBox );
+ lcl_CalcWidth( pLMBox ); // calculate the Box's width
+ }
+ }
+ // Left/Right
+ else
+ {
+ // Find only the Line from which we need to move
+ nStt = pULPara->bUL ? 0 : rFndLine.GetBoxes().size()-1;
+ nEnd = nStt+1;
+ }
+ pLines = &pULPara->pInsBox->GetTabLines();
+
+ SwTableLine* pNewLine = new SwTableLine(
+ static_cast<SwTableLineFormat*>(rFndLine.GetLine()->GetFrameFormat()), 0, pULPara->pInsBox );
+ InsULPara aPara( *pULPara ); // copying
+ aPara.pInsLine = pNewLine;
+ FndBoxes_t & rLineBoxes = rFndLine.GetBoxes();
+ for (FndBoxes_t::iterator it = rLineBoxes.begin() + nStt;
+ it != rLineBoxes.begin() + nEnd; ++it)
+ {
+ lcl_Merge_MoveBox(**it, &aPara);
+ }
+
+ if( !pNewLine->GetTabBoxes().empty() )
+ {
+ if( USHRT_MAX == nInsPos )
+ nInsPos = pLines->size();
+ pLines->insert( pLines->begin() + nInsPos, pNewLine );
+ }
+ else
+ delete pNewLine;
+}
+
+static void lcl_BoxSetHeadCondColl( const SwTableBox* pBox );
+
+bool SwTable::OldMerge( SwDoc* pDoc, const SwSelBoxes& rBoxes,
+ SwTableBox* pMergeBox, SwUndoTableMerge* pUndo )
+{
+ OSL_ENSURE( !rBoxes.empty() && pMergeBox, "no valid values" );
+ SwTableNode* pTableNd = const_cast<SwTableNode*>(rBoxes[0]->GetSttNd()->FindTableNode());
+ if( !pTableNd )
+ return false;
+
+ // Find all Boxes/Lines
+ FndBox_ aFndBox( nullptr, nullptr );
+ {
+ FndPara aPara( rBoxes, &aFndBox );
+ ForEach_FndLineCopyCol( GetTabLines(), &aPara );
+ }
+ if( aFndBox.GetLines().empty() )
+ return false;
+
+ // TL_CHART2: splitting/merging of a number of cells or rows will usually make
+ // the table too complex to be handled with chart.
+ // Thus we tell the charts to use their own data provider and forget about this table
+ pDoc->getIDocumentChartDataProviderAccess().CreateChartInternalDataProviders( this );
+
+ SetHTMLTableLayout(std::shared_ptr<SwHTMLTableLayout>()); // Delete HTML Layout
+
+ if( pUndo )
+ pUndo->SetSelBoxes( rBoxes );
+
+ // Find Lines for the Layout update
+ aFndBox.SetTableLines( *this );
+ aFndBox.DelFrames( *this );
+
+ FndBox_* pFndBox = &aFndBox;
+ while( 1 == pFndBox->GetLines().size() &&
+ 1 == pFndBox->GetLines().front()->GetBoxes().size() )
+ {
+ pFndBox = pFndBox->GetLines().front()->GetBoxes().front().get();
+ }
+
+ SwTableLine* pInsLine = new SwTableLine(
+ static_cast<SwTableLineFormat*>(pFndBox->GetLines().front()->GetLine()->GetFrameFormat()), 0,
+ !pFndBox->GetUpper() ? nullptr : pFndBox->GetBox() );
+ pInsLine->ClaimFrameFormat()->ResetFormatAttr( RES_FRM_SIZE );
+
+ // Add the new Line
+ SwTableLines* pLines = pFndBox->GetUpper() ?
+ &pFndBox->GetBox()->GetTabLines() : &GetTabLines();
+
+ SwTableLine* pNewLine = pFndBox->GetLines().front()->GetLine();
+ sal_uInt16 nInsPos = pLines->GetPos( pNewLine );
+ pLines->insert( pLines->begin() + nInsPos, pInsLine );
+
+ SwTableBox* pLeftBox = new SwTableBox( static_cast<SwTableBoxFormat*>(pMergeBox->GetFrameFormat()), 0, pInsLine );
+ SwTableBox* pRightBox = new SwTableBox( static_cast<SwTableBoxFormat*>(pMergeBox->GetFrameFormat()), 0, pInsLine );
+ pMergeBox->SetUpper( pInsLine );
+ pInsLine->GetTabBoxes().insert( pInsLine->GetTabBoxes().begin(), pLeftBox );
+ pLeftBox->ClaimFrameFormat();
+ pInsLine->GetTabBoxes().insert( pInsLine->GetTabBoxes().begin() + 1, pMergeBox);
+ pInsLine->GetTabBoxes().insert( pInsLine->GetTabBoxes().begin() + 2, pRightBox );
+ pRightBox->ClaimFrameFormat();
+
+ // This contains all Lines that are above the selected Area,
+ // thus they form a Upper/Lower Line
+ InsULPara aPara( pTableNd, pLeftBox, pInsLine );
+
+ // Move the overlapping upper/lower Lines of the selected Area
+ for (auto & it : pFndBox->GetLines().front()->GetBoxes())
+ {
+ lcl_Merge_MoveBox(*it, &aPara);
+ }
+ aPara.SetLower( pInsLine );
+ const auto nEnd = pFndBox->GetLines().size()-1;
+ for (auto & it : pFndBox->GetLines()[nEnd]->GetBoxes())
+ {
+ lcl_Merge_MoveBox(*it, &aPara);
+ }
+
+ // Move the Boxes extending into the selected Area from left/right
+ aPara.SetLeft( pLeftBox );
+ for (auto & rpFndLine : pFndBox->GetLines())
+ {
+ lcl_Merge_MoveLine( *rpFndLine, &aPara );
+ }
+
+ aPara.SetRight( pRightBox );
+ for (auto & rpFndLine : pFndBox->GetLines())
+ {
+ lcl_Merge_MoveLine( *rpFndLine, &aPara );
+ }
+
+ if( pLeftBox->GetTabLines().empty() )
+ DeleteBox_( *this, pLeftBox, nullptr, false, false );
+ else
+ {
+ lcl_CalcWidth( pLeftBox ); // calculate the Box's width
+ if( pUndo && pLeftBox->GetSttNd() )
+ pUndo->AddNewBox( pLeftBox->GetSttIdx() );
+ }
+ if( pRightBox->GetTabLines().empty() )
+ DeleteBox_( *this, pRightBox, nullptr, false, false );
+ else
+ {
+ lcl_CalcWidth( pRightBox ); // calculate the Box's width
+ if( pUndo && pRightBox->GetSttNd() )
+ pUndo->AddNewBox( pRightBox->GetSttIdx() );
+ }
+
+ DeleteSel( pDoc, rBoxes, nullptr, nullptr, false, false );
+
+ // Clean up this Line's structure once again, generally all of them
+ GCLines();
+
+ for( const auto& rpBox : GetTabLines()[0]->GetTabBoxes() )
+ lcl_BoxSetHeadCondColl(rpBox);
+
+ aFndBox.MakeFrames( *this );
+
+ CHECKBOXWIDTH
+ CHECKTABLELAYOUT
+
+ return true;
+}
+
+static void lcl_CheckRowSpan( SwTable &rTable )
+{
+ const tools::Long nLineCount = static_cast<tools::Long>(rTable.GetTabLines().size());
+ tools::Long nMaxSpan = nLineCount;
+ tools::Long nMinSpan = 1;
+ while( nMaxSpan )
+ {
+ SwTableLine* pLine = rTable.GetTabLines()[ nLineCount - nMaxSpan ];
+ for( auto pBox : pLine->GetTabBoxes() )
+ {
+ sal_Int32 nRowSpan = pBox->getRowSpan();
+ if( nRowSpan > nMaxSpan )
+ pBox->setRowSpan( nMaxSpan );
+ else if( nRowSpan < nMinSpan )
+ pBox->setRowSpan( nMinSpan > 0 ? nMaxSpan : nMinSpan );
+ }
+ --nMaxSpan;
+ nMinSpan = -nMaxSpan;
+ }
+}
+
+static sal_uInt16 lcl_GetBoxOffset( const FndBox_& rBox )
+{
+ // Find the first Box
+ const FndBox_* pFirstBox = &rBox;
+ while (!pFirstBox->GetLines().empty())
+ {
+ pFirstBox = pFirstBox->GetLines().front()->GetBoxes().front().get();
+ }
+
+ sal_uInt16 nRet = 0;
+ // Calculate the position relative to above via the Lines
+ const SwTableBox* pBox = pFirstBox->GetBox();
+ do {
+ const SwTableBoxes& rBoxes = pBox->GetUpper()->GetTabBoxes();
+ for( auto pCmp : rBoxes )
+ {
+ if (pBox==pCmp)
+ break;
+ nRet = nRet + o3tl::narrowing<sal_uInt16>(pCmp->GetFrameFormat()->GetFrameSize().GetWidth());
+ }
+ pBox = pBox->GetUpper()->GetUpper();
+ } while( pBox );
+ return nRet;
+}
+
+static sal_uInt16 lcl_GetLineWidth( const FndLine_& rLine )
+{
+ sal_uInt16 nRet = 0;
+ for( auto n = rLine.GetBoxes().size(); n; )
+ {
+ nRet = nRet + o3tl::narrowing<sal_uInt16>(rLine.GetBoxes()[--n]->GetBox()
+ ->GetFrameFormat()->GetFrameSize().GetWidth());
+ }
+ return nRet;
+}
+
+static void lcl_CalcNewWidths(const FndLines_t& rFndLines, CpyPara& rPara)
+{
+ rPara.pWidths.reset();
+ const size_t nLineCount = rFndLines.size();
+ if( nLineCount )
+ {
+ rPara.pWidths = std::make_shared< std::vector< std::vector< sal_uLong > > >
+ ( nLineCount );
+ // First we collect information about the left/right borders of all
+ // selected cells
+ for( size_t nLine = 0; nLine < nLineCount; ++nLine )
+ {
+ std::vector< sal_uLong > &rWidth = (*rPara.pWidths)[ nLine ];
+ const FndLine_ *pFndLine = rFndLines[ nLine ].get();
+ if( pFndLine && !pFndLine->GetBoxes().empty() )
+ {
+ const SwTableLine *pLine = pFndLine->GetLine();
+ if( pLine && !pLine->GetTabBoxes().empty() )
+ {
+ size_t nBoxCount = pLine->GetTabBoxes().size();
+ sal_uLong nPos = 0;
+ // The first selected box...
+ const SwTableBox *const pSel =
+ pFndLine->GetBoxes().front()->GetBox();
+ size_t nBox = 0;
+ // Sum up the width of all boxes before the first selected box
+ while( nBox < nBoxCount )
+ {
+ SwTableBox* pBox = pLine->GetTabBoxes()[nBox++];
+ if( pBox != pSel )
+ nPos += pBox->GetFrameFormat()->GetFrameSize().GetWidth();
+ else
+ break;
+ }
+ // nPos is now the left border of the first selected box
+ if( rPara.nMinLeft > nPos )
+ rPara.nMinLeft = nPos;
+ nBoxCount = pFndLine->GetBoxes().size();
+ rWidth = std::vector< sal_uLong >( nBoxCount+2 );
+ rWidth[ 0 ] = nPos;
+ // Add now the widths of all selected boxes and store
+ // the positions in the vector
+ for( nBox = 0; nBox < nBoxCount; )
+ {
+ nPos += pFndLine->GetBoxes()[nBox]
+ ->GetBox()->GetFrameFormat()->GetFrameSize().GetWidth();
+ rWidth[ ++nBox ] = nPos;
+ }
+ // nPos: The right border of the last selected box
+ if( rPara.nMaxRight < nPos )
+ rPara.nMaxRight = nPos;
+ if( nPos <= rWidth[ 0 ] )
+ rWidth.clear();
+ }
+ }
+ }
+ }
+ // Second step: calculate the new widths for the copied cells
+ sal_uLong nSelSize = rPara.nMaxRight - rPara.nMinLeft;
+ if( !nSelSize )
+ return;
+
+ for( size_t nLine = 0; nLine < nLineCount; ++nLine )
+ {
+ std::vector< sal_uLong > &rWidth = (*rPara.pWidths)[ nLine ];
+ const size_t nCount = rWidth.size();
+ if( nCount > 2 )
+ {
+ rWidth[ nCount - 1 ] = rPara.nMaxRight;
+ sal_uLong nLastPos = 0;
+ for( size_t nBox = 0; nBox < nCount; ++nBox )
+ {
+ sal_uInt64 nNextPos = rWidth[ nBox ];
+ nNextPos -= rPara.nMinLeft;
+ nNextPos *= rPara.nNewSize;
+ nNextPos /= nSelSize;
+ rWidth[ nBox ] = static_cast<sal_uLong>(nNextPos - nLastPos);
+ nLastPos = static_cast<sal_uLong>(nNextPos);
+ }
+ }
+ }
+}
+
+static void
+lcl_CopyLineToDoc(FndLine_ const& rpFndLn, CpyPara *const pCpyPara);
+
+static void lcl_CopyBoxToDoc(FndBox_ const& rFndBox, CpyPara *const pCpyPara)
+{
+ // Calculation of new size
+ sal_uLong nRealSize;
+ sal_uLong nDummy1 = 0;
+ sal_uLong nDummy2 = 0;
+ if( pCpyPara->pTableNd->GetTable().IsNewModel() )
+ {
+ if( pCpyPara->nBoxIdx == 1 )
+ nDummy1 = (*pCpyPara->pWidths)[pCpyPara->nLnIdx][0];
+ nRealSize = (*pCpyPara->pWidths)[pCpyPara->nLnIdx][pCpyPara->nBoxIdx++];
+ if( pCpyPara->nBoxIdx == (*pCpyPara->pWidths)[pCpyPara->nLnIdx].size()-1 )
+ nDummy2 = (*pCpyPara->pWidths)[pCpyPara->nLnIdx][pCpyPara->nBoxIdx];
+ }
+ else
+ {
+ nRealSize = pCpyPara->nNewSize;
+ nRealSize *= rFndBox.GetBox()->GetFrameFormat()->GetFrameSize().GetWidth();
+ if (pCpyPara->nOldSize == 0)
+ throw o3tl::divide_by_zero();
+ nRealSize /= pCpyPara->nOldSize;
+ }
+
+ sal_uLong nSize;
+ bool bDummy = nDummy1 > 0;
+ if( bDummy )
+ nSize = nDummy1;
+ else
+ {
+ nSize = nRealSize;
+ nRealSize = 0;
+ }
+ do
+ {
+ // Find the Frame Format in the list of all Frame Formats
+ CpyTabFrame aFindFrame(static_cast<SwTableBoxFormat*>(rFndBox.GetBox()->GetFrameFormat()));
+
+ std::shared_ptr<SwFormatFrameSize> aFrameSz(std::make_shared<SwFormatFrameSize>());
+ CpyTabFrames::const_iterator itFind = pCpyPara->rTabFrameArr.lower_bound( aFindFrame );
+ const CpyTabFrames::size_type nFndPos = itFind - pCpyPara->rTabFrameArr.begin();
+
+ // It *is* sometimes cool to have multiple tests/if's and assignments
+ // in a single statement, and it is technically possible. But it is definitely
+ // not simply readable - where from my POV reading code is done 1000 times
+ // more often than writing it. Thus I dismantled the expression in smaller
+ // chunks to keep it handy/understandable/changeable (hopefully without error)
+ // The original for reference:
+ // if( itFind == pCpyPara->rTabFrameArr.end() || !(*itFind == aFindFrame) ||
+ // ( aFrameSz = ( aFindFrame = pCpyPara->rTabFrameArr[ nFndPos ]).pNewFrameFormat->
+ // GetFrameSize()).GetWidth() != static_cast<SwTwips>(nSize) )
+
+ bool DoCopyIt(itFind == pCpyPara->rTabFrameArr.end());
+
+ if(!DoCopyIt)
+ {
+ DoCopyIt = !(*itFind == aFindFrame);
+ }
+
+ if(!DoCopyIt)
+ {
+ aFindFrame = pCpyPara->rTabFrameArr[ nFndPos ];
+ aFrameSz.reset(aFindFrame.pNewFrameFormat->GetFrameSize().Clone());
+ DoCopyIt = aFrameSz->GetWidth() != static_cast<SwTwips>(nSize);
+ }
+
+ if(DoCopyIt)
+ {
+ // It doesn't exist yet, so copy it
+ aFindFrame.pNewFrameFormat = pCpyPara->rDoc.MakeTableBoxFormat();
+ aFindFrame.pNewFrameFormat->CopyAttrs( *rFndBox.GetBox()->GetFrameFormat() );
+ if( !pCpyPara->bCpyContent )
+ aFindFrame.pNewFrameFormat->ResetFormatAttr( RES_BOXATR_FORMULA, RES_BOXATR_VALUE );
+ aFrameSz->SetWidth( nSize );
+ aFindFrame.pNewFrameFormat->SetFormatAttr( *aFrameSz );
+ pCpyPara->rTabFrameArr.insert( aFindFrame );
+ }
+
+ SwTableBox* pBox;
+ if (!rFndBox.GetLines().empty())
+ {
+ pBox = new SwTableBox( aFindFrame.pNewFrameFormat,
+ rFndBox.GetLines().size(), pCpyPara->pInsLine );
+ pCpyPara->pInsLine->GetTabBoxes().insert( pCpyPara->pInsLine->GetTabBoxes().begin() + pCpyPara->nInsPos++, pBox );
+ CpyPara aPara( *pCpyPara, pBox );
+ aPara.nNewSize = nSize; // get the size
+ for (auto const& rpFndLine : rFndBox.GetLines())
+ {
+ lcl_CopyLineToDoc( *rpFndLine, &aPara );
+ }
+ }
+ else
+ {
+ // Create an empty Box
+ pCpyPara->rDoc.GetNodes().InsBoxen( pCpyPara->pTableNd, pCpyPara->pInsLine,
+ aFindFrame.pNewFrameFormat,
+ pCpyPara->rDoc.GetDfltTextFormatColl(),
+ nullptr, pCpyPara->nInsPos );
+ pBox = pCpyPara->pInsLine->GetTabBoxes()[ pCpyPara->nInsPos ];
+ if( bDummy )
+ pBox->setDummyFlag( true );
+ else if( pCpyPara->bCpyContent )
+ {
+ // Copy the content into this empty Box
+ pBox->setRowSpan(rFndBox.GetBox()->getRowSpan());
+
+ // We can also copy formulas and values, if we copy the content
+ {
+ SfxItemSetFixed<RES_BOXATR_FORMAT, RES_BOXATR_VALUE> aBoxAttrSet( pCpyPara->rDoc.GetAttrPool() );
+ aBoxAttrSet.Put(rFndBox.GetBox()->GetFrameFormat()->GetAttrSet());
+ if( aBoxAttrSet.Count() )
+ {
+ const SwTableBoxNumFormat* pItem;
+ SvNumberFormatter* pN = pCpyPara->rDoc.GetNumberFormatter( false );
+ if( pN && pN->HasMergeFormatTable() && (pItem = aBoxAttrSet.
+ GetItemIfSet( RES_BOXATR_FORMAT, false )) )
+ {
+ sal_uLong nOldIdx = pItem->GetValue();
+ sal_uLong nNewIdx = pN->GetMergeFormatIndex( nOldIdx );
+ if( nNewIdx != nOldIdx )
+ aBoxAttrSet.Put( SwTableBoxNumFormat( nNewIdx ));
+ }
+ pBox->ClaimFrameFormat()->SetFormatAttr( aBoxAttrSet );
+ }
+ }
+ SwDoc* pFromDoc = rFndBox.GetBox()->GetFrameFormat()->GetDoc();
+ SwNodeRange aCpyRg( *rFndBox.GetBox()->GetSttNd(), SwNodeOffset(1),
+ *rFndBox.GetBox()->GetSttNd()->EndOfSectionNode() );
+ SwNodeIndex aInsIdx( *pBox->GetSttNd(), 1 );
+
+ pFromDoc->GetDocumentContentOperationsManager().CopyWithFlyInFly(aCpyRg, aInsIdx.GetNode(), nullptr, false);
+ // Delete the initial TextNode
+ pCpyPara->rDoc.GetNodes().Delete( aInsIdx );
+ }
+ ++pCpyPara->nInsPos;
+ }
+ if( nRealSize )
+ {
+ bDummy = false;
+ nSize = nRealSize;
+ nRealSize = 0;
+ }
+ else
+ {
+ bDummy = true;
+ nSize = nDummy2;
+ nDummy2 = 0;
+ }
+ }
+ while( nSize );
+}
+
+static void
+lcl_CopyLineToDoc(const FndLine_& rFndLine, CpyPara *const pCpyPara)
+{
+ // Find the Frame Format in the list of all Frame Formats
+ CpyTabFrame aFindFrame( rFndLine.GetLine()->GetFrameFormat() );
+ CpyTabFrames::const_iterator itFind = pCpyPara->rTabFrameArr.find( aFindFrame );
+ if( itFind == pCpyPara->rTabFrameArr.end() )
+ {
+ // It doesn't exist yet, so copy it
+ aFindFrame.pNewFrameFormat = reinterpret_cast<SwTableBoxFormat*>(pCpyPara->rDoc.MakeTableLineFormat());
+ aFindFrame.pNewFrameFormat->CopyAttrs( *rFndLine.GetLine()->GetFrameFormat() );
+ pCpyPara->rTabFrameArr.insert( aFindFrame );
+ }
+ else
+ aFindFrame = *itFind;
+
+ SwTableLine* pNewLine = new SwTableLine( reinterpret_cast<SwTableLineFormat*>(aFindFrame.pNewFrameFormat),
+ rFndLine.GetBoxes().size(), pCpyPara->pInsBox );
+ if( pCpyPara->pInsBox )
+ {
+ SwTableLines& rLines = pCpyPara->pInsBox->GetTabLines();
+ rLines.insert( rLines.begin() + pCpyPara->nInsPos++, pNewLine );
+ }
+ else
+ {
+ SwTableLines& rLines = pCpyPara->pTableNd->GetTable().GetTabLines();
+ rLines.insert( rLines.begin() + pCpyPara->nInsPos++, pNewLine);
+ }
+
+ CpyPara aPara( *pCpyPara, pNewLine );
+
+ if( pCpyPara->pTableNd->GetTable().IsNewModel() )
+ {
+ aPara.nOldSize = 0; // will not be used
+ aPara.nBoxIdx = 1;
+ }
+ else if( rFndLine.GetBoxes().size() ==
+ rFndLine.GetLine()->GetTabBoxes().size() )
+ {
+ // Get the Parent's size
+ const SwFrameFormat* pFormat;
+
+ if( rFndLine.GetLine()->GetUpper() )
+ pFormat = rFndLine.GetLine()->GetUpper()->GetFrameFormat();
+ else
+ pFormat = pCpyPara->pTableNd->GetTable().GetFrameFormat();
+ aPara.nOldSize = pFormat->GetFrameSize().GetWidth();
+ }
+ else
+ // Calculate it
+ for (auto &rpBox : rFndLine.GetBoxes())
+ {
+ aPara.nOldSize += rpBox->GetBox()->GetFrameFormat()->GetFrameSize().GetWidth();
+ }
+
+ const FndBoxes_t& rBoxes = rFndLine.GetBoxes();
+ for (auto const& it : rBoxes)
+ {
+ lcl_CopyBoxToDoc(*it, &aPara);
+ }
+ if( pCpyPara->pTableNd->GetTable().IsNewModel() )
+ ++pCpyPara->nLnIdx;
+}
+
+void SwTable::CopyHeadlineIntoTable( SwTableNode& rTableNd )
+{
+ // Find all Boxes/Lines
+ SwSelBoxes aSelBoxes;
+ SwTableBox* pBox = GetTabSortBoxes()[ 0 ];
+ pBox = GetTableBox( pBox->GetSttNd()->StartOfSectionNode()->GetIndex() + 1 );
+ SelLineFromBox( pBox, aSelBoxes );
+
+ FndBox_ aFndBox( nullptr, nullptr );
+ {
+ FndPara aPara( aSelBoxes, &aFndBox );
+ ForEach_FndLineCopyCol( GetTabLines(), &aPara );
+ }
+ if( aFndBox.GetLines().empty() )
+ return;
+
+ SwitchFormulasToRelativeRepresentation();
+
+ CpyTabFrames aCpyFormat;
+ CpyPara aPara( &rTableNd, 1, aCpyFormat );
+ aPara.nNewSize = aPara.nOldSize = rTableNd.GetTable().GetFrameFormat()->GetFrameSize().GetWidth();
+ // Copy
+ if( IsNewModel() )
+ lcl_CalcNewWidths( aFndBox.GetLines(), aPara );
+ for (const auto & rpFndLine : aFndBox.GetLines())
+ {
+ lcl_CopyLineToDoc( *rpFndLine, &aPara );
+ }
+ if( rTableNd.GetTable().IsNewModel() )
+ { // The copied line must not contain any row span attributes > 1
+ SwTableLine* pLine = rTableNd.GetTable().GetTabLines()[0];
+ OSL_ENSURE( !pLine->GetTabBoxes().empty(), "Empty Table Line" );
+ for( auto pTableBox : pLine->GetTabBoxes() )
+ {
+ OSL_ENSURE( pTableBox, "Missing Table Box" );
+ pTableBox->setRowSpan( 1 );
+ }
+ }
+}
+
+bool SwTable::MakeCopy( SwDoc& rInsDoc, const SwPosition& rPos,
+ const SwSelBoxes& rSelBoxes,
+ bool bCpyName, const OUString& rStyleName ) const
+{
+ // Find all Boxes/Lines
+ FndBox_ aFndBox( nullptr, nullptr );
+ {
+ FndPara aPara( rSelBoxes, &aFndBox );
+ ForEach_FndLineCopyCol( const_cast<SwTableLines&>(GetTabLines()), &aPara );
+ }
+ if( aFndBox.GetLines().empty() )
+ return false;
+
+ // First copy the PoolTemplates for the Table, so that the Tables are
+ // actually copied and have valid values.
+ SwDoc* pSrcDoc = GetFrameFormat()->GetDoc();
+ if( pSrcDoc != &rInsDoc )
+ {
+ rInsDoc.CopyTextColl( *pSrcDoc->getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_TABLE ) );
+ rInsDoc.CopyTextColl( *pSrcDoc->getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_TABLE_HDLN ) );
+ }
+
+ SwTable* pNewTable = const_cast<SwTable*>(rInsDoc.InsertTable(
+ SwInsertTableOptions( SwInsertTableFlags::HeadlineNoBorder, 1 ),
+ rPos, 1, 1, GetFrameFormat()->GetHoriOrient().GetHoriOrient(),
+ nullptr, nullptr, false, IsNewModel() ));
+ if( !pNewTable )
+ return false;
+
+ SwNodeIndex aIdx( rPos.GetNode(), -1 );
+ SwTableNode* pTableNd = aIdx.GetNode().FindTableNode();
+ ++aIdx;
+ OSL_ENSURE( pTableNd, "Where is the TableNode now?" );
+
+ pTableNd->GetTable().SetRowsToRepeat( GetRowsToRepeat() );
+
+ pNewTable->SetTableStyleName(pTableNd->GetTable().GetTableStyleName());
+
+ pTableNd->GetTable().SetTableStyleName(rStyleName);
+ if( auto pSwDDETable = dynamic_cast<const SwDDETable*>(this) )
+ {
+ // A DDE-Table is being copied
+ // Does the new Document actually have it's FieldType?
+ SwFieldType* pFieldType = rInsDoc.getIDocumentFieldsAccess().InsertFieldType(
+ *pSwDDETable->GetDDEFieldType() );
+ OSL_ENSURE( pFieldType, "unknown FieldType" );
+
+ // Change the Table Pointer at the Node
+ pNewTable = new SwDDETable( *pNewTable,
+ static_cast<SwDDEFieldType*>(pFieldType) );
+ pTableNd->SetNewTable( std::unique_ptr<SwTable>(pNewTable), false );
+ }
+
+ pNewTable->GetFrameFormat()->CopyAttrs( *GetFrameFormat() );
+ pNewTable->SetTableChgMode( GetTableChgMode() );
+
+ // Destroy the already created Frames
+ pTableNd->DelFrames();
+
+ const_cast<SwTable*>(this)->SwitchFormulasToRelativeRepresentation();
+
+ SwTableNumFormatMerge aTNFM(*pSrcDoc, rInsDoc);
+
+ // Also copy Names or enforce a new unique one
+ if( bCpyName )
+ pNewTable->GetFrameFormat()->SetFormatName( GetFrameFormat()->GetName() );
+
+ CpyTabFrames aCpyFormat;
+ CpyPara aPara( pTableNd, 1, aCpyFormat );
+ aPara.nNewSize = aPara.nOldSize = GetFrameFormat()->GetFrameSize().GetWidth();
+
+ if( IsNewModel() )
+ lcl_CalcNewWidths( aFndBox.GetLines(), aPara );
+ // Copy
+ for (const auto & rpFndLine : aFndBox.GetLines())
+ {
+ lcl_CopyLineToDoc( *rpFndLine, &aPara );
+ }
+
+ // Set the "right" margin above/below
+ {
+ FndLine_* pFndLn = aFndBox.GetLines().front().get();
+ SwTableLine* pLn = pFndLn->GetLine();
+ const SwTableLine* pTmp = pLn;
+ sal_uInt16 nLnPos = GetTabLines().GetPos( pTmp );
+ if( USHRT_MAX != nLnPos && nLnPos )
+ {
+ // There is a Line before it
+ SwCollectTableLineBoxes aLnPara( false, SplitTable_HeadlineOption::BorderCopy );
+
+ pLn = GetTabLines()[ nLnPos - 1 ];
+ for( const auto& rpBox : pLn->GetTabBoxes() )
+ sw_Box_CollectBox( rpBox, &aLnPara );
+
+ if( aLnPara.Resize( lcl_GetBoxOffset( aFndBox ),
+ lcl_GetLineWidth( *pFndLn )) )
+ {
+ aLnPara.SetValues( true );
+ pLn = pNewTable->GetTabLines()[ 0 ];
+ for( const auto& rpBox : pLn->GetTabBoxes() )
+ sw_BoxSetSplitBoxFormats(rpBox, &aLnPara );
+ }
+ }
+
+ pFndLn = aFndBox.GetLines().back().get();
+ pLn = pFndLn->GetLine();
+ pTmp = pLn;
+ nLnPos = GetTabLines().GetPos( pTmp );
+ if( nLnPos < GetTabLines().size() - 1 )
+ {
+ // There is a Line following it
+ SwCollectTableLineBoxes aLnPara( true, SplitTable_HeadlineOption::BorderCopy );
+
+ pLn = GetTabLines()[ nLnPos + 1 ];
+ for( const auto& rpBox : pLn->GetTabBoxes() )
+ sw_Box_CollectBox( rpBox, &aLnPara );
+
+ if( aLnPara.Resize( lcl_GetBoxOffset( aFndBox ),
+ lcl_GetLineWidth( *pFndLn )) )
+ {
+ aLnPara.SetValues( false );
+ pLn = pNewTable->GetTabLines().back();
+ for( const auto& rpBox : pLn->GetTabBoxes() )
+ sw_BoxSetSplitBoxFormats(rpBox, &aLnPara );
+ }
+ }
+ }
+
+ // We need to delete the initial Box
+ DeleteBox_( *pNewTable, pNewTable->GetTabLines().back()->GetTabBoxes()[0],
+ nullptr, false, false );
+
+ if( pNewTable->IsNewModel() )
+ lcl_CheckRowSpan( *pNewTable );
+ // Clean up
+ pNewTable->GCLines();
+
+ pTableNd->MakeOwnFrames(); // re-generate the Frames
+
+ CHECKTABLELAYOUT
+
+ return true;
+}
+
+// Find the next Box with content from this Line
+SwTableBox* SwTableLine::FindNextBox( const SwTable& rTable,
+ const SwTableBox* pSrchBox, bool bOvrTableLns ) const
+{
+ const SwTableLine* pLine = this; // for M800
+ SwTableBox* pBox;
+ sal_uInt16 nFndPos;
+ if( !GetTabBoxes().empty() && pSrchBox )
+ {
+ nFndPos = GetBoxPos( pSrchBox );
+ if( USHRT_MAX != nFndPos &&
+ nFndPos + 1 != o3tl::narrowing<sal_uInt16>(GetTabBoxes().size()) )
+ {
+ pBox = GetTabBoxes()[ nFndPos + 1 ];
+ while( !pBox->GetTabLines().empty() )
+ pBox = pBox->GetTabLines().front()->GetTabBoxes()[0];
+ return pBox;
+ }
+ }
+
+ if( GetUpper() )
+ {
+ nFndPos = GetUpper()->GetTabLines().GetPos( pLine );
+ OSL_ENSURE( USHRT_MAX != nFndPos, "Line is not in the Table" );
+ // Is there another Line?
+ if( nFndPos+1 >= o3tl::narrowing<sal_uInt16>(GetUpper()->GetTabLines().size()) )
+ return GetUpper()->GetUpper()->FindNextBox( rTable, GetUpper(), bOvrTableLns );
+ pLine = GetUpper()->GetTabLines()[nFndPos+1];
+ }
+ else if( bOvrTableLns ) // Over a Table's the "BaseLines"??
+ {
+ // Search for the next Line in the Table
+ nFndPos = rTable.GetTabLines().GetPos( pLine );
+ if( nFndPos + 1 >= o3tl::narrowing<sal_uInt16>(rTable.GetTabLines().size()) )
+ return nullptr; // there are no more Boxes
+
+ pLine = rTable.GetTabLines()[ nFndPos+1 ];
+ }
+ else
+ return nullptr;
+
+ if( !pLine->GetTabBoxes().empty() )
+ {
+ pBox = pLine->GetTabBoxes().front();
+ while( !pBox->GetTabLines().empty() )
+ pBox = pBox->GetTabLines().front()->GetTabBoxes().front();
+ return pBox;
+ }
+ return pLine->FindNextBox( rTable, nullptr, bOvrTableLns );
+}
+
+// Find the previous Box from this Line
+SwTableBox* SwTableLine::FindPreviousBox( const SwTable& rTable,
+ const SwTableBox* pSrchBox, bool bOvrTableLns ) const
+{
+ const SwTableLine* pLine = this; // for M800
+ SwTableBox* pBox;
+ sal_uInt16 nFndPos;
+ if( !GetTabBoxes().empty() && pSrchBox )
+ {
+ nFndPos = GetBoxPos( pSrchBox );
+ if( USHRT_MAX != nFndPos && nFndPos )
+ {
+ pBox = GetTabBoxes()[ nFndPos - 1 ];
+ while( !pBox->GetTabLines().empty() )
+ {
+ pLine = pBox->GetTabLines().back();
+ pBox = pLine->GetTabBoxes().back();
+ }
+ return pBox;
+ }
+ }
+
+ if( GetUpper() )
+ {
+ nFndPos = GetUpper()->GetTabLines().GetPos( pLine );
+ OSL_ENSURE( USHRT_MAX != nFndPos, "Line is not in the Table" );
+ // Is there another Line?
+ if( !nFndPos )
+ return GetUpper()->GetUpper()->FindPreviousBox( rTable, GetUpper(), bOvrTableLns );
+ pLine = GetUpper()->GetTabLines()[nFndPos-1];
+ }
+ else if( bOvrTableLns ) // Over a Table's the "BaseLines"??
+ {
+ // Search for the next Line in the Table
+ nFndPos = rTable.GetTabLines().GetPos( pLine );
+ if( !nFndPos )
+ return nullptr; // there are no more Boxes
+
+ pLine = rTable.GetTabLines()[ nFndPos-1 ];
+ }
+ else
+ return nullptr;
+
+ if( !pLine->GetTabBoxes().empty() )
+ {
+ pBox = pLine->GetTabBoxes().back();
+ while( !pBox->GetTabLines().empty() )
+ {
+ pLine = pBox->GetTabLines().back();
+ pBox = pLine->GetTabBoxes().back();
+ }
+ return pBox;
+ }
+ return pLine->FindPreviousBox( rTable, nullptr, bOvrTableLns );
+}
+
+// Find the next Box with content from this Line
+SwTableBox* SwTableBox::FindNextBox( const SwTable& rTable,
+ const SwTableBox* pSrchBox, bool bOvrTableLns ) const
+{
+ if( !pSrchBox && GetTabLines().empty() )
+ return const_cast<SwTableBox*>(this);
+ return GetUpper()->FindNextBox( rTable, pSrchBox ? pSrchBox : this,
+ bOvrTableLns );
+
+}
+
+// Find the next Box with content from this Line
+SwTableBox* SwTableBox::FindPreviousBox( const SwTable& rTable,
+ const SwTableBox* pSrchBox ) const
+{
+ if( !pSrchBox && GetTabLines().empty() )
+ return const_cast<SwTableBox*>(this);
+ return GetUpper()->FindPreviousBox( rTable, pSrchBox ? pSrchBox : this );
+}
+
+static void lcl_BoxSetHeadCondColl( const SwTableBox* pBox )
+{
+ // We need to adapt the paragraphs with conditional templates in the HeadLine
+ const SwStartNode* pSttNd = pBox->GetSttNd();
+ if( pSttNd )
+ pSttNd->CheckSectionCondColl();
+ else
+ for( const SwTableLine* pLine : pBox->GetTabLines() )
+ sw_LineSetHeadCondColl( pLine );
+}
+
+void sw_LineSetHeadCondColl( const SwTableLine* pLine )
+{
+ for( const SwTableBox* pBox : pLine->GetTabBoxes() )
+ lcl_BoxSetHeadCondColl(pBox);
+}
+
+static SwTwips lcl_GetDistance( SwTableBox* pBox, bool bLeft )
+{
+ bool bFirst = true;
+ SwTwips nRet = 0;
+ SwTableLine* pLine;
+ while( pBox )
+ {
+ pLine = pBox->GetUpper();
+ if( !pLine )
+ break;
+ sal_uInt16 nStt = 0, nPos = pLine->GetBoxPos( pBox );
+
+ if( bFirst && !bLeft )
+ ++nPos;
+ bFirst = false;
+
+ while( nStt < nPos )
+ nRet += pLine->GetTabBoxes()[ nStt++ ]->GetFrameFormat()
+ ->GetFrameSize().GetWidth();
+ pBox = pLine->GetUpper();
+ }
+ return nRet;
+}
+
+static bool lcl_SetSelBoxWidth( SwTableLine* pLine, CR_SetBoxWidth& rParam,
+ SwTwips nDist, bool bCheck )
+{
+ SwTableBoxes& rBoxes = pLine->GetTabBoxes();
+ for( auto pBox : rBoxes )
+ {
+ SwFrameFormat* pFormat = pBox->GetFrameFormat();
+ const SwFormatFrameSize& rSz = pFormat->GetFrameSize();
+ SwTwips nWidth = rSz.GetWidth();
+ bool bGreaterBox = false;
+
+ if( bCheck )
+ {
+ for( auto pLn : pBox->GetTabLines() )
+ if( !::lcl_SetSelBoxWidth( pLn, rParam, nDist, true ))
+ return false;
+
+ // Collect all "ContentBoxes"
+ bGreaterBox = (TableChgMode::FixedWidthChangeAbs != rParam.nMode)
+ && ((nDist + (rParam.bLeft ? 0 : nWidth)) >= rParam.nSide);
+ if (bGreaterBox
+ || (!rParam.bBigger
+ && (std::abs(nDist + ((rParam.nMode != TableChgMode::FixedWidthChangeAbs && rParam.bLeft) ? 0 : nWidth) - rParam.nSide) < COLFUZZY)))
+ {
+ SwTwips nLowerDiff;
+ if( bGreaterBox && TableChgMode::FixedWidthChangeProp == rParam.nMode )
+ {
+ // The "other Boxes" have been adapted, so change by this value
+ nLowerDiff = (nDist + ( rParam.bLeft ? 0 : nWidth ) ) - rParam.nSide;
+ nLowerDiff *= rParam.nDiff;
+ nLowerDiff /= rParam.nMaxSize;
+ nLowerDiff = rParam.nDiff - nLowerDiff;
+ }
+ else
+ nLowerDiff = rParam.nDiff;
+
+ if( nWidth < nLowerDiff || nWidth - nLowerDiff < MINLAY )
+ return false;
+ }
+ }
+ else
+ {
+ SwTwips nLowerDiff = 0, nOldLower = rParam.nLowerDiff;
+ for( auto pLn : pBox->GetTabLines() )
+ {
+ rParam.nLowerDiff = 0;
+ lcl_SetSelBoxWidth( pLn, rParam, nDist, false );
+
+ if( nLowerDiff < rParam.nLowerDiff )
+ nLowerDiff = rParam.nLowerDiff;
+ }
+ rParam.nLowerDiff = nOldLower;
+
+ if( nLowerDiff ||
+ (bGreaterBox = !nOldLower && TableChgMode::FixedWidthChangeAbs != rParam.nMode &&
+ ( nDist + ( rParam.bLeft ? 0 : nWidth ) ) >= rParam.nSide) ||
+ ( std::abs( nDist + ( (rParam.nMode != TableChgMode::FixedWidthChangeAbs && rParam.bLeft) ? 0 : nWidth )
+ - rParam.nSide ) < COLFUZZY ))
+ {
+ // This column contains the Cursor - so decrease/increase
+ SwFormatFrameSize aNew( rSz );
+
+ if( !nLowerDiff )
+ {
+ if( bGreaterBox && TableChgMode::FixedWidthChangeProp == rParam.nMode )
+ {
+ // The "other Boxes" have been adapted, so change by this value
+ nLowerDiff = (nDist + ( rParam.bLeft ? 0 : nWidth ) ) - rParam.nSide;
+ nLowerDiff *= rParam.nDiff;
+ nLowerDiff /= rParam.nMaxSize;
+ nLowerDiff = rParam.nDiff - nLowerDiff;
+ }
+ else
+ nLowerDiff = rParam.nDiff;
+ }
+
+ rParam.nLowerDiff += nLowerDiff;
+
+ if( rParam.bBigger )
+ aNew.SetWidth( nWidth + nLowerDiff );
+ else
+ aNew.SetWidth( nWidth - nLowerDiff );
+ rParam.aShareFormats.SetSize( *pBox, aNew );
+ break;
+ }
+ }
+
+ if( rParam.bLeft && rParam.nMode != TableChgMode::FixedWidthChangeAbs && nDist >= rParam.nSide )
+ break;
+
+ nDist += nWidth;
+
+ // If it gets bigger, then that's it
+ if( ( TableChgMode::FixedWidthChangeAbs == rParam.nMode || !rParam.bLeft ) &&
+ nDist >= rParam.nSide )
+ break;
+ }
+ return true;
+}
+
+static bool lcl_SetOtherBoxWidth( SwTableLine* pLine, CR_SetBoxWidth& rParam,
+ SwTwips nDist, bool bCheck )
+{
+ SwTableBoxes& rBoxes = pLine->GetTabBoxes();
+ for( auto pBox : rBoxes )
+ {
+ SwFrameFormat* pFormat = pBox->GetFrameFormat();
+ const SwFormatFrameSize& rSz = pFormat->GetFrameSize();
+ SwTwips nWidth = rSz.GetWidth();
+
+ if( bCheck )
+ {
+ for( auto pLn : pBox->GetTabLines() )
+ if( !::lcl_SetOtherBoxWidth( pLn, rParam, nDist, true ))
+ return false;
+
+ if( rParam.bBigger && ( TableChgMode::FixedWidthChangeAbs == rParam.nMode
+ ? std::abs( nDist - rParam.nSide ) < COLFUZZY
+ : ( rParam.bLeft ? nDist < rParam.nSide - COLFUZZY
+ : nDist >= rParam.nSide - COLFUZZY )) )
+ {
+ SwTwips nDiff;
+ if( TableChgMode::FixedWidthChangeProp == rParam.nMode ) // Table fixed, proportional
+ {
+ // calculate relative
+ nDiff = nWidth;
+ nDiff *= rParam.nDiff;
+ nDiff /= rParam.nMaxSize;
+ }
+ else
+ nDiff = rParam.nDiff;
+
+ if( nWidth < nDiff || nWidth - nDiff < MINLAY )
+ return false;
+ }
+ }
+ else
+ {
+ SwTwips nLowerDiff = 0, nOldLower = rParam.nLowerDiff;
+ for( auto pLn : pBox->GetTabLines() )
+ {
+ rParam.nLowerDiff = 0;
+ lcl_SetOtherBoxWidth( pLn, rParam, nDist, false );
+
+ if( nLowerDiff < rParam.nLowerDiff )
+ nLowerDiff = rParam.nLowerDiff;
+ }
+ rParam.nLowerDiff = nOldLower;
+
+ if( nLowerDiff ||
+ ( TableChgMode::FixedWidthChangeAbs == rParam.nMode
+ ? std::abs( nDist - rParam.nSide ) < COLFUZZY
+ : ( rParam.bLeft ? nDist < rParam.nSide - COLFUZZY
+ : nDist >= rParam.nSide - COLFUZZY)
+ ) )
+ {
+ SwFormatFrameSize aNew( rSz );
+
+ if( !nLowerDiff )
+ {
+ if( TableChgMode::FixedWidthChangeProp == rParam.nMode ) // Table fixed, proportional
+ {
+ // calculate relative
+ nLowerDiff = nWidth;
+ nLowerDiff *= rParam.nDiff;
+ nLowerDiff /= rParam.nMaxSize;
+ }
+ else
+ nLowerDiff = rParam.nDiff;
+ }
+
+ rParam.nLowerDiff += nLowerDiff;
+
+ if( rParam.bBigger )
+ aNew.SetWidth( nWidth - nLowerDiff );
+ else
+ aNew.SetWidth( nWidth + nLowerDiff );
+
+ rParam.aShareFormats.SetSize( *pBox, aNew );
+ }
+ }
+
+ nDist += nWidth;
+ if( ( TableChgMode::FixedWidthChangeAbs == rParam.nMode || rParam.bLeft ) &&
+ nDist > rParam.nSide )
+ break;
+ }
+ return true;
+}
+
+static void lcl_AjustLines( SwTableLine* pLine, CR_SetBoxWidth& rParam )
+{
+ SwTableBoxes& rBoxes = pLine->GetTabBoxes();
+ for( auto pBox : rBoxes )
+ {
+ SwFormatFrameSize aSz( pBox->GetFrameFormat()->GetFrameSize() );
+ SwTwips nWidth = aSz.GetWidth();
+ nWidth *= rParam.nDiff;
+ nWidth /= rParam.nMaxSize;
+ aSz.SetWidth( nWidth );
+ rParam.aShareFormats.SetSize( *pBox, aSz );
+
+ for( auto pLn : pBox->GetTabLines() )
+ ::lcl_AjustLines( pLn, rParam );
+ }
+}
+
+#ifdef DBG_UTIL
+void CheckBoxWidth( const SwTableLine& rLine, SwTwips nSize )
+{
+ const SwTableBoxes& rBoxes = rLine.GetTabBoxes();
+
+ SwTwips nCurrentSize = 0;
+ // See if the tables have a correct width
+ for (const SwTableBox* pBox : rBoxes)
+ {
+ const SwTwips nBoxW = pBox->GetFrameFormat()->GetFrameSize().GetWidth();
+ nCurrentSize += nBoxW;
+
+ for( auto pLn : pBox->GetTabLines() )
+ CheckBoxWidth( *pLn, nBoxW );
+ }
+
+ if (sal::static_int_cast< tools::ULong >(std::abs(nCurrentSize - nSize)) >
+ (COLFUZZY * rBoxes.size()))
+ {
+ OSL_FAIL( "Line's Boxes are too small or too large" );
+ }
+}
+#endif
+
+bool SwTable::SetColWidth( SwTableBox& rCurrentBox, TableChgWidthHeightType eType,
+ SwTwips nAbsDiff, SwTwips nRelDiff, std::unique_ptr<SwUndo>* ppUndo )
+{
+ SetHTMLTableLayout(std::shared_ptr<SwHTMLTableLayout>()); // Delete HTML Layout
+
+ const SwFormatFrameSize& rSz = GetFrameFormat()->GetFrameSize();
+ const SvxLRSpaceItem& rLR = GetFrameFormat()->GetLRSpace();
+
+ bool bBigger,
+ bRet = false,
+ bLeft = TableChgWidthHeightType::ColLeft == extractPosition( eType ) ||
+ TableChgWidthHeightType::CellLeft == extractPosition( eType );
+
+ // Get the current Box's edge
+ // Only needed for manipulating the width
+ const SwTwips nDist = ::lcl_GetDistance( &rCurrentBox, bLeft );
+ SwTwips nDistStt = 0;
+ CR_SetBoxWidth aParam( eType, nRelDiff, nDist,
+ bLeft ? nDist : rSz.GetWidth() - nDist,
+ const_cast<SwTableNode*>(rCurrentBox.GetSttNd()->FindTableNode()) );
+ bBigger = aParam.bBigger;
+
+ FN_lcl_SetBoxWidth fnSelBox, fnOtherBox;
+ fnSelBox = lcl_SetSelBoxWidth;
+ fnOtherBox = lcl_SetOtherBoxWidth;
+
+ switch( extractPosition(eType) )
+ {
+ case TableChgWidthHeightType::ColRight:
+ case TableChgWidthHeightType::ColLeft:
+ if( TableChgMode::VarWidthChangeAbs == m_eTableChgMode )
+ {
+ // First test if we have room at all
+ bool bChgLRSpace = true;
+ if( bBigger )
+ {
+ if( GetFrameFormat()->getIDocumentSettingAccess().get(DocumentSettingId::BROWSE_MODE) &&
+ !rSz.GetWidthPercent() )
+ {
+ // silence -Wsign-compare on Android with the static cast
+ bRet = rSz.GetWidth() < static_cast<unsigned short>(USHRT_MAX) - nRelDiff;
+ bChgLRSpace = bLeft ? rLR.GetLeft() >= nAbsDiff
+ : rLR.GetRight() >= nAbsDiff;
+ }
+ else
+ bRet = bLeft ? rLR.GetLeft() >= nAbsDiff
+ : rLR.GetRight() >= nAbsDiff;
+
+ if( !bRet )
+ {
+ // Then call itself recursively; only with another mode (proportional)
+ TableChgMode eOld = m_eTableChgMode;
+ m_eTableChgMode = TableChgMode::FixedWidthChangeProp;
+
+ bRet = SetColWidth( rCurrentBox, eType, nAbsDiff, nRelDiff,
+ ppUndo );
+ m_eTableChgMode = eOld;
+ return bRet;
+ }
+ }
+ else
+ {
+ bRet = true;
+ for( auto const & n: m_aLines )
+ {
+ aParam.LoopClear();
+ if( !(*fnSelBox)( n, aParam, nDistStt, true ))
+ {
+ bRet = false;
+ break;
+ }
+ }
+ }
+
+ if( bRet )
+ {
+ if( ppUndo )
+ ppUndo->reset(new SwUndoAttrTable( *aParam.pTableNd, true ));
+
+ tools::Long nFrameWidth = LONG_MAX;
+ LockModify();
+ SwFormatFrameSize aSz( rSz );
+ SvxLRSpaceItem aLR( rLR );
+ if( bBigger )
+ {
+ // If the Table does not have any room to grow, we need to create some!
+ // silence -Wsign-compare on Android with the static cast
+ if( aSz.GetWidth() + nRelDiff > static_cast<unsigned short>(USHRT_MAX) )
+ {
+ // Break down to USHRT_MAX / 2
+ CR_SetBoxWidth aTmpPara( TableChgWidthHeightType::ColLeft, aSz.GetWidth() / 2,
+ 0, aSz.GetWidth(), aParam.pTableNd );
+ for( size_t nLn = 0; nLn < m_aLines.size(); ++nLn )
+ ::lcl_AjustLines( m_aLines[ nLn ], aTmpPara );
+ aSz.SetWidth( aSz.GetWidth() / 2 );
+ aParam.nDiff = nRelDiff /= 2;
+ aParam.nSide /= 2;
+ aParam.nMaxSize /= 2;
+ }
+
+ if( bLeft )
+ aLR.SetLeft( sal_uInt16( aLR.GetLeft() - nAbsDiff ) );
+ else
+ aLR.SetRight( sal_uInt16( aLR.GetRight() - nAbsDiff ) );
+ }
+ else if( bLeft )
+ aLR.SetLeft( sal_uInt16( aLR.GetLeft() + nAbsDiff ) );
+ else
+ aLR.SetRight( sal_uInt16( aLR.GetRight() + nAbsDiff ) );
+
+ if( bChgLRSpace )
+ GetFrameFormat()->SetFormatAttr( aLR );
+ const SwFormatHoriOrient& rHOri = GetFrameFormat()->GetHoriOrient();
+ if( text::HoriOrientation::FULL == rHOri.GetHoriOrient() ||
+ (text::HoriOrientation::LEFT == rHOri.GetHoriOrient() && aLR.GetLeft()) ||
+ (text::HoriOrientation::RIGHT == rHOri.GetHoriOrient() && aLR.GetRight()))
+ {
+ SwFormatHoriOrient aHOri( rHOri );
+ aHOri.SetHoriOrient( text::HoriOrientation::NONE );
+ GetFrameFormat()->SetFormatAttr( aHOri );
+
+ // If the Table happens to contain relative values (USHORT_MAX),
+ // we need to convert them to absolute ones now.
+ // Bug 61494
+ if( GetFrameFormat()->getIDocumentSettingAccess().get(DocumentSettingId::BROWSE_MODE) &&
+ !rSz.GetWidthPercent() )
+ {
+ SwTabFrame* pTabFrame = SwIterator<SwTabFrame,SwFormat>( *GetFrameFormat() ).First();
+ if( pTabFrame &&
+ pTabFrame->getFramePrintArea().Width() != rSz.GetWidth() )
+ {
+ nFrameWidth = pTabFrame->getFramePrintArea().Width();
+ if( bBigger )
+ nFrameWidth += nAbsDiff;
+ else
+ nFrameWidth -= nAbsDiff;
+ }
+ }
+ }
+
+ if( bBigger )
+ aSz.SetWidth( aSz.GetWidth() + nRelDiff );
+ else
+ aSz.SetWidth( aSz.GetWidth() - nRelDiff );
+
+ if( rSz.GetWidthPercent() )
+ aSz.SetWidthPercent( static_cast<sal_uInt8>(( aSz.GetWidth() * 100 ) /
+ ( aSz.GetWidth() + aLR.GetRight() + aLR.GetLeft())));
+
+ GetFrameFormat()->SetFormatAttr( aSz );
+
+ UnlockModify();
+
+ for( sal_uInt16 n = m_aLines.size(); n; )
+ {
+ --n;
+ aParam.LoopClear();
+ (*fnSelBox)( m_aLines[ n ], aParam, nDistStt, false );
+ }
+
+ // If the Table happens to contain relative values (USHORT_MAX),
+ // we need to convert them to absolute ones now.
+ // Bug 61494
+ if( LONG_MAX != nFrameWidth )
+ {
+ SwFormatFrameSize aAbsSz( aSz );
+ aAbsSz.SetWidth( nFrameWidth );
+ GetFrameFormat()->SetFormatAttr( aAbsSz );
+ }
+ }
+ }
+ else if( bLeft ? nDist != 0 : std::abs( rSz.GetWidth() - nDist ) > COLFUZZY )
+ {
+ bRet = true;
+ if( bLeft && TableChgMode::FixedWidthChangeAbs == m_eTableChgMode )
+ aParam.bBigger = !bBigger;
+
+ // First test if we have room at all
+ if( aParam.bBigger )
+ {
+ for( auto const & n: m_aLines )
+ {
+ aParam.LoopClear();
+ if( !(*fnOtherBox)( n, aParam, 0, true ))
+ {
+ bRet = false;
+ break;
+ }
+ }
+ }
+ else
+ {
+ for( auto const & n: m_aLines )
+ {
+ aParam.LoopClear();
+ if( !(*fnSelBox)( n, aParam, nDistStt, true ))
+ {
+ bRet = false;
+ break;
+ }
+ }
+ }
+
+ // If true, set it
+ if( bRet )
+ {
+ CR_SetBoxWidth aParam1( aParam );
+ if( ppUndo )
+ ppUndo->reset(new SwUndoAttrTable( *aParam.pTableNd, true ));
+
+ if( TableChgMode::FixedWidthChangeAbs != m_eTableChgMode && bLeft )
+ {
+ for( sal_uInt16 n = m_aLines.size(); n; )
+ {
+ --n;
+ aParam.LoopClear();
+ aParam1.LoopClear();
+ (*fnSelBox)( m_aLines[ n ], aParam, nDistStt, false );
+ (*fnOtherBox)( m_aLines[ n ], aParam1, nDistStt, false );
+ }
+ }
+ else
+ {
+ for( sal_uInt16 n = m_aLines.size(); n; )
+ {
+ --n;
+ aParam.LoopClear();
+ aParam1.LoopClear();
+ (*fnOtherBox)( m_aLines[ n ], aParam1, nDistStt, false );
+ (*fnSelBox)( m_aLines[ n ], aParam, nDistStt, false );
+ }
+ }
+ }
+ }
+ break;
+
+ case TableChgWidthHeightType::CellRight:
+ case TableChgWidthHeightType::CellLeft:
+ if( TableChgMode::VarWidthChangeAbs == m_eTableChgMode )
+ {
+ // Then call itself recursively; only with another mode (proportional)
+ TableChgMode eOld = m_eTableChgMode;
+ m_eTableChgMode = TableChgMode::FixedWidthChangeAbs;
+
+ bRet = SetColWidth( rCurrentBox, eType, nAbsDiff, nRelDiff,
+ ppUndo );
+ m_eTableChgMode = eOld;
+ return bRet;
+ }
+ else if( bLeft ? nDist != 0 : (rSz.GetWidth() - nDist) > COLFUZZY )
+ {
+ if( bLeft && TableChgMode::FixedWidthChangeAbs == m_eTableChgMode )
+ aParam.bBigger = !bBigger;
+
+ // First, see if there is enough room at all
+ SwTableBox* pBox = &rCurrentBox;
+ SwTableLine* pLine = rCurrentBox.GetUpper();
+ while( pLine->GetUpper() )
+ {
+ const SwTableBoxes::size_type nPos = pLine->GetBoxPos( pBox );
+ if( bLeft ? nPos != 0 : nPos + 1 != pLine->GetTabBoxes().size() )
+ break;
+
+ pBox = pLine->GetUpper();
+ pLine = pBox->GetUpper();
+ }
+
+ if( pLine->GetUpper() )
+ {
+ // We need to correct the distance once again!
+ aParam.nSide -= ::lcl_GetDistance( pLine->GetUpper(), true );
+
+ if( bLeft )
+ aParam.nMaxSize = aParam.nSide;
+ else
+ aParam.nMaxSize = pLine->GetUpper()->GetFrameFormat()->
+ GetFrameSize().GetWidth() - aParam.nSide;
+ }
+
+ // First, see if there is enough room at all
+ FN_lcl_SetBoxWidth fnTmp = aParam.bBigger ? fnOtherBox : fnSelBox;
+ bRet = (*fnTmp)( pLine, aParam, nDistStt, true );
+
+ // If true, set it
+ if( bRet )
+ {
+ CR_SetBoxWidth aParam1( aParam );
+ if( ppUndo )
+ ppUndo->reset(new SwUndoAttrTable( *aParam.pTableNd, true ));
+
+ if( TableChgMode::FixedWidthChangeAbs != m_eTableChgMode && bLeft )
+ {
+ (*fnSelBox)( pLine, aParam, nDistStt, false );
+ (*fnOtherBox)( pLine, aParam1, nDistStt, false );
+ }
+ else
+ {
+ (*fnOtherBox)( pLine, aParam1, nDistStt, false );
+ (*fnSelBox)( pLine, aParam, nDistStt, false );
+ }
+ }
+ }
+ break;
+ default: break;
+ }
+
+#if defined DBG_UTIL
+ if( bRet )
+ {
+ CHECKBOXWIDTH
+ CHECKTABLELAYOUT
+ }
+#endif
+
+ return bRet;
+}
+
+static void SetLineHeight( SwTableLine& rLine, SwTwips nOldHeight, SwTwips nNewHeight,
+ bool bMinSize )
+{
+ SwLayoutFrame* pLineFrame = GetRowFrame( rLine );
+ OSL_ENSURE( pLineFrame, "Where is the Frame from the SwTableLine?" );
+
+ SwFrameFormat* pFormat = rLine.ClaimFrameFormat();
+
+ SwTwips nMyNewH, nMyOldH = pLineFrame->getFrameArea().Height();
+ if( !nOldHeight ) // the BaseLine and absolute
+ nMyNewH = nMyOldH + nNewHeight;
+ else
+ {
+ // Calculate as exactly as possible
+ Fraction aTmp( nMyOldH );
+ aTmp *= Fraction( nNewHeight, nOldHeight );
+ aTmp += Fraction( 1, 2 ); // round up if needed
+ nMyNewH = tools::Long(aTmp);
+ }
+
+ SwFrameSize eSize = SwFrameSize::Minimum;
+ if( !bMinSize &&
+ ( nMyOldH - nMyNewH ) > ( CalcRowRstHeight( pLineFrame ) + ROWFUZZY ))
+ eSize = SwFrameSize::Fixed;
+
+ pFormat->SetFormatAttr( SwFormatFrameSize( eSize, 0, nMyNewH ) );
+
+ // First adapt all internal ones
+ for( auto pBox : rLine.GetTabBoxes() )
+ {
+ for( auto pLine : pBox->GetTabLines() )
+ SetLineHeight( *pLine, nMyOldH, nMyNewH, bMinSize );
+ }
+}
+
+static bool lcl_SetSelLineHeight( SwTableLine* pLine, const CR_SetLineHeight& rParam,
+ SwTwips nDist, bool bCheck )
+{
+ bool bRet = true;
+ if( !bCheck )
+ {
+ // Set line height
+ SetLineHeight( *pLine, 0, rParam.bBigger ? nDist : -nDist,
+ rParam.bBigger );
+ }
+ else if( !rParam.bBigger )
+ {
+ // Calculate the new relative size by means of the old one
+ SwLayoutFrame* pLineFrame = GetRowFrame( *pLine );
+ OSL_ENSURE( pLineFrame, "Where is the Frame from the SwTableLine?" );
+ SwTwips nRstHeight = CalcRowRstHeight( pLineFrame );
+ if( (nRstHeight + ROWFUZZY) < nDist )
+ bRet = false;
+ }
+ return bRet;
+}
+
+static bool lcl_SetOtherLineHeight( SwTableLine* pLine, const CR_SetLineHeight& rParam,
+ SwTwips nDist, bool bCheck )
+{
+ bool bRet = true;
+ if( bCheck )
+ {
+ if( rParam.bBigger )
+ {
+ // Calculate the new relative size by means of the old one
+ SwLayoutFrame* pLineFrame = GetRowFrame( *pLine );
+ OSL_ENSURE( pLineFrame, "Where is the Frame from the SwTableLine?" );
+
+ if( TableChgMode::FixedWidthChangeProp == rParam.nMode )
+ {
+ nDist *= pLineFrame->getFrameArea().Height();
+ nDist /= rParam.nMaxHeight;
+ }
+ bRet = nDist <= CalcRowRstHeight( pLineFrame );
+ }
+ }
+ else
+ {
+ // Set line height
+ // pLine is the following/preceding, thus adjust it
+ if( TableChgMode::FixedWidthChangeProp == rParam.nMode )
+ {
+ SwLayoutFrame* pLineFrame = GetRowFrame( *pLine );
+ OSL_ENSURE( pLineFrame, "Where is the Frame from the SwTableLine??" );
+
+ // Calculate the new relative size by means of the old one
+ // If the selected Box get bigger, adjust via the max space else
+ // via the max height.
+ if( (true) /*!rParam.bBigger*/ )
+ {
+ nDist *= pLineFrame->getFrameArea().Height();
+ nDist /= rParam.nMaxHeight;
+ }
+ else
+ {
+ // Calculate the new relative size by means of the old one
+ nDist *= CalcRowRstHeight( pLineFrame );
+ nDist /= rParam.nMaxSpace;
+ }
+ }
+ SetLineHeight( *pLine, 0, rParam.bBigger ? -nDist : nDist,
+ !rParam.bBigger );
+ }
+ return bRet;
+}
+
+bool SwTable::SetRowHeight( SwTableBox& rCurrentBox, TableChgWidthHeightType eType,
+ SwTwips nAbsDiff, SwTwips nRelDiff, std::unique_ptr<SwUndo>* ppUndo )
+{
+ SwTableLine* pLine = rCurrentBox.GetUpper();
+
+ SwTableLine* pBaseLine = pLine;
+ while( pBaseLine->GetUpper() )
+ pBaseLine = pBaseLine->GetUpper()->GetUpper();
+
+ bool bBigger,
+ bRet = false,
+ bTop = TableChgWidthHeightType::CellTop == extractPosition( eType );
+ sal_uInt16 nBaseLinePos = GetTabLines().GetPos( pBaseLine );
+
+ CR_SetLineHeight aParam( eType,
+ const_cast<SwTableNode*>(rCurrentBox.GetSttNd()->FindTableNode()) );
+ bBigger = aParam.bBigger;
+
+ SwTableLines* pLines = &m_aLines;
+
+ // How do we get to the height?
+ switch( extractPosition(eType) )
+ {
+ case TableChgWidthHeightType::CellTop:
+ case TableChgWidthHeightType::CellBottom:
+ if( pLine == pBaseLine )
+ break; // it doesn't work then!
+
+ // Is a nested Line (Box!)
+ pLines = &pLine->GetUpper()->GetTabLines();
+ nBaseLinePos = pLines->GetPos( pLine );
+ [[fallthrough]];
+
+ case TableChgWidthHeightType::RowBottom:
+ {
+ if( TableChgMode::VarWidthChangeAbs == m_eTableChgMode )
+ {
+ // First test if we have room at all
+ if( bBigger )
+ bRet = true;
+ else
+ bRet = lcl_SetSelLineHeight( (*pLines)[ nBaseLinePos ], aParam,
+ nAbsDiff, true );
+
+ if( bRet )
+ {
+ if( ppUndo )
+ ppUndo->reset(new SwUndoAttrTable( *aParam.pTableNd, true ));
+
+ lcl_SetSelLineHeight( (*pLines)[ nBaseLinePos ], aParam,
+ nAbsDiff, false );
+ }
+ }
+ else
+ {
+ bRet = true;
+ SwTableLines::size_type nStt;
+ SwTableLines::size_type nEnd;
+ if( bTop )
+ {
+ nStt = 0;
+ nEnd = nBaseLinePos;
+ }
+ else
+ {
+ nStt = nBaseLinePos + 1;
+ nEnd = pLines->size();
+ }
+
+ // Get the current Lines' height
+ if( TableChgMode::FixedWidthChangeProp == m_eTableChgMode )
+ {
+ for( auto n = nStt; n < nEnd; ++n )
+ {
+ SwLayoutFrame* pLineFrame = GetRowFrame( *(*pLines)[ n ] );
+ OSL_ENSURE( pLineFrame, "Where is the Frame from the SwTableLine??" );
+ aParam.nMaxSpace += CalcRowRstHeight( pLineFrame );
+ aParam.nMaxHeight += pLineFrame->getFrameArea().Height();
+ }
+ if( bBigger && aParam.nMaxSpace < nAbsDiff )
+ bRet = false;
+ }
+ else
+ {
+ if( bTop ? nEnd != 0 : nStt < nEnd )
+ {
+ if( bTop )
+ nStt = nEnd - 1;
+ else
+ nEnd = nStt + 1;
+ }
+ else
+ bRet = false;
+ }
+
+ if( bRet )
+ {
+ if( bBigger )
+ {
+ for( auto n = nStt; n < nEnd; ++n )
+ {
+ if( !lcl_SetOtherLineHeight( (*pLines)[ n ], aParam,
+ nAbsDiff, true ))
+ {
+ bRet = false;
+ break;
+ }
+ }
+ }
+ else
+ bRet = lcl_SetSelLineHeight( (*pLines)[ nBaseLinePos ], aParam,
+ nAbsDiff, true );
+ }
+
+ if( bRet )
+ {
+ // Adjust
+ if( ppUndo )
+ ppUndo->reset(new SwUndoAttrTable( *aParam.pTableNd, true ));
+
+ CR_SetLineHeight aParam1( aParam );
+
+ if( bTop )
+ {
+ lcl_SetSelLineHeight( (*pLines)[ nBaseLinePos ], aParam,
+ nAbsDiff, false );
+ for( auto n = nStt; n < nEnd; ++n )
+ lcl_SetOtherLineHeight( (*pLines)[ n ], aParam1,
+ nAbsDiff, false );
+ }
+ else
+ {
+ for( auto n = nStt; n < nEnd; ++n )
+ lcl_SetOtherLineHeight( (*pLines)[ n ], aParam1,
+ nAbsDiff, false );
+ lcl_SetSelLineHeight( (*pLines)[ nBaseLinePos ], aParam,
+ nAbsDiff, false );
+ }
+ }
+ else
+ {
+ // Then call itself recursively; only with another mode (proportional)
+ TableChgMode eOld = m_eTableChgMode;
+ m_eTableChgMode = TableChgMode::VarWidthChangeAbs;
+
+ bRet = SetRowHeight( rCurrentBox, eType, nAbsDiff,
+ nRelDiff, ppUndo );
+
+ m_eTableChgMode = eOld;
+ }
+ }
+ }
+ break;
+ default: break;
+ }
+
+ CHECKTABLELAYOUT
+
+ return bRet;
+}
+
+SwFrameFormat* SwShareBoxFormat::GetFormat( tools::Long nWidth ) const
+{
+ SwFrameFormat *pRet = nullptr, *pTmp;
+ for( auto n = m_aNewFormats.size(); n; )
+ if( ( pTmp = m_aNewFormats[ --n ])->GetFrameSize().GetWidth()
+ == nWidth )
+ {
+ pRet = pTmp;
+ break;
+ }
+ return pRet;
+}
+
+SwFrameFormat* SwShareBoxFormat::GetFormat( const SfxPoolItem& rItem ) const
+{
+ const SfxPoolItem* pItem;
+ sal_uInt16 nWhich = rItem.Which();
+ SwFrameFormat *pRet = nullptr, *pTmp;
+ const SwFormatFrameSize& rFrameSz = m_pOldFormat->GetFormatAttr( RES_FRM_SIZE, false );
+ for( auto n = m_aNewFormats.size(); n; )
+ if( SfxItemState::SET == ( pTmp = m_aNewFormats[ --n ])->
+ GetItemState( nWhich, false, &pItem ) && *pItem == rItem &&
+ pTmp->GetFormatAttr( RES_FRM_SIZE, false ) == rFrameSz )
+ {
+ pRet = pTmp;
+ break;
+ }
+ return pRet;
+}
+
+void SwShareBoxFormat::AddFormat( SwFrameFormat& rNew )
+{
+ m_aNewFormats.push_back( &rNew );
+}
+
+bool SwShareBoxFormat::RemoveFormat( const SwFrameFormat& rFormat )
+{
+ // returns true, if we can delete
+ if( m_pOldFormat == &rFormat )
+ return true;
+
+ std::vector<SwFrameFormat*>::iterator it = std::find( m_aNewFormats.begin(), m_aNewFormats.end(), &rFormat );
+ if( m_aNewFormats.end() != it )
+ m_aNewFormats.erase( it );
+ return m_aNewFormats.empty();
+}
+
+SwShareBoxFormats::~SwShareBoxFormats()
+{
+}
+
+SwFrameFormat* SwShareBoxFormats::GetFormat( const SwFrameFormat& rFormat, tools::Long nWidth ) const
+{
+ sal_uInt16 nPos;
+ return Seek_Entry( rFormat, &nPos )
+ ? m_ShareArr[ nPos ].GetFormat(nWidth)
+ : nullptr;
+}
+SwFrameFormat* SwShareBoxFormats::GetFormat( const SwFrameFormat& rFormat,
+ const SfxPoolItem& rItem ) const
+{
+ sal_uInt16 nPos;
+ return Seek_Entry( rFormat, &nPos )
+ ? m_ShareArr[ nPos ].GetFormat(rItem)
+ : nullptr;
+}
+
+void SwShareBoxFormats::AddFormat( const SwFrameFormat& rOld, SwFrameFormat& rNew )
+{
+ sal_uInt16 nPos;
+ if( !Seek_Entry( rOld, &nPos ))
+ {
+ SwShareBoxFormat aEntry(rOld);
+ aEntry.AddFormat( rNew );
+ m_ShareArr.insert(m_ShareArr.begin() + nPos, aEntry);
+ }
+ else
+ m_ShareArr[ nPos ].AddFormat(rNew);
+}
+
+void SwShareBoxFormats::ChangeFrameFormat( SwTableBox* pBox, SwTableLine* pLn,
+ SwFrameFormat& rFormat )
+{
+ SwClient aCl;
+ SwFrameFormat* pOld = nullptr;
+ if( pBox )
+ {
+ pOld = pBox->GetFrameFormat();
+ pOld->Add( &aCl );
+ pBox->ChgFrameFormat( static_cast<SwTableBoxFormat*>(&rFormat) );
+ }
+ else if( pLn )
+ {
+ pOld = pLn->GetFrameFormat();
+ pOld->Add( &aCl );
+ pLn->ChgFrameFormat( static_cast<SwTableLineFormat*>(&rFormat) );
+ }
+ if( pOld && pOld->HasOnlyOneListener() )
+ {
+ RemoveFormat( *pOld );
+ delete pOld;
+ }
+}
+
+void SwShareBoxFormats::SetSize( SwTableBox& rBox, const SwFormatFrameSize& rSz )
+{
+ SwFrameFormat *pBoxFormat = rBox.GetFrameFormat(),
+ *pRet = GetFormat( *pBoxFormat, rSz.GetWidth() );
+ if( pRet )
+ ChangeFrameFormat( &rBox, nullptr, *pRet );
+ else
+ {
+ pRet = rBox.ClaimFrameFormat();
+ pRet->SetFormatAttr( rSz );
+ AddFormat( *pBoxFormat, *pRet );
+ }
+}
+
+void SwShareBoxFormats::SetAttr( SwTableBox& rBox, const SfxPoolItem& rItem )
+{
+ SwFrameFormat *pBoxFormat = rBox.GetFrameFormat(),
+ *pRet = GetFormat( *pBoxFormat, rItem );
+ if( pRet )
+ ChangeFrameFormat( &rBox, nullptr, *pRet );
+ else
+ {
+ pRet = rBox.ClaimFrameFormat();
+ pRet->SetFormatAttr( rItem );
+ AddFormat( *pBoxFormat, *pRet );
+ }
+}
+
+void SwShareBoxFormats::SetAttr( SwTableLine& rLine, const SfxPoolItem& rItem )
+{
+ SwFrameFormat *pLineFormat = rLine.GetFrameFormat(),
+ *pRet = GetFormat( *pLineFormat, rItem );
+ if( pRet )
+ ChangeFrameFormat( nullptr, &rLine, *pRet );
+ else
+ {
+ pRet = rLine.ClaimFrameFormat();
+ pRet->SetFormatAttr( rItem );
+ AddFormat( *pLineFormat, *pRet );
+ }
+}
+
+void SwShareBoxFormats::RemoveFormat( const SwFrameFormat& rFormat )
+{
+ for (auto i = m_ShareArr.size(); i; )
+ {
+ if (m_ShareArr[ --i ].RemoveFormat(rFormat))
+ {
+ m_ShareArr.erase( m_ShareArr.begin() + i );
+ }
+ }
+}
+
+bool SwShareBoxFormats::Seek_Entry( const SwFrameFormat& rFormat, sal_uInt16* pPos ) const
+{
+ sal_uIntPtr nIdx = reinterpret_cast<sal_uIntPtr>(&rFormat);
+ auto nO = m_ShareArr.size();
+ decltype(nO) nU = 0;
+ if( nO > 0 )
+ {
+ nO--;
+ while( nU <= nO )
+ {
+ const auto nM = nU + ( nO - nU ) / 2;
+ sal_uIntPtr nFormat = reinterpret_cast<sal_uIntPtr>(&m_ShareArr[ nM ].GetOldFormat());
+ if( nFormat == nIdx )
+ {
+ if( pPos )
+ *pPos = nM;
+ return true;
+ }
+ else if( nFormat < nIdx )
+ nU = nM + 1;
+ else if( nM == 0 )
+ {
+ if( pPos )
+ *pPos = nU;
+ return false;
+ }
+ else
+ nO = nM - 1;
+ }
+ }
+ if( pPos )
+ *pPos = nU;
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/textboxhelper.cxx b/sw/source/core/doc/textboxhelper.cxx
new file mode 100644
index 0000000000..253e39a293
--- /dev/null
+++ b/sw/source/core/doc/textboxhelper.cxx
@@ -0,0 +1,1960 @@
+/* -*- 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/.
+ */
+
+#include <textboxhelper.hxx>
+#include <fmtcntnt.hxx>
+#include <fmtanchr.hxx>
+#include <fmtcnct.hxx>
+#include <fmtornt.hxx>
+#include <fmtfsize.hxx>
+#include <doc.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <IDocumentState.hxx>
+#include <docsh.hxx>
+#include <unocoll.hxx>
+#include <unoframe.hxx>
+#include <unodraw.hxx>
+#include <unotextrange.hxx>
+#include <cmdid.h>
+#include <unomid.h>
+#include <unoprnms.hxx>
+#include <mvsave.hxx>
+#include <fmtsrnd.hxx>
+#include <fmtfollowtextflow.hxx>
+#include <frmfmt.hxx>
+#include <frameformats.hxx>
+#include <dflyobj.hxx>
+#include <swtable.hxx>
+
+#include <editeng/unoprnms.hxx>
+#include <editeng/memberids.h>
+#include <svx/svdoashp.hxx>
+#include <svx/svdpage.hxx>
+#include <svl/itemiter.hxx>
+#include <comphelper/sequenceashashmap.hxx>
+#include <sal/log.hxx>
+#include <tools/UnitConversion.hxx>
+#include <svx/swframetypes.hxx>
+#include <drawdoc.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <IDocumentDrawModelAccess.hxx>
+#include <frmatr.hxx>
+
+#include <com/sun/star/document/XActionLockable.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <com/sun/star/text/SizeType.hpp>
+#include <com/sun/star/text/WrapTextMode.hpp>
+#include <com/sun/star/text/XTextDocument.hpp>
+#include <com/sun/star/text/XTextFrame.hpp>
+#include <com/sun/star/table/BorderLine2.hpp>
+#include <com/sun/star/text/WritingMode.hpp>
+#include <com/sun/star/text/WritingMode2.hpp>
+#include <com/sun/star/drawing/TextHorizontalAdjust.hpp>
+#include <com/sun/star/style/ParagraphAdjust.hpp>
+
+using namespace com::sun::star;
+
+void SwTextBoxHelper::create(SwFrameFormat* pShape, SdrObject* pObject, bool bCopyText)
+{
+ assert(pShape);
+ assert(pObject);
+
+ // If TextBox wasn't enabled previously
+ if (pShape->GetOtherTextBoxFormats() && pShape->GetOtherTextBoxFormats()->GetTextBox(pObject))
+ return;
+
+ // Store the current text content of the shape
+ OUString sCopyableText;
+
+ if (bCopyText)
+ {
+ if (pObject)
+ {
+ uno::Reference<text::XText> xSrcCnt(pObject->getWeakUnoShape().get(), uno::UNO_QUERY);
+ auto xCur = xSrcCnt->createTextCursor();
+ xCur->gotoStart(false);
+ xCur->gotoEnd(true);
+ sCopyableText = xCur->getText()->getString();
+ }
+ }
+
+ // Create the associated TextFrame and insert it into the document.
+ uno::Reference<text::XTextContent> xTextFrame(
+ SwXServiceProvider::MakeInstance(SwServiceType::TypeTextFrame, *pShape->GetDoc()),
+ uno::UNO_QUERY);
+ uno::Reference<text::XTextDocument> xTextDocument(
+ pShape->GetDoc()->GetDocShell()->GetBaseModel(), uno::UNO_QUERY);
+ uno::Reference<text::XTextContentAppend> xTextContentAppend(xTextDocument->getText(),
+ uno::UNO_QUERY);
+ xTextContentAppend->appendTextContent(xTextFrame, uno::Sequence<beans::PropertyValue>());
+
+ // Link FLY and DRAW formats, so it becomes a text box (needed for syncProperty calls).
+ uno::Reference<text::XTextFrame> xRealTextFrame(xTextFrame, uno::UNO_QUERY);
+ auto pTextFrame = dynamic_cast<SwXTextFrame*>(xRealTextFrame.get());
+ assert(nullptr != pTextFrame);
+ SwFrameFormat* pFormat = pTextFrame->GetFrameFormat();
+
+ assert(nullptr != dynamic_cast<SwDrawFrameFormat*>(pShape));
+ assert(nullptr != dynamic_cast<SwFlyFrameFormat*>(pFormat));
+
+ if (!pShape->GetOtherTextBoxFormats())
+ {
+ auto pTextBox = std::make_shared<SwTextBoxNode>(SwTextBoxNode(pShape));
+ pTextBox->AddTextBox(pObject, pFormat);
+ pShape->SetOtherTextBoxFormats(pTextBox);
+ pFormat->SetOtherTextBoxFormats(pTextBox);
+ }
+ else
+ {
+ auto& pTextBox = pShape->GetOtherTextBoxFormats();
+ pTextBox->AddTextBox(pObject, pFormat);
+ pFormat->SetOtherTextBoxFormats(pTextBox);
+ }
+ // Initialize properties.
+ uno::Reference<beans::XPropertySet> xPropertySet(xTextFrame, uno::UNO_QUERY);
+ uno::Any aEmptyBorder{ table::BorderLine2() };
+ xPropertySet->setPropertyValue(UNO_NAME_TOP_BORDER, aEmptyBorder);
+ xPropertySet->setPropertyValue(UNO_NAME_BOTTOM_BORDER, aEmptyBorder);
+ xPropertySet->setPropertyValue(UNO_NAME_LEFT_BORDER, aEmptyBorder);
+ xPropertySet->setPropertyValue(UNO_NAME_RIGHT_BORDER, aEmptyBorder);
+
+ xPropertySet->setPropertyValue(UNO_NAME_FILL_TRANSPARENCE, uno::Any(sal_Int32(100)));
+
+ xPropertySet->setPropertyValue(UNO_NAME_SIZE_TYPE, uno::Any(text::SizeType::FIX));
+
+ xPropertySet->setPropertyValue(UNO_NAME_SURROUND, uno::Any(text::WrapTextMode_THROUGH));
+
+ uno::Reference<container::XNamed> xNamed(xTextFrame, uno::UNO_QUERY);
+ assert(!xNamed->getName().isEmpty());
+ (void)xNamed;
+
+ // Link its text range to the original shape.
+ uno::Reference<text::XTextRange> xTextBox(xTextFrame, uno::UNO_QUERY_THROW);
+ SwUnoInternalPaM aInternalPaM(*pShape->GetDoc());
+ if (sw::XTextRangeToSwPaM(aInternalPaM, xTextBox))
+ {
+ SwAttrSet aSet(pShape->GetAttrSet());
+ SwFormatContent aContent(aInternalPaM.GetPointNode().StartOfSectionNode());
+ aSet.Put(aContent);
+ pShape->SetFormatAttr(aSet);
+ }
+
+ DoTextBoxZOrderCorrection(pShape, pObject);
+
+ // Also initialize the properties, which are not constant, but inherited from the shape's ones.
+ uno::Reference<drawing::XShape> xShape(pObject->getUnoShape(), uno::UNO_QUERY);
+ syncProperty(pShape, RES_FRM_SIZE, MID_FRMSIZE_SIZE, uno::Any(xShape->getSize()), pObject);
+
+ uno::Reference<beans::XPropertySet> xShapePropertySet(xShape, uno::UNO_QUERY);
+ syncProperty(pShape, RES_FOLLOW_TEXT_FLOW, MID_FOLLOW_TEXT_FLOW,
+ xShapePropertySet->getPropertyValue(UNO_NAME_IS_FOLLOWING_TEXT_FLOW), pObject);
+ syncProperty(pShape, RES_ANCHOR, MID_ANCHOR_ANCHORTYPE,
+ xShapePropertySet->getPropertyValue(UNO_NAME_ANCHOR_TYPE), pObject);
+ syncProperty(pShape, RES_HORI_ORIENT, MID_HORIORIENT_ORIENT,
+ xShapePropertySet->getPropertyValue(UNO_NAME_HORI_ORIENT), pObject);
+ syncProperty(pShape, RES_HORI_ORIENT, MID_HORIORIENT_RELATION,
+ xShapePropertySet->getPropertyValue(UNO_NAME_HORI_ORIENT_RELATION), pObject);
+ syncProperty(pShape, RES_VERT_ORIENT, MID_VERTORIENT_ORIENT,
+ xShapePropertySet->getPropertyValue(UNO_NAME_VERT_ORIENT), pObject);
+ syncProperty(pShape, RES_VERT_ORIENT, MID_VERTORIENT_RELATION,
+ xShapePropertySet->getPropertyValue(UNO_NAME_VERT_ORIENT_RELATION), pObject);
+ syncProperty(pShape, RES_HORI_ORIENT, MID_HORIORIENT_POSITION,
+ xShapePropertySet->getPropertyValue(UNO_NAME_HORI_ORIENT_POSITION), pObject);
+ syncProperty(pShape, RES_VERT_ORIENT, MID_VERTORIENT_POSITION,
+ xShapePropertySet->getPropertyValue(UNO_NAME_VERT_ORIENT_POSITION), pObject);
+ syncProperty(pShape, RES_FRM_SIZE, MID_FRMSIZE_IS_AUTO_HEIGHT,
+ xShapePropertySet->getPropertyValue(UNO_NAME_TEXT_AUTOGROWHEIGHT), pObject);
+ syncProperty(pShape, RES_TEXT_VERT_ADJUST, 0,
+ xShapePropertySet->getPropertyValue(UNO_NAME_TEXT_VERT_ADJUST), pObject);
+ text::WritingMode eMode;
+ if (xShapePropertySet->getPropertyValue(UNO_NAME_TEXT_WRITINGMODE) >>= eMode)
+ syncProperty(pShape, RES_FRAMEDIR, 0, uno::Any(sal_Int16(eMode)), pObject);
+
+ changeAnchor(pShape, pObject);
+ syncTextBoxSize(pShape, pObject);
+
+ // Check if the shape had text before and move it to the new textframe
+ if (!bCopyText || sCopyableText.isEmpty())
+ return;
+
+ if (pObject)
+ {
+ auto pSourceText = DynCastSdrTextObj(pObject);
+ uno::Reference<text::XTextRange> xDestText(xRealTextFrame, uno::UNO_QUERY);
+
+ xDestText->setString(sCopyableText);
+
+ if (pSourceText)
+ pSourceText->SetText(OUString());
+
+ pShape->GetDoc()->getIDocumentState().SetModified();
+ }
+}
+
+void SwTextBoxHelper::set(SwFrameFormat* pShapeFormat, SdrObject* pObj,
+ uno::Reference<text::XTextFrame> xNew)
+{
+ // Do not set invalid data
+ assert(pShapeFormat && pObj && xNew);
+ // Firstly find the format of the new textbox.
+ SwFrameFormat* pFormat = nullptr;
+ if (auto pTextFrame = dynamic_cast<SwXTextFrame*>(xNew.get()))
+ pFormat = pTextFrame->GetFrameFormat();
+ if (!pFormat)
+ return;
+
+ // If there is a format, check if the shape already has a textbox assigned to.
+ if (auto& pTextBoxNode = pShapeFormat->GetOtherTextBoxFormats())
+ {
+ // If it has a texbox, destroy it.
+ if (pTextBoxNode->GetTextBox(pObj))
+ pTextBoxNode->DelTextBox(pObj, true);
+ // And set the new one.
+ pTextBoxNode->AddTextBox(pObj, pFormat);
+ pFormat->SetOtherTextBoxFormats(pTextBoxNode);
+ }
+ else
+ {
+ // If the shape do not have a texbox node and textbox,
+ // create that for the shape.
+ auto pTextBox = std::make_shared<SwTextBoxNode>(SwTextBoxNode(pShapeFormat));
+ pTextBox->AddTextBox(pObj, pFormat);
+ pShapeFormat->SetOtherTextBoxFormats(pTextBox);
+ pFormat->SetOtherTextBoxFormats(pTextBox);
+ }
+ // Initialize its properties
+ uno::Reference<beans::XPropertySet> xPropertySet(xNew, uno::UNO_QUERY);
+ uno::Any aEmptyBorder{ table::BorderLine2() };
+ xPropertySet->setPropertyValue(UNO_NAME_TOP_BORDER, aEmptyBorder);
+ xPropertySet->setPropertyValue(UNO_NAME_BOTTOM_BORDER, aEmptyBorder);
+ xPropertySet->setPropertyValue(UNO_NAME_LEFT_BORDER, aEmptyBorder);
+ xPropertySet->setPropertyValue(UNO_NAME_RIGHT_BORDER, aEmptyBorder);
+ xPropertySet->setPropertyValue(UNO_NAME_FILL_TRANSPARENCE, uno::Any(sal_Int32(100)));
+ xPropertySet->setPropertyValue(UNO_NAME_SIZE_TYPE, uno::Any(text::SizeType::FIX));
+ xPropertySet->setPropertyValue(UNO_NAME_SURROUND, uno::Any(text::WrapTextMode_THROUGH));
+ // Add a new name to it
+ uno::Reference<container::XNamed> xNamed(xNew, uno::UNO_QUERY);
+ assert(!xNamed->getName().isEmpty());
+ (void)xNamed;
+ // And sync. properties.
+ uno::Reference<drawing::XShape> xShape(pObj->getUnoShape(), uno::UNO_QUERY);
+ syncProperty(pShapeFormat, RES_FRM_SIZE, MID_FRMSIZE_SIZE, uno::Any(xShape->getSize()), pObj);
+ uno::Reference<beans::XPropertySet> xShapePropertySet(xShape, uno::UNO_QUERY);
+ syncProperty(pShapeFormat, RES_ANCHOR, MID_ANCHOR_ANCHORTYPE,
+ xShapePropertySet->getPropertyValue(UNO_NAME_ANCHOR_TYPE), pObj);
+ syncProperty(pShapeFormat, RES_HORI_ORIENT, MID_HORIORIENT_ORIENT,
+ xShapePropertySet->getPropertyValue(UNO_NAME_HORI_ORIENT), pObj);
+ syncProperty(pShapeFormat, RES_HORI_ORIENT, MID_HORIORIENT_RELATION,
+ xShapePropertySet->getPropertyValue(UNO_NAME_HORI_ORIENT_RELATION), pObj);
+ syncProperty(pShapeFormat, RES_VERT_ORIENT, MID_VERTORIENT_ORIENT,
+ xShapePropertySet->getPropertyValue(UNO_NAME_VERT_ORIENT), pObj);
+ syncProperty(pShapeFormat, RES_VERT_ORIENT, MID_VERTORIENT_RELATION,
+ xShapePropertySet->getPropertyValue(UNO_NAME_VERT_ORIENT_RELATION), pObj);
+ syncProperty(pShapeFormat, RES_HORI_ORIENT, MID_HORIORIENT_POSITION,
+ xShapePropertySet->getPropertyValue(UNO_NAME_HORI_ORIENT_POSITION), pObj);
+ syncProperty(pShapeFormat, RES_VERT_ORIENT, MID_VERTORIENT_POSITION,
+ xShapePropertySet->getPropertyValue(UNO_NAME_VERT_ORIENT_POSITION), pObj);
+ syncProperty(pShapeFormat, RES_FRM_SIZE, MID_FRMSIZE_IS_AUTO_HEIGHT,
+ xShapePropertySet->getPropertyValue(UNO_NAME_TEXT_AUTOGROWHEIGHT), pObj);
+ drawing::TextVerticalAdjust aVertAdj = drawing::TextVerticalAdjust_CENTER;
+ if ((uno::Reference<beans::XPropertyState>(xShape, uno::UNO_QUERY_THROW))
+ ->getPropertyState(UNO_NAME_TEXT_VERT_ADJUST)
+ != beans::PropertyState::PropertyState_DEFAULT_VALUE)
+ {
+ aVertAdj = xShapePropertySet->getPropertyValue(UNO_NAME_TEXT_VERT_ADJUST)
+ .get<drawing::TextVerticalAdjust>();
+ }
+ xPropertySet->setPropertyValue(UNO_NAME_TEXT_VERT_ADJUST, uno::Any(aVertAdj));
+ text::WritingMode eMode;
+ if (xShapePropertySet->getPropertyValue(UNO_NAME_TEXT_WRITINGMODE) >>= eMode)
+ syncProperty(pShapeFormat, RES_FRAMEDIR, 0, uno::Any(sal_Int16(eMode)), pObj);
+
+ // Do sync for the new textframe.
+ synchronizeGroupTextBoxProperty(&changeAnchor, pShapeFormat, pObj);
+ synchronizeGroupTextBoxProperty(&syncTextBoxSize, pShapeFormat, pObj);
+
+ updateTextBoxMargin(pObj);
+}
+
+void SwTextBoxHelper::destroy(const SwFrameFormat* pShape, const SdrObject* pObject)
+{
+ // If a TextBox was enabled previously
+ auto& pTextBox = pShape->GetOtherTextBoxFormats();
+ if (pTextBox)
+ {
+ // Unlink the TextBox's text range from the original shape.
+ // Delete the associated TextFrame.
+ pTextBox->DelTextBox(pObject, true);
+ }
+}
+
+bool SwTextBoxHelper::isTextBox(const SwFrameFormat* pFormat, sal_uInt16 nType,
+ const SdrObject* pObject)
+{
+ DBG_TESTSOLARMUTEX();
+ assert(nType == RES_FLYFRMFMT || nType == RES_DRAWFRMFMT);
+ if (!pFormat || pFormat->Which() != nType)
+ return false;
+
+ auto& pTextBox = pFormat->GetOtherTextBoxFormats();
+ if (!pTextBox)
+ return false;
+
+ if (nType == RES_DRAWFRMFMT)
+ {
+ if (pObject)
+ return pTextBox->GetTextBox(pObject);
+ if (auto pObj = pFormat->FindRealSdrObject())
+ return pTextBox->GetTextBox(pObj);
+ }
+
+ if (nType == RES_FLYFRMFMT)
+ {
+ return pTextBox->GetOwnerShape();
+ }
+
+ return false;
+}
+
+bool SwTextBoxHelper::hasTextFrame(const SdrObject* pObj)
+{
+ if (!pObj)
+ return false;
+
+ uno::Reference<drawing::XShape> xShape(pObj->getWeakUnoShape().get(), uno::UNO_QUERY);
+ if (!xShape)
+ return false;
+ return SwTextBoxHelper::getOtherTextBoxFormat(xShape);
+}
+
+sal_Int32 SwTextBoxHelper::getCount(SdrPage const* pPage)
+{
+ sal_Int32 nRet = 0;
+ for (const rtl::Reference<SdrObject>& p : *pPage)
+ {
+ if (p && p->IsTextBox())
+ continue;
+ ++nRet;
+ }
+ return nRet;
+}
+
+sal_Int32 SwTextBoxHelper::getCount(const SwDoc& rDoc)
+{
+ sal_Int32 nRet = 0;
+ for (const sw::SpzFrameFormat* pFormat : *rDoc.GetSpzFrameFormats())
+ {
+ if (isTextBox(pFormat, RES_FLYFRMFMT))
+ ++nRet;
+ }
+ return nRet;
+}
+
+uno::Any SwTextBoxHelper::getByIndex(SdrPage const* pPage, sal_Int32 nIndex)
+{
+ if (nIndex < 0)
+ throw lang::IndexOutOfBoundsException();
+
+ SdrObject* pRet = nullptr;
+ sal_Int32 nCount = 0; // Current logical index.
+ for (const rtl::Reference<SdrObject>& p : *pPage)
+ {
+ if (p && p->IsTextBox())
+ continue;
+ if (nCount == nIndex)
+ {
+ pRet = p.get();
+ break;
+ }
+ ++nCount;
+ }
+
+ if (!pRet)
+ throw lang::IndexOutOfBoundsException();
+
+ return uno::Any(uno::Reference<drawing::XShape>(pRet->getUnoShape(), uno::UNO_QUERY));
+}
+
+sal_Int32 SwTextBoxHelper::getOrdNum(const SdrObject* pObject)
+{
+ if (const SdrPage* pPage = pObject->getSdrPageFromSdrObject())
+ {
+ sal_Int32 nOrder = 0; // Current logical order.
+ for (const rtl::Reference<SdrObject>& p : *pPage)
+ {
+ if (p && p->IsTextBox())
+ continue;
+ if (p == pObject)
+ return nOrder;
+ ++nOrder;
+ }
+ }
+
+ SAL_WARN("sw.core", "SwTextBoxHelper::getOrdNum: no page or page doesn't contain the object");
+ return pObject->GetOrdNum();
+}
+
+void SwTextBoxHelper::getShapeWrapThrough(const SwFrameFormat* pTextBox, bool& rWrapThrough)
+{
+ SwFrameFormat* pShape = SwTextBoxHelper::getOtherTextBoxFormat(pTextBox, RES_FLYFRMFMT);
+ if (pShape)
+ rWrapThrough = pShape->GetSurround().GetSurround() == css::text::WrapTextMode_THROUGH;
+}
+
+SwFrameFormat* SwTextBoxHelper::getOtherTextBoxFormat(const SwFrameFormat* pFormat,
+ sal_uInt16 nType, const SdrObject* pObject)
+{
+ SolarMutexGuard aGuard;
+ if (!isTextBox(pFormat, nType, pObject))
+ return nullptr;
+
+ if (nType == RES_DRAWFRMFMT)
+ {
+ if (pObject)
+ return pFormat->GetOtherTextBoxFormats()->GetTextBox(pObject);
+ if (pFormat->FindRealSdrObject())
+ return pFormat->GetOtherTextBoxFormats()->GetTextBox(pFormat->FindRealSdrObject());
+ return nullptr;
+ }
+ if (nType == RES_FLYFRMFMT)
+ {
+ return pFormat->GetOtherTextBoxFormats()->GetOwnerShape();
+ }
+ return nullptr;
+}
+
+SwFrameFormat* SwTextBoxHelper::getOtherTextBoxFormat(uno::Reference<drawing::XShape> const& xShape)
+{
+ auto pShape = dynamic_cast<SwXShape*>(xShape.get());
+ if (!pShape)
+ return nullptr;
+
+ SwFrameFormat* pFormat = pShape->GetFrameFormat();
+ return getOtherTextBoxFormat(pFormat, RES_DRAWFRMFMT,
+ SdrObject::getSdrObjectFromXShape(xShape));
+}
+
+uno::Reference<text::XTextFrame>
+SwTextBoxHelper::getUnoTextFrame(uno::Reference<drawing::XShape> const& xShape)
+{
+ if (xShape)
+ {
+ auto pFrameFormat = SwTextBoxHelper::getOtherTextBoxFormat(xShape);
+ if (pFrameFormat)
+ {
+ auto pSdrObj = pFrameFormat->FindSdrObject();
+ if (pSdrObj)
+ {
+ return { pSdrObj->getUnoShape(), uno::UNO_QUERY };
+ }
+ }
+ }
+ return {};
+}
+
+template <typename T>
+static void lcl_queryInterface(const SwFrameFormat* pShape, uno::Any& rAny, SdrObject* pObj)
+{
+ if (SwFrameFormat* pFormat
+ = SwTextBoxHelper::getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT, pObj))
+ {
+ uno::Reference<T> const xInterface(
+ getXWeak(SwXTextFrame::CreateXTextFrame(*pFormat->GetDoc(), pFormat).get()),
+ uno::UNO_QUERY);
+ rAny <<= xInterface;
+ }
+}
+
+uno::Any SwTextBoxHelper::queryInterface(const SwFrameFormat* pShape, const uno::Type& rType,
+ SdrObject* pObj)
+{
+ uno::Any aRet;
+
+ if (rType == cppu::UnoType<css::text::XTextAppend>::get())
+ {
+ lcl_queryInterface<text::XTextAppend>(pShape, aRet, pObj);
+ }
+ else if (rType == cppu::UnoType<css::text::XText>::get())
+ {
+ lcl_queryInterface<text::XText>(pShape, aRet, pObj);
+ }
+ else if (rType == cppu::UnoType<css::text::XTextRange>::get())
+ {
+ lcl_queryInterface<text::XTextRange>(pShape, aRet, pObj);
+ }
+
+ return aRet;
+}
+
+tools::Rectangle SwTextBoxHelper::getRelativeTextRectangle(SdrObject* pShape)
+{
+ tools::Rectangle aRet;
+ aRet.SetEmpty();
+
+ assert(pShape);
+
+ auto pCustomShape = dynamic_cast<SdrObjCustomShape*>(pShape);
+ if (pCustomShape)
+ {
+ // Need to temporarily release the lock acquired in
+ // SdXMLShapeContext::AddShape(), otherwise we get an empty rectangle,
+ // see EnhancedCustomShapeEngine::getTextBounds().
+ uno::Reference<document::XActionLockable> xLockable(pCustomShape->getUnoShape(),
+ uno::UNO_QUERY);
+ sal_Int16 nLocks = 0;
+ if (xLockable.is())
+ nLocks = xLockable->resetActionLocks();
+ pCustomShape->GetTextBounds(aRet);
+ if (nLocks)
+ xLockable->setActionLocks(nLocks);
+ }
+ else if (pShape)
+ {
+ // fallback - get *any* bound rect we can possibly get hold of
+ aRet = pShape->GetCurrentBoundRect();
+ }
+
+ if (pShape)
+ {
+ // Relative, so count the logic (reference) rectangle, see the EnhancedCustomShape2d ctor.
+ Point aPoint(pShape->GetSnapRect().Center());
+ Size aSize(pShape->GetLogicRect().GetSize());
+ aPoint.AdjustX(-(aSize.Width() / 2));
+ aPoint.AdjustY(-(aSize.Height() / 2));
+ tools::Rectangle aLogicRect(aPoint, aSize);
+ aRet.Move(-1 * aLogicRect.Left(), -1 * aLogicRect.Top());
+ }
+
+ return aRet;
+}
+
+void SwTextBoxHelper::syncProperty(SwFrameFormat* pShape, std::u16string_view rPropertyName,
+ const css::uno::Any& rValue, SdrObject* pObj)
+{
+ // Textframes does not have valid horizontal adjust property, so map it to paragraph adjust property
+ if (rPropertyName == UNO_NAME_TEXT_HORZADJUST)
+ {
+ SwFrameFormat* pFormat = getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT, pObj);
+ if (!pFormat)
+ return;
+
+ auto xTextFrame = SwXTextFrame::CreateXTextFrame(*pFormat->GetDoc(), pFormat);
+ uno::Reference<text::XTextCursor> xCursor = xTextFrame->getText()->createTextCursor();
+
+ // Select all paragraphs in the textframe
+ xCursor->gotoStart(false);
+ xCursor->gotoEnd(true);
+ uno::Reference<beans::XPropertySet> xFrameParaProps(xCursor, uno::UNO_QUERY);
+
+ // And simply map the property
+ const auto eValue = rValue.get<drawing::TextHorizontalAdjust>();
+ switch (eValue)
+ {
+ case drawing::TextHorizontalAdjust::TextHorizontalAdjust_CENTER:
+ xFrameParaProps->setPropertyValue(
+ UNO_NAME_PARA_ADJUST,
+ uno::Any(style::ParagraphAdjust::ParagraphAdjust_CENTER)); //3
+ break;
+ case drawing::TextHorizontalAdjust::TextHorizontalAdjust_LEFT:
+ xFrameParaProps->setPropertyValue(
+ UNO_NAME_PARA_ADJUST,
+ uno::Any(style::ParagraphAdjust::ParagraphAdjust_LEFT)); //0
+ break;
+ case drawing::TextHorizontalAdjust::TextHorizontalAdjust_RIGHT:
+ xFrameParaProps->setPropertyValue(
+ UNO_NAME_PARA_ADJUST,
+ uno::Any(style::ParagraphAdjust::ParagraphAdjust_RIGHT)); //1
+ break;
+ default:
+ SAL_WARN("sw.core",
+ "SwTextBoxHelper::syncProperty: unhandled TextHorizontalAdjust: "
+ << static_cast<sal_Int32>(eValue));
+ break;
+ }
+ return;
+ }
+
+ if (rPropertyName == u"CustomShapeGeometry")
+ {
+ // CustomShapeGeometry changes the textbox position offset and size, so adjust both.
+ syncProperty(pShape, RES_FRM_SIZE, MID_FRMSIZE_SIZE, uno::Any());
+
+ SdrObject* pObject = pObj ? pObj : pShape->FindRealSdrObject();
+ if (pObject)
+ {
+ tools::Rectangle aRectangle(pObject->GetSnapRect());
+ syncProperty(pShape, RES_HORI_ORIENT, MID_HORIORIENT_POSITION,
+ uno::Any(static_cast<sal_Int32>(convertTwipToMm100(aRectangle.Left()))));
+ syncProperty(pShape, RES_VERT_ORIENT, MID_VERTORIENT_POSITION,
+ uno::Any(static_cast<sal_Int32>(convertTwipToMm100(aRectangle.Top()))));
+ }
+
+ SwFrameFormat* pFormat = getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT, pObj);
+ if (!pFormat)
+ return;
+
+ // Older documents or documents in ODF strict do not have WritingMode, but have used the
+ // TextRotateAngle values -90 and -270 to emulate these text directions of frames.
+ // ToDo: Is TextPreRotateAngle needed for diagrams or can it be removed?
+ comphelper::SequenceAsHashMap aCustomShapeGeometry(rValue);
+ auto it = aCustomShapeGeometry.find("TextPreRotateAngle");
+ if (it == aCustomShapeGeometry.end())
+ {
+ it = aCustomShapeGeometry.find("TextRotateAngle");
+ }
+
+ if (it != aCustomShapeGeometry.end())
+ {
+ auto nAngle = it->second.has<sal_Int32>() ? it->second.get<sal_Int32>() : 0;
+ if (nAngle == 0)
+ {
+ nAngle = it->second.has<double>() ? it->second.get<double>() : 0;
+ }
+
+ sal_Int16 nDirection = 0;
+ switch (nAngle)
+ {
+ case -90:
+ nDirection = text::WritingMode2::TB_RL90;
+ break;
+ case -270:
+ nDirection = text::WritingMode2::BT_LR;
+ break;
+ default:
+ SAL_WARN("sw.core", "SwTextBoxHelper::syncProperty: unhandled property value: "
+ "CustomShapeGeometry:TextPreRotateAngle: "
+ << nAngle);
+ break;
+ }
+
+ if (nDirection)
+ {
+ syncProperty(pShape, RES_FRAMEDIR, 0, uno::Any(nDirection), pObj);
+ }
+ }
+ }
+ else if (rPropertyName == UNO_NAME_TEXT_VERT_ADJUST)
+ syncProperty(pShape, RES_TEXT_VERT_ADJUST, 0, rValue, pObj);
+ else if (rPropertyName == UNO_NAME_TEXT_AUTOGROWHEIGHT)
+ syncProperty(pShape, RES_FRM_SIZE, MID_FRMSIZE_IS_AUTO_HEIGHT, rValue, pObj);
+ else if (rPropertyName == UNO_NAME_TEXT_LEFTDIST)
+ syncProperty(pShape, RES_BOX, LEFT_BORDER_DISTANCE, rValue, pObj);
+ else if (rPropertyName == UNO_NAME_TEXT_RIGHTDIST)
+ syncProperty(pShape, RES_BOX, RIGHT_BORDER_DISTANCE, rValue, pObj);
+ else if (rPropertyName == UNO_NAME_TEXT_UPPERDIST)
+ syncProperty(pShape, RES_BOX, TOP_BORDER_DISTANCE, rValue, pObj);
+ else if (rPropertyName == UNO_NAME_TEXT_LOWERDIST)
+ syncProperty(pShape, RES_BOX, BOTTOM_BORDER_DISTANCE, rValue, pObj);
+ else if (rPropertyName == UNO_NAME_TEXT_WRITINGMODE)
+ {
+ text::WritingMode eMode;
+ sal_Int16 eMode2;
+ if (rValue >>= eMode)
+ syncProperty(pShape, RES_FRAMEDIR, 0, uno::Any(sal_Int16(eMode)), pObj);
+ else if (rValue >>= eMode2)
+ syncProperty(pShape, RES_FRAMEDIR, 0, uno::Any(eMode2), pObj);
+ }
+ else if (rPropertyName == u"WritingMode")
+ {
+ sal_Int16 eMode2;
+ if (rValue >>= eMode2)
+ syncProperty(pShape, RES_FRAMEDIR, 0, uno::Any(eMode2), pObj);
+ }
+ else
+ SAL_INFO("sw.core", "SwTextBoxHelper::syncProperty: unhandled property: "
+ << static_cast<OUString>(rPropertyName));
+}
+
+void SwTextBoxHelper::getProperty(SwFrameFormat const* pShape, sal_uInt16 nWID, sal_uInt8 nMemberID,
+ css::uno::Any& rValue)
+{
+ if (!pShape)
+ return;
+
+ nMemberID &= ~CONVERT_TWIPS;
+
+ SwFrameFormat* pFormat = getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT);
+ if (!pFormat)
+ return;
+
+ if (nWID != RES_CHAIN)
+ return;
+
+ switch (nMemberID)
+ {
+ case MID_CHAIN_PREVNAME:
+ case MID_CHAIN_NEXTNAME:
+ {
+ const SwFormatChain& rChain = pFormat->GetChain();
+ rChain.QueryValue(rValue, nMemberID);
+ }
+ break;
+ case MID_CHAIN_NAME:
+ rValue <<= pFormat->GetName();
+ break;
+ default:
+ SAL_WARN("sw.core", "SwTextBoxHelper::getProperty: unhandled member-id: "
+ << o3tl::narrowing<sal_uInt16>(nMemberID));
+ break;
+ }
+}
+
+css::uno::Any SwTextBoxHelper::getProperty(SwFrameFormat const* pShape, const OUString& rPropName)
+{
+ if (!pShape)
+ return {};
+
+ SwFrameFormat* pFormat = getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT);
+ if (!pFormat)
+ return {};
+
+ rtl::Reference<SwXTextFrame> xPropertySet
+ = SwXTextFrame::CreateXTextFrame(*pFormat->GetDoc(), pFormat);
+
+ return xPropertySet->getPropertyValue(rPropName);
+}
+
+void SwTextBoxHelper::syncProperty(SwFrameFormat* pShape, sal_uInt16 nWID, sal_uInt8 nMemberID,
+ const css::uno::Any& rValue, SdrObject* pObj)
+{
+ // No shape yet? Then nothing to do, initial properties are set by create().
+ if (!pShape)
+ return;
+
+ uno::Any aValue(rValue);
+ nMemberID &= ~CONVERT_TWIPS;
+
+ SwFrameFormat* pFormat = getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT, pObj);
+ if (!pFormat)
+ return;
+
+ OUString aPropertyName;
+ bool bAdjustX = false;
+ bool bAdjustY = false;
+ bool bAdjustSize = false;
+ switch (nWID)
+ {
+ case RES_HORI_ORIENT:
+ switch (nMemberID)
+ {
+ case MID_HORIORIENT_ORIENT:
+ aPropertyName = UNO_NAME_HORI_ORIENT;
+ break;
+ case MID_HORIORIENT_RELATION:
+ if (pShape->GetAnchor().GetAnchorId() != RndStdIds::FLY_AS_CHAR)
+ aPropertyName = UNO_NAME_HORI_ORIENT_RELATION;
+ else
+ return;
+ break;
+ case MID_HORIORIENT_POSITION:
+ aPropertyName = UNO_NAME_HORI_ORIENT_POSITION;
+ bAdjustX = true;
+ break;
+ default:
+ SAL_WARN("sw.core", "SwTextBoxHelper::syncProperty: unhandled member-id: "
+ << o3tl::narrowing<sal_uInt16>(nMemberID)
+ << " (which-id: " << nWID << ")");
+ break;
+ }
+ break;
+ case RES_LR_SPACE:
+ {
+ switch (nMemberID)
+ {
+ case MID_L_MARGIN:
+ aPropertyName = UNO_NAME_LEFT_MARGIN;
+ break;
+ case MID_R_MARGIN:
+ aPropertyName = UNO_NAME_RIGHT_MARGIN;
+ break;
+ default:
+ SAL_WARN("sw.core", "SwTextBoxHelper::syncProperty: unhandled member-id: "
+ << o3tl::narrowing<sal_uInt16>(nMemberID)
+ << " (which-id: " << nWID << ")");
+ break;
+ }
+ break;
+ }
+ case RES_VERT_ORIENT:
+ switch (nMemberID)
+ {
+ case MID_VERTORIENT_ORIENT:
+ aPropertyName = UNO_NAME_VERT_ORIENT;
+ break;
+ case MID_VERTORIENT_RELATION:
+ if (pShape->GetAnchor().GetAnchorId() != RndStdIds::FLY_AS_CHAR)
+ aPropertyName = UNO_NAME_VERT_ORIENT_RELATION;
+ else
+ return;
+ break;
+ case MID_VERTORIENT_POSITION:
+ aPropertyName = UNO_NAME_VERT_ORIENT_POSITION;
+ bAdjustY = true;
+ break;
+ default:
+ SAL_WARN("sw.core", "SwTextBoxHelper::syncProperty: unhandled member-id: "
+ << o3tl::narrowing<sal_uInt16>(nMemberID)
+ << " (which-id: " << nWID << ")");
+ break;
+ }
+ break;
+ case RES_FRM_SIZE:
+ switch (nMemberID)
+ {
+ case MID_FRMSIZE_WIDTH_TYPE:
+ aPropertyName = UNO_NAME_WIDTH_TYPE;
+ break;
+ case MID_FRMSIZE_IS_AUTO_HEIGHT:
+ aPropertyName = UNO_NAME_FRAME_ISAUTOMATIC_HEIGHT;
+ break;
+ case MID_FRMSIZE_REL_HEIGHT_RELATION:
+ aPropertyName = UNO_NAME_RELATIVE_HEIGHT_RELATION;
+ break;
+ case MID_FRMSIZE_REL_WIDTH_RELATION:
+ aPropertyName = UNO_NAME_RELATIVE_WIDTH_RELATION;
+ break;
+ default:
+ aPropertyName = UNO_NAME_SIZE;
+ bAdjustSize = true;
+ break;
+ }
+ break;
+ case RES_ANCHOR:
+ switch (nMemberID)
+ {
+ case MID_ANCHOR_ANCHORTYPE:
+ {
+ changeAnchor(pShape, pObj);
+ return;
+ }
+ break;
+ default:
+ SAL_WARN("sw.core", "SwTextBoxHelper::syncProperty: unhandled member-id: "
+ << o3tl::narrowing<sal_uInt16>(nMemberID)
+ << " (which-id: " << nWID << ")");
+ break;
+ }
+ break;
+ case FN_TEXT_RANGE:
+ {
+ uno::Reference<text::XTextRange> xRange;
+ rValue >>= xRange;
+ SwUnoInternalPaM aInternalPaM(*pFormat->GetDoc());
+ if (sw::XTextRangeToSwPaM(aInternalPaM, xRange))
+ {
+ SwFormatAnchor aAnchor(pFormat->GetAnchor());
+ aAnchor.SetAnchor(aInternalPaM.Start());
+ pFormat->SetFormatAttr(aAnchor);
+ }
+ }
+ break;
+ case RES_CHAIN:
+ switch (nMemberID)
+ {
+ case MID_CHAIN_PREVNAME:
+ aPropertyName = UNO_NAME_CHAIN_PREV_NAME;
+ break;
+ case MID_CHAIN_NEXTNAME:
+ aPropertyName = UNO_NAME_CHAIN_NEXT_NAME;
+ break;
+ default:
+ SAL_WARN("sw.core", "SwTextBoxHelper::syncProperty: unhandled member-id: "
+ << o3tl::narrowing<sal_uInt16>(nMemberID)
+ << " (which-id: " << nWID << ")");
+ break;
+ }
+ break;
+ case RES_TEXT_VERT_ADJUST:
+ aPropertyName = UNO_NAME_TEXT_VERT_ADJUST;
+ break;
+ case RES_BOX:
+ switch (nMemberID)
+ {
+ case LEFT_BORDER_DISTANCE:
+ aPropertyName = UNO_NAME_LEFT_BORDER_DISTANCE;
+ break;
+ case RIGHT_BORDER_DISTANCE:
+ aPropertyName = UNO_NAME_RIGHT_BORDER_DISTANCE;
+ break;
+ case TOP_BORDER_DISTANCE:
+ aPropertyName = UNO_NAME_TOP_BORDER_DISTANCE;
+ break;
+ case BOTTOM_BORDER_DISTANCE:
+ aPropertyName = UNO_NAME_BOTTOM_BORDER_DISTANCE;
+ break;
+ default:
+ SAL_WARN("sw.core", "SwTextBoxHelper::syncProperty: unhandled member-id: "
+ << o3tl::narrowing<sal_uInt16>(nMemberID)
+ << " (which-id: " << nWID << ")");
+ break;
+ }
+ break;
+ case RES_OPAQUE:
+ aPropertyName = UNO_NAME_OPAQUE;
+ break;
+ case RES_FRAMEDIR:
+ aPropertyName = UNO_NAME_WRITING_MODE;
+ break;
+ case RES_WRAP_INFLUENCE_ON_OBJPOS:
+ switch (nMemberID)
+ {
+ case MID_ALLOW_OVERLAP:
+ aPropertyName = UNO_NAME_ALLOW_OVERLAP;
+ break;
+ default:
+ SAL_WARN("sw.core", "SwTextBoxHelper::syncProperty: unhandled member-id: "
+ << o3tl::narrowing<sal_uInt16>(nMemberID)
+ << " (which-id: " << nWID << ")");
+ break;
+ }
+ break;
+ default:
+ SAL_WARN("sw.core", "SwTextBoxHelper::syncProperty: unhandled which-id: "
+ << nWID << " (member-id: "
+ << o3tl::narrowing<sal_uInt16>(nMemberID) << ")");
+ break;
+ }
+
+ if (aPropertyName.isEmpty())
+ return;
+
+ // Position/size should be the text position/size, not the shape one as-is.
+ if (bAdjustX || bAdjustY || bAdjustSize)
+ {
+ changeAnchor(pShape, pObj);
+ tools::Rectangle aRect
+ = getRelativeTextRectangle(pObj ? pObj : pShape->FindRealSdrObject());
+ if (!aRect.IsEmpty())
+ {
+ if (bAdjustX || bAdjustY)
+ {
+ sal_Int32 nValue;
+ if (aValue >>= nValue)
+ {
+ nValue += convertTwipToMm100(bAdjustX ? aRect.Left() : aRect.Top());
+ aValue <<= nValue;
+ }
+ }
+ else if (bAdjustSize)
+ {
+ awt::Size aSize(convertTwipToMm100(aRect.getOpenWidth()),
+ convertTwipToMm100(aRect.getOpenHeight()));
+ aValue <<= aSize;
+ }
+ }
+ }
+ auto aGuard = SwTextBoxLockGuard(*pShape->GetOtherTextBoxFormats());
+ rtl::Reference<SwXTextFrame> const xPropertySet
+ = SwXTextFrame::CreateXTextFrame(*pFormat->GetDoc(), pFormat);
+ xPropertySet->setPropertyValue(aPropertyName, aValue);
+}
+
+void SwTextBoxHelper::saveLinks(const sw::FrameFormats<sw::SpzFrameFormat*>& rFormats,
+ std::map<const SwFrameFormat*, const SwFrameFormat*>& rLinks)
+{
+ for (const auto pFormat : rFormats)
+ {
+ if (SwFrameFormat* pTextBox = getOtherTextBoxFormat(pFormat, RES_DRAWFRMFMT))
+ rLinks[pFormat] = pTextBox;
+ }
+}
+
+void SwTextBoxHelper::restoreLinks(std::set<ZSortFly>& rOld, std::vector<SwFrameFormat*>& rNew,
+ SavedLink& rSavedLinks)
+{
+ std::size_t i = 0;
+ for (const auto& rIt : rOld)
+ {
+ auto aTextBoxIt = rSavedLinks.find(rIt.GetFormat());
+ if (aTextBoxIt != rSavedLinks.end())
+ {
+ std::size_t j = 0;
+ for (const auto& rJt : rOld)
+ {
+ if (rJt.GetFormat() == aTextBoxIt->second)
+ rNew[i]->SetFormatAttr(rNew[j]->GetContent());
+ ++j;
+ }
+ }
+ ++i;
+ }
+}
+
+text::TextContentAnchorType SwTextBoxHelper::mapAnchorType(const RndStdIds& rAnchorID)
+{
+ text::TextContentAnchorType aAnchorType;
+ switch (rAnchorID)
+ {
+ case RndStdIds::FLY_AS_CHAR:
+ aAnchorType = text::TextContentAnchorType::TextContentAnchorType_AS_CHARACTER;
+ break;
+ case RndStdIds::FLY_AT_CHAR:
+ aAnchorType = text::TextContentAnchorType::TextContentAnchorType_AT_CHARACTER;
+ break;
+ case RndStdIds::FLY_AT_PARA:
+ aAnchorType = text::TextContentAnchorType::TextContentAnchorType_AT_PARAGRAPH;
+ break;
+ case RndStdIds::FLY_AT_PAGE:
+ aAnchorType = text::TextContentAnchorType::TextContentAnchorType_AT_PAGE;
+ break;
+ case RndStdIds::FLY_AT_FLY:
+ aAnchorType = text::TextContentAnchorType::TextContentAnchorType_AT_FRAME;
+ break;
+ default:
+ aAnchorType = text::TextContentAnchorType::TextContentAnchorType_AT_PARAGRAPH;
+ SAL_WARN("sw.core", "SwTextBoxHelper::mapAnchorType: Unknown AnchorType!");
+ break;
+ }
+ return aAnchorType;
+}
+
+void SwTextBoxHelper::syncFlyFrameAttr(SwFrameFormat& rShape, SfxItemSet const& rSet,
+ SdrObject* pObj)
+{
+ SwFrameFormat* pFormat = getOtherTextBoxFormat(&rShape, RES_DRAWFRMFMT, pObj);
+ if (!pFormat)
+ return;
+
+ const bool bInlineAnchored = rShape.GetAnchor().GetAnchorId() == RndStdIds::FLY_AS_CHAR;
+ const bool bLayoutInCell = rShape.GetFollowTextFlow().GetValue()
+ && rShape.GetAnchor().GetAnchorNode()
+ && rShape.GetAnchor().GetAnchorNode()->FindTableNode();
+ SfxItemSet aTextBoxSet(pFormat->GetDoc()->GetAttrPool(), aFrameFormatSetRange);
+
+ SfxItemIter aIter(rSet);
+ const SfxPoolItem* pItem = aIter.GetCurItem();
+
+ do
+ {
+ switch (pItem->Which())
+ {
+ case RES_VERT_ORIENT:
+ {
+ // The new position can be with anchor changing so sync it!
+ const text::TextContentAnchorType aNewAnchorType
+ = mapAnchorType(rShape.GetAnchor().GetAnchorId());
+ syncProperty(&rShape, RES_ANCHOR, MID_ANCHOR_ANCHORTYPE, uno::Any(aNewAnchorType),
+ pObj);
+ if (bInlineAnchored || bLayoutInCell)
+ return;
+ SwFormatVertOrient aOrient(pItem->StaticWhichCast(RES_VERT_ORIENT));
+
+ tools::Rectangle aRect
+ = getRelativeTextRectangle(pObj ? pObj : rShape.FindRealSdrObject());
+ if (!aRect.IsEmpty())
+ aOrient.SetPos(aOrient.GetPos() + aRect.Top());
+
+ if (rShape.GetAnchor().GetAnchorId() == RndStdIds::FLY_AT_PAGE
+ && rShape.GetAnchor().GetPageNum() != 0)
+ aOrient.SetRelationOrient(rShape.GetVertOrient().GetRelationOrient());
+ aTextBoxSet.Put(aOrient);
+
+ // restore height (shrunk for extending beyond the page bottom - tdf#91260)
+ SwFormatFrameSize aSize(pFormat->GetFrameSize());
+ if (!aRect.IsEmpty())
+ {
+ aSize.SetHeight(aRect.getOpenHeight());
+ aTextBoxSet.Put(aSize);
+ }
+ }
+ break;
+ case RES_HORI_ORIENT:
+ {
+ // The new position can be with anchor changing so sync it!
+ const text::TextContentAnchorType aNewAnchorType
+ = mapAnchorType(rShape.GetAnchor().GetAnchorId());
+ syncProperty(&rShape, RES_ANCHOR, MID_ANCHOR_ANCHORTYPE, uno::Any(aNewAnchorType),
+ pObj);
+ if (bInlineAnchored || bLayoutInCell)
+ return;
+ SwFormatHoriOrient aOrient(pItem->StaticWhichCast(RES_HORI_ORIENT));
+
+ tools::Rectangle aRect
+ = getRelativeTextRectangle(pObj ? pObj : rShape.FindRealSdrObject());
+ if (!aRect.IsEmpty())
+ aOrient.SetPos(aOrient.GetPos() + aRect.Left());
+
+ if (rShape.GetAnchor().GetAnchorId() == RndStdIds::FLY_AT_PAGE
+ && rShape.GetAnchor().GetPageNum() != 0)
+ aOrient.SetRelationOrient(rShape.GetHoriOrient().GetRelationOrient());
+ aTextBoxSet.Put(aOrient);
+ }
+ break;
+ case RES_FRM_SIZE:
+ {
+ // In case the shape got resized, then we need to adjust both
+ // the position and the size of the textbox (e.g. larger
+ // rounded edges of a rectangle -> need to push right/down the
+ // textbox).
+ SwFormatVertOrient aVertOrient(rShape.GetVertOrient());
+ SwFormatHoriOrient aHoriOrient(rShape.GetHoriOrient());
+ SwFormatFrameSize aSize(pFormat->GetFrameSize());
+
+ tools::Rectangle aRect
+ = getRelativeTextRectangle(pObj ? pObj : rShape.FindRealSdrObject());
+ if (!aRect.IsEmpty())
+ {
+ if (!bInlineAnchored)
+ {
+ aVertOrient.SetPos(
+ (pObj ? pObj->GetRelativePos().getX() : aVertOrient.GetPos())
+ + aRect.Top());
+ aHoriOrient.SetPos(
+ (pObj ? pObj->GetRelativePos().getY() : aHoriOrient.GetPos())
+ + aRect.Left());
+
+ aTextBoxSet.Put(aVertOrient);
+ aTextBoxSet.Put(aHoriOrient);
+ }
+
+ aSize.SetWidth(aRect.getOpenWidth());
+ aSize.SetHeight(aRect.getOpenHeight());
+ aTextBoxSet.Put(aSize);
+ }
+ }
+ break;
+ case RES_ANCHOR:
+ {
+ if (pItem->StaticWhichCast(RES_ANCHOR) == rShape.GetAnchor())
+ // the anchor have to be synced
+ {
+ const text::TextContentAnchorType aNewAnchorType
+ = mapAnchorType(rShape.GetAnchor().GetAnchorId());
+ syncProperty(&rShape, RES_ANCHOR, MID_ANCHOR_ANCHORTYPE,
+ uno::Any(aNewAnchorType), pObj);
+ }
+ else
+ {
+ SAL_WARN("sw.core", "SwTextBoxHelper::syncFlyFrameAttr: The anchor of the "
+ "shape different from the textframe!");
+ }
+ }
+ break;
+ default:
+ SAL_WARN("sw.core", "SwTextBoxHelper::syncFlyFrameAttr: unhandled which-id: "
+ << pItem->Which());
+ break;
+ }
+
+ pItem = aIter.NextItem();
+ } while (pItem && (0 != pItem->Which()));
+
+ if (aTextBoxSet.Count())
+ {
+ auto aGuard = SwTextBoxLockGuard(*rShape.GetOtherTextBoxFormats());
+ pFormat->SetFormatAttr(aTextBoxSet);
+ }
+ DoTextBoxZOrderCorrection(&rShape, pObj);
+}
+
+void SwTextBoxHelper::updateTextBoxMargin(SdrObject* pObj)
+{
+ if (!pObj)
+ return;
+ uno::Reference<drawing::XShape> xShape(pObj->getUnoShape(), uno::UNO_QUERY);
+ if (!xShape)
+ return;
+ uno::Reference<beans::XPropertySet> const xPropertySet(xShape, uno::UNO_QUERY);
+
+ auto pParentFormat = getOtherTextBoxFormat(getOtherTextBoxFormat(xShape), RES_FLYFRMFMT);
+ if (!pParentFormat)
+ return;
+
+ // Sync the padding
+ syncProperty(pParentFormat, UNO_NAME_TEXT_LEFTDIST,
+ xPropertySet->getPropertyValue(UNO_NAME_TEXT_LEFTDIST), pObj);
+ syncProperty(pParentFormat, UNO_NAME_TEXT_RIGHTDIST,
+ xPropertySet->getPropertyValue(UNO_NAME_TEXT_RIGHTDIST), pObj);
+ syncProperty(pParentFormat, UNO_NAME_TEXT_UPPERDIST,
+ xPropertySet->getPropertyValue(UNO_NAME_TEXT_UPPERDIST), pObj);
+ syncProperty(pParentFormat, UNO_NAME_TEXT_LOWERDIST,
+ xPropertySet->getPropertyValue(UNO_NAME_TEXT_LOWERDIST), pObj);
+
+ // Sync the text aligning
+ syncProperty(pParentFormat, UNO_NAME_TEXT_VERTADJUST,
+ xPropertySet->getPropertyValue(UNO_NAME_TEXT_VERTADJUST), pObj);
+ syncProperty(pParentFormat, UNO_NAME_TEXT_HORZADJUST,
+ xPropertySet->getPropertyValue(UNO_NAME_TEXT_HORZADJUST), pObj);
+
+ // tdf137803: Sync autogrow:
+ const bool bIsAutoGrow
+ = xPropertySet->getPropertyValue(UNO_NAME_TEXT_AUTOGROWHEIGHT).get<bool>();
+ const bool bIsAutoWrap = xPropertySet->getPropertyValue(UNO_NAME_TEXT_WORDWRAP).get<bool>();
+
+ syncProperty(pParentFormat, RES_FRM_SIZE, MID_FRMSIZE_IS_AUTO_HEIGHT, uno::Any(bIsAutoGrow),
+ pObj);
+
+ syncProperty(pParentFormat, RES_FRM_SIZE, MID_FRMSIZE_WIDTH_TYPE,
+ uno::Any(bIsAutoWrap ? text::SizeType::FIX : text::SizeType::MIN), pObj);
+
+ changeAnchor(pParentFormat, pObj);
+ DoTextBoxZOrderCorrection(pParentFormat, pObj);
+}
+
+bool SwTextBoxHelper::changeAnchor(SwFrameFormat* pShape, SdrObject* pObj)
+{
+ if (auto pFormat = getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT, pObj))
+ {
+ if (!isAnchorSyncNeeded(pShape, pFormat))
+ {
+ doTextBoxPositioning(pShape, pObj);
+ DoTextBoxZOrderCorrection(pShape, pObj);
+ if (pShape->GetAnchor().GetAnchorId() == RndStdIds::FLY_AS_CHAR
+ && pFormat->GetAnchor().GetAnchorId() == RndStdIds::FLY_AT_CHAR
+ && pFormat->GetVertOrient().GetRelationOrient() != text::RelOrientation::PRINT_AREA)
+ {
+ SwFormatVertOrient aTmp = pFormat->GetVertOrient();
+ aTmp.SetRelationOrient(text::RelOrientation::PRINT_AREA);
+ pFormat->SetFormatAttr(aTmp);
+ }
+
+ return false;
+ }
+
+ const SwFormatAnchor& rOldAnch = pFormat->GetAnchor();
+ const SwFormatAnchor& rNewAnch = pShape->GetAnchor();
+
+ const auto pOldCnt = rOldAnch.GetContentAnchor();
+ const auto pNewCnt = rNewAnch.GetContentAnchor();
+
+ const uno::Any aShapeHorRelOrient(pShape->GetHoriOrient().GetRelationOrient());
+
+ try
+ {
+ auto aGuard = SwTextBoxLockGuard(*pShape->GetOtherTextBoxFormats());
+ ::sw::UndoGuard const UndoGuard(pShape->GetDoc()->GetIDocumentUndoRedo());
+ rtl::Reference<SwXTextFrame> const xPropertySet
+ = SwXTextFrame::CreateXTextFrame(*pFormat->GetDoc(), pFormat);
+ if (pOldCnt && rNewAnch.GetAnchorId() == RndStdIds::FLY_AT_PAGE
+ && rNewAnch.GetPageNum())
+ {
+ uno::Any aValue(text::TextContentAnchorType_AT_PAGE);
+ xPropertySet->setPropertyValue(UNO_NAME_HORI_ORIENT_RELATION, aShapeHorRelOrient);
+ xPropertySet->setPropertyValue(UNO_NAME_ANCHOR_TYPE, aValue);
+ xPropertySet->setPropertyValue(UNO_NAME_ANCHOR_PAGE_NO,
+ uno::Any(rNewAnch.GetPageNum()));
+ }
+ else if (rOldAnch.GetAnchorId() == RndStdIds::FLY_AT_PAGE && pNewCnt)
+ {
+ if (rNewAnch.GetAnchorId() == RndStdIds::FLY_AS_CHAR)
+ {
+ assert(pNewCnt);
+ uno::Any aValue(text::TextContentAnchorType_AT_CHARACTER);
+ xPropertySet->setPropertyValue(UNO_NAME_ANCHOR_TYPE, aValue);
+ xPropertySet->setPropertyValue(UNO_NAME_HORI_ORIENT_RELATION,
+ uno::Any(text::RelOrientation::CHAR));
+ xPropertySet->setPropertyValue(UNO_NAME_VERT_ORIENT_RELATION,
+ uno::Any(text::RelOrientation::PRINT_AREA));
+ SwFormatAnchor aPos(pFormat->GetAnchor());
+ aPos.SetAnchor(pNewCnt);
+ pFormat->SetFormatAttr(aPos);
+ }
+ else
+ {
+ uno::Any aValue(mapAnchorType(rNewAnch.GetAnchorId()));
+ xPropertySet->setPropertyValue(UNO_NAME_HORI_ORIENT_RELATION,
+ aShapeHorRelOrient);
+ xPropertySet->setPropertyValue(UNO_NAME_ANCHOR_TYPE, aValue);
+ pFormat->SetFormatAttr(rNewAnch);
+ }
+ }
+ else
+ {
+ if (rNewAnch.GetAnchorId() == RndStdIds::FLY_AS_CHAR)
+ {
+ assert(pNewCnt);
+ uno::Any aValue(text::TextContentAnchorType_AT_CHARACTER);
+ xPropertySet->setPropertyValue(UNO_NAME_ANCHOR_TYPE, aValue);
+ xPropertySet->setPropertyValue(UNO_NAME_HORI_ORIENT_RELATION,
+ uno::Any(text::RelOrientation::CHAR));
+ xPropertySet->setPropertyValue(UNO_NAME_VERT_ORIENT_RELATION,
+ uno::Any(text::RelOrientation::PRINT_AREA));
+ SwFormatAnchor aPos(pFormat->GetAnchor());
+ aPos.SetAnchor(pNewCnt);
+ pFormat->SetFormatAttr(aPos);
+ }
+ else
+ {
+ xPropertySet->setPropertyValue(UNO_NAME_HORI_ORIENT_RELATION,
+ aShapeHorRelOrient);
+ if (rNewAnch.GetAnchorId() == RndStdIds::FLY_AT_PAGE
+ && rNewAnch.GetPageNum() == 0)
+ {
+ pFormat->SetFormatAttr(SwFormatAnchor(RndStdIds::FLY_AT_PAGE, 1));
+ }
+ else
+ pFormat->SetFormatAttr(pShape->GetAnchor());
+ }
+ }
+ }
+ catch (uno::Exception& e)
+ {
+ SAL_WARN("sw.core", "SwTextBoxHelper::changeAnchor(): " << e.Message);
+ }
+
+ doTextBoxPositioning(pShape, pObj);
+ DoTextBoxZOrderCorrection(pShape, pObj);
+ return true;
+ }
+
+ return false;
+}
+
+bool SwTextBoxHelper::doTextBoxPositioning(SwFrameFormat* pShape, SdrObject* pObj)
+{
+ // Set the position of the textboxes according to the position of its shape-pair
+ const bool bIsGroupObj = (pObj != pShape->FindRealSdrObject()) && pObj;
+ if (auto pFormat = getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT, pObj))
+ {
+ // Do not create undo entry for the positioning
+ ::sw::UndoGuard const UndoGuard(pShape->GetDoc()->GetIDocumentUndoRedo());
+ auto aGuard = SwTextBoxLockGuard(*pShape->GetOtherTextBoxFormats());
+ // Special treatment for AS_CHAR textboxes:
+ if (pShape->GetAnchor().GetAnchorId() == RndStdIds::FLY_AS_CHAR)
+ {
+ // Get the text area of the shape
+ tools::Rectangle aRect
+ = getRelativeTextRectangle(pObj ? pObj : pShape->FindRealSdrObject());
+
+ // Get the left spacing of the text area of the shape
+ auto nLeftSpace = pShape->GetLRSpace().GetLeft();
+
+ // Set the textbox position at the X-axis:
+ SwFormatHoriOrient aNewHOri(pFormat->GetHoriOrient());
+ if (bIsGroupObj && aNewHOri.GetHoriOrient() != text::HoriOrientation::NONE)
+ aNewHOri.SetHoriOrient(text::HoriOrientation::NONE);
+ aNewHOri.SetPos(aRect.Left() + nLeftSpace
+ + (bIsGroupObj ? pObj->GetRelativePos().getX() : 0));
+ SwFormatVertOrient aNewVOri(pFormat->GetVertOrient());
+
+ // Special handling of group textboxes
+ if (bIsGroupObj)
+ {
+ // There are the following cases:
+ // case 1: The textbox should be in that position where the shape is.
+ // case 2: The shape has negative offset so that have to be subtracted
+ // case 3: The shape and its parent shape also has negative offset, so subtract
+ aNewVOri.SetPos(
+ ((pObj->GetRelativePos().getY()) > 0
+ ? (pShape->GetVertOrient().GetPos() > 0
+ ? pObj->GetRelativePos().getY()
+ : pObj->GetRelativePos().getY() - pShape->GetVertOrient().GetPos())
+ : (pShape->GetVertOrient().GetPos() > 0
+ ? 0 // Is this can be a variation?
+ : pObj->GetRelativePos().getY() - pShape->GetVertOrient().GetPos()))
+ + aRect.Top());
+ }
+ else
+ {
+ // Simple textboxes: vertical position equals to the vertical offset of the shape
+ aNewVOri.SetPos(
+ ((pShape->GetVertOrient().GetPos()) > 0 ? pShape->GetVertOrient().GetPos() : 0)
+ + aRect.Top());
+ }
+
+ // Special cases when the shape is aligned to the line
+ if (pShape->GetVertOrient().GetVertOrient() != text::VertOrientation::NONE)
+ {
+ aNewVOri.SetVertOrient(text::VertOrientation::NONE);
+ switch (pShape->GetVertOrient().GetVertOrient())
+ {
+ // Top aligned shape
+ case text::VertOrientation::TOP:
+ case text::VertOrientation::CHAR_TOP:
+ case text::VertOrientation::LINE_TOP:
+ {
+ aNewVOri.SetPos(aNewVOri.GetPos() - pShape->GetFrameSize().GetHeight());
+ break;
+ }
+ // Bottom aligned shape
+ case text::VertOrientation::BOTTOM:
+ case text::VertOrientation::CHAR_BOTTOM:
+ case text::VertOrientation::LINE_BOTTOM:
+ {
+ aNewVOri.SetPos(aNewVOri.GetPos() + pShape->GetFrameSize().GetHeight());
+ break;
+ }
+ // Center aligned shape
+ case text::VertOrientation::CENTER:
+ case text::VertOrientation::CHAR_CENTER:
+ case text::VertOrientation::LINE_CENTER:
+ {
+ aNewVOri.SetPos(aNewVOri.GetPos()
+ + std::lroundf(pShape->GetFrameSize().GetHeight() / 2));
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ pFormat->SetFormatAttr(aNewHOri);
+ pFormat->SetFormatAttr(aNewVOri);
+ }
+ // Other cases when the shape has different anchor from AS_CHAR
+ else
+ {
+ // Text area of the shape
+ tools::Rectangle aRect
+ = getRelativeTextRectangle(pObj ? pObj : pShape->FindRealSdrObject());
+
+ // X Offset of the shape spacing
+ auto nLeftSpace = pShape->GetLRSpace().GetLeft();
+
+ // Set the same position as the (child) shape has
+ SwFormatHoriOrient aNewHOri(pShape->GetHoriOrient());
+ if (bIsGroupObj && aNewHOri.GetHoriOrient() != text::HoriOrientation::NONE)
+ aNewHOri.SetHoriOrient(text::HoriOrientation::NONE);
+
+ aNewHOri.SetPos(
+ (bIsGroupObj && pObj ? pObj->GetRelativePos().getX() : aNewHOri.GetPos())
+ + aRect.Left());
+ SwFormatVertOrient aNewVOri(pShape->GetVertOrient());
+ aNewVOri.SetPos(
+ (bIsGroupObj && pObj ? pObj->GetRelativePos().getY() : aNewVOri.GetPos())
+ + aRect.Top());
+
+ // Get the distance of the child shape inside its parent
+ const auto& nInshapePos
+ = pObj ? pObj->GetRelativePos() - pShape->FindRealSdrObject()->GetRelativePos()
+ : Point();
+
+ // Special case: the shape has relative position from the page
+ if (pShape->GetHoriOrient().GetRelationOrient() == text::RelOrientation::PAGE_FRAME
+ && pShape->GetAnchor().GetAnchorId() != RndStdIds::FLY_AT_PAGE)
+ {
+ aNewHOri.SetRelationOrient(text::RelOrientation::PAGE_FRAME);
+ aNewHOri.SetPos(pShape->GetHoriOrient().GetPos() + nInshapePos.getX()
+ + aRect.Left());
+ }
+
+ if (pShape->GetVertOrient().GetRelationOrient() == text::RelOrientation::PAGE_FRAME
+ && pShape->GetAnchor().GetAnchorId() != RndStdIds::FLY_AT_PAGE)
+ {
+ aNewVOri.SetRelationOrient(text::RelOrientation::PAGE_FRAME);
+ aNewVOri.SetPos(pShape->GetVertOrient().GetPos() + nInshapePos.getY()
+ + aRect.Top());
+ }
+
+ // Other special case: shape is inside a table or floating table following the text flow
+ if (pShape->GetFollowTextFlow().GetValue() && pShape->GetAnchor().GetAnchorNode()
+ && pShape->GetAnchor().GetAnchorNode()->FindTableNode())
+ {
+ // Table position
+ Point nTableOffset;
+ // Floating table
+ if (auto pFly
+ = pShape->GetAnchor().GetAnchorNode()->FindTableNode()->FindFlyStartNode())
+ {
+ if (auto pFlyFormat = pFly->GetFlyFormat())
+ {
+ nTableOffset.setX(pFlyFormat->GetHoriOrient().GetPos());
+ nTableOffset.setY(pFlyFormat->GetVertOrient().GetPos());
+ }
+ }
+ else
+ // Normal table
+ {
+ auto pTableNode = pShape->GetAnchor().GetAnchorNode()->FindTableNode();
+ if (auto pTableFormat = pTableNode->GetTable().GetFrameFormat())
+ {
+ nTableOffset.setX(pTableFormat->GetHoriOrient().GetPos());
+ nTableOffset.setY(pTableFormat->GetVertOrient().GetPos());
+ }
+ }
+
+ // Add the table positions to the textbox.
+ aNewHOri.SetPos(aNewHOri.GetPos() + nTableOffset.getX() + nLeftSpace);
+ if (pShape->GetVertOrient().GetRelationOrient() == text::RelOrientation::PAGE_FRAME
+ || pShape->GetVertOrient().GetRelationOrient()
+ == text::RelOrientation::PAGE_PRINT_AREA)
+ aNewVOri.SetPos(aNewVOri.GetPos() + nTableOffset.getY());
+ }
+
+ pFormat->SetFormatAttr(aNewHOri);
+ pFormat->SetFormatAttr(aNewVOri);
+ }
+ return true;
+ }
+
+ return false;
+}
+
+bool SwTextBoxHelper::syncTextBoxSize(SwFrameFormat* pShape, SdrObject* pObj)
+{
+ if (!pShape || !pObj)
+ return false;
+
+ if (auto pTextBox = getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT, pObj))
+ {
+ auto aGuard = SwTextBoxLockGuard(*pShape->GetOtherTextBoxFormats());
+ const auto& rSize = getRelativeTextRectangle(pObj).GetSize();
+ if (!rSize.IsEmpty())
+ {
+ SwFormatFrameSize aSize(pTextBox->GetFrameSize());
+ aSize.SetSize(rSize);
+ return pTextBox->SetFormatAttr(aSize);
+ }
+ }
+
+ return false;
+}
+
+bool SwTextBoxHelper::DoTextBoxZOrderCorrection(SwFrameFormat* pShape, const SdrObject* pObj)
+{
+ // TODO: do this with group shape textboxes.
+ SdrObject* pShpObj = nullptr;
+
+ pShpObj = pShape->FindRealSdrObject();
+
+ if (pShpObj)
+ {
+ auto pTextBox = getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT, pObj);
+ if (!pTextBox)
+ return false;
+ SdrObject* pFrmObj = pTextBox->FindRealSdrObject();
+ if (!pFrmObj)
+ {
+ // During loading there is no ready SdrObj for z-ordering, so create and cache it here
+ pFrmObj
+ = SwXTextFrame::GetOrCreateSdrObject(*dynamic_cast<SwFlyFrameFormat*>(pTextBox));
+ }
+ if (pFrmObj)
+ {
+ // Get the draw model from the doc
+ SwDrawModel* pDrawModel
+ = pShape->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel();
+ if (pDrawModel)
+ {
+ // Not really sure this will work on all pages, but it seems it will.
+ auto pPage = pDrawModel->GetPage(0);
+ // Recalc all Z-orders
+ pPage->RecalcObjOrdNums();
+ // Here is a counter avoiding running to in infinity:
+ sal_uInt16 nIterator = 0;
+ // If the shape is behind the frame, is good, but if there are some objects
+ // between of them that is wrong so put the frame exactly one level higher
+ // than the shape.
+ if (pFrmObj->GetOrdNum() > pShpObj->GetOrdNum())
+ pPage->SetObjectOrdNum(pFrmObj->GetOrdNum(), pShpObj->GetOrdNum() + 1);
+ else
+ // Else, if the frame is behind the shape, bring to the front of it.
+ while (pFrmObj->GetOrdNum() <= pShpObj->GetOrdNum())
+ {
+ pPage->SetObjectOrdNum(pFrmObj->GetOrdNum(), pFrmObj->GetOrdNum() + 1);
+ // If there is any problem with the indexes, do not run over the infinity
+ if (pPage->GetObjCount() == pFrmObj->GetOrdNum())
+ break;
+ ++nIterator;
+ if (nIterator > 300)
+ break; // Do not run to infinity
+ }
+ pPage->RecalcObjOrdNums();
+ return true; // Success
+ }
+ SAL_WARN("sw.core", "SwTextBoxHelper::DoTextBoxZOrderCorrection(): "
+ "No Valid Draw model for SdrObject for the shape!");
+ }
+ SAL_WARN("sw.core", "SwTextBoxHelper::DoTextBoxZOrderCorrection(): "
+ "No Valid SdrObject for the frame!");
+ }
+ SAL_WARN("sw.core", "SwTextBoxHelper::DoTextBoxZOrderCorrection(): "
+ "No Valid SdrObject for the shape!");
+
+ return false;
+}
+
+void SwTextBoxHelper::synchronizeGroupTextBoxProperty(bool pFunc(SwFrameFormat*, SdrObject*),
+ SwFrameFormat* pFormat, SdrObject* pObj)
+{
+ if (auto pChildren = pObj->getChildrenOfSdrObject())
+ {
+ for (const rtl::Reference<SdrObject>& pChildObj : *pChildren)
+ synchronizeGroupTextBoxProperty(pFunc, pFormat, pChildObj.get());
+ }
+ else
+ {
+ (*pFunc)(pFormat, pObj);
+ }
+}
+
+std::vector<SwFrameFormat*> SwTextBoxHelper::CollectTextBoxes(const SdrObject* pGroupObject,
+ SwFrameFormat* pFormat)
+{
+ std::vector<SwFrameFormat*> vRet;
+ if (auto pChildren = pGroupObject->getChildrenOfSdrObject())
+ {
+ for (const rtl::Reference<SdrObject>& pObj : *pChildren)
+ {
+ auto pChildTextBoxes = CollectTextBoxes(pObj.get(), pFormat);
+ for (auto& rChildTextBox : pChildTextBoxes)
+ vRet.push_back(rChildTextBox);
+ }
+ }
+ else
+ {
+ if (isTextBox(pFormat, RES_DRAWFRMFMT, pGroupObject))
+ vRet.push_back(getOtherTextBoxFormat(pFormat, RES_DRAWFRMFMT, pGroupObject));
+ }
+ return vRet;
+}
+
+bool SwTextBoxHelper::isAnchorSyncNeeded(const SwFrameFormat* pFirst, const SwFrameFormat* pSecond)
+{
+ if (!pFirst)
+ return false;
+
+ if (!pSecond)
+ return false;
+
+ if (pFirst == pSecond)
+ return false;
+
+ if (!pFirst->GetOtherTextBoxFormats())
+ return false;
+
+ if (!pSecond->GetOtherTextBoxFormats())
+ return false;
+
+ if (pFirst->GetOtherTextBoxFormats() != pSecond->GetOtherTextBoxFormats())
+ return false;
+
+ if (pFirst->GetOtherTextBoxFormats()->GetOwnerShape() == pSecond
+ || pFirst == pSecond->GetOtherTextBoxFormats()->GetOwnerShape())
+ {
+ const auto& rShapeAnchor
+ = pFirst->Which() == RES_DRAWFRMFMT ? pFirst->GetAnchor() : pSecond->GetAnchor();
+ const auto& rFrameAnchor
+ = pFirst->Which() == RES_FLYFRMFMT ? pFirst->GetAnchor() : pSecond->GetAnchor();
+
+ if (rShapeAnchor.GetAnchorId() == rFrameAnchor.GetAnchorId())
+ {
+ if (rShapeAnchor.GetAnchorNode() && rFrameAnchor.GetAnchorNode())
+ {
+ if (*rShapeAnchor.GetContentAnchor() != *rFrameAnchor.GetContentAnchor())
+ return true;
+
+ return false;
+ }
+
+ if (rShapeAnchor.GetAnchorId() == RndStdIds::FLY_AT_PAGE
+ && rFrameAnchor.GetAnchorId() == RndStdIds::FLY_AT_PAGE)
+ {
+ if (rShapeAnchor.GetPageNum() == rFrameAnchor.GetPageNum())
+ return false;
+ else
+ return true;
+ }
+
+ return true;
+ }
+
+ if (rShapeAnchor.GetAnchorId() == RndStdIds::FLY_AS_CHAR
+ && rFrameAnchor.GetAnchorId() == RndStdIds::FLY_AT_CHAR)
+ {
+ if (rShapeAnchor.GetAnchorNode() && rFrameAnchor.GetAnchorNode())
+ {
+ if (*rShapeAnchor.GetContentAnchor() != *rFrameAnchor.GetContentAnchor())
+ return true;
+
+ return false;
+ }
+ }
+ return true;
+ }
+ return false;
+}
+
+SwTextBoxNode::SwTextBoxNode(SwFrameFormat* pOwnerShape)
+{
+ assert(pOwnerShape);
+ assert(pOwnerShape->Which() == RES_DRAWFRMFMT);
+
+ m_bIsCloningInProgress = false;
+ m_bLock = false;
+
+ m_pOwnerShapeFormat = pOwnerShape;
+ if (!m_pTextBoxes.empty())
+ m_pTextBoxes.clear();
+}
+
+SwTextBoxNode::~SwTextBoxNode()
+{
+ if (m_pTextBoxes.size() != 0)
+ {
+ SAL_WARN("sw.core", "SwTextBoxNode::~SwTextBoxNode(): Text-Box-Vector still not empty!");
+ assert(false);
+ }
+}
+
+void SwTextBoxNode::AddTextBox(SdrObject* pDrawObject, SwFrameFormat* pNewTextBox)
+{
+ assert(pNewTextBox);
+ assert(pNewTextBox->Which() == RES_FLYFRMFMT);
+
+ assert(pDrawObject);
+
+ SwTextBoxElement aElem;
+ aElem.m_pDrawObject = pDrawObject;
+ aElem.m_pTextBoxFormat = pNewTextBox;
+
+ for (const auto& rE : m_pTextBoxes)
+ {
+ if (rE.m_pDrawObject == pDrawObject || rE.m_pTextBoxFormat == pNewTextBox)
+ {
+ SAL_WARN("sw.core", "SwTextBoxNode::AddTextBox(): Already exist!");
+ return;
+ }
+ }
+
+ auto pSwFlyDraw = dynamic_cast<SwFlyDrawObj*>(pDrawObject);
+ if (pSwFlyDraw)
+ {
+ pSwFlyDraw->SetTextBox(true);
+ }
+ m_pTextBoxes.push_back(aElem);
+}
+
+void SwTextBoxNode::DelTextBox(const SdrObject* pDrawObject, bool bDelFromDoc)
+{
+ assert(pDrawObject);
+ if (m_pTextBoxes.empty())
+ return;
+
+ for (auto it = m_pTextBoxes.begin(); it != m_pTextBoxes.end();)
+ {
+ if (it->m_pDrawObject == pDrawObject)
+ {
+ if (bDelFromDoc)
+ {
+ it->m_pTextBoxFormat->GetDoc()->getIDocumentLayoutAccess().DelLayoutFormat(
+ it->m_pTextBoxFormat);
+ // What about m_pTextBoxes? So, when the DelLayoutFormat() removes the format
+ // then the ~SwFrameFormat() will call this method again to remove the entry.
+ return;
+ }
+ else
+ {
+ it = m_pTextBoxes.erase(it);
+ return;
+ }
+ }
+ ++it;
+ }
+
+ SAL_WARN("sw.core", "SwTextBoxNode::DelTextBox(): Not found!");
+}
+
+void SwTextBoxNode::DelTextBox(const SwFrameFormat* pTextBox, bool bDelFromDoc)
+{
+ if (m_pTextBoxes.empty())
+ return;
+
+ for (auto it = m_pTextBoxes.begin(); it != m_pTextBoxes.end();)
+ {
+ if (it->m_pTextBoxFormat == pTextBox)
+ {
+ if (bDelFromDoc)
+ {
+ it->m_pTextBoxFormat->GetDoc()->getIDocumentLayoutAccess().DelLayoutFormat(
+ it->m_pTextBoxFormat);
+ // What about m_pTextBoxes? So, when the DelLayoutFormat() removes the format
+ // then the ~SwFrameFormat() will call this method again to remove the entry.
+ return;
+ }
+ else
+ {
+ it = m_pTextBoxes.erase(it);
+ return;
+ }
+ }
+ ++it;
+ }
+
+ SAL_WARN("sw.core", "SwTextBoxNode::DelTextBox(): Not found!");
+}
+
+SwFrameFormat* SwTextBoxNode::GetTextBox(const SdrObject* pDrawObject) const
+{
+ assert(pDrawObject);
+ assert(m_pOwnerShapeFormat);
+
+ if (auto& pTextBoxes = m_pOwnerShapeFormat->GetOtherTextBoxFormats())
+ {
+ if (size_t(pTextBoxes.use_count()) != pTextBoxes->GetTextBoxCount() + size_t(1))
+ {
+ SAL_WARN("sw.core", "SwTextBoxNode::GetTextBox(): RefCount and TexBox count mismatch!");
+ }
+ }
+
+ if (m_bLock)
+ return nullptr;
+
+ if (!m_pTextBoxes.empty())
+ {
+ for (auto it = m_pTextBoxes.begin(); it != m_pTextBoxes.end(); it++)
+ {
+ if (it->m_pDrawObject == pDrawObject)
+ {
+ return it->m_pTextBoxFormat;
+ }
+ }
+ SAL_WARN("sw.core", "SwTextBoxNode::GetTextBox(): Not found!");
+ }
+
+ return nullptr;
+}
+
+void SwTextBoxNode::ClearAll()
+{
+ // If this called from ~SwDoc(), then only the address entries
+ // have to be removed, the format will be deleted by the
+ // the mpSpzFrameFormatTable->DeleteAndDestroyAll() in ~SwDoc()!
+ if (m_pOwnerShapeFormat->GetDoc()->IsInDtor())
+ {
+ m_pTextBoxes.clear();
+ return;
+ }
+
+ // For loop control
+ sal_uInt16 nLoopCount = 0;
+
+ // Reference not enough, copy needed.
+ const size_t nTextBoxCount = m_pTextBoxes.size();
+
+ // For loop has problems: When one entry deleted, the iterator has
+ // to be refreshed according to the new situation. So using While() instead.
+ while (!m_pTextBoxes.empty())
+ {
+ // Delete the last textbox of the vector from the doc
+ // (what will call deregister in ~SwFrameFormat()
+ m_pOwnerShapeFormat->GetDoc()->getIDocumentLayoutAccess().DelLayoutFormat(
+ m_pTextBoxes.back().m_pTextBoxFormat);
+
+ // Check if we are looping
+ if (nLoopCount > (nTextBoxCount + 1))
+ {
+ SAL_WARN("sw.core", "SwTextBoxNode::ClearAll(): Maximum loop count reached!");
+ break;
+ }
+ else
+ {
+ nLoopCount++;
+ }
+ }
+
+ // Ensure the vector is empty.
+ if (!m_pTextBoxes.empty())
+ {
+ SAL_WARN("sw.core", "SwTextBoxNode::ClearAll(): Text-Box-Vector still not empty!");
+ assert(false);
+ }
+}
+
+bool SwTextBoxNode::IsGroupTextBox() const { return m_pTextBoxes.size() > 1; }
+
+std::map<SdrObject*, SwFrameFormat*> SwTextBoxNode::GetAllTextBoxes() const
+{
+ std::map<SdrObject*, SwFrameFormat*> aRet;
+ for (auto& rElem : m_pTextBoxes)
+ {
+ aRet.emplace(rElem.m_pDrawObject, rElem.m_pTextBoxFormat);
+ }
+ return aRet;
+}
+
+void SwTextBoxNode::Clone(SwDoc* pDoc, const SwFormatAnchor& rNewAnc, SwFrameFormat* o_pTarget,
+ bool bSetAttr, bool bMakeFrame) const
+{
+ if (!o_pTarget || !pDoc)
+ return;
+
+ if (o_pTarget->Which() != RES_DRAWFRMFMT)
+ return;
+
+ if (m_bIsCloningInProgress)
+ return;
+
+ m_bIsCloningInProgress = true;
+
+ Clone_Impl(pDoc, rNewAnc, o_pTarget, m_pOwnerShapeFormat->FindSdrObject(),
+ o_pTarget->FindSdrObject(), bSetAttr, bMakeFrame);
+
+ m_bIsCloningInProgress = false;
+
+ for (auto& rElem : m_pTextBoxes)
+ {
+ SwTextBoxHelper::changeAnchor(m_pOwnerShapeFormat, rElem.m_pDrawObject);
+ SwTextBoxHelper::doTextBoxPositioning(m_pOwnerShapeFormat, rElem.m_pDrawObject);
+ SwTextBoxHelper::DoTextBoxZOrderCorrection(m_pOwnerShapeFormat, rElem.m_pDrawObject);
+ SwTextBoxHelper::syncTextBoxSize(m_pOwnerShapeFormat, rElem.m_pDrawObject);
+ }
+}
+
+void SwTextBoxNode::Clone_Impl(SwDoc* pDoc, const SwFormatAnchor& rNewAnc, SwFrameFormat* o_pTarget,
+ const SdrObject* pSrcObj, SdrObject* pDestObj, bool bSetAttr,
+ bool bMakeFrame) const
+{
+ if (!pSrcObj || !pDestObj)
+ return;
+
+ auto pSrcList = pSrcObj->getChildrenOfSdrObject();
+ auto pDestList = pDestObj->getChildrenOfSdrObject();
+
+ if (pSrcList && pDestList)
+ {
+ if (pSrcList->GetObjCount() != pDestList->GetObjCount())
+ {
+ SAL_WARN("sw.core", "SwTextBoxNode::Clone_Impl(): Difference between the shapes!");
+ return;
+ }
+
+ for (auto itSrc = pSrcList->begin(), itDest = pDestList->begin(); itSrc != pSrcList->end();
+ ++itSrc, ++itDest)
+ {
+ Clone_Impl(pDoc, rNewAnc, o_pTarget, itSrc->get(), itDest->get(), bSetAttr, bMakeFrame);
+ }
+ return;
+ }
+
+ if (!pSrcList && !pDestList)
+ {
+ if (auto pSrcFormat = GetTextBox(pSrcObj))
+ {
+ SwFormatAnchor aNewAnchor(rNewAnc);
+ if (aNewAnchor.GetAnchorId() == RndStdIds::FLY_AS_CHAR)
+ {
+ aNewAnchor.SetType(RndStdIds::FLY_AT_CHAR);
+
+ if (!bMakeFrame)
+ bMakeFrame = true;
+ }
+
+ if (auto pTargetFormat = pDoc->getIDocumentLayoutAccess().CopyLayoutFormat(
+ *pSrcFormat, aNewAnchor, bSetAttr, bMakeFrame))
+ {
+ if (!o_pTarget->GetOtherTextBoxFormats())
+ {
+ auto pNewTextBoxes = std::make_shared<SwTextBoxNode>(SwTextBoxNode(o_pTarget));
+ o_pTarget->SetOtherTextBoxFormats(pNewTextBoxes);
+ pNewTextBoxes->AddTextBox(pDestObj, pTargetFormat);
+ pTargetFormat->SetOtherTextBoxFormats(pNewTextBoxes);
+ }
+ else
+ {
+ o_pTarget->GetOtherTextBoxFormats()->AddTextBox(pDestObj, pTargetFormat);
+ pTargetFormat->SetOtherTextBoxFormats(o_pTarget->GetOtherTextBoxFormats());
+ }
+ o_pTarget->SetFormatAttr(pTargetFormat->GetContent());
+ }
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/visiturl.cxx b/sw/source/core/doc/visiturl.cxx
new file mode 100644
index 0000000000..24db9230b3
--- /dev/null
+++ b/sw/source/core/doc/visiturl.cxx
@@ -0,0 +1,125 @@
+/* -*- 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 <sfx2/docfile.hxx>
+#include <svl/inethist.hxx>
+#include <fmtinfmt.hxx>
+#include <txtinet.hxx>
+#include <doc.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <visiturl.hxx>
+#include <hints.hxx>
+#include <ndtxt.hxx>
+#include <editsh.hxx>
+#include <docsh.hxx>
+
+SwURLStateChanged::SwURLStateChanged( SwDoc& rD )
+ : m_rDoc( rD )
+{
+ StartListening( *INetURLHistory::GetOrCreate() );
+}
+
+SwURLStateChanged::~SwURLStateChanged()
+{
+ EndListening( *INetURLHistory::GetOrCreate() );
+}
+
+void SwURLStateChanged::Notify( SfxBroadcaster& , const SfxHint& rHint )
+{
+ if( !(dynamic_cast<const INetURLHistoryHint*>(&rHint) && m_rDoc.getIDocumentLayoutAccess().GetCurrentViewShell()) )
+ return;
+
+ // This URL has been changed:
+ const INetURLObject* pIURL = static_cast<const INetURLHistoryHint&>(rHint).GetObject();
+ OUString sURL( pIURL->GetMainURL( INetURLObject::DecodeMechanism::NONE ) ), sBkmk;
+
+ SwEditShell* pESh = m_rDoc.GetEditShell();
+
+ if( m_rDoc.GetDocShell() && m_rDoc.GetDocShell()->GetMedium() &&
+ // If this is our Doc, we can also have local jumps!
+ m_rDoc.GetDocShell()->GetMedium()->GetName() == sURL )
+ sBkmk = "#" + pIURL->GetMark();
+
+ bool bAction = false, bUnLockView = false;
+ for (const SfxPoolItem* pItem : m_rDoc.GetAttrPool().GetItemSurrogates(RES_TXTATR_INETFMT))
+ {
+ const SwFormatINetFormat* pFormatItem = dynamic_cast<const SwFormatINetFormat*>(pItem);
+ if( pFormatItem != nullptr &&
+ ( pFormatItem->GetValue() == sURL || ( !sBkmk.isEmpty() && pFormatItem->GetValue() == sBkmk )))
+ {
+ const SwTextINetFormat* pTextAttr = pFormatItem->GetTextINetFormat();
+ if (pTextAttr != nullptr)
+ {
+ const SwTextNode* pTextNd = pTextAttr->GetpTextNode();
+ if (pTextNd != nullptr)
+ {
+ if( !bAction && pESh )
+ {
+ pESh->StartAllAction();
+ bAction = true;
+ bUnLockView = !pESh->IsViewLocked();
+ pESh->LockView( true );
+ }
+ const_cast<SwTextINetFormat*>(pTextAttr)->SetVisitedValid(false);
+ const SwTextAttr* pAttr = pTextAttr;
+ SwUpdateAttr aUpdateAttr(
+ pAttr->GetStart(),
+ *pAttr->End(),
+ RES_FMT_CHG);
+
+ const_cast<SwTextNode*>(pTextNd)->TriggerNodeUpdate(sw::LegacyModifyHint(&aUpdateAttr, &aUpdateAttr));
+ }
+ }
+ }
+ }
+
+ if( bAction )
+ pESh->EndAllAction();
+ if( bUnLockView )
+ pESh->LockView( false );
+}
+
+// Check if the URL has been visited before. Via the Doc, if only one Bookmark is set
+// We need to put the Doc's name before it!
+bool SwDoc::IsVisitedURL( std::u16string_view rURL )
+{
+ bool bRet = false;
+ if( !rURL.empty() )
+ {
+ INetURLHistory *pHist = INetURLHistory::GetOrCreate();
+ if( '#' == rURL[0] && mpDocShell && mpDocShell->GetMedium() )
+ {
+ INetURLObject aIObj( mpDocShell->GetMedium()->GetURLObject() );
+ aIObj.SetMark( rURL.substr( 1 ) );
+ bRet = pHist->QueryUrl( aIObj );
+ }
+ else
+ bRet = pHist->QueryUrl( rURL );
+
+ // We also want to be informed about status updates in the History
+ if( !mpURLStateChgd )
+ {
+ SwDoc* pD = this;
+ pD->mpURLStateChgd.reset( new SwURLStateChanged(*this) );
+ }
+ }
+ return bRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */