summaryrefslogtreecommitdiffstats
path: root/sw/source/core/docnode
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
commited5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch)
tree7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /sw/source/core/docnode
parentInitial commit. (diff)
downloadlibreoffice-upstream.tar.xz
libreoffice-upstream.zip
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'sw/source/core/docnode')
-rw-r--r--sw/source/core/docnode/cancellablejob.cxx33
-rw-r--r--sw/source/core/docnode/cancellablejob.hxx47
-rw-r--r--sw/source/core/docnode/finalthreadmanager.cxx424
-rw-r--r--sw/source/core/docnode/ndcopy.cxx378
-rw-r--r--sw/source/core/docnode/ndnotxt.cxx294
-rw-r--r--sw/source/core/docnode/ndnum.cxx98
-rw-r--r--sw/source/core/docnode/ndsect.cxx1475
-rw-r--r--sw/source/core/docnode/ndsect.hxx31
-rw-r--r--sw/source/core/docnode/ndtbl.cxx4684
-rw-r--r--sw/source/core/docnode/ndtbl1.cxx1774
-rw-r--r--sw/source/core/docnode/node.cxx2138
-rw-r--r--sw/source/core/docnode/node2lay.cxx517
-rw-r--r--sw/source/core/docnode/nodes.cxx2392
-rw-r--r--sw/source/core/docnode/observablethread.cxx63
-rw-r--r--sw/source/core/docnode/pausethreadstarting.cxx48
-rw-r--r--sw/source/core/docnode/retrievedinputstreamdata.cxx143
-rw-r--r--sw/source/core/docnode/retrieveinputstream.cxx83
-rw-r--r--sw/source/core/docnode/retrieveinputstreamconsumer.cxx63
-rw-r--r--sw/source/core/docnode/section.cxx1512
-rw-r--r--sw/source/core/docnode/swbaslnk.cxx326
-rw-r--r--sw/source/core/docnode/swthreadjoiner.cxx49
-rw-r--r--sw/source/core/docnode/swthreadmanager.cxx78
-rw-r--r--sw/source/core/docnode/threadlistener.cxx47
-rw-r--r--sw/source/core/docnode/threadmanager.cxx250
-rw-r--r--sw/source/core/docnode/threadmanager.hxx147
25 files changed, 17094 insertions, 0 deletions
diff --git a/sw/source/core/docnode/cancellablejob.cxx b/sw/source/core/docnode/cancellablejob.cxx
new file mode 100644
index 000000000..eecd1d336
--- /dev/null
+++ b/sw/source/core/docnode/cancellablejob.cxx
@@ -0,0 +1,33 @@
+/* -*- 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 "cancellablejob.hxx"
+#include <observablethread.hxx>
+
+CancellableJob::CancellableJob( const rtl::Reference< ObservableThread >& rThread ) :
+ mrThread( rThread )
+{
+}
+
+// css::util::XCancellable:
+void SAL_CALL CancellableJob::cancel()
+{
+ mrThread->join();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/docnode/cancellablejob.hxx b/sw/source/core/docnode/cancellablejob.hxx
new file mode 100644
index 000000000..010e1f4de
--- /dev/null
+++ b/sw/source/core/docnode/cancellablejob.hxx
@@ -0,0 +1,47 @@
+/* -*- 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_DOCNODE_CANCELLABLEJOB_HXX
+#define INCLUDED_SW_SOURCE_CORE_DOCNODE_CANCELLABLEJOB_HXX
+
+#include <sal/config.h>
+#include <cppuhelper/implbase.hxx>
+#include <com/sun/star/util/XCancellable.hpp>
+
+#include <rtl/ref.hxx>
+
+#include <observablethread.hxx>
+
+class CancellableJob : public ::cppu::WeakImplHelper<css::util::XCancellable>
+{
+public:
+ explicit CancellableJob(const ::rtl::Reference<ObservableThread>& rThread);
+
+ // css::util::XCancellable:
+ virtual void SAL_CALL cancel() override;
+
+private:
+ CancellableJob(CancellableJob const&) = delete;
+ void operator=(CancellableJob const&) = delete;
+
+ ::rtl::Reference<ObservableThread> mrThread;
+};
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/docnode/finalthreadmanager.cxx b/sw/source/core/docnode/finalthreadmanager.cxx
new file mode 100644
index 000000000..bfae4a6b4
--- /dev/null
+++ b/sw/source/core/docnode/finalthreadmanager.cxx
@@ -0,0 +1,424 @@
+/* -*- 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 <finalthreadmanager.hxx>
+
+#include <osl/diagnose.h>
+#include <osl/thread.hxx>
+#include <pausethreadstarting.hxx>
+#include <swthreadjoiner.hxx>
+
+#include <com/sun/star/frame/Desktop.hpp>
+#include <com/sun/star/frame/TerminationVetoException.hpp>
+#include <rtl/ustring.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <mutex>
+
+/** thread to cancel a give list of cancellable jobs
+
+ helper class for FinalThreadManager
+*/
+class CancelJobsThread : public osl::Thread
+{
+ public:
+ explicit CancelJobsThread( std::list< css::uno::Reference< css::util::XCancellable > >&& rJobs )
+ : maJobs( std::move(rJobs) ),
+ mbAllJobsCancelled( false ),
+ mbStopped( false )
+ {
+ }
+
+ void addJobs( std::list< css::uno::Reference< css::util::XCancellable > >& rJobs );
+ bool allJobsCancelled() const;
+ void stopWhenAllJobsCancelled();
+
+ private:
+ bool existJobs() const;
+
+ css::uno::Reference< css::util::XCancellable > getNextJob();
+
+ bool stopped() const;
+ virtual void SAL_CALL run() override;
+ mutable std::mutex maMutex;
+
+ std::list< css::uno::Reference< css::util::XCancellable > > maJobs;
+
+ bool mbAllJobsCancelled;
+ bool mbStopped;
+};
+
+void CancelJobsThread::addJobs( std::list< css::uno::Reference< css::util::XCancellable > >& rJobs )
+{
+ std::scoped_lock aGuard(maMutex);
+
+ maJobs.insert( maJobs.end(), rJobs.begin(), rJobs.end() );
+ mbAllJobsCancelled = !maJobs.empty();
+}
+
+bool CancelJobsThread::existJobs() const
+{
+ std::scoped_lock aGuard(maMutex);
+
+ return !maJobs.empty();
+}
+
+bool CancelJobsThread::allJobsCancelled() const
+{
+ std::scoped_lock aGuard(maMutex);
+
+ return maJobs.empty() && mbAllJobsCancelled;
+}
+
+void CancelJobsThread::stopWhenAllJobsCancelled()
+{
+ std::scoped_lock aGuard(maMutex);
+
+ mbStopped = true;
+}
+
+css::uno::Reference< css::util::XCancellable > CancelJobsThread::getNextJob()
+{
+ css::uno::Reference< css::util::XCancellable > xRet;
+
+ {
+ std::scoped_lock aGuard(maMutex);
+
+ if ( !maJobs.empty() )
+ {
+ xRet = maJobs.front();
+ maJobs.pop_front();
+ }
+ }
+
+ return xRet;
+}
+
+bool CancelJobsThread::stopped() const
+{
+ std::scoped_lock aGuard(maMutex);
+
+ return mbStopped;
+}
+
+void SAL_CALL CancelJobsThread::run()
+{
+ osl_setThreadName("sw CancelJobsThread");
+
+ while ( !stopped() )
+ {
+ while ( existJobs() )
+ {
+ css::uno::Reference< css::util::XCancellable > aJob( getNextJob() );
+ if ( aJob.is() )
+ aJob->cancel();
+ }
+
+ mbAllJobsCancelled = true;
+
+ {
+ osl::Thread::wait(std::chrono::seconds(1));
+ }
+ }
+}
+
+/** thread to terminate office, when all jobs are cancelled.
+
+ helper class for FinalThreadManager
+*/
+class TerminateOfficeThread : public osl::Thread
+{
+ public:
+ TerminateOfficeThread( CancelJobsThread const & rCancelJobsThread,
+ css::uno::Reference< css::uno::XComponentContext > const & xContext )
+ : mrCancelJobsThread( rCancelJobsThread ),
+ mbStopOfficeTermination( false ),
+ mxContext( xContext )
+ {
+ }
+
+ void StopOfficeTermination();
+
+ private:
+ virtual void SAL_CALL run() override;
+ virtual void SAL_CALL onTerminated() override;
+ bool OfficeTerminationStopped();
+ void PerformOfficeTermination();
+
+ osl::Mutex maMutex;
+
+ const CancelJobsThread& mrCancelJobsThread;
+ bool mbStopOfficeTermination;
+
+ css::uno::Reference< css::uno::XComponentContext > mxContext;
+};
+
+void TerminateOfficeThread::StopOfficeTermination()
+{
+ osl::MutexGuard aGuard(maMutex);
+
+ mbStopOfficeTermination = true;
+}
+
+bool TerminateOfficeThread::OfficeTerminationStopped()
+{
+ osl::MutexGuard aGuard(maMutex);
+
+ return mbStopOfficeTermination;
+}
+
+void SAL_CALL TerminateOfficeThread::run()
+{
+ osl_setThreadName("sw TerminateOfficeThread");
+
+ while ( !OfficeTerminationStopped() )
+ {
+ osl::MutexGuard aGuard(maMutex);
+
+ if ( mrCancelJobsThread.allJobsCancelled() )
+ break;
+ }
+
+ if ( !OfficeTerminationStopped() )
+ PerformOfficeTermination();
+}
+
+void TerminateOfficeThread::PerformOfficeTermination()
+{
+ css::uno::Reference< css::frame::XDesktop2 > xDesktop = css::frame::Desktop::create(mxContext);
+
+ css::uno::Reference< css::container::XElementAccess > xList = xDesktop->getFrames();
+ if ( !xList.is() )
+ {
+ OSL_FAIL( "<TerminateOfficeThread::PerformOfficeTermination()> - no XElementAccess!" );
+ return;
+ }
+
+ if ( !xList->hasElements() )
+ {
+ if ( !OfficeTerminationStopped() )
+ xDesktop->terminate();
+ }
+}
+
+void SAL_CALL TerminateOfficeThread::onTerminated()
+{
+ if ( OfficeTerminationStopped() )
+ delete this;
+}
+
+FinalThreadManager::FinalThreadManager(css::uno::Reference< css::uno::XComponentContext > const & context)
+ : m_xContext(context),
+ mpTerminateOfficeThread( nullptr ),
+ mbRegisteredAtDesktop( false )
+{
+
+}
+
+void FinalThreadManager::registerAsListenerAtDesktop()
+{
+ css::uno::Reference< css::frame::XDesktop2 > xDesktop = css::frame::Desktop::create(m_xContext);
+ xDesktop->addTerminateListener( css::uno::Reference< css::frame::XTerminateListener >( this ) );
+}
+
+FinalThreadManager::~FinalThreadManager()
+{
+ if ( mpPauseThreadStarting )
+ {
+ mpPauseThreadStarting.reset();
+ }
+
+ if ( mpTerminateOfficeThread != nullptr )
+ {
+ mpTerminateOfficeThread->StopOfficeTermination(); // thread kills itself.
+ mpTerminateOfficeThread = nullptr;
+ }
+
+ if ( !maThreads.empty() )
+ {
+ OSL_FAIL( "<FinalThreadManager::~FinalThreadManager()> - still registered jobs are existing -> perform cancellation" );
+ cancelAllJobs();
+ }
+
+ if ( mpCancelJobsThread != nullptr )
+ {
+ if ( !mpCancelJobsThread->allJobsCancelled() )
+ OSL_FAIL( "<FinalThreadManager::~FinalThreadManager()> - cancellation of registered jobs not yet finished -> wait for its finish" );
+
+ mpCancelJobsThread->stopWhenAllJobsCancelled();
+ mpCancelJobsThread->join();
+ mpCancelJobsThread.reset();
+ }
+}
+
+// com.sun.star.uno.XServiceInfo:
+OUString SAL_CALL FinalThreadManager::getImplementationName()
+{
+ return "com.sun.star.util.comp.FinalThreadManager";
+}
+
+sal_Bool SAL_CALL FinalThreadManager::supportsService(OUString const & serviceName)
+{
+ return cppu::supportsService(this, serviceName);
+}
+
+css::uno::Sequence< OUString > SAL_CALL FinalThreadManager::getSupportedServiceNames()
+{
+ return { "com.sun.star.util.JobManager" };
+}
+
+// css::util::XJobManager:
+void SAL_CALL FinalThreadManager::registerJob(const css::uno::Reference< css::util::XCancellable > & Job)
+{
+ osl::MutexGuard aGuard(maMutex);
+
+ maThreads.push_back( Job );
+
+ if ( !mbRegisteredAtDesktop )
+ {
+ registerAsListenerAtDesktop();
+ mbRegisteredAtDesktop = true;
+ }
+}
+
+void SAL_CALL FinalThreadManager::releaseJob(const css::uno::Reference< css::util::XCancellable > & Job)
+{
+ osl::MutexGuard aGuard(maMutex);
+
+ maThreads.remove( Job );
+}
+
+void SAL_CALL FinalThreadManager::cancelAllJobs()
+{
+ std::list< css::uno::Reference< css::util::XCancellable > > aThreads;
+ {
+ osl::MutexGuard aGuard(maMutex);
+
+ aThreads.insert( aThreads.end(), maThreads.begin(), maThreads.end() );
+ maThreads.clear();
+ }
+
+ if ( aThreads.empty() )
+ return;
+
+ osl::MutexGuard aGuard(maMutex);
+
+ if ( mpCancelJobsThread == nullptr )
+ {
+ mpCancelJobsThread.reset(new CancelJobsThread( std::list(aThreads) ));
+ if ( !mpCancelJobsThread->create() )
+ {
+ mpCancelJobsThread.reset();
+ for (auto const& elem : aThreads)
+ {
+ elem->cancel();
+ }
+ aThreads.clear();
+ }
+ }
+ else
+ mpCancelJobsThread->addJobs( aThreads );
+}
+
+// css::frame::XTerminateListener
+void SAL_CALL FinalThreadManager::queryTermination( const css::lang::EventObject& )
+{
+ osl::MutexGuard aGuard(maMutex);
+
+ cancelAllJobs();
+ // Sleep 1 second to give the thread for job cancellation some time.
+ // Probably, all started threads have already finished its work.
+ if ( mpCancelJobsThread != nullptr &&
+ !mpCancelJobsThread->allJobsCancelled() )
+ {
+ osl::Thread::wait(std::chrono::seconds(1));
+ }
+
+ if ( mpCancelJobsThread != nullptr &&
+ !mpCancelJobsThread->allJobsCancelled() )
+ {
+ if ( mpTerminateOfficeThread != nullptr )
+ {
+ if ( mpTerminateOfficeThread->isRunning() )
+ mpTerminateOfficeThread->StopOfficeTermination(); // thread kills itself.
+ else
+ delete mpTerminateOfficeThread;
+
+ mpTerminateOfficeThread = nullptr;
+ }
+ mpTerminateOfficeThread = new TerminateOfficeThread( *mpCancelJobsThread,
+ m_xContext );
+ if ( !mpTerminateOfficeThread->create() )
+ {
+ delete mpTerminateOfficeThread;
+ mpTerminateOfficeThread = nullptr;
+ }
+
+ throw css::frame::TerminationVetoException();
+ }
+
+ mpPauseThreadStarting.reset(new SwPauseThreadStarting());
+}
+
+void SAL_CALL FinalThreadManager::cancelTermination( const css::lang::EventObject& )
+{
+ mpPauseThreadStarting.reset();
+}
+
+void SAL_CALL FinalThreadManager::notifyTermination( const css::lang::EventObject& )
+{
+ if ( mpTerminateOfficeThread != nullptr )
+ {
+ if ( mpTerminateOfficeThread->isRunning() )
+ mpTerminateOfficeThread->StopOfficeTermination(); // thread kills itself.
+ else
+ delete mpTerminateOfficeThread;
+
+ mpTerminateOfficeThread = nullptr;
+ }
+
+ if ( !maThreads.empty() )
+ cancelAllJobs();
+
+ if ( mpCancelJobsThread != nullptr )
+ {
+ mpCancelJobsThread->stopWhenAllJobsCancelled();
+ mpCancelJobsThread->join();
+ mpCancelJobsThread.reset();
+ }
+
+ // get reference of this
+ css::uno::Reference< css::uno::XInterface > aOwnRef( static_cast< cppu::OWeakObject* >( this ));
+ // notify <SwThreadJoiner> to release its reference
+ SwThreadJoiner::ReleaseThreadJoiner();
+}
+
+// ::com::sun:star::lang::XEventListener (inherited via css::frame::XTerminateListener)
+void SAL_CALL FinalThreadManager::disposing( const css::lang::EventObject& )
+{
+ // nothing to do, because instance doesn't hold any references of observed objects
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_util_comp_FinalThreadManager_get_implementation(css::uno::XComponentContext* context,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new FinalThreadManager(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/docnode/ndcopy.cxx b/sw/source/core/docnode/ndcopy.cxx
new file mode 100644
index 000000000..4b26acc0e
--- /dev/null
+++ b/sw/source/core/docnode/ndcopy.cxx
@@ -0,0 +1,378 @@
+/* -*- 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 <IDocumentFieldsAccess.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <node.hxx>
+#include <frmfmt.hxx>
+#include <swtable.hxx>
+#include <ndtxt.hxx>
+#include <swtblfmt.hxx>
+#include <cellatr.hxx>
+#include <ddefld.hxx>
+#include <swddetbl.hxx>
+#include <ndindex.hxx>
+#include <frameformats.hxx>
+#include <vector>
+#include <osl/diagnose.h>
+#include <svl/numformat.hxx>
+
+
+#ifdef DBG_UTIL
+#define CHECK_TABLE(t) (t).CheckConsistency();
+#else
+#define CHECK_TABLE(t)
+#endif
+
+namespace {
+
+// Structure for the mapping from old and new frame formats to the
+// boxes and lines of a table
+struct MapTableFrameFormat
+{
+ const SwFrameFormat *pOld;
+ SwFrameFormat *pNew;
+ MapTableFrameFormat( const SwFrameFormat *pOldFormat, SwFrameFormat*pNewFormat )
+ : pOld( pOldFormat ), pNew( pNewFormat )
+ {}
+};
+
+}
+
+typedef std::vector<MapTableFrameFormat> MapTableFrameFormats;
+
+SwContentNode* SwTextNode::MakeCopy(SwDoc& rDoc, const SwNodeIndex& rIdx, bool const bNewFrames) const
+{
+ // the Copy-Textnode is the Node with the Text, the Copy-Attrnode is the
+ // node with the collection and hard attributes. Normally is the same
+ // node, but if insert a glossary without formatting, then the Attrnode
+ // is the prev node of the destination position in dest. document.
+ SwTextNode* pCpyTextNd = const_cast<SwTextNode*>(this);
+ SwTextNode* pCpyAttrNd = pCpyTextNd;
+
+ // Copy the formats to the other document
+ SwTextFormatColl* pColl = nullptr;
+ if( rDoc.IsInsOnlyTextGlossary() )
+ {
+ SwNodeIndex aIdx( rIdx, -1 );
+ if( aIdx.GetNode().IsTextNode() )
+ {
+ pCpyAttrNd = aIdx.GetNode().GetTextNode();
+ pColl = &pCpyAttrNd->GetTextColl()->GetNextTextFormatColl();
+ }
+ }
+ if( !pColl )
+ pColl = rDoc.CopyTextColl( *GetTextColl() );
+
+ SwTextNode* pTextNd = rDoc.GetNodes().MakeTextNode(rIdx, pColl, bNewFrames);
+
+ // METADATA: register copy
+ pTextNd->RegisterAsCopyOf(*pCpyTextNd);
+
+ // Copy Attribute/Text
+ if( !pCpyAttrNd->HasSwAttrSet() )
+ // An AttrSet was added for numbering, so delete it
+ pTextNd->ResetAllAttr();
+
+ // if Copy-Textnode unequal to Copy-Attrnode, then copy first
+ // the attributes into the new Node.
+ if( pCpyAttrNd != pCpyTextNd )
+ {
+ pCpyAttrNd->CopyAttr( pTextNd, 0, 0 );
+ if( pCpyAttrNd->HasSwAttrSet() )
+ {
+ SwAttrSet aSet( *pCpyAttrNd->GetpSwAttrSet() );
+ aSet.ClearItem( RES_PAGEDESC );
+ aSet.ClearItem( RES_BREAK );
+ aSet.CopyToModify( *pTextNd );
+ }
+ }
+
+ // Is that enough? What about PostIts/Fields/FieldTypes?
+ // #i96213# - force copy of all attributes
+ pCpyTextNd->CopyText( pTextNd, SwIndex( pCpyTextNd ),
+ pCpyTextNd->GetText().getLength(), true );
+
+ if( RES_CONDTXTFMTCOLL == pColl->Which() )
+ pTextNd->ChkCondColl();
+
+ return pTextNd;
+}
+
+static bool lcl_SrchNew( const MapTableFrameFormat& rMap, SwFrameFormat** pPara )
+{
+ if( rMap.pOld != *pPara )
+ return true;
+ *pPara = rMap.pNew;
+ return false;
+}
+
+namespace {
+
+struct CopyTable
+{
+ SwDoc& m_rDoc;
+ SwNodeOffset m_nOldTableSttIdx;
+ MapTableFrameFormats& m_rMapArr;
+ SwTableLine* m_pInsLine;
+ SwTableBox* m_pInsBox;
+ SwTableNode *m_pTableNd;
+ const SwTable *m_pOldTable;
+
+ CopyTable(SwDoc& rDc, MapTableFrameFormats& rArr, SwNodeOffset nOldStt,
+ SwTableNode& rTableNd, const SwTable* pOldTable)
+ : m_rDoc(rDc), m_nOldTableSttIdx(nOldStt), m_rMapArr(rArr),
+ m_pInsLine(nullptr), m_pInsBox(nullptr), m_pTableNd(&rTableNd), m_pOldTable(pOldTable)
+ {}
+};
+
+}
+
+static void lcl_CopyTableLine( const SwTableLine* pLine, CopyTable* pCT );
+
+static void lcl_CopyTableBox( SwTableBox* pBox, CopyTable* pCT )
+{
+ SwTableBoxFormat * pBoxFormat = static_cast<SwTableBoxFormat*>(pBox->GetFrameFormat());
+ for (const auto& rMap : pCT->m_rMapArr)
+ if ( !lcl_SrchNew( rMap, reinterpret_cast<SwFrameFormat**>(&pBoxFormat) ) )
+ break;
+
+ if (pBoxFormat == pBox->GetFrameFormat()) // Create a new one?
+ {
+ const SwTableBoxFormula* pFormulaItem = pBoxFormat->GetItemIfSet( RES_BOXATR_FORMULA, false );
+ if( pFormulaItem && pFormulaItem->IsIntrnlName() )
+ {
+ const_cast<SwTableBoxFormula*>(pFormulaItem)->PtrToBoxNm(pCT->m_pOldTable);
+ }
+
+ pBoxFormat = pCT->m_rDoc.MakeTableBoxFormat();
+ pBoxFormat->CopyAttrs( *pBox->GetFrameFormat() );
+
+ if( pBox->GetSttIdx() )
+ {
+ SvNumberFormatter* pN = pCT->m_rDoc.GetNumberFormatter(false);
+ const SwTableBoxNumFormat* pFormatItem;
+ if( pN && pN->HasMergeFormatTable() &&
+ (pFormatItem = pBoxFormat->GetItemIfSet( RES_BOXATR_FORMAT, false )) )
+ {
+ sal_uLong nOldIdx = pFormatItem->GetValue();
+ sal_uLong nNewIdx = pN->GetMergeFormatIndex( nOldIdx );
+ if( nNewIdx != nOldIdx )
+ pBoxFormat->SetFormatAttr( SwTableBoxNumFormat( nNewIdx ));
+
+ }
+ }
+
+ pCT->m_rMapArr.emplace_back(pBox->GetFrameFormat(), pBoxFormat);
+ }
+
+ sal_uInt16 nLines = pBox->GetTabLines().size();
+ SwTableBox* pNewBox;
+ if( nLines )
+ pNewBox = new SwTableBox(pBoxFormat, nLines, pCT->m_pInsLine);
+ else
+ {
+ SwNodeIndex aNewIdx(*pCT->m_pTableNd, pBox->GetSttIdx() - pCT->m_nOldTableSttIdx);
+ assert(aNewIdx.GetNode().IsStartNode() && "Index is not on the start node");
+
+ pNewBox = new SwTableBox(pBoxFormat, aNewIdx, pCT->m_pInsLine);
+ pNewBox->setRowSpan( pBox->getRowSpan() );
+ }
+
+ pCT->m_pInsLine->GetTabBoxes().push_back( pNewBox );
+
+ if (nLines)
+ {
+ CopyTable aPara(*pCT);
+ aPara.m_pInsBox = pNewBox;
+ for( const SwTableLine* pLine : pBox->GetTabLines() )
+ lcl_CopyTableLine( pLine, &aPara );
+ }
+ else if (pNewBox->IsInHeadline(&pCT->m_pTableNd->GetTable()))
+ {
+ // In the headline, the paragraphs must match conditional styles
+ pNewBox->GetSttNd()->CheckSectionCondColl();
+ }
+}
+
+static void lcl_CopyTableLine( const SwTableLine* pLine, CopyTable* pCT )
+{
+ SwTableLineFormat * pLineFormat = static_cast<SwTableLineFormat*>(pLine->GetFrameFormat());
+ for (const auto& rMap : pCT->m_rMapArr)
+ if ( !lcl_SrchNew( rMap, reinterpret_cast<SwFrameFormat**>(&pLineFormat) ) )
+ break;
+
+ if( pLineFormat == pLine->GetFrameFormat() ) // Create a new one?
+ {
+ pLineFormat = pCT->m_rDoc.MakeTableLineFormat();
+ pLineFormat->CopyAttrs( *pLine->GetFrameFormat() );
+ pCT->m_rMapArr.emplace_back(pLine->GetFrameFormat(), pLineFormat);
+ }
+
+ SwTableLine* pNewLine = new SwTableLine(pLineFormat, pLine->GetTabBoxes().size(), pCT->m_pInsBox);
+ // Insert the new row into the table
+ if (pCT->m_pInsBox)
+ {
+ pCT->m_pInsBox->GetTabLines().push_back(pNewLine);
+ }
+ else
+ {
+ pCT->m_pTableNd->GetTable().GetTabLines().push_back(pNewLine);
+ }
+
+ pCT->m_pInsLine = pNewLine;
+ for( auto& rpBox : const_cast<SwTableLine*>(pLine)->GetTabBoxes() )
+ lcl_CopyTableBox(rpBox, pCT);
+}
+
+SwTableNode* SwTableNode::MakeCopy( SwDoc& rDoc, const SwNodeIndex& rIdx ) const
+{
+ // In which array are we? Nodes? UndoNodes?
+ SwNodes& rNds = const_cast<SwNodes&>(GetNodes());
+
+ {
+ if( rIdx < rDoc.GetNodes().GetEndOfInserts().GetIndex() &&
+ rIdx >= rDoc.GetNodes().GetEndOfInserts().StartOfSectionIndex() )
+ return nullptr;
+ }
+
+ // Copy the TableFrameFormat
+ OUString sTableName( GetTable().GetFrameFormat()->GetName() );
+ if( !rDoc.IsCopyIsMove() )
+ {
+ const SwFrameFormats& rTableFormats = *rDoc.GetTableFrameFormats();
+ for( size_t n = rTableFormats.size(); n; )
+ if( rTableFormats[ --n ]->GetName() == sTableName )
+ {
+ sTableName = rDoc.GetUniqueTableName();
+ break;
+ }
+ }
+
+ SwFrameFormat* pTableFormat = rDoc.MakeTableFrameFormat( sTableName, rDoc.GetDfltFrameFormat() );
+ pTableFormat->CopyAttrs( *GetTable().GetFrameFormat() );
+ SwTableNode* pTableNd = new SwTableNode( rIdx );
+ SwEndNode* pEndNd = new SwEndNode( rIdx, *pTableNd );
+ SwNodeIndex aInsPos( *pEndNd );
+
+ SwTable& rTable = pTableNd->GetTable();
+ rTable.SetTableStyleName(GetTable().GetTableStyleName());
+ rTable.RegisterToFormat( *pTableFormat );
+
+ rTable.SetRowsToRepeat( GetTable().GetRowsToRepeat() );
+ rTable.SetTableChgMode( GetTable().GetTableChgMode() );
+ rTable.SetTableModel( GetTable().IsNewModel() );
+
+ SwDDEFieldType* pDDEType = nullptr;
+ if( auto pSwDDETable = dynamic_cast<const SwDDETable*>( &GetTable() ) )
+ {
+ // We're copying a DDE table
+ // Is the field type available in the new document?
+ pDDEType = const_cast<SwDDETable*>(pSwDDETable)->GetDDEFieldType();
+ if( pDDEType->IsDeleted() )
+ rDoc.getIDocumentFieldsAccess().InsDeletedFieldType( *pDDEType );
+ else
+ pDDEType = static_cast<SwDDEFieldType*>(rDoc.getIDocumentFieldsAccess().InsertFieldType( *pDDEType ));
+ OSL_ENSURE( pDDEType, "unknown FieldType" );
+
+ // Swap the table pointers in the node
+ std::unique_ptr<SwDDETable> pNewTable(new SwDDETable( pTableNd->GetTable(), pDDEType ));
+ pTableNd->SetNewTable( std::move(pNewTable), false );
+ }
+ // First copy the content of the tables, we will later assign the
+ // boxes/lines and create the frames
+ SwNodeRange aRg( *this, SwNodeOffset(+1), *EndOfSectionNode() );
+
+ // If there is a table in this table, the table format for the outer table
+ // does not seem to be used, because the table does not have any contents yet
+ // (see IsUsed). Therefore the inner table gets the same name as the outer table.
+ // We have to make sure that the table node of the SwTable is accessible, even
+ // without any content in m_TabSortContentBoxes. #i26629#
+ pTableNd->GetTable().SetTableNode( pTableNd );
+ rNds.Copy_( aRg, aInsPos, false );
+ pTableNd->GetTable().SetTableNode( nullptr );
+
+ // Special case for a single box
+ if( 1 == GetTable().GetTabSortBoxes().size() )
+ {
+ aRg.aStart.Assign( *pTableNd, 1 );
+ aRg.aEnd.Assign( *pTableNd->EndOfSectionNode() );
+ rDoc.GetNodes().SectionDown( &aRg, SwTableBoxStartNode );
+ }
+
+ // Delete all frames from the copied area, they will be created
+ // during the generation of the table frame
+ pTableNd->DelFrames();
+
+ MapTableFrameFormats aMapArr;
+ CopyTable aPara( rDoc, aMapArr, GetIndex(), *pTableNd, &GetTable() );
+
+ for( const SwTableLine* pLine : GetTable().GetTabLines() )
+ lcl_CopyTableLine( pLine, &aPara );
+
+ if( pDDEType )
+ pDDEType->IncRefCnt();
+
+ CHECK_TABLE( GetTable() );
+ return pTableNd;
+}
+
+void SwTextNode::CopyCollFormat(SwTextNode& rDestNd, bool const bUndoForChgFormatColl)
+{
+ // Copy the formats into the other document:
+ // Special case for PageBreak/PageDesc/ColBrk
+ SwDoc& rDestDoc = rDestNd.GetDoc();
+ SwAttrSet aPgBrkSet( rDestDoc.GetAttrPool(), aBreakSetRange );
+ const SwAttrSet* pSet;
+
+ pSet = rDestNd.GetpSwAttrSet();
+ if( nullptr != pSet )
+ {
+ // Special cases for Break-Attributes
+ const SfxPoolItem* pAttr;
+ if( SfxItemState::SET == pSet->GetItemState( RES_BREAK, false, &pAttr ) )
+ aPgBrkSet.Put( *pAttr );
+
+ if( SfxItemState::SET == pSet->GetItemState( RES_PAGEDESC, false, &pAttr ) )
+ aPgBrkSet.Put( *pAttr );
+ }
+
+ // this may create undo action SwUndoFormatCreate
+ auto const pCopy( rDestDoc.CopyTextColl( *GetTextColl() ) );
+ if (bUndoForChgFormatColl)
+ {
+ rDestNd.ChgFormatColl(pCopy);
+ }
+ else // tdf#138897
+ {
+ ::sw::UndoGuard const ug(rDestDoc.GetIDocumentUndoRedo());
+ rDestNd.ChgFormatColl(pCopy);
+ }
+ pSet = GetpSwAttrSet();
+ if( nullptr != pSet )
+ {
+ // note: this may create undo actions but not for setting the items
+ pSet->CopyToModify( rDestNd );
+ }
+
+ if( aPgBrkSet.Count() )
+ rDestNd.SetAttr( aPgBrkSet );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/docnode/ndnotxt.cxx b/sw/source/core/docnode/ndnotxt.cxx
new file mode 100644
index 000000000..7ae423a04
--- /dev/null
+++ b/sw/source/core/docnode/ndnotxt.cxx
@@ -0,0 +1,294 @@
+/* -*- 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 <tools/poly.hxx>
+#include <svl/stritem.hxx>
+#include <svx/contdlg.hxx>
+#include <vcl/svapp.hxx>
+#include <doc.hxx>
+#include <fmtcol.hxx>
+#include <ndnotxt.hxx>
+#include <ndgrf.hxx>
+#include <ndole.hxx>
+#include <ndindex.hxx>
+#include <istyleaccess.hxx>
+#include <SwStyleNameMapper.hxx>
+
+#include <frmfmt.hxx>
+
+SwNoTextNode::SwNoTextNode( const SwNodeIndex & rWhere,
+ const SwNodeType nNdType,
+ SwGrfFormatColl *pGrfColl,
+ SwAttrSet const * pAutoAttr ) :
+ SwContentNode( rWhere, nNdType, pGrfColl ),
+ m_bAutomaticContour( false ),
+ m_bContourMapModeValid( true ),
+ m_bPixelContour( false )
+{
+ // Should this set a hard attribute?
+ if( pAutoAttr )
+ SetAttr( *pAutoAttr );
+}
+
+SwNoTextNode::~SwNoTextNode()
+{
+}
+
+/// Creates an AttrSet for all derivations with ranges for frame-
+/// and graphics-attributes.
+void SwNoTextNode::NewAttrSet( SwAttrPool& rPool )
+{
+ OSL_ENSURE( !mpAttrSet, "AttrSet is already set" );
+ SwAttrSet aNewAttrSet( rPool, aNoTextNodeSetRange );
+
+ // put names of parent style and conditional style:
+ const SwFormatColl* pFormatColl = GetFormatColl();
+ OUString sVal;
+ SwStyleNameMapper::FillProgName( pFormatColl->GetName(), sVal, SwGetPoolIdFromName::TxtColl );
+ SfxStringItem aFormatColl( RES_FRMATR_STYLE_NAME, sVal );
+ aNewAttrSet.Put( aFormatColl );
+
+ aNewAttrSet.SetParent( &GetFormatColl()->GetAttrSet() );
+ mpAttrSet = GetDoc().GetIStyleAccess().getAutomaticStyle( aNewAttrSet, IStyleAccess::AUTO_STYLE_NOTXT );
+}
+
+/// Dummies for loading/saving of persistent data
+/// when working with graphics and OLE objects
+bool SwNoTextNode::RestorePersistentData()
+{
+ return true;
+}
+
+bool SwNoTextNode::SavePersistentData()
+{
+ return true;
+}
+
+void SwNoTextNode::SetContour( const tools::PolyPolygon *pPoly, bool bAutomatic )
+{
+ if ( pPoly )
+ m_pContour = *pPoly;
+ else
+ m_pContour.reset();
+ m_bAutomaticContour = bAutomatic;
+ m_bContourMapModeValid = true;
+ m_bPixelContour = false;
+}
+
+void SwNoTextNode::CreateContour()
+{
+ OSL_ENSURE( !m_pContour, "Contour available." );
+ m_pContour = SvxContourDlg::CreateAutoContour(GetGraphic());
+ m_bAutomaticContour = true;
+ m_bContourMapModeValid = true;
+ m_bPixelContour = false;
+}
+
+const tools::PolyPolygon *SwNoTextNode::HasContour() const
+{
+ if( !m_bContourMapModeValid )
+ {
+ const MapMode aGrfMap( GetGraphic().GetPrefMapMode() );
+ bool bPixelGrf = aGrfMap.GetMapUnit() == MapUnit::MapPixel;
+ const MapMode aContourMap( bPixelGrf ? MapUnit::MapPixel : MapUnit::Map100thMM );
+ if( bPixelGrf ? !m_bPixelContour : aGrfMap != aContourMap )
+ {
+ double nGrfDPIx = 0.0;
+ double nGrfDPIy = 0.0;
+ {
+ if ( !bPixelGrf && m_bPixelContour )
+ {
+ basegfx::B2DSize aDPI = GetGraphic().GetPPI();
+ nGrfDPIx = aDPI.getX();
+ nGrfDPIy = aDPI.getY();
+ }
+ }
+ OSL_ENSURE( !bPixelGrf || aGrfMap == aContourMap,
+ "scale factor for pixel unsupported" );
+ OutputDevice* pOutDev =
+ (bPixelGrf || m_bPixelContour) ? Application::GetDefaultDevice()
+ : nullptr;
+ sal_uInt16 nPolyCount = m_pContour->Count();
+ for( sal_uInt16 j=0; j<nPolyCount; j++ )
+ {
+ tools::Polygon& rPoly = (*m_pContour)[j];
+
+ sal_uInt16 nCount = rPoly.GetSize();
+ for( sal_uInt16 i=0 ; i<nCount; i++ )
+ {
+ if( bPixelGrf )
+ rPoly[i] = pOutDev->LogicToPixel( rPoly[i],
+ aContourMap );
+ else if( m_bPixelContour )
+ {
+ rPoly[i] = pOutDev->PixelToLogic( rPoly[i], aGrfMap );
+
+ if ( nGrfDPIx != 0 && nGrfDPIy != 0 )
+ {
+ rPoly[i] = Point( rPoly[i].getX() * pOutDev->GetDPIX() / nGrfDPIx,
+ rPoly[i].getY() * pOutDev->GetDPIY() / nGrfDPIy );
+ }
+ }
+ else
+ rPoly[i] = OutputDevice::LogicToLogic( rPoly[i],
+ aContourMap,
+ aGrfMap );
+ }
+ }
+ }
+ m_bContourMapModeValid = true;
+ m_bPixelContour = false;
+ }
+
+ return m_pContour ? &*m_pContour : nullptr;
+}
+
+void SwNoTextNode::GetContour( tools::PolyPolygon &rPoly ) const
+{
+ OSL_ENSURE( m_pContour, "Contour not available." );
+ rPoly = *HasContour();
+}
+
+void SwNoTextNode::SetContourAPI( const tools::PolyPolygon *pPoly )
+{
+ if ( pPoly )
+ m_pContour = *pPoly;
+ else
+ m_pContour.reset();
+ m_bContourMapModeValid = false;
+}
+
+bool SwNoTextNode::GetContourAPI( tools::PolyPolygon &rContour ) const
+{
+ if( !m_pContour )
+ return false;
+
+ rContour = *m_pContour;
+ if( m_bContourMapModeValid )
+ {
+ const MapMode aGrfMap( GetGraphic().GetPrefMapMode() );
+ const MapMode aContourMap( MapUnit::Map100thMM );
+ OSL_ENSURE( aGrfMap.GetMapUnit() != MapUnit::MapPixel ||
+ aGrfMap == MapMode( MapUnit::MapPixel ),
+ "scale factor for pixel unsupported" );
+ if( aGrfMap.GetMapUnit() != MapUnit::MapPixel &&
+ aGrfMap != aContourMap )
+ {
+ sal_uInt16 nPolyCount = rContour.Count();
+ for( sal_uInt16 j=0; j<nPolyCount; j++ )
+ {
+ tools::Polygon& rPoly = rContour[j];
+
+ sal_uInt16 nCount = rPoly.GetSize();
+ for( sal_uInt16 i=0 ; i<nCount; i++ )
+ {
+ rPoly[i] = OutputDevice::LogicToLogic( rPoly[i], aGrfMap,
+ aContourMap );
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+bool SwNoTextNode::IsPixelContour() const
+{
+ bool bRet;
+ if( m_bContourMapModeValid )
+ {
+ const MapMode aGrfMap( GetGraphic().GetPrefMapMode() );
+ bRet = aGrfMap.GetMapUnit() == MapUnit::MapPixel;
+ }
+ else
+ {
+ bRet = m_bPixelContour;
+ }
+
+ return bRet;
+}
+
+Graphic SwNoTextNode::GetGraphic() const
+{
+ Graphic aRet;
+ if ( GetGrfNode() )
+ {
+ aRet = static_cast<const SwGrfNode*>(this)->GetGrf(true);
+ }
+ else
+ {
+ OSL_ENSURE( GetOLENode(), "new type of Node?" );
+ aRet = *const_cast<SwOLENode*>(static_cast<const SwOLENode*>(this))->SwOLENode::GetGraphic();
+ }
+ return aRet;
+}
+
+// #i73249#
+void SwNoTextNode::SetTitle( const OUString& rTitle )
+{
+ // Title attribute of <SdrObject> replaces own AlternateText attribute
+ SwFlyFrameFormat* pFlyFormat = dynamic_cast<SwFlyFrameFormat*>(GetFlyFormat());
+ OSL_ENSURE( pFlyFormat, "<SwNoTextNode::SetTitle(..)> - missing <SwFlyFrameFormat> instance" );
+ if ( !pFlyFormat )
+ {
+ return;
+ }
+
+ pFlyFormat->SetObjTitle( rTitle );
+}
+
+OUString SwNoTextNode::GetTitle() const
+{
+ const SwFlyFrameFormat* pFlyFormat = dynamic_cast<const SwFlyFrameFormat*>(GetFlyFormat());
+ OSL_ENSURE( pFlyFormat, "<SwNoTextNode::GetTitle(..)> - missing <SwFlyFrameFormat> instance" );
+ if ( !pFlyFormat )
+ {
+ return OUString();
+ }
+
+ return pFlyFormat->GetObjTitle();
+}
+
+void SwNoTextNode::SetDescription( const OUString& rDescription )
+{
+ SwFlyFrameFormat* pFlyFormat = dynamic_cast<SwFlyFrameFormat*>(GetFlyFormat());
+ OSL_ENSURE( pFlyFormat, "<SwNoTextNode::SetDescription(..)> - missing <SwFlyFrameFormat> instance" );
+ if ( !pFlyFormat )
+ {
+ return;
+ }
+
+ pFlyFormat->SetObjDescription( rDescription );
+}
+
+OUString SwNoTextNode::GetDescription() const
+{
+ const SwFlyFrameFormat* pFlyFormat = dynamic_cast<const SwFlyFrameFormat*>(GetFlyFormat());
+ OSL_ENSURE( pFlyFormat, "<SwNoTextNode::GetDescription(..)> - missing <SwFlyFrameFormat> instance" );
+ if ( !pFlyFormat )
+ {
+ return OUString();
+ }
+
+ return pFlyFormat->GetObjDescription();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/docnode/ndnum.cxx b/sw/source/core/docnode/ndnum.cxx
new file mode 100644
index 000000000..89a7acb09
--- /dev/null
+++ b/sw/source/core/docnode/ndnum.cxx
@@ -0,0 +1,98 @@
+/* -*- 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 <node.hxx>
+#include <doc.hxx>
+#include <IDocumentFieldsAccess.hxx>
+#include <ndtxt.hxx>
+#include <fldbas.hxx>
+#include <osl/diagnose.h>
+
+bool CompareSwOutlineNodes::operator()( SwNode* const& lhs, SwNode* const& rhs) const
+{
+ return lhs->GetIndex() < rhs->GetIndex();
+}
+
+bool SwOutlineNodes::Seek_Entry(SwNode* rP, size_type* pnPos) const
+{
+ const_iterator it = lower_bound(rP);
+ *pnPos = it - begin();
+ return it != end() && rP->GetIndex() == (*it)->GetIndex();
+}
+
+void SwNodes::UpdateOutlineNode(SwNode & rNd)
+{
+ assert(IsDocNodes()); // no point in m_pOutlineNodes for undo nodes
+
+ SwTextNode * pTextNd = rNd.GetTextNode();
+
+ if (!pTextNd || !pTextNd->IsOutlineStateChanged())
+ return;
+
+ bool bFound = m_pOutlineNodes->find(pTextNd) != m_pOutlineNodes->end();
+
+ if (pTextNd->IsOutline())
+ {
+ if (! bFound)
+ {
+ // assure that text is in the correct nodes array
+ if ( &(pTextNd->GetNodes()) == this )
+ {
+ m_pOutlineNodes->insert(pTextNd);
+ }
+ else
+ {
+ OSL_FAIL( "<SwNodes::UpdateOutlineNode(..)> - given text node isn't in the correct nodes array. This is a serious defect" );
+ }
+ }
+ }
+ else
+ {
+ if (bFound)
+ m_pOutlineNodes->erase(pTextNd);
+ }
+
+ pTextNd->UpdateOutlineState();
+
+ // update the structure fields
+ GetDoc().getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::Chapter )->UpdateFields();
+}
+
+void SwNodes::UpdateOutlineIdx( const SwNode& rNd )
+{
+ if( m_pOutlineNodes->empty() ) // no OutlineNodes present ?
+ return;
+
+ SwNode* const pSrch = const_cast<SwNode*>(&rNd);
+
+ SwOutlineNodes::size_type nPos;
+ if (!m_pOutlineNodes->Seek_Entry(pSrch, &nPos))
+ return;
+ if( nPos == m_pOutlineNodes->size() ) // none present for updating ?
+ return;
+
+ if( nPos )
+ --nPos;
+
+ if( !GetDoc().IsInDtor() && IsDocNodes() )
+ UpdateOutlineNode( *(*m_pOutlineNodes)[ nPos ]);
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/docnode/ndsect.cxx b/sw/source/core/docnode/ndsect.cxx
new file mode 100644
index 000000000..76831ae61
--- /dev/null
+++ b/sw/source/core/docnode/ndsect.cxx
@@ -0,0 +1,1475 @@
+/* -*- 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_wasm_strip.h>
+
+#include <libxml/xmlwriter.h>
+
+#include <hintids.hxx>
+#include <osl/diagnose.h>
+#include <sfx2/linkmgr.hxx>
+#include <svl/itemiter.hxx>
+#include <sal/log.hxx>
+#include <fmtcntnt.hxx>
+#include <txtftn.hxx>
+#include <doc.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <IDocumentLinksAdministration.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <IDocumentFieldsAccess.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <IDocumentState.hxx>
+#include <rootfrm.hxx>
+#include <pam.hxx>
+#include <ndtxt.hxx>
+#include <section.hxx>
+#include <UndoSection.hxx>
+#include <UndoDelete.hxx>
+#include <swundo.hxx>
+#include <calc.hxx>
+#include <swtable.hxx>
+#include <swserv.hxx>
+#include <frmfmt.hxx>
+#include <frmtool.hxx>
+#include <ftnidx.hxx>
+#include <docary.hxx>
+#include <redline.hxx>
+#include <sectfrm.hxx>
+#include <cntfrm.hxx>
+#include <node2lay.hxx>
+#include <doctxm.hxx>
+#include <fmtftntx.hxx>
+#include <strings.hrc>
+#include <viewsh.hxx>
+#include <memory>
+#include "ndsect.hxx"
+#include <tools/datetimeutils.hxx>
+#include <o3tl/string_view.hxx>
+
+// #i21457# - new implementation of local method <lcl_IsInSameTableBox(..)>.
+// Method now determines the previous/next on its own. Thus, it can be controlled,
+// for which previous/next is checked, if it's visible.
+static bool lcl_IsInSameTableBox( SwNodes const & _rNds,
+ const SwNode& _rNd,
+ const bool _bPrev )
+{
+ const SwTableNode* pTableNd = _rNd.FindTableNode();
+ if ( !pTableNd )
+ {
+ return true;
+ }
+
+ // determine index to be checked. Its assumed that a previous/next exist.
+ SwNodeIndex aChkIdx( _rNd );
+ {
+ // determine index of previous/next - skip hidden ones, which are
+ // inside the table.
+ // If found one is before/after table, this one isn't in the same
+ // table box as <_rNd>.
+ bool bFound = false;
+ do
+ {
+ if ( _bPrev
+ ? !SwNodes::GoPrevSection( &aChkIdx, false, false )
+ : !_rNds.GoNextSection( &aChkIdx, false, false ) )
+ {
+ OSL_FAIL( "<lcl_IsInSameTableBox(..)> - no previous/next!" );
+ return false;
+ }
+ else
+ {
+ if ( aChkIdx < pTableNd->GetIndex() ||
+ aChkIdx > pTableNd->EndOfSectionNode()->GetIndex() )
+ {
+ return false;
+ }
+ else
+ {
+ // check, if found one isn't inside a hidden section, which
+ // is also inside the table.
+ SwSectionNode* pSectNd = aChkIdx.GetNode().FindSectionNode();
+ if ( !pSectNd ||
+ pSectNd->GetIndex() < pTableNd->GetIndex() ||
+ !pSectNd->GetSection().IsHiddenFlag() )
+ {
+ bFound = true;
+ }
+ }
+ }
+ } while ( !bFound );
+ }
+
+ // Find the Box's StartNode
+ const SwTableSortBoxes& rSortBoxes = pTableNd->GetTable().GetTabSortBoxes();
+ SwNodeOffset nIdx = _rNd.GetIndex();
+ for (size_t n = 0; n < rSortBoxes.size(); ++n)
+ {
+ const SwStartNode* pNd = rSortBoxes[ n ]->GetSttNd();
+ if ( pNd->GetIndex() < nIdx && nIdx < pNd->EndOfSectionIndex() )
+ {
+ // The other index needs to be within the same Section
+ nIdx = aChkIdx.GetIndex();
+ return pNd->GetIndex() < nIdx && nIdx < pNd->EndOfSectionIndex();
+ }
+ }
+
+ return true;
+}
+
+static void lcl_CheckEmptyLayFrame( SwNodes const & rNds, SwSectionData& rSectionData,
+ const SwNode& rStt, const SwNode& rEnd )
+{
+ SwNodeIndex aIdx( rStt );
+ if( !SwNodes::GoPrevSection( &aIdx, true, false ) ||
+ !CheckNodesRange( rStt, aIdx, true ) ||
+ // #i21457#
+ !lcl_IsInSameTableBox( rNds, rStt, true ))
+ {
+ aIdx = rEnd;
+ if( !rNds.GoNextSection( &aIdx, true, false ) ||
+ !CheckNodesRange( rEnd, aIdx, true ) ||
+ // #i21457#
+ !lcl_IsInSameTableBox( rNds, rEnd, false ))
+ {
+ rSectionData.SetHidden( false );
+ }
+ }
+}
+
+SwSection *
+SwDoc::InsertSwSection(SwPaM const& rRange, SwSectionData & rNewData,
+ std::tuple<SwTOXBase const*, sw::RedlineMode, sw::FieldmarkMode> const*const pTOXBaseAndMode,
+ SfxItemSet const*const pAttr, bool const bUpdate)
+{
+ const SwNode* pPrvNd = nullptr;
+ sal_uInt16 nRegionRet = 0;
+ if( rRange.HasMark() )
+ {
+ nRegionRet = IsInsRegionAvailable( rRange, &pPrvNd );
+ if( 0 == nRegionRet )
+ {
+ // demoted to info because this is called from SwXTextSection::attach,
+ // so it could be invalid input
+ SAL_INFO("sw.core" , "InsertSwSection: rRange overlaps other sections");
+ return nullptr;
+ }
+ }
+
+ // See if the whole Document should be hidden, which we currently are not able to do.
+ if (rNewData.IsHidden() && rRange.HasMark())
+ {
+ const SwPosition *pStt = rRange.Start(), *pEnd = rRange.End();
+ if( !pStt->nContent.GetIndex() &&
+ pEnd->nNode.GetNode().GetContentNode()->Len() ==
+ pEnd->nContent.GetIndex() )
+ {
+ ::lcl_CheckEmptyLayFrame( GetNodes(),
+ rNewData,
+ pStt->nNode.GetNode(),
+ pEnd->nNode.GetNode() );
+ }
+ }
+
+ SwUndoInsSection* pUndoInsSect = nullptr;
+ bool const bUndo(GetIDocumentUndoRedo().DoesUndo());
+ if (bUndo)
+ {
+ pUndoInsSect = new SwUndoInsSection(rRange, rNewData, pAttr, pTOXBaseAndMode);
+ GetIDocumentUndoRedo().AppendUndo( std::unique_ptr<SwUndo>(pUndoInsSect) );
+ GetIDocumentUndoRedo().DoUndo(false);
+ }
+
+ SwSectionFormat* const pFormat = MakeSectionFormat();
+ pFormat->SetName(rNewData.GetSectionName());
+ if ( pAttr )
+ {
+ pFormat->SetFormatAttr( *pAttr );
+ }
+
+ SwTOXBase const*const pTOXBase(pTOXBaseAndMode ? std::get<0>(*pTOXBaseAndMode) : nullptr);
+ SwSectionNode* pNewSectNode = nullptr;
+
+ RedlineFlags eOld = getIDocumentRedlineAccess().GetRedlineFlags();
+ getIDocumentRedlineAccess().SetRedlineFlags_intern( (eOld & ~RedlineFlags::ShowMask) | RedlineFlags::Ignore );
+
+ if( rRange.HasMark() )
+ {
+ SwPosition *pSttPos = const_cast<SwPosition*>(rRange.Start()),
+ *pEndPos = const_cast<SwPosition*>(rRange.End());
+ if( pPrvNd && 3 == nRegionRet )
+ {
+ OSL_ENSURE( pPrvNd, "The SectionNode is missing" );
+ SwNodeIndex aStt( pSttPos->nNode ), aEnd( pEndPos->nNode, +1 );
+ while( pPrvNd != aStt.GetNode().StartOfSectionNode() )
+ --aStt;
+ while( pPrvNd != aEnd.GetNode().StartOfSectionNode() )
+ ++aEnd;
+
+ --aEnd; // End is inclusive in the InsertSection
+ pNewSectNode = GetNodes().InsertTextSection(
+ aStt, *pFormat, rNewData, pTOXBase, & aEnd);
+ }
+ else
+ {
+ if( pUndoInsSect )
+ {
+ if( !( pPrvNd && 1 == nRegionRet ) &&
+ pSttPos->nContent.GetIndex() )
+ {
+ SwTextNode* const pTNd =
+ pSttPos->nNode.GetNode().GetTextNode();
+ if (pTNd)
+ {
+ pUndoInsSect->SaveSplitNode( pTNd, true );
+ }
+ }
+
+ if ( !( pPrvNd && 2 == nRegionRet ) )
+ {
+ SwTextNode *const pTNd =
+ pEndPos->nNode.GetNode().GetTextNode();
+ if (pTNd && (pTNd->GetText().getLength()
+ != pEndPos->nContent.GetIndex()))
+ {
+ pUndoInsSect->SaveSplitNode( pTNd, false );
+ }
+ }
+ }
+
+ if( pPrvNd && 1 == nRegionRet )
+ {
+ pSttPos->nNode.Assign( *pPrvNd );
+ pSttPos->nContent.Assign( pSttPos->nNode.GetNode().GetContentNode(), 0 );
+ }
+ else if( pSttPos->nContent.GetIndex() )
+ {
+ getIDocumentContentOperations().SplitNode( *pSttPos, false );
+ }
+
+ if( pPrvNd && 2 == nRegionRet )
+ {
+ pEndPos->nNode.Assign( *pPrvNd );
+ pEndPos->nContent.Assign( pEndPos->nNode.GetNode().GetContentNode(), 0 );
+ }
+ else
+ {
+ const SwContentNode* pCNd = pEndPos->nNode.GetNode().GetContentNode();
+ if( pCNd && pCNd->Len() != pEndPos->nContent.GetIndex() )
+ {
+ sal_Int32 nContent = pSttPos->nContent.GetIndex();
+ getIDocumentContentOperations().SplitNode( *pEndPos, false );
+
+ SwTextNode* pTNd;
+ if( pEndPos->nNode.GetIndex() == pSttPos->nNode.GetIndex() )
+ {
+ --pSttPos->nNode;
+ --pEndPos->nNode;
+ pTNd = pSttPos->nNode.GetNode().GetTextNode();
+ pSttPos->nContent.Assign( pTNd, nContent );
+ }
+ else
+ {
+ // Set to the end of the previous
+ --pEndPos->nNode;
+ pTNd = pEndPos->nNode.GetNode().GetTextNode();
+ }
+ nContent = pTNd ? pTNd->GetText().getLength() : 0;
+ pEndPos->nContent.Assign( pTNd, nContent );
+ }
+ }
+ pNewSectNode = GetNodes().InsertTextSection(
+ pSttPos->nNode, *pFormat, rNewData, pTOXBase, &pEndPos->nNode);
+ }
+ }
+ else
+ {
+ const SwPosition* pPos = rRange.GetPoint();
+ const SwContentNode* pCNd = pPos->nNode.GetNode().GetContentNode();
+ if( !pPos->nContent.GetIndex() )
+ {
+ pNewSectNode = GetNodes().InsertTextSection(
+ pPos->nNode, *pFormat, rNewData, pTOXBase, nullptr);
+ }
+ else if( pPos->nContent.GetIndex() == pCNd->Len() )
+ {
+ pNewSectNode = GetNodes().InsertTextSection(
+ pPos->nNode, *pFormat, rNewData, pTOXBase, nullptr, false);
+ }
+ else
+ {
+ if( pUndoInsSect && pCNd->IsTextNode() )
+ {
+ pUndoInsSect->SaveSplitNode( const_cast<SwTextNode*>(static_cast<const SwTextNode*>(pCNd)), true );
+ }
+ getIDocumentContentOperations().SplitNode( *pPos, false );
+ pNewSectNode = GetNodes().InsertTextSection(
+ pPos->nNode, *pFormat, rNewData, pTOXBase, nullptr);
+ }
+ }
+
+//FEATURE::CONDCOLL
+ pNewSectNode->CheckSectionCondColl();
+//FEATURE::CONDCOLL
+
+ getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
+
+ // To-Do - add 'SwExtraRedlineTable' also ?
+ if( getIDocumentRedlineAccess().IsRedlineOn() || (!getIDocumentRedlineAccess().IsIgnoreRedline() && !getIDocumentRedlineAccess().GetRedlineTable().empty() ))
+ {
+ SwPaM aPam( *pNewSectNode->EndOfSectionNode(), *pNewSectNode, SwNodeOffset(1) );
+ if( getIDocumentRedlineAccess().IsRedlineOn() )
+ {
+ getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( RedlineType::Insert, aPam ), true);
+ }
+ else
+ {
+ getIDocumentRedlineAccess().SplitRedline( aPam );
+ }
+ }
+
+ // Is a Condition set?
+ if (rNewData.IsHidden() && !rNewData.GetCondition().isEmpty())
+ {
+ // The calculate up to that position
+ SwCalc aCalc( *this );
+ if( ! IsInReading() )
+ {
+ getIDocumentFieldsAccess().FieldsToCalc(aCalc, pNewSectNode->GetIndex(), SAL_MAX_INT32);
+ }
+ SwSection& rNewSect = pNewSectNode->GetSection();
+ rNewSect.SetCondHidden( aCalc.Calculate( rNewSect.GetCondition() ).GetBool() );
+ }
+
+ bool bUpdateFootnote = false;
+ if( !GetFootnoteIdxs().empty() && pAttr )
+ {
+ sal_uInt16 nVal = pAttr->Get( RES_FTN_AT_TXTEND ).GetValue();
+ if( ( FTNEND_ATTXTEND_OWNNUMSEQ == nVal ||
+ FTNEND_ATTXTEND_OWNNUMANDFMT == nVal ) ||
+ ( FTNEND_ATTXTEND_OWNNUMSEQ == ( nVal = pAttr->Get( RES_END_AT_TXTEND ).GetValue() ) ||
+ FTNEND_ATTXTEND_OWNNUMANDFMT == nVal ))
+ {
+ bUpdateFootnote = true;
+ }
+ }
+
+ if( pUndoInsSect )
+ {
+ pUndoInsSect->SetSectNdPos( pNewSectNode->GetIndex() );
+ pUndoInsSect->SetUpdateFootnoteFlag( bUpdateFootnote );
+ GetIDocumentUndoRedo().DoUndo(bUndo);
+ }
+
+ if (rNewData.IsLinkType())
+ {
+ pNewSectNode->GetSection().CreateLink( bUpdate ? LinkCreateType::Update : LinkCreateType::Connect );
+ }
+
+ if( bUpdateFootnote )
+ {
+ GetFootnoteIdxs().UpdateFootnote( SwNodeIndex( *pNewSectNode ));
+ }
+
+ getIDocumentState().SetModified();
+ return &pNewSectNode->GetSection();
+}
+
+sal_uInt16 SwDoc::IsInsRegionAvailable( const SwPaM& rRange,
+ const SwNode** ppSttNd )
+{
+ sal_uInt16 nRet = 1;
+ if( rRange.HasMark() )
+ {
+ // See if we have a valid Section
+ const SwPosition* pStt = rRange.Start();
+ const SwPosition* pEnd = rRange.End();
+
+ const SwContentNode* pCNd = pEnd->nNode.GetNode().GetContentNode();
+ const SwNode* pNd = &pStt->nNode.GetNode();
+ const SwSectionNode* pSectNd = pNd->FindSectionNode();
+ const SwSectionNode* pEndSectNd = pCNd ? pCNd->FindSectionNode() : nullptr;
+ if( pSectNd && pEndSectNd && pSectNd != pEndSectNd )
+ {
+ // Try to create an enclosing Section, but only if Start is
+ // located at the Section's beginning and End at it's end
+ nRet = 0;
+ if( !pStt->nContent.GetIndex()
+ && pSectNd->GetIndex() == pStt->nNode.GetIndex() - 1
+ && pEnd->nContent.GetIndex() == pCNd->Len() )
+ {
+ SwNodeIndex aIdx( pStt->nNode, -1 );
+ SwNodeOffset nCmp = pEnd->nNode.GetIndex();
+ const SwStartNode* pPrvNd;
+ const SwEndNode* pNxtNd;
+ while( nullptr != ( pPrvNd = (pNd = &aIdx.GetNode())->GetSectionNode() ) &&
+ ( aIdx.GetIndex() >= nCmp ||
+ nCmp >= pPrvNd->EndOfSectionIndex() ) )
+ {
+ --aIdx;
+ }
+ if( !pPrvNd )
+ pPrvNd = pNd->IsStartNode() ? static_cast<const SwStartNode*>(pNd)
+ : pNd->StartOfSectionNode();
+
+ aIdx = pEnd->nNode.GetIndex() + 1;
+ nCmp = pStt->nNode.GetIndex();
+ while( nullptr != ( pNxtNd = (pNd = &aIdx.GetNode())->GetEndNode() ) &&
+ pNxtNd->StartOfSectionNode()->IsSectionNode() &&
+ ( pNxtNd->StartOfSectionIndex() >= nCmp ||
+ nCmp >= aIdx.GetIndex() ) )
+ {
+ ++aIdx;
+ }
+ if( !pNxtNd )
+ pNxtNd = pNd->EndOfSectionNode();
+
+ if( pPrvNd && pNxtNd && pPrvNd == pNxtNd->StartOfSectionNode() )
+ {
+ nRet = 3;
+
+ if( ppSttNd )
+ *ppSttNd = pPrvNd;
+ }
+ }
+ }
+ else if( !pSectNd && pEndSectNd )
+ {
+ // Try to create an enclosing Section, but only if the End
+ // is at the Section's end.
+ nRet = 0;
+ if( pEnd->nContent.GetIndex() == pCNd->Len() )
+ {
+ SwNodeIndex aIdx( pEnd->nNode, 1 );
+ if( aIdx.GetNode().IsEndNode() &&
+ nullptr != aIdx.GetNode().FindSectionNode() )
+ {
+ do {
+ ++aIdx;
+ } while( aIdx.GetNode().IsEndNode() &&
+ nullptr != aIdx.GetNode().FindSectionNode() );
+ {
+ nRet = 2;
+ if( ppSttNd )
+ {
+ --aIdx;
+ *ppSttNd = &aIdx.GetNode();
+ }
+ }
+ }
+ }
+ }
+ else if( pSectNd && !pEndSectNd )
+ {
+ // Try to create an enclosing Section, but only if Start
+ // is at the Section's start.
+ nRet = 0;
+ if( !pStt->nContent.GetIndex() )
+ {
+ SwNodeIndex aIdx( pStt->nNode, -1 );
+ if( aIdx.GetNode().IsSectionNode() )
+ {
+ do {
+ --aIdx;
+ } while( aIdx.GetNode().IsSectionNode() );
+ if( !aIdx.GetNode().IsSectionNode() )
+ {
+ nRet = 1;
+ if( ppSttNd )
+ {
+ ++aIdx;
+ *ppSttNd = &aIdx.GetNode();
+ }
+ }
+ }
+ }
+ }
+ }
+ return nRet;
+}
+
+SwSection* SwDoc::GetCurrSection( const SwPosition& rPos )
+{
+ const SwSectionNode* pSectNd = rPos.nNode.GetNode().FindSectionNode();
+ if( pSectNd )
+ return const_cast<SwSection*>(&pSectNd->GetSection());
+ return nullptr;
+}
+
+SwSectionFormat* SwDoc::MakeSectionFormat()
+{
+ SwSectionFormat* pNew = new SwSectionFormat( mpDfltFrameFormat.get(), this );
+ mpSectionFormatTable->push_back( pNew );
+ return pNew;
+}
+
+void SwDoc::DelSectionFormat( SwSectionFormat *pFormat, bool bDelNodes )
+{
+ SwSectionFormats::iterator itFormatPos = std::find( mpSectionFormatTable->begin(), mpSectionFormatTable->end(), pFormat );
+
+ GetIDocumentUndoRedo().StartUndo(SwUndoId::DELSECTION, nullptr);
+
+ if( mpSectionFormatTable->end() != itFormatPos )
+ {
+ const SwNodeIndex* pIdx = pFormat->GetContent( false ).GetContentIdx();
+ const SfxPoolItem* pFootnoteEndAtTextEnd = pFormat->GetItemIfSet(
+ RES_FTN_AT_TXTEND);
+ if( !pFootnoteEndAtTextEnd )
+ pFootnoteEndAtTextEnd = pFormat->GetItemIfSet(RES_END_AT_TXTEND);
+
+ const SwSectionNode* pSectNd;
+
+ if( GetIDocumentUndoRedo().DoesUndo() )
+ {
+ if( bDelNodes && pIdx && &GetNodes() == &pIdx->GetNodes() &&
+ nullptr != (pSectNd = pIdx->GetNode().GetSectionNode() ))
+ {
+ SwNodeIndex aUpdIdx( *pIdx );
+ SwPaM aPaM( *pSectNd->EndOfSectionNode(), *pSectNd );
+ GetIDocumentUndoRedo().AppendUndo(std::make_unique<SwUndoDelete>(aPaM, SwDeleteFlags::Default));
+ if( pFootnoteEndAtTextEnd )
+ GetFootnoteIdxs().UpdateFootnote( aUpdIdx );
+ getIDocumentState().SetModified();
+ //#126178# start/end undo have to be pairs!
+ GetIDocumentUndoRedo().EndUndo(SwUndoId::DELSECTION, nullptr);
+ return ;
+ }
+ GetIDocumentUndoRedo().AppendUndo( MakeUndoDelSection( *pFormat ) );
+ }
+ else if( bDelNodes && pIdx && &GetNodes() == &pIdx->GetNodes() &&
+ nullptr != (pSectNd = pIdx->GetNode().GetSectionNode() ))
+ {
+ SwNodeIndex aUpdIdx( *pIdx );
+ getIDocumentContentOperations().DeleteSection( const_cast<SwNode*>(static_cast<SwNode const *>(pSectNd)) );
+ if( pFootnoteEndAtTextEnd )
+ GetFootnoteIdxs().UpdateFootnote( aUpdIdx );
+ getIDocumentState().SetModified();
+ //#126178# start/end undo have to be pairs!
+ GetIDocumentUndoRedo().EndUndo(SwUndoId::DELSECTION, nullptr);
+ return ;
+ }
+
+ pFormat->RemoveAllUnos();
+
+ // A ClearRedo could result in a recursive call of this function and delete some section
+ // formats, thus the position inside the SectionFormatTable could have changed
+ itFormatPos = std::find( mpSectionFormatTable->begin(), mpSectionFormatTable->end(), pFormat );
+
+ // WARNING: First remove from the array and then delete,
+ // as the Section DTOR tries to delete it's format itself.
+ mpSectionFormatTable->erase( itFormatPos );
+//FEATURE::CONDCOLL
+ SwNodeOffset nCnt(0), nSttNd(0);
+ if( pIdx && &GetNodes() == &pIdx->GetNodes() &&
+ nullptr != (pSectNd = pIdx->GetNode().GetSectionNode() ))
+ {
+ nSttNd = pSectNd->GetIndex();
+ nCnt = pSectNd->EndOfSectionIndex() - nSttNd - 1;
+ }
+//FEATURE::CONDCOLL
+
+ delete pFormat;
+
+ if( nSttNd && pFootnoteEndAtTextEnd )
+ {
+ SwNodeIndex aUpdIdx( GetNodes(), nSttNd );
+ GetFootnoteIdxs().UpdateFootnote( aUpdIdx );
+ }
+
+//FEATURE::CONDCOLL
+ SwContentNode* pCNd;
+ for( ; nCnt--; ++nSttNd )
+ if( nullptr != (pCNd = GetNodes()[ nSttNd ]->GetContentNode() ) &&
+ RES_CONDTXTFMTCOLL == pCNd->GetFormatColl()->Which() )
+ pCNd->ChkCondColl();
+//FEATURE::CONDCOLL
+ }
+
+ GetIDocumentUndoRedo().EndUndo(SwUndoId::DELSECTION, nullptr);
+
+ if (GetIDocumentUndoRedo().DoesUndo())
+ { // TODO is this ever needed?
+ getIDocumentState().SetModified();
+ }
+}
+
+void SwDoc::UpdateSection( size_t const nPos, SwSectionData & rNewData,
+ SfxItemSet const*const pAttr, bool const bPreventLinkUpdate )
+{
+ SwSectionFormat* pFormat = (*mpSectionFormatTable)[ nPos ];
+ SwSection* pSection = pFormat->GetSection();
+
+ /// remember hidden condition flag of SwSection before changes
+ bool bOldCondHidden = pSection->IsCondHidden();
+
+ if (pSection->DataEquals(rNewData))
+ {
+ // Check Attributes
+ bool bOnlyAttrChg = false;
+ if( pAttr && pAttr->Count() )
+ {
+ SfxItemIter aIter( *pAttr );
+ const SfxPoolItem* pItem = aIter.GetCurItem();
+ do
+ {
+ if (pFormat->GetFormatAttr(pItem->Which()) != *pItem)
+ {
+ bOnlyAttrChg = true;
+ break;
+ }
+
+ pItem = aIter.NextItem();
+ } while (pItem);
+ }
+
+ if( bOnlyAttrChg )
+ {
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().AppendUndo(
+ MakeUndoUpdateSection( *pFormat, true ) );
+ }
+ // #i32968# Inserting columns in the section causes MakeFrameFormat
+ // to put two objects of type SwUndoFrameFormat on the undo stack.
+ // We don't want them.
+ ::sw::UndoGuard const undoGuard(GetIDocumentUndoRedo());
+ pFormat->SetFormatAttr( *pAttr );
+ getIDocumentState().SetModified();
+ }
+ return;
+ }
+
+ // Test if the whole Content Section (Document/TableBox/Fly) should be hidden,
+ // which we're currently not able to do.
+ const SwNodeIndex* pIdx = nullptr;
+ {
+ if (rNewData.IsHidden())
+ {
+ pIdx = pFormat->GetContent().GetContentIdx();
+ if (pIdx)
+ {
+ const SwSectionNode* pSectNd =
+ pIdx->GetNode().GetSectionNode();
+ if (pSectNd)
+ {
+ ::lcl_CheckEmptyLayFrame( GetNodes(), rNewData,
+ *pSectNd, *pSectNd->EndOfSectionNode() );
+ }
+ }
+ }
+ }
+
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().AppendUndo(MakeUndoUpdateSection(*pFormat, false));
+ }
+ // #i32968# Inserting columns in the section causes MakeFrameFormat to put two
+ // objects of type SwUndoFrameFormat on the undo stack. We don't want them.
+ ::sw::UndoGuard const undoGuard(GetIDocumentUndoRedo());
+
+ // The LinkFileName could only consist of separators
+ OUString sCompareString = OUStringChar(sfx2::cTokenSeparator) + OUStringChar(sfx2::cTokenSeparator);
+ const bool bUpdate =
+ (!pSection->IsLinkType() && rNewData.IsLinkType())
+ || (!rNewData.GetLinkFileName().isEmpty()
+ && (rNewData.GetLinkFileName() != sCompareString)
+ && (rNewData.GetLinkFileName() != pSection->GetLinkFileName()));
+
+ OUString sSectName( rNewData.GetSectionName() );
+ if (sSectName != pSection->GetSectionName())
+ sSectName = GetUniqueSectionName( &sSectName );
+ else
+ sSectName.clear();
+
+ /// In SwSection::operator=(..) class member m_bCondHiddenFlag is always set to true.
+ /// IMHO this have to be changed, but I can't estimate the consequences:
+ /// Either it is set to true using corresponding method <SwSection.SetCondHidden(..)>,
+ /// or it is set to the value of SwSection which is assigned to it.
+ /// Discussion with AMA results that the adjustment to the assignment operator
+ /// could be very risky.
+ pSection->SetSectionData(rNewData);
+
+ if( pAttr )
+ pSection->GetFormat()->SetFormatAttr( *pAttr );
+
+ if( !sSectName.isEmpty() )
+ {
+ pSection->SetSectionName( sSectName );
+ }
+
+ // Is a Condition set
+ if( pSection->IsHidden() && !pSection->GetCondition().isEmpty() )
+ {
+ // Then calculate up to that position
+ SwCalc aCalc( *this );
+ if( !pIdx )
+ pIdx = pFormat->GetContent().GetContentIdx();
+ getIDocumentFieldsAccess().FieldsToCalc(aCalc, pIdx->GetIndex(), SAL_MAX_INT32);
+
+ /// Because on using SwSection::operator=() to set up <pSection>
+ /// with <rNewData> and the above given note, the hidden condition flag
+ /// has to be set to false, if hidden condition flag of <pFormat->GetSection()>
+ /// (SwSection before the changes) is false (already saved in <bOldCondHidden>)
+ /// and new calculated condition is true.
+ /// This is necessary, because otherwise the <SetCondHidden> would have
+ /// no effect.
+ bool bCalculatedCondHidden =
+ aCalc.Calculate( pSection->GetCondition() ).GetBool();
+ if ( bCalculatedCondHidden && !bOldCondHidden )
+ {
+ pSection->SetCondHidden( false );
+ }
+ pSection->SetCondHidden( bCalculatedCondHidden );
+ }
+
+ if( bUpdate )
+ pSection->CreateLink( bPreventLinkUpdate ? LinkCreateType::Connect : LinkCreateType::Update );
+ else if( !pSection->IsLinkType() && pSection->IsConnected() )
+ {
+ pSection->Disconnect();
+ getIDocumentLinksAdministration().GetLinkManager().Remove( &pSection->GetBaseLink() );
+ }
+
+ getIDocumentState().SetModified();
+}
+
+void sw_DeleteFootnote( SwSectionNode *pNd, SwNodeOffset nStt, SwNodeOffset nEnd )
+{
+ SwFootnoteIdxs& rFootnoteArr = pNd->GetDoc().GetFootnoteIdxs();
+ if( rFootnoteArr.empty() )
+ return;
+
+ size_t nPos = 0;
+ rFootnoteArr.SeekEntry( SwNodeIndex( *pNd ), &nPos );
+ SwTextFootnote* pSrch;
+
+ // Delete all succeeding Footnotes
+ while( nPos < rFootnoteArr.size() &&
+ SwTextFootnote_GetIndex( (pSrch = rFootnoteArr[ nPos ]) ) <= nEnd )
+ {
+ // If the Nodes are not deleted, they need to deregister at the Pages
+ // (delete Frames) or else they will remain there (Undo does not delete them!)
+ pSrch->DelFrames(nullptr);
+ ++nPos;
+ }
+
+ while( nPos-- &&
+ SwTextFootnote_GetIndex( (pSrch = rFootnoteArr[ nPos ]) ) >= nStt )
+ {
+ // If the Nodes are not deleted, they need to deregister at the Pages
+ // (delete Frames) or else they will remain there (Undo does not delete them!)
+ pSrch->DelFrames(nullptr);
+ }
+}
+
+static bool lcl_IsTOXSection(SwSectionData const& rSectionData)
+{
+ return (SectionType::ToxContent == rSectionData.GetType())
+ || (SectionType::ToxHeader == rSectionData.GetType());
+}
+
+SwSectionNode* SwNodes::InsertTextSection(SwNodeIndex const& rNdIdx,
+ SwSectionFormat& rSectionFormat,
+ SwSectionData const& rSectionData,
+ SwTOXBase const*const pTOXBase,
+ SwNodeIndex const*const pEnd,
+ bool const bInsAtStart, bool const bCreateFrames)
+{
+ SwNodeIndex aInsPos( rNdIdx );
+ if( !pEnd ) // No Area, thus create a new Section before/after it
+ {
+ // #i26762#
+ OSL_ENSURE(!pEnd || rNdIdx <= *pEnd,
+ "Section start and end in wrong order!");
+
+ if( bInsAtStart )
+ {
+ if (!lcl_IsTOXSection(rSectionData))
+ {
+ do {
+ --aInsPos;
+ } while( aInsPos.GetNode().IsSectionNode() );
+ ++aInsPos;
+ }
+ }
+ else
+ {
+ ++aInsPos;
+ if (!lcl_IsTOXSection(rSectionData))
+ {
+ SwNode* pNd;
+ while( aInsPos.GetIndex() < Count() - 1 &&
+ ( pNd = &aInsPos.GetNode())->IsEndNode() &&
+ pNd->StartOfSectionNode()->IsSectionNode())
+ {
+ ++aInsPos;
+ }
+ }
+ }
+ }
+
+ SwSectionNode *const pSectNd =
+ new SwSectionNode(aInsPos, rSectionFormat, pTOXBase);
+
+ if (lcl_IsTOXSection(rSectionData))
+ {
+ // We're inserting a ToX. Make sure that if a redline ends right before the ToX start, then
+ // that end now doesn't cross a section start node.
+ SwRedlineTable& rRedlines = GetDoc().getIDocumentRedlineAccess().GetRedlineTable();
+ for (SwRedlineTable::size_type nIndex = 0; nIndex < rRedlines.size(); ++nIndex)
+ {
+ SwRangeRedline* pRedline = rRedlines[nIndex];
+ if ( RedlineType::Delete != pRedline->GetType() ||
+ !pRedline->HasMark() || pRedline->GetMark()->nNode != aInsPos )
+ {
+ continue;
+ }
+
+ // The redline ends at the new section content start, so it originally ended before the
+ // section start: move it back.
+ SwPaM aRedlineEnd(*pRedline->GetMark());
+ aRedlineEnd.Move(fnMoveBackward);
+ *pRedline->GetMark() = *aRedlineEnd.GetPoint();
+ break;
+ }
+ }
+
+ if( pEnd )
+ {
+ // Special case for the Reader/Writer
+ if( &pEnd->GetNode() != &GetEndOfContent() )
+ aInsPos = pEnd->GetIndex()+1;
+ // #i58710: We created a RTF document with a section break inside a table cell
+ // We are not able to handle a section start inside a table and the section end outside.
+ const SwNode* pLastNode = pSectNd->StartOfSectionNode()->EndOfSectionNode();
+ if( aInsPos > pLastNode->GetIndex() )
+ aInsPos = pLastNode->GetIndex();
+ // Another way round: if the section starts outside a table but the end is inside...
+ // aInsPos is at the moment the Position where my EndNode will be inserted
+ const SwStartNode* pStartNode = aInsPos.GetNode().StartOfSectionNode();
+ // This StartNode should be in front of me, but if not, I want to survive
+ SwNodeOffset nMyIndex = pSectNd->GetIndex();
+ if( pStartNode->GetIndex() > nMyIndex ) // Suspicious!
+ {
+ const SwNode* pTemp;
+ do
+ {
+ pTemp = pStartNode; // pTemp is a suspicious one
+ pStartNode = pStartNode->StartOfSectionNode();
+ }
+ while( pStartNode->GetIndex() > nMyIndex );
+ pTemp = pTemp->EndOfSectionNode();
+ // If it starts behind me but ends behind my end...
+ if( pTemp->GetIndex() >= aInsPos.GetIndex() )
+ aInsPos = pTemp->GetIndex()+1; // ...I have to correct my end position
+ }
+ }
+ else
+ {
+ SwTextNode* pCpyTNd = rNdIdx.GetNode().GetTextNode();
+ if( pCpyTNd )
+ {
+ SwTextNode* pTNd = new SwTextNode( aInsPos, pCpyTNd->GetTextColl() );
+ if( pCpyTNd->HasSwAttrSet() )
+ {
+ // Move PageDesc/Break to the first Node of the section
+ const SfxItemSet& rSet = *pCpyTNd->GetpSwAttrSet();
+ if( SfxItemState::SET == rSet.GetItemState( RES_BREAK ) ||
+ SfxItemState::SET == rSet.GetItemState( RES_PAGEDESC ))
+ {
+ SfxItemSet aSet( rSet );
+ if( bInsAtStart )
+ pCpyTNd->ResetAttr( RES_PAGEDESC, RES_BREAK );
+ else
+ {
+ aSet.ClearItem( RES_PAGEDESC );
+ aSet.ClearItem( RES_BREAK );
+ }
+ pTNd->SetAttr( aSet );
+ }
+ else
+ pTNd->SetAttr( rSet );
+ }
+ // Do not forget to create the Frame!
+ pCpyTNd->MakeFramesForAdjacentContentNode(*pTNd);
+ }
+ else
+ new SwTextNode( aInsPos, GetDoc().GetDfltTextFormatColl() );
+ }
+ new SwEndNode( aInsPos, *pSectNd );
+
+ pSectNd->GetSection().SetSectionData(rSectionData);
+ SwSectionFormat* pSectFormat = pSectNd->GetSection().GetFormat();
+
+ // We could optimize this, by not removing already contained Frames and recreating them,
+ // but by simply rewiring them
+ bool bInsFrame = bCreateFrames && !pSectNd->GetSection().IsHiddenFlag() &&
+ GetDoc().getIDocumentLayoutAccess().GetCurrentViewShell();
+ SwNode2LayoutSaveUpperFrames *pNode2Layout = nullptr;
+ if( bInsFrame )
+ {
+ SwNodeIndex aTmp( *pSectNd );
+ if( !pSectNd->GetNodes().FindPrvNxtFrameNode( aTmp, pSectNd->EndOfSectionNode() ) )
+ // Collect all Uppers
+ pNode2Layout = new SwNode2LayoutSaveUpperFrames(*pSectNd);
+ }
+
+ // Set the right StartNode for all in this Area
+ SwNodeOffset nEnd = pSectNd->EndOfSectionIndex();
+ SwNodeOffset nStart = pSectNd->GetIndex()+1;
+ SwNodeOffset nSkipIdx = NODE_OFFSET_MAX;
+ for( SwNodeOffset n = nStart; n < nEnd; ++n )
+ {
+ SwNode* pNd = (*this)[n];
+
+ // Attach all Sections in the NodeSection underneath the new one
+ if( NODE_OFFSET_MAX == nSkipIdx )
+ pNd->m_pStartOfSection = pSectNd;
+ else if( n >= nSkipIdx )
+ nSkipIdx = NODE_OFFSET_MAX;
+
+ if( pNd->IsStartNode() )
+ {
+ // Make up the Format's nesting
+ if( pNd->IsSectionNode() )
+ {
+ static_cast<SwSectionNode*>(pNd)->GetSection().GetFormat()->
+ SetDerivedFrom( pSectFormat );
+ static_cast<SwSectionNode*>(pNd)->DelFrames();
+ n = pNd->EndOfSectionIndex();
+ }
+ else
+ {
+ if( pNd->IsTableNode() )
+ static_cast<SwTableNode*>(pNd)->DelFrames();
+
+ if( NODE_OFFSET_MAX == nSkipIdx )
+ nSkipIdx = pNd->EndOfSectionIndex();
+ }
+ }
+ else if( pNd->IsContentNode() )
+ static_cast<SwContentNode*>(pNd)->DelFrames(nullptr);
+ }
+
+ sw_DeleteFootnote( pSectNd, nStart, nEnd );
+
+ if( bInsFrame )
+ {
+ if( pNode2Layout )
+ {
+ SwNodeOffset nIdx = pSectNd->GetIndex();
+ pNode2Layout->RestoreUpperFrames( pSectNd->GetNodes(), nIdx, nIdx + 1 );
+ delete pNode2Layout;
+ }
+ else
+ pSectNd->MakeOwnFrames(&aInsPos);
+ }
+
+ return pSectNd;
+}
+
+SwSectionNode* SwNode::FindSectionNode()
+{
+ if( IsSectionNode() )
+ return GetSectionNode();
+ SwStartNode* pTmp = m_pStartOfSection;
+ while( !pTmp->IsSectionNode() && pTmp->GetIndex() )
+ pTmp = pTmp->m_pStartOfSection;
+ return pTmp->GetSectionNode();
+}
+
+// SwSectionNode
+
+// ugly hack to make m_pSection const
+static SwSectionFormat &
+lcl_initParent(SwSectionNode & rThis, SwSectionFormat & rFormat)
+{
+ SwSectionNode *const pParent =
+ rThis.StartOfSectionNode()->FindSectionNode();
+ if( pParent )
+ {
+ // Register the Format at the right Parent
+ rFormat.SetDerivedFrom( pParent->GetSection().GetFormat() );
+ }
+ return rFormat;
+}
+
+SwSectionNode::SwSectionNode(SwNodeIndex const& rIdx,
+ SwSectionFormat & rFormat, SwTOXBase const*const pTOXBase)
+ : SwStartNode( rIdx, SwNodeType::Section )
+ , m_pSection( pTOXBase
+ ? new SwTOXBaseSection(*pTOXBase, lcl_initParent(*this, rFormat))
+ : new SwSection( SectionType::Content, rFormat.GetName(),
+ lcl_initParent(*this, rFormat) ) )
+{
+ // Set the connection from Format to Node
+ // Suppress Modify; no one's interested anyway
+ rFormat.LockModify();
+ rFormat.SetFormatAttr( SwFormatContent( this ) );
+ rFormat.UnlockModify();
+}
+
+SwSectionNode::~SwSectionNode()
+{
+ // mba: test if iteration works as clients will be removed in callback
+ // use hint which allows to specify, if the content shall be saved or not
+ m_pSection->GetFormat()->CallSwClientNotify( SwSectionFrameMoveAndDeleteHint( true ) );
+ SwSectionFormat* pFormat = m_pSection->GetFormat();
+ if( pFormat )
+ {
+ // Remove the Attribute, because the Section deletes it's Format
+ // and it will neutralize the Section, if the Content Attribute is set
+ pFormat->LockModify();
+ pFormat->ResetFormatAttr( RES_CNTNT );
+ pFormat->UnlockModify();
+ }
+}
+
+SwFrame *SwSectionNode::MakeFrame( SwFrame *pSib )
+{
+ m_pSection->m_Data.SetHiddenFlag(false);
+ return new SwSectionFrame( *m_pSection, pSib );
+}
+
+// Creates all Document Views for the preceding Node.
+// The created ContentFrames are attached to the corresponding Layout
+void SwSectionNode::MakeFramesForAdjacentContentNode(const SwNodeIndex & rIdx)
+{
+ // Take my successive or preceding ContentFrame
+ SwNodes& rNds = GetNodes();
+ if( !(rNds.IsDocNodes() && rNds.GetDoc().getIDocumentLayoutAccess().GetCurrentViewShell()) )
+ return;
+
+ if (GetSection().IsHiddenFlag() || IsContentHidden())
+ {
+ SwNodeIndex aIdx( *EndOfSectionNode() );
+ SwContentNode* pCNd = rNds.GoNextSection( &aIdx, true, false );
+ if( !pCNd )
+ {
+ aIdx = *this;
+ pCNd = SwNodes::GoPrevSection(&aIdx, true, false);
+ if (!pCNd)
+ return;
+ }
+ pCNd = aIdx.GetNode().GetContentNode();
+ pCNd->MakeFramesForAdjacentContentNode(static_cast<SwContentNode&>(rIdx.GetNode()));
+ }
+ else
+ {
+ SwNode2Layout aNode2Layout( *this, rIdx.GetIndex() );
+ SwFrame *pFrame;
+ while( nullptr != (pFrame = aNode2Layout.NextFrame()) )
+ {
+ OSL_ENSURE( pFrame->IsSctFrame(), "Depend of Section not a Section." );
+ if (pFrame->getRootFrame()->HasMergedParas()
+ && !rIdx.GetNode().IsCreateFrameWhenHidingRedlines())
+ {
+ continue;
+ }
+ SwFrame *pNew = rIdx.GetNode().GetContentNode()->MakeFrame( pFrame );
+
+ SwSectionNode* pS = rIdx.GetNode().FindSectionNode();
+
+ // Assure that node is not inside a table, which is inside the
+ // found section.
+ if ( pS )
+ {
+ SwTableNode* pTableNode = rIdx.GetNode().FindTableNode();
+ if ( pTableNode &&
+ pTableNode->GetIndex() > pS->GetIndex() )
+ {
+ pS = nullptr;
+ }
+ }
+
+ // if the node is in a section, the sectionframe now
+ // has to be created...
+ // boolean to control <Init()> of a new section frame.
+ bool bInitNewSect = false;
+ if( pS )
+ {
+ SwSectionFrame *pSct = new SwSectionFrame( pS->GetSection(), pFrame );
+ // prepare <Init()> of new section frame.
+ bInitNewSect = true;
+ SwLayoutFrame* pUp = pSct;
+ while( pUp->Lower() ) // for columned sections
+ {
+ OSL_ENSURE( pUp->Lower()->IsLayoutFrame(),"Who's in there?" );
+ pUp = static_cast<SwLayoutFrame*>(pUp->Lower());
+ }
+ pNew->Paste( pUp );
+ // #i27138#
+ // notify accessibility paragraphs objects about changed
+ // CONTENT_FLOWS_FROM/_TO relation.
+ // Relation CONTENT_FLOWS_FROM for next paragraph will change
+ // and relation CONTENT_FLOWS_TO for previous paragraph will change.
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ if ( pNew->IsTextFrame() )
+ {
+ SwViewShell* pViewShell( pNew->getRootFrame()->GetCurrShell() );
+ if ( pViewShell && pViewShell->GetLayout() &&
+ pViewShell->GetLayout()->IsAnyShellAccessible() )
+ {
+ auto pNext = pNew->FindNextCnt( true );
+ auto pPrev = pNew->FindPrevCnt();
+ pViewShell->InvalidateAccessibleParaFlowRelation(
+ pNext ? pNext->DynCastTextFrame() : nullptr,
+ pPrev ? pPrev->DynCastTextFrame() : nullptr );
+ }
+ }
+#endif
+ pNew = pSct;
+ }
+
+ // If a Node got Frames attached before or after
+ if ( rIdx < GetIndex() )
+ // the new one precedes me
+ pNew->Paste( pFrame->GetUpper(), pFrame );
+ else
+ // the new one succeeds me
+ pNew->Paste( pFrame->GetUpper(), pFrame->GetNext() );
+ // #i27138#
+ // notify accessibility paragraphs objects about changed
+ // CONTENT_FLOWS_FROM/_TO relation.
+ // Relation CONTENT_FLOWS_FROM for next paragraph will change
+ // and relation CONTENT_FLOWS_TO for previous paragraph will change.
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ if ( pNew->IsTextFrame() )
+ {
+ SwViewShell* pViewShell( pNew->getRootFrame()->GetCurrShell() );
+ if ( pViewShell && pViewShell->GetLayout() &&
+ pViewShell->GetLayout()->IsAnyShellAccessible() )
+ {
+ auto pNext = pNew->FindNextCnt( true );
+ auto pPrev = pNew->FindPrevCnt();
+ pViewShell->InvalidateAccessibleParaFlowRelation(
+ pNext ? pNext->DynCastTextFrame() : nullptr,
+ pPrev ? pPrev->DynCastTextFrame() : nullptr );
+ }
+ }
+#endif
+ if ( bInitNewSect )
+ static_cast<SwSectionFrame*>(pNew)->Init();
+ }
+ }
+}
+
+// Create a new SectionFrame for every occurrence in the Layout and insert before
+// the corresponding ContentFrame
+void SwSectionNode::MakeOwnFrames(SwNodeIndex* pIdxBehind, SwNodeIndex* pEndIdx)
+{
+ OSL_ENSURE( pIdxBehind, "no Index" );
+ SwNodes& rNds = GetNodes();
+ SwDoc& rDoc = rNds.GetDoc();
+
+ *pIdxBehind = *this;
+
+ m_pSection->m_Data.SetHiddenFlag(true);
+
+ if( rNds.IsDocNodes() )
+ {
+ SwNodeIndex *pEnd = pEndIdx ? pEndIdx :
+ new SwNodeIndex( *EndOfSectionNode(), 1 );
+ ::MakeFrames( &rDoc, *pIdxBehind, *pEnd );
+ if( !pEndIdx )
+ delete pEnd;
+ }
+}
+
+void SwSectionNode::DelFrames(SwRootFrame const*const /*FIXME TODO*/, bool const bForce)
+{
+ SwNodeOffset nStt = GetIndex()+1, nEnd = EndOfSectionIndex();
+ if( nStt >= nEnd )
+ {
+ return ;
+ }
+
+ SwNodes& rNds = GetNodes();
+ m_pSection->GetFormat()->DelFrames();
+
+ // Update our Flag
+ m_pSection->m_Data.SetHiddenFlag(true);
+
+ // If the Area is within a Fly or TableBox, we can only hide it if
+ // there is more Content which has Frames.
+ // Or else the Fly/TableBox Frame does not have a Lower!
+ if (bForce)
+ return;
+
+ SwNodeIndex aIdx( *this );
+ if( !SwNodes::GoPrevSection( &aIdx, true, false ) ||
+ !CheckNodesRange( *this, aIdx, true ) ||
+ // #i21457#
+ !lcl_IsInSameTableBox( rNds, *this, true ))
+ {
+ aIdx = *EndOfSectionNode();
+ if( !rNds.GoNextSection( &aIdx, true, false ) ||
+ !CheckNodesRange( *EndOfSectionNode(), aIdx, true ) ||
+ // #i21457#
+ !lcl_IsInSameTableBox( rNds, *EndOfSectionNode(), false ))
+ {
+ m_pSection->m_Data.SetHiddenFlag(false);
+ }
+ }
+}
+
+SwSectionNode* SwSectionNode::MakeCopy( SwDoc& rDoc, const SwNodeIndex& rIdx ) const
+{
+ // In which array am I: Nodes, UndoNodes?
+ const SwNodes& rNds = GetNodes();
+
+ // Copy the SectionFrameFormat
+ SwSectionFormat* pSectFormat = rDoc.MakeSectionFormat();
+ pSectFormat->CopyAttrs( *GetSection().GetFormat() );
+
+ std::unique_ptr<SwTOXBase> pTOXBase;
+ if (SectionType::ToxContent == GetSection().GetType())
+ {
+ assert( dynamic_cast< const SwTOXBaseSection* >( &GetSection() ) && "no TOXBaseSection!" );
+ SwTOXBaseSection const& rTBS(
+ dynamic_cast<SwTOXBaseSection const&>(GetSection()));
+ pTOXBase.reset( new SwTOXBase(rTBS, &rDoc) );
+ }
+
+ SwSectionNode *const pSectNd =
+ new SwSectionNode(rIdx, *pSectFormat, pTOXBase.get());
+ SwEndNode* pEndNd = new SwEndNode( rIdx, *pSectNd );
+ SwNodeIndex aInsPos( *pEndNd );
+
+ // Take over values
+ SwSection *const pNewSect = pSectNd->m_pSection.get();
+
+ if (SectionType::ToxContent != GetSection().GetType())
+ {
+ // Keep the Name for Move
+ if( &rNds.GetDoc() == &rDoc && rDoc.IsCopyIsMove() )
+ {
+ pNewSect->SetSectionName( GetSection().GetSectionName() );
+ }
+ else
+ {
+ const OUString sSectionName(GetSection().GetSectionName());
+ pNewSect->SetSectionName(rDoc.GetUniqueSectionName( &sSectionName ));
+ }
+ }
+
+ pNewSect->SetType( GetSection().GetType() );
+ pNewSect->SetCondition( GetSection().GetCondition() );
+ pNewSect->SetCondHidden( GetSection().IsCondHidden() );
+ pNewSect->SetLinkFileName( GetSection().GetLinkFileName() );
+ if( !pNewSect->IsHiddenFlag() && GetSection().IsHidden() )
+ pNewSect->SetHidden();
+ if( !pNewSect->IsProtectFlag() && GetSection().IsProtect() )
+ pNewSect->SetProtect();
+ // edit in readonly sections
+ if( !pNewSect->IsEditInReadonlyFlag() && GetSection().IsEditInReadonly() )
+ pNewSect->SetEditInReadonly();
+
+ SwNodeRange aRg( *this, SwNodeOffset(+1), *EndOfSectionNode() ); // Where am I?
+ rNds.Copy_( aRg, aInsPos, false );
+
+ // Delete all Frames from the copied Area. They are created when creating
+ // the SectionFrames.
+ pSectNd->DelFrames();
+
+ // Copy the Links/Server
+ if( pNewSect->IsLinkType() ) // Add the Link
+ pNewSect->CreateLink( rDoc.getIDocumentLayoutAccess().GetCurrentViewShell() ? LinkCreateType::Connect : LinkCreateType::NONE );
+
+ // If we copy from the Undo as Server, enter it again
+ if (m_pSection->IsServer()
+ && rDoc.GetIDocumentUndoRedo().IsUndoNodes(rNds))
+ {
+ pNewSect->SetRefObject( m_pSection->GetObject() );
+ rDoc.getIDocumentLinksAdministration().GetLinkManager().InsertServer( pNewSect->GetObject() );
+ }
+
+ // METADATA: copy xml:id; must be done after insertion of node
+ pSectFormat->RegisterAsCopyOf(*GetSection().GetFormat());
+
+ return pSectNd;
+}
+
+bool SwSectionNode::IsContentHidden() const
+{
+ OSL_ENSURE( !m_pSection->IsHidden(),
+ "That's simple: Hidden Section => Hidden Content" );
+ SwNodeIndex aTmp( *this, 1 );
+ SwNodeOffset nEnd = EndOfSectionIndex();
+ while( aTmp < nEnd )
+ {
+ if( aTmp.GetNode().IsSectionNode() )
+ {
+ const SwSection& rSect = static_cast<SwSectionNode&>(aTmp.GetNode()).GetSection();
+ if( rSect.IsHiddenFlag() )
+ // Skip this Section
+ aTmp = *aTmp.GetNode().EndOfSectionNode();
+ }
+ else
+ {
+ if( aTmp.GetNode().IsContentNode() || aTmp.GetNode().IsTableNode() )
+ return false; // We found non-hidden content
+ OSL_ENSURE( aTmp.GetNode().IsEndNode(), "EndNode expected" );
+ }
+ ++aTmp;
+ }
+ return true; // Hide everything
+}
+
+void SwSectionNode::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("section"));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
+ (void)xmlTextWriterWriteAttribute(
+ pWriter, BAD_CAST("type"),
+ BAD_CAST(OString::number(static_cast<sal_uInt8>(GetNodeType())).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("index"),
+ BAD_CAST(OString::number(sal_Int32(GetIndex())).getStr()));
+
+ if (m_pSection)
+ {
+ m_pSection->dumpAsXml(pWriter);
+ }
+
+ // (void)xmlTextWriterEndElement(pWriter); - it is a start node, so don't end, will make xml better nested
+}
+
+void SwSectionNode::NodesArrChgd()
+{
+ SwSectionFormat *const pFormat = m_pSection->GetFormat();
+ if( !pFormat )
+ return;
+
+ SwNodes& rNds = GetNodes();
+ SwDoc* pDoc = pFormat->GetDoc();
+
+ if( !rNds.IsDocNodes() )
+ {
+ pFormat->RemoveAllUnos();
+ }
+
+ pFormat->LockModify();
+ pFormat->SetFormatAttr( SwFormatContent( this ));
+ pFormat->UnlockModify();
+
+ SwSectionNode* pSectNd = StartOfSectionNode()->FindSectionNode();
+ // set the correct parent from the new section
+ pFormat->SetDerivedFrom( pSectNd ? pSectNd->GetSection().GetFormat()
+ : pDoc->GetDfltFrameFormat() );
+
+ // Set the right StartNode for all in this Area
+ SwNodeOffset nStart = GetIndex()+1, nEnd = EndOfSectionIndex();
+ for( SwNodeOffset n = nStart; n < nEnd; ++n )
+ {
+ // Make up the Format's nesting
+ pSectNd = rNds[ n ]->GetSectionNode();
+ if( nullptr != pSectNd )
+ {
+ pSectNd->GetSection().GetFormat()->SetDerivedFrom( pFormat );
+ n = pSectNd->EndOfSectionIndex();
+ }
+ }
+
+ // Moving Nodes to the UndoNodes array?
+ if( rNds.IsDocNodes() )
+ {
+ OSL_ENSURE( pDoc == &GetDoc(),
+ "Moving to different Documents?" );
+ if( m_pSection->IsLinkType() ) // Remove the Link
+ m_pSection->CreateLink( pDoc->getIDocumentLayoutAccess().GetCurrentViewShell() ? LinkCreateType::Connect : LinkCreateType::NONE );
+
+ if (m_pSection->IsServer())
+ pDoc->getIDocumentLinksAdministration().GetLinkManager().InsertServer( m_pSection->GetObject() );
+ }
+ else
+ {
+ if (SectionType::Content != m_pSection->GetType()
+ && m_pSection->IsConnected())
+ {
+ pDoc->getIDocumentLinksAdministration().GetLinkManager().Remove( &m_pSection->GetBaseLink() );
+ }
+ if (m_pSection->IsServer())
+ pDoc->getIDocumentLinksAdministration().GetLinkManager().RemoveServer( m_pSection->GetObject() );
+ }
+
+}
+
+OUString SwDoc::GetUniqueSectionName( const OUString* pChkStr ) const
+{
+ if( IsInMailMerge())
+ {
+ OUString newName = "MailMergeSection"
+ + OStringToOUString( DateTimeToOString( DateTime( DateTime::SYSTEM )), RTL_TEXTENCODING_ASCII_US )
+ + OUString::number( mpSectionFormatTable->size() + 1 );
+ if( pChkStr )
+ newName += *pChkStr;
+ return newName;
+ }
+
+ const OUString aName(SwResId(STR_REGION_DEFNAME));
+
+ 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 pFormat : *mpSectionFormatTable )
+ {
+ const SwSectionNode *const pSectNd = pFormat->GetSectionNode();
+ if( pSectNd != nullptr )
+ {
+ const OUString& rNm = pSectNd->GetSection().GetSectionName();
+ if (rNm.startsWith( aName ))
+ {
+ // Calculate the Number and reset the Flag
+ nNum = o3tl::toInt32(rNm.subView( aName.getLength() ));
+ if( nNum-- && nNum < mpSectionFormatTable->size() )
+ pSetFlags[ nNum / 8 ] |= (0x01 << ( nNum & 0x07 ));
+ }
+ if( pChkStr && *pChkStr==rNm )
+ pChkStr = nullptr;
+ }
+ }
+
+ if( !pChkStr )
+ {
+ // Flagged all Numbers accordingly, so get the right Number
+ nNum = mpSectionFormatTable->size();
+ for( SwSectionFormats::size_type n = 0; n < nFlagSize; ++n )
+ {
+ auto nTmp = pSetFlags[ n ];
+ if( nTmp != 0xFF )
+ {
+ // Calculate the Number
+ nNum = n * 8;
+ while( nTmp & 1 )
+ {
+ ++nNum;
+ nTmp >>= 1;
+ }
+ break;
+ }
+ }
+ }
+ pSetFlags.reset();
+ if( pChkStr )
+ return *pChkStr;
+ return aName + OUString::number( ++nNum );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/docnode/ndsect.hxx b/sw/source/core/docnode/ndsect.hxx
new file mode 100644
index 000000000..57a9f7080
--- /dev/null
+++ b/sw/source/core/docnode/ndsect.hxx
@@ -0,0 +1,31 @@
+/* -*- 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_DOCNODE_NDSECT_HXX
+#define INCLUDED_SW_SOURCE_CORE_DOCNODE_NDSECT_HXX
+
+#include <nodeoffset.hxx>
+
+class SwSectionNode;
+
+void sw_DeleteFootnote(SwSectionNode* pNd, SwNodeOffset nStt, SwNodeOffset nEnd);
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/docnode/ndtbl.cxx b/sw/source/core/docnode/ndtbl.cxx
new file mode 100644
index 000000000..e76a0bbf1
--- /dev/null
+++ b/sw/source/core/docnode/ndtbl.cxx
@@ -0,0 +1,4684 @@
+/* -*- 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 <config_wasm_strip.h>
+
+#include <memory>
+#include <fesh.hxx>
+#include <hintids.hxx>
+#include <editeng/lrspitem.hxx>
+#include <editeng/protitem.hxx>
+#include <editeng/boxitem.hxx>
+#include <svl/stritem.hxx>
+#include <editeng/shaditem.hxx>
+#include <fmtfsize.hxx>
+#include <fmtornt.hxx>
+#include <fmtfordr.hxx>
+#include <fmtpdsc.hxx>
+#include <fmtanchr.hxx>
+#include <fmtlsplt.hxx>
+#include <frmatr.hxx>
+#include <cellfrm.hxx>
+#include <pagefrm.hxx>
+#include <tabcol.hxx>
+#include <doc.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <UndoManager.hxx>
+#include <DocumentSettingManager.hxx>
+#include <IDocumentChartDataProviderAccess.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <IDocumentStylePoolAccess.hxx>
+#include <IDocumentFieldsAccess.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <IDocumentState.hxx>
+#include <cntfrm.hxx>
+#include <pam.hxx>
+#include <swcrsr.hxx>
+#include <swtable.hxx>
+#include <swundo.hxx>
+#include <tblsel.hxx>
+#include <poolfmt.hxx>
+#include <tabfrm.hxx>
+#include <UndoCore.hxx>
+#include <UndoRedline.hxx>
+#include <UndoDelete.hxx>
+#include <UndoNumbering.hxx>
+#include <UndoTable.hxx>
+#include <hints.hxx>
+#include <tblafmt.hxx>
+#include <frminf.hxx>
+#include <cellatr.hxx>
+#include <swtblfmt.hxx>
+#include <swddetbl.hxx>
+#include <mvsave.hxx>
+#include <docary.hxx>
+#include <redline.hxx>
+#include <rolbck.hxx>
+#include <tblrwcl.hxx>
+#include <editsh.hxx>
+#include <txtfrm.hxx>
+#include <section.hxx>
+#include <frmtool.hxx>
+#include <node2lay.hxx>
+#include <strings.hrc>
+#include <docsh.hxx>
+#include <unochart.hxx>
+#include <node.hxx>
+#include <ndtxt.hxx>
+#include <cstdlib>
+#include <map>
+#include <algorithm>
+#include <rootfrm.hxx>
+#include <fldupde.hxx>
+#include <calbck.hxx>
+#include <fntcache.hxx>
+#include <frameformats.hxx>
+#include <o3tl/numeric.hxx>
+#include <o3tl/string_view.hxx>
+#include <svl/numformat.hxx>
+#include <tools/datetimeutils.hxx>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+
+#ifdef DBG_UTIL
+#define CHECK_TABLE(t) (t).CheckConsistency();
+#else
+#define CHECK_TABLE(t)
+#endif
+
+using ::editeng::SvxBorderLine;
+using namespace ::com::sun::star;
+
+const sal_Unicode T2T_PARA = 0x0a;
+
+static void lcl_SetDfltBoxAttr( SwFrameFormat& rFormat, sal_uInt8 nId )
+{
+ bool bTop = false, bBottom = false, bLeft = false, bRight = false;
+ switch ( nId )
+ {
+ case 0: bTop = bBottom = bLeft = true; break;
+ case 1: bTop = bBottom = bLeft = bRight = true; break;
+ case 2: bBottom = bLeft = true; break;
+ case 3: bBottom = bLeft = bRight = true; break;
+ }
+
+ const bool bHTML = rFormat.getIDocumentSettingAccess().get(DocumentSettingId::HTML_MODE);
+ Color aCol( bHTML ? COL_GRAY : COL_BLACK );
+ // Default border in Writer: 0.5pt (matching Word)
+ SvxBorderLine aLine( &aCol, SvxBorderLineWidth::VeryThin );
+ if ( bHTML )
+ {
+ aLine.SetBorderLineStyle(SvxBorderLineStyle::DOUBLE);
+ }
+ SvxBoxItem aBox(RES_BOX);
+ aBox.SetAllDistances(55);
+ if ( bTop )
+ aBox.SetLine( &aLine, SvxBoxItemLine::TOP );
+ if ( bBottom )
+ aBox.SetLine( &aLine, SvxBoxItemLine::BOTTOM );
+ if ( bLeft )
+ aBox.SetLine( &aLine, SvxBoxItemLine::LEFT );
+ if ( bRight )
+ aBox.SetLine( &aLine, SvxBoxItemLine::RIGHT );
+ rFormat.SetFormatAttr( aBox );
+}
+
+typedef std::map<SwFrameFormat *, SwTableBoxFormat *> DfltBoxAttrMap_t;
+typedef std::vector<DfltBoxAttrMap_t *> DfltBoxAttrList_t;
+
+static void
+lcl_SetDfltBoxAttr(SwTableBox& rBox, DfltBoxAttrList_t & rBoxFormatArr,
+ sal_uInt8 const nId, SwTableAutoFormat const*const pAutoFormat = nullptr)
+{
+ DfltBoxAttrMap_t * pMap = rBoxFormatArr[ nId ];
+ if (!pMap)
+ {
+ pMap = new DfltBoxAttrMap_t;
+ rBoxFormatArr[ nId ] = pMap;
+ }
+
+ SwTableBoxFormat* pNewTableBoxFormat = nullptr;
+ SwFrameFormat* pBoxFrameFormat = rBox.GetFrameFormat();
+ DfltBoxAttrMap_t::iterator const iter(pMap->find(pBoxFrameFormat));
+ if (pMap->end() != iter)
+ {
+ pNewTableBoxFormat = iter->second;
+ }
+ else
+ {
+ SwDoc* pDoc = pBoxFrameFormat->GetDoc();
+ // format does not exist, so create it
+ pNewTableBoxFormat = pDoc->MakeTableBoxFormat();
+ pNewTableBoxFormat->SetFormatAttr( pBoxFrameFormat->GetAttrSet().Get( RES_FRM_SIZE ) );
+
+ if( pAutoFormat )
+ pAutoFormat->UpdateToSet( nId, false, false,
+ const_cast<SfxItemSet&>(static_cast<SfxItemSet const &>(pNewTableBoxFormat->GetAttrSet())),
+ SwTableAutoFormatUpdateFlags::Box,
+ pDoc->GetNumberFormatter() );
+ else
+ ::lcl_SetDfltBoxAttr( *pNewTableBoxFormat, nId );
+
+ (*pMap)[pBoxFrameFormat] = pNewTableBoxFormat;
+ }
+ rBox.ChgFrameFormat( pNewTableBoxFormat );
+}
+
+static SwTableBoxFormat *lcl_CreateDfltBoxFormat( SwDoc &rDoc, std::vector<SwTableBoxFormat*> &rBoxFormatArr,
+ sal_uInt16 nCols, sal_uInt8 nId )
+{
+ if ( !rBoxFormatArr[nId] )
+ {
+ SwTableBoxFormat* pBoxFormat = rDoc.MakeTableBoxFormat();
+ if( USHRT_MAX != nCols )
+ pBoxFormat->SetFormatAttr( SwFormatFrameSize( SwFrameSize::Variable,
+ USHRT_MAX / nCols, 0 ));
+ ::lcl_SetDfltBoxAttr( *pBoxFormat, nId );
+ rBoxFormatArr[ nId ] = pBoxFormat;
+ }
+ return rBoxFormatArr[nId];
+}
+
+static SwTableBoxFormat *lcl_CreateAFormatBoxFormat( SwDoc &rDoc, std::vector<SwTableBoxFormat*> &rBoxFormatArr,
+ const SwTableAutoFormat& rAutoFormat,
+ const sal_uInt16 nRows, const sal_uInt16 nCols, sal_uInt8 nId )
+{
+ if( !rBoxFormatArr[nId] )
+ {
+ SwTableBoxFormat* pBoxFormat = rDoc.MakeTableBoxFormat();
+ rAutoFormat.UpdateToSet( nId, nRows==1, nCols==1,
+ const_cast<SfxItemSet&>(static_cast<SfxItemSet const &>(pBoxFormat->GetAttrSet())),
+ SwTableAutoFormatUpdateFlags::Box,
+ rDoc.GetNumberFormatter( ) );
+ if( USHRT_MAX != nCols )
+ pBoxFormat->SetFormatAttr( SwFormatFrameSize( SwFrameSize::Variable,
+ USHRT_MAX / nCols, 0 ));
+ rBoxFormatArr[ nId ] = pBoxFormat;
+ }
+ return rBoxFormatArr[nId];
+}
+
+SwTableNode* SwDoc::IsIdxInTable(const SwNodeIndex& rIdx)
+{
+ SwTableNode* pTableNd = nullptr;
+ SwNodeOffset nIndex = rIdx.GetIndex();
+ do {
+ SwNode* pNd = GetNodes()[ nIndex ]->StartOfSectionNode();
+ pTableNd = pNd->GetTableNode();
+ if( nullptr != pTableNd )
+ break;
+
+ nIndex = pNd->GetIndex();
+ } while ( nIndex );
+ return pTableNd;
+}
+
+/**
+ * Insert a new Box before the InsPos
+ */
+bool SwNodes::InsBoxen( SwTableNode* pTableNd,
+ SwTableLine* pLine,
+ SwTableBoxFormat* pBoxFormat,
+ SwTextFormatColl* pTextColl,
+ const SfxItemSet* pAutoAttr,
+ sal_uInt16 nInsPos,
+ sal_uInt16 nCnt )
+{
+ if( !nCnt )
+ return false;
+ OSL_ENSURE( pLine, "No valid Line" );
+
+ // Move Index after the Line's last Box
+ SwNodeOffset nIdxPos(0);
+ SwTableBox *pPrvBox = nullptr, *pNxtBox = nullptr;
+ if( !pLine->GetTabBoxes().empty() )
+ {
+ if( nInsPos < pLine->GetTabBoxes().size() )
+ {
+ pPrvBox = pLine->FindPreviousBox( pTableNd->GetTable(),
+ pLine->GetTabBoxes()[ nInsPos ] );
+ if( nullptr == pPrvBox )
+ pPrvBox = pLine->FindPreviousBox( pTableNd->GetTable() );
+ }
+ else
+ {
+ pNxtBox = pLine->FindNextBox( pTableNd->GetTable(),
+ pLine->GetTabBoxes().back() );
+ if( nullptr == pNxtBox )
+ pNxtBox = pLine->FindNextBox( pTableNd->GetTable() );
+ }
+ }
+ else
+ {
+ pNxtBox = pLine->FindNextBox( pTableNd->GetTable() );
+ if( nullptr == pNxtBox )
+ pPrvBox = pLine->FindPreviousBox( pTableNd->GetTable() );
+ }
+
+ if( !pPrvBox && !pNxtBox )
+ {
+ bool bSetIdxPos = true;
+ if( !pTableNd->GetTable().GetTabLines().empty() && !nInsPos )
+ {
+ const SwTableLine* pTableLn = pLine;
+ while( pTableLn->GetUpper() )
+ pTableLn = pTableLn->GetUpper()->GetUpper();
+
+ if( pTableNd->GetTable().GetTabLines()[ 0 ] == pTableLn )
+ {
+ // Before the Table's first Box
+ while( !( pNxtBox = pLine->GetTabBoxes()[0])->GetTabLines().empty() )
+ pLine = pNxtBox->GetTabLines()[0];
+ nIdxPos = pNxtBox->GetSttIdx();
+ bSetIdxPos = false;
+ }
+ }
+ if( bSetIdxPos )
+ // Tables without content or at the end; move before the End
+ nIdxPos = pTableNd->EndOfSectionIndex();
+ }
+ else if( pNxtBox ) // There is a successor
+ nIdxPos = pNxtBox->GetSttIdx();
+ else // There is a predecessor
+ nIdxPos = pPrvBox->GetSttNd()->EndOfSectionIndex() + 1;
+
+ SwNodeIndex aEndIdx( *this, nIdxPos );
+ for( sal_uInt16 n = 0; n < nCnt; ++n )
+ {
+ SwStartNode* pSttNd = new SwStartNode( aEndIdx, SwNodeType::Start,
+ SwTableBoxStartNode );
+ pSttNd->m_pStartOfSection = pTableNd;
+ new SwEndNode( aEndIdx, *pSttNd );
+
+ pPrvBox = new SwTableBox( pBoxFormat, *pSttNd, pLine );
+
+ SwTableBoxes & rTabBoxes = pLine->GetTabBoxes();
+ sal_uInt16 nRealInsPos = nInsPos + n;
+ if (nRealInsPos > rTabBoxes.size())
+ nRealInsPos = rTabBoxes.size();
+
+ rTabBoxes.insert( rTabBoxes.begin() + nRealInsPos, pPrvBox );
+
+ if( ! pTextColl->IsAssignedToListLevelOfOutlineStyle()
+//FEATURE::CONDCOLL
+ && RES_CONDTXTFMTCOLL != pTextColl->Which()
+//FEATURE::CONDCOLL
+ )
+ new SwTextNode( SwNodeIndex( *pSttNd->EndOfSectionNode() ),
+ pTextColl, pAutoAttr );
+ else
+ {
+ // Handle Outline numbering correctly!
+ SwTextNode* pTNd = new SwTextNode(
+ SwNodeIndex( *pSttNd->EndOfSectionNode() ),
+ GetDoc().GetDfltTextFormatColl(),
+ pAutoAttr );
+ pTNd->ChgFormatColl( pTextColl );
+ }
+ }
+ return true;
+}
+
+/**
+ * Insert a new Table
+ */
+const SwTable* SwDoc::InsertTable( const SwInsertTableOptions& rInsTableOpts,
+ const SwPosition& rPos, sal_uInt16 nRows,
+ sal_uInt16 nCols, sal_Int16 eAdjust,
+ const SwTableAutoFormat* pTAFormat,
+ const std::vector<sal_uInt16> *pColArr,
+ bool bCalledFromShell,
+ bool bNewModel )
+{
+ assert(nRows && "Table without line?");
+ assert(nCols && "Table without rows?");
+
+ {
+ // Do not copy into Footnotes!
+ if( rPos.nNode < GetNodes().GetEndOfInserts().GetIndex() &&
+ rPos.nNode >= GetNodes().GetEndOfInserts().StartOfSectionIndex() )
+ return nullptr;
+
+ // If the ColumnArray has a wrong count, ignore it!
+ if( pColArr &&
+ static_cast<size_t>(nCols + ( text::HoriOrientation::NONE == eAdjust ? 2 : 1 )) != pColArr->size() )
+ pColArr = nullptr;
+ }
+
+ OUString aTableName = GetUniqueTableName();
+
+ if( GetIDocumentUndoRedo().DoesUndo() )
+ {
+ GetIDocumentUndoRedo().AppendUndo(
+ std::make_unique<SwUndoInsTable>( rPos, nCols, nRows, o3tl::narrowing<sal_uInt16>(eAdjust),
+ rInsTableOpts, pTAFormat, pColArr,
+ aTableName));
+ }
+
+ // Start with inserting the Nodes and get the AutoFormat for the Table
+ SwTextFormatColl *pBodyColl = getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_TABLE ),
+ *pHeadColl = pBodyColl;
+
+ bool bDfltBorders( rInsTableOpts.mnInsMode & SwInsertTableFlags::DefaultBorder );
+
+ if( (rInsTableOpts.mnInsMode & SwInsertTableFlags::Headline) && (1 != nRows || !bDfltBorders) )
+ pHeadColl = getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_TABLE_HDLN );
+
+ const sal_uInt16 nRowsToRepeat =
+ SwInsertTableFlags::Headline == (rInsTableOpts.mnInsMode & SwInsertTableFlags::Headline) ?
+ rInsTableOpts.mnRowsToRepeat :
+ 0;
+
+ /* Save content node to extract FRAMEDIR from. */
+ const SwContentNode * pContentNd = rPos.nNode.GetNode().GetContentNode();
+
+ /* If we are called from a shell pass the attrset from
+ pContentNd (aka the node the table is inserted at) thus causing
+ SwNodes::InsertTable to propagate an adjust item if
+ necessary. */
+ SwTableNode *pTableNd = SwNodes::InsertTable(
+ rPos.nNode,
+ nCols,
+ pBodyColl,
+ nRows,
+ nRowsToRepeat,
+ pHeadColl,
+ bCalledFromShell ? &pContentNd->GetSwAttrSet() : nullptr );
+
+ // Create the Box/Line/Table construct
+ SwTableLineFormat* pLineFormat = MakeTableLineFormat();
+ SwTableFormat* pTableFormat = MakeTableFrameFormat( aTableName, GetDfltFrameFormat() );
+
+ /* If the node to insert the table at is a context node and has a
+ non-default FRAMEDIR propagate it to the table. */
+ if (pContentNd)
+ {
+ const SwAttrSet & aNdSet = pContentNd->GetSwAttrSet();
+ if (const SvxFrameDirectionItem* pItem = aNdSet.GetItemIfSet( RES_FRAMEDIR ))
+ {
+ pTableFormat->SetFormatAttr( *pItem );
+ }
+ }
+
+ // Set Orientation at the Table's Format
+ pTableFormat->SetFormatAttr( SwFormatHoriOrient( 0, eAdjust ) );
+ // All lines use the left-to-right Fill-Order!
+ pLineFormat->SetFormatAttr( SwFormatFillOrder( ATT_LEFT_TO_RIGHT ));
+
+ // Set USHRT_MAX as the Table's default SSize
+ SwTwips nWidth = USHRT_MAX;
+ if( pColArr )
+ {
+ sal_uInt16 nSttPos = pColArr->front();
+ sal_uInt16 nLastPos = pColArr->back();
+ if( text::HoriOrientation::NONE == eAdjust )
+ {
+ sal_uInt16 nFrameWidth = nLastPos;
+ nLastPos = (*pColArr)[ pColArr->size()-2 ];
+ pTableFormat->SetFormatAttr( SvxLRSpaceItem( nSttPos, nFrameWidth - nLastPos, 0, 0, RES_LR_SPACE ) );
+ }
+ nWidth = nLastPos - nSttPos;
+ }
+ else
+ {
+ nWidth /= nCols;
+ nWidth *= nCols; // to avoid rounding problems
+ }
+ pTableFormat->SetFormatAttr( SwFormatFrameSize( SwFrameSize::Variable, nWidth ));
+ if( !(rInsTableOpts.mnInsMode & SwInsertTableFlags::SplitLayout) )
+ pTableFormat->SetFormatAttr( SwFormatLayoutSplit( false ));
+
+ // Move the hard PageDesc/PageBreak Attributes if needed
+ SwContentNode* pNextNd = GetNodes()[ pTableNd->EndOfSectionIndex()+1 ]
+ ->GetContentNode();
+ if( pNextNd && pNextNd->HasSwAttrSet() )
+ {
+ const SfxItemSet* pNdSet = pNextNd->GetpSwAttrSet();
+ if( const SwFormatPageDesc* pItem = pNdSet->GetItemIfSet( RES_PAGEDESC, false ) )
+ {
+ pTableFormat->SetFormatAttr( *pItem );
+ pNextNd->ResetAttr( RES_PAGEDESC );
+ pNdSet = pNextNd->GetpSwAttrSet();
+ }
+ const SvxFormatBreakItem* pItem;
+ if( pNdSet && (pItem = pNdSet->GetItemIfSet( RES_BREAK, false )) )
+ {
+ pTableFormat->SetFormatAttr( *pItem );
+ pNextNd->ResetAttr( RES_BREAK );
+ }
+ }
+
+ SwTable& rNdTable = pTableNd->GetTable();
+ rNdTable.RegisterToFormat( *pTableFormat );
+
+ rNdTable.SetRowsToRepeat( nRowsToRepeat );
+ rNdTable.SetTableModel( bNewModel );
+
+ std::vector<SwTableBoxFormat*> aBoxFormatArr;
+ SwTableBoxFormat* pBoxFormat = nullptr;
+ if( !bDfltBorders && !pTAFormat )
+ {
+ pBoxFormat = MakeTableBoxFormat();
+ pBoxFormat->SetFormatAttr( SwFormatFrameSize( SwFrameSize::Variable, USHRT_MAX / nCols, 0 ));
+ }
+ else
+ {
+ const sal_uInt16 nBoxArrLen = pTAFormat ? 16 : 4;
+ aBoxFormatArr.resize( nBoxArrLen, nullptr );
+ }
+ SfxItemSetFixed<RES_CHRATR_BEGIN, RES_PARATR_LIST_END-1> aCharSet( GetAttrPool() );
+
+ SwNodeIndex aNdIdx( *pTableNd, 1 ); // Set to StartNode of first Box
+ SwTableLines& rLines = rNdTable.GetTabLines();
+ for( sal_uInt16 n = 0; n < nRows; ++n )
+ {
+ SwTableLine* pLine = new SwTableLine( pLineFormat, nCols, nullptr );
+ rLines.insert( rLines.begin() + n, pLine );
+ SwTableBoxes& rBoxes = pLine->GetTabBoxes();
+ for( sal_uInt16 i = 0; i < nCols; ++i )
+ {
+ SwTableBoxFormat *pBoxF;
+ if( pTAFormat )
+ {
+ sal_uInt8 nId = SwTableAutoFormat::CountPos(i, nCols, n, nRows);
+ pBoxF = ::lcl_CreateAFormatBoxFormat( *this, aBoxFormatArr, *pTAFormat,
+ nRows, nCols, nId );
+
+ // Set the Paragraph/Character Attributes if needed
+ if( pTAFormat->IsFont() || pTAFormat->IsJustify() )
+ {
+ aCharSet.ClearItem();
+ pTAFormat->UpdateToSet( nId, nRows==1, nCols==1, aCharSet,
+ SwTableAutoFormatUpdateFlags::Char, nullptr );
+ if( aCharSet.Count() )
+ GetNodes()[ aNdIdx.GetIndex()+1 ]->GetContentNode()->
+ SetAttr( aCharSet );
+ }
+ }
+ else if( bDfltBorders )
+ {
+ sal_uInt8 nBoxId = (i < nCols - 1 ? 0 : 1) + (n ? 2 : 0 );
+ pBoxF = ::lcl_CreateDfltBoxFormat( *this, aBoxFormatArr, nCols, nBoxId);
+ }
+ else
+ pBoxF = pBoxFormat;
+
+ // For AutoFormat on input: the columns are set when inserting the Table
+ // The Array contains the columns positions and not their widths!
+ if( pColArr )
+ {
+ nWidth = (*pColArr)[ i + 1 ] - (*pColArr)[ i ];
+ if( pBoxF->GetFrameSize().GetWidth() != nWidth )
+ {
+ if( pBoxF->HasWriterListeners() ) // Create new Format
+ {
+ SwTableBoxFormat *pNewFormat = MakeTableBoxFormat();
+ *pNewFormat = *pBoxF;
+ pBoxF = pNewFormat;
+ }
+ pBoxF->SetFormatAttr( SwFormatFrameSize( SwFrameSize::Variable, nWidth ));
+ }
+ }
+
+ SwTableBox *pBox = new SwTableBox( pBoxF, aNdIdx, pLine);
+ rBoxes.insert( rBoxes.begin() + i, pBox );
+ aNdIdx += SwNodeOffset(3); // StartNode, TextNode, EndNode == 3 Nodes
+ }
+ }
+ // Insert Frames
+ GetNodes().GoNext( &aNdIdx ); // Go to the next ContentNode
+ pTableNd->MakeOwnFrames( &aNdIdx );
+
+ // To-Do - add 'SwExtraRedlineTable' also ?
+ if( getIDocumentRedlineAccess().IsRedlineOn() || (!getIDocumentRedlineAccess().IsIgnoreRedline() && !getIDocumentRedlineAccess().GetRedlineTable().empty() ))
+ {
+ SwPaM aPam( *pTableNd->EndOfSectionNode(), *pTableNd, SwNodeOffset(1) );
+ if( getIDocumentRedlineAccess().IsRedlineOn() )
+ getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( RedlineType::Insert, aPam ), true);
+ else
+ getIDocumentRedlineAccess().SplitRedline( aPam );
+ }
+
+ getIDocumentState().SetModified();
+ CHECK_TABLE(rNdTable);
+ return &rNdTable;
+}
+
+SwTableNode* SwNodes::InsertTable( const SwNodeIndex& rNdIdx,
+ sal_uInt16 nBoxes,
+ SwTextFormatColl* pContentTextColl,
+ sal_uInt16 nLines,
+ sal_uInt16 nRepeat,
+ SwTextFormatColl* pHeadlineTextColl,
+ const SwAttrSet * pAttrSet)
+{
+ if( !nBoxes )
+ return nullptr;
+
+ // If Lines is given, create the Matrix from Lines and Boxes
+ if( !pHeadlineTextColl || !nLines )
+ pHeadlineTextColl = pContentTextColl;
+
+ SwTableNode * pTableNd = new SwTableNode( rNdIdx );
+ SwEndNode* pEndNd = new SwEndNode( rNdIdx, *pTableNd );
+
+ if( !nLines ) // For the for loop
+ ++nLines;
+
+ SwNodeIndex aIdx( *pEndNd );
+ SwTextFormatColl* pTextColl = pHeadlineTextColl;
+ for( sal_uInt16 nL = 0; nL < nLines; ++nL )
+ {
+ for( sal_uInt16 nB = 0; nB < nBoxes; ++nB )
+ {
+ SwStartNode* pSttNd = new SwStartNode( aIdx, SwNodeType::Start,
+ SwTableBoxStartNode );
+ pSttNd->m_pStartOfSection = pTableNd;
+
+ SwTextNode * pTmpNd = new SwTextNode( aIdx, pTextColl );
+
+ // #i60422# Propagate some more attributes.
+ const SfxPoolItem* pItem = nullptr;
+ if ( nullptr != pAttrSet )
+ {
+ static const sal_uInt16 aPropagateItems[] = {
+ RES_PARATR_ADJUST,
+ RES_CHRATR_FONT, RES_CHRATR_FONTSIZE,
+ RES_CHRATR_CJK_FONT, RES_CHRATR_CJK_FONTSIZE,
+ RES_CHRATR_CTL_FONT, RES_CHRATR_CTL_FONTSIZE, 0 };
+
+ const sal_uInt16* pIdx = aPropagateItems;
+ while ( *pIdx != 0 )
+ {
+ if ( SfxItemState::SET != pTmpNd->GetSwAttrSet().GetItemState( *pIdx ) &&
+ SfxItemState::SET == pAttrSet->GetItemState( *pIdx, true, &pItem ) )
+ static_cast<SwContentNode *>(pTmpNd)->SetAttr(*pItem);
+ ++pIdx;
+ }
+ }
+
+ new SwEndNode( aIdx, *pSttNd );
+ }
+ if ( nL + 1 >= nRepeat )
+ pTextColl = pContentTextColl;
+ }
+ return pTableNd;
+}
+
+/**
+ * Text to Table
+ */
+const SwTable* SwDoc::TextToTable( const SwInsertTableOptions& rInsTableOpts,
+ const SwPaM& rRange, sal_Unicode cCh,
+ sal_Int16 eAdjust,
+ const SwTableAutoFormat* pTAFormat )
+{
+ // See if the selection contains a Table
+ const SwPosition *pStt = rRange.Start(), *pEnd = rRange.End();
+ {
+ SwNodeOffset nCnt = pStt->nNode.GetIndex();
+ for( ; nCnt <= pEnd->nNode.GetIndex(); ++nCnt )
+ if( !GetNodes()[ nCnt ]->IsTextNode() )
+ return nullptr;
+ }
+
+ // Save first node in the selection if it is a context node
+ SwContentNode * pSttContentNd = pStt->nNode.GetNode().GetContentNode();
+
+ SwPaM aOriginal( *pStt, *pEnd );
+ pStt = aOriginal.GetMark();
+ pEnd = aOriginal.GetPoint();
+
+ SwUndoTextToTable* pUndo = nullptr;
+ if( GetIDocumentUndoRedo().DoesUndo() )
+ {
+ GetIDocumentUndoRedo().StartUndo( SwUndoId::TEXTTOTABLE, nullptr );
+ pUndo = new SwUndoTextToTable( aOriginal, rInsTableOpts, cCh,
+ o3tl::narrowing<sal_uInt16>(eAdjust), pTAFormat );
+ GetIDocumentUndoRedo().AppendUndo( std::unique_ptr<SwUndo>(pUndo) );
+
+ // Do not add splitting the TextNode to the Undo history
+ GetIDocumentUndoRedo().DoUndo( false );
+ }
+
+ ::PaMCorrAbs( aOriginal, *pEnd );
+
+ // Make sure that the range is on Node Edges
+ SwNodeRange aRg( pStt->nNode, pEnd->nNode );
+ if( pStt->nContent.GetIndex() )
+ getIDocumentContentOperations().SplitNode( *pStt, false );
+
+ bool bEndContent = 0 != pEnd->nContent.GetIndex();
+
+ // Do not split at the End of a Line (except at the End of the Doc)
+ if( bEndContent )
+ {
+ if( pEnd->nNode.GetNode().GetContentNode()->Len() != pEnd->nContent.GetIndex()
+ || pEnd->nNode.GetIndex() >= GetNodes().GetEndOfContent().GetIndex()-1 )
+ {
+ getIDocumentContentOperations().SplitNode( *pEnd, false );
+ --const_cast<SwNodeIndex&>(pEnd->nNode);
+ const_cast<SwIndex&>(pEnd->nContent).Assign(
+ pEnd->nNode.GetNode().GetContentNode(), 0 );
+ // A Node and at the End?
+ if( pStt->nNode.GetIndex() >= pEnd->nNode.GetIndex() )
+ --aRg.aStart;
+ }
+ else
+ ++aRg.aEnd;
+ }
+
+ if( aRg.aEnd.GetIndex() == aRg.aStart.GetIndex() )
+ {
+ OSL_FAIL( "empty range" );
+ ++aRg.aEnd;
+ }
+
+ // We always use Upper to insert the Table
+ SwNode2LayoutSaveUpperFrames aNode2Layout( aRg.aStart.GetNode() );
+
+ GetIDocumentUndoRedo().DoUndo( nullptr != pUndo );
+
+ // Create the Box/Line/Table construct
+ SwTableBoxFormat* pBoxFormat = MakeTableBoxFormat();
+ SwTableLineFormat* pLineFormat = MakeTableLineFormat();
+ SwTableFormat* pTableFormat = MakeTableFrameFormat( GetUniqueTableName(), GetDfltFrameFormat() );
+
+ // All Lines have a left-to-right Fill Order
+ pLineFormat->SetFormatAttr( SwFormatFillOrder( ATT_LEFT_TO_RIGHT ));
+ // The Table's SSize is USHRT_MAX
+ pTableFormat->SetFormatAttr( SwFormatFrameSize( SwFrameSize::Variable, USHRT_MAX ));
+ if( !(rInsTableOpts.mnInsMode & SwInsertTableFlags::SplitLayout) )
+ pTableFormat->SetFormatAttr( SwFormatLayoutSplit( false ));
+
+ /* If the first node in the selection is a context node and if it
+ has an item FRAMEDIR set (no default) propagate the item to the
+ replacing table. */
+ if (pSttContentNd)
+ {
+ const SwAttrSet & aNdSet = pSttContentNd->GetSwAttrSet();
+ if (const SvxFrameDirectionItem *pItem = aNdSet.GetItemIfSet( RES_FRAMEDIR ) )
+ {
+ pTableFormat->SetFormatAttr( *pItem );
+ }
+ }
+
+ //Resolves: tdf#87977, tdf#78599, disable broadcasting modifications
+ //until after RegisterToFormat is completed
+ bool bEnableSetModified = getIDocumentState().IsEnableSetModified();
+ getIDocumentState().SetEnableSetModified(false);
+
+ SwTableNode* pTableNd = GetNodes().TextToTable(
+ aRg, cCh, pTableFormat, pLineFormat, pBoxFormat,
+ getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_STANDARD ), pUndo );
+
+ SwTable& rNdTable = pTableNd->GetTable();
+
+ const sal_uInt16 nRowsToRepeat =
+ SwInsertTableFlags::Headline == (rInsTableOpts.mnInsMode & SwInsertTableFlags::Headline) ?
+ rInsTableOpts.mnRowsToRepeat :
+ 0;
+ rNdTable.SetRowsToRepeat(nRowsToRepeat);
+
+ bool bUseBoxFormat = false;
+ if( !pBoxFormat->HasWriterListeners() )
+ {
+ // The Box's Formats already have the right size, we must only set
+ // the right Border/AutoFormat.
+ bUseBoxFormat = true;
+ pTableFormat->SetFormatAttr( pBoxFormat->GetFrameSize() );
+ delete pBoxFormat;
+ eAdjust = text::HoriOrientation::NONE;
+ }
+
+ // Set Orientation in the Table's Format
+ pTableFormat->SetFormatAttr( SwFormatHoriOrient( 0, eAdjust ) );
+ rNdTable.RegisterToFormat(*pTableFormat);
+
+ if( pTAFormat || ( rInsTableOpts.mnInsMode & SwInsertTableFlags::DefaultBorder) )
+ {
+ sal_uInt8 nBoxArrLen = pTAFormat ? 16 : 4;
+ std::unique_ptr< DfltBoxAttrList_t > aBoxFormatArr1;
+ std::optional< std::vector<SwTableBoxFormat*> > aBoxFormatArr2;
+ if( bUseBoxFormat )
+ {
+ aBoxFormatArr1.reset(new DfltBoxAttrList_t( nBoxArrLen, nullptr ));
+ }
+ else
+ {
+ aBoxFormatArr2 = std::vector<SwTableBoxFormat*>( nBoxArrLen, nullptr );
+ }
+
+ SfxItemSetFixed<RES_CHRATR_BEGIN, RES_PARATR_LIST_END-1> aCharSet( GetAttrPool() );
+
+ SwHistory* pHistory = pUndo ? &pUndo->GetHistory() : nullptr;
+
+ SwTableBoxFormat *pBoxF = nullptr;
+ SwTableLines& rLines = rNdTable.GetTabLines();
+ const SwTableLines::size_type nRows = rLines.size();
+ for( SwTableLines::size_type n = 0; n < nRows; ++n )
+ {
+ SwTableBoxes& rBoxes = rLines[ n ]->GetTabBoxes();
+ const SwTableBoxes::size_type nCols = rBoxes.size();
+ for( SwTableBoxes::size_type i = 0; i < nCols; ++i )
+ {
+ SwTableBox* pBox = rBoxes[ i ];
+ bool bChgSz = false;
+
+ if( pTAFormat )
+ {
+ sal_uInt8 nId = static_cast<sal_uInt8>(!n ? 0 : (( n+1 == nRows )
+ ? 12 : (4 * (1 + ((n-1) & 1 )))));
+ nId = nId + static_cast<sal_uInt8>(!i ? 0 :
+ ( i+1 == nCols ? 3 : (1 + ((i-1) & 1))));
+ if( bUseBoxFormat )
+ ::lcl_SetDfltBoxAttr( *pBox, *aBoxFormatArr1, nId, pTAFormat );
+ else
+ {
+ bChgSz = nullptr == (*aBoxFormatArr2)[ nId ];
+ pBoxF = ::lcl_CreateAFormatBoxFormat( *this, *aBoxFormatArr2,
+ *pTAFormat, USHRT_MAX, USHRT_MAX, nId );
+ }
+
+ // Set Paragraph/Character Attributes if needed
+ if( pTAFormat->IsFont() || pTAFormat->IsJustify() )
+ {
+ aCharSet.ClearItem();
+ pTAFormat->UpdateToSet( nId, nRows==1, nCols==1, aCharSet,
+ SwTableAutoFormatUpdateFlags::Char, nullptr );
+ if( aCharSet.Count() )
+ {
+ SwNodeOffset nSttNd = pBox->GetSttIdx()+1;
+ SwNodeOffset nEndNd = pBox->GetSttNd()->EndOfSectionIndex();
+ for( ; nSttNd < nEndNd; ++nSttNd )
+ {
+ SwContentNode* pNd = GetNodes()[ nSttNd ]->GetContentNode();
+ if( pNd )
+ {
+ if( pHistory )
+ {
+ SwRegHistory aReg( pNd, *pNd, pHistory );
+ pNd->SetAttr( aCharSet );
+ }
+ else
+ pNd->SetAttr( aCharSet );
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ sal_uInt8 nId = (i < nCols - 1 ? 0 : 1) + (n ? 2 : 0 );
+ if( bUseBoxFormat )
+ ::lcl_SetDfltBoxAttr( *pBox, *aBoxFormatArr1, nId );
+ else
+ {
+ bChgSz = nullptr == (*aBoxFormatArr2)[ nId ];
+ pBoxF = ::lcl_CreateDfltBoxFormat( *this, *aBoxFormatArr2,
+ USHRT_MAX, nId );
+ }
+ }
+
+ if( !bUseBoxFormat )
+ {
+ if( bChgSz )
+ pBoxF->SetFormatAttr( pBox->GetFrameFormat()->GetFrameSize() );
+ pBox->ChgFrameFormat( pBoxF );
+ }
+ }
+ }
+
+ if( bUseBoxFormat )
+ {
+ for( sal_uInt8 i = 0; i < nBoxArrLen; ++i )
+ {
+ delete (*aBoxFormatArr1)[ i ];
+ }
+ }
+ }
+
+ // Check the boxes for numbers
+ if( IsInsTableFormatNum() )
+ {
+ for (size_t nBoxes = rNdTable.GetTabSortBoxes().size(); nBoxes; )
+ {
+ ChkBoxNumFormat(*rNdTable.GetTabSortBoxes()[ --nBoxes ], false);
+ }
+ }
+
+ SwNodeOffset nIdx = pTableNd->GetIndex();
+ aNode2Layout.RestoreUpperFrames( GetNodes(), nIdx, nIdx + 1 );
+
+ {
+ SwPaM& rTmp = const_cast<SwPaM&>(rRange); // Point always at the Start
+ rTmp.DeleteMark();
+ rTmp.GetPoint()->nNode = *pTableNd;
+ SwContentNode* pCNd = GetNodes().GoNext( &rTmp.GetPoint()->nNode );
+ rTmp.GetPoint()->nContent.Assign( pCNd, 0 );
+ }
+
+ if( pUndo )
+ {
+ GetIDocumentUndoRedo().EndUndo( SwUndoId::TEXTTOTABLE, nullptr );
+ }
+
+ getIDocumentState().SetEnableSetModified(bEnableSetModified);
+ getIDocumentState().SetModified();
+ getIDocumentFieldsAccess().SetFieldsDirty(true, nullptr, SwNodeOffset(0));
+ return &rNdTable;
+}
+
+static void lcl_RemoveBreaks(SwContentNode & rNode, SwTableFormat *const pTableFormat)
+{
+ // delete old layout frames, new ones need to be created...
+ rNode.DelFrames(nullptr);
+
+ if (!rNode.IsTextNode())
+ {
+ return;
+ }
+
+ SwTextNode & rTextNode = *rNode.GetTextNode();
+ // remove PageBreaks/PageDesc/ColBreak
+ SfxItemSet const* pSet = rTextNode.GetpSwAttrSet();
+ if (!pSet)
+ return;
+
+ if (const SvxFormatBreakItem* pItem = pSet->GetItemIfSet(RES_BREAK, false))
+ {
+ if (pTableFormat)
+ {
+ pTableFormat->SetFormatAttr(*pItem);
+ }
+ rTextNode.ResetAttr(RES_BREAK);
+ pSet = rTextNode.GetpSwAttrSet();
+ }
+
+ const SwFormatPageDesc* pPageDescItem;
+ if (pSet
+ && (pPageDescItem = pSet->GetItemIfSet(RES_PAGEDESC, false))
+ && pPageDescItem->GetPageDesc())
+ {
+ if (pTableFormat)
+ {
+ pTableFormat->SetFormatAttr(*pPageDescItem);
+ }
+ rTextNode.ResetAttr(RES_PAGEDESC);
+ }
+}
+
+/**
+ * balance lines in table, insert empty boxes so all lines have the size
+ */
+static void
+lcl_BalanceTable(SwTable & rTable, size_t const nMaxBoxes,
+ SwTableNode & rTableNd, SwTableBoxFormat & rBoxFormat, SwTextFormatColl & rTextColl,
+ SwUndoTextToTable *const pUndo, std::vector<sal_uInt16> *const pPositions)
+{
+ for (size_t n = 0; n < rTable.GetTabLines().size(); ++n)
+ {
+ SwTableLine *const pCurrLine = rTable.GetTabLines()[ n ];
+ size_t const nBoxes = pCurrLine->GetTabBoxes().size();
+ if (nMaxBoxes != nBoxes)
+ {
+ rTableNd.GetNodes().InsBoxen(&rTableNd, pCurrLine, &rBoxFormat, &rTextColl,
+ nullptr, nBoxes, nMaxBoxes - nBoxes);
+
+ if (pUndo)
+ {
+ for (size_t i = nBoxes; i < nMaxBoxes; ++i)
+ {
+ pUndo->AddFillBox( *pCurrLine->GetTabBoxes()[i] );
+ }
+ }
+
+ // if the first line is missing boxes, the width array is useless!
+ if (!n && pPositions)
+ {
+ pPositions->clear();
+ }
+ }
+ }
+}
+
+static void
+lcl_SetTableBoxWidths(SwTable & rTable, size_t const nMaxBoxes,
+ SwTableBoxFormat & rBoxFormat, SwDoc & rDoc,
+ std::vector<sal_uInt16> *const pPositions)
+{
+ if (pPositions && !pPositions->empty())
+ {
+ SwTableLines& rLns = rTable.GetTabLines();
+ sal_uInt16 nLastPos = 0;
+ for (size_t n = 0; n < pPositions->size(); ++n)
+ {
+ SwTableBoxFormat *pNewFormat = rDoc.MakeTableBoxFormat();
+ pNewFormat->SetFormatAttr(
+ SwFormatFrameSize(SwFrameSize::Variable, (*pPositions)[n] - nLastPos));
+ for (size_t nTmpLine = 0; nTmpLine < rLns.size(); ++nTmpLine)
+ {
+ // Have to do an Add here, because the BoxFormat
+ // is still needed by the caller
+ pNewFormat->Add( rLns[ nTmpLine ]->GetTabBoxes()[ n ] );
+ }
+
+ nLastPos = (*pPositions)[ n ];
+ }
+
+ // propagate size upwards from format, so the table gets the right size
+ SAL_WARN_IF(rBoxFormat.HasWriterListeners(), "sw.core",
+ "who is still registered in the format?");
+ rBoxFormat.SetFormatAttr( SwFormatFrameSize( SwFrameSize::Variable, nLastPos ));
+ }
+ else
+ {
+ size_t nWidth = nMaxBoxes ? USHRT_MAX / nMaxBoxes : USHRT_MAX;
+ rBoxFormat.SetFormatAttr(SwFormatFrameSize(SwFrameSize::Variable, nWidth));
+ }
+}
+
+SwTableNode* SwNodes::TextToTable( const SwNodeRange& rRange, sal_Unicode cCh,
+ SwTableFormat* pTableFormat,
+ SwTableLineFormat* pLineFormat,
+ SwTableBoxFormat* pBoxFormat,
+ SwTextFormatColl* pTextColl,
+ SwUndoTextToTable* pUndo )
+{
+ if( rRange.aStart >= rRange.aEnd )
+ return nullptr;
+
+ SwTableNode * pTableNd = new SwTableNode( rRange.aStart );
+ new SwEndNode( rRange.aEnd, *pTableNd );
+
+ SwDoc& rDoc = GetDoc();
+ std::vector<sal_uInt16> aPosArr;
+ SwTable& rTable = pTableNd->GetTable();
+ SwTableBox* pBox;
+ sal_uInt16 nBoxes, nLines, nMaxBoxes = 0;
+
+ SwNodeIndex aSttIdx( *pTableNd, 1 );
+ SwNodeIndex aEndIdx( rRange.aEnd, -1 );
+ for( nLines = 0, nBoxes = 0;
+ aSttIdx.GetIndex() < aEndIdx.GetIndex();
+ aSttIdx += SwNodeOffset(2), nLines++, nBoxes = 0 )
+ {
+ SwTextNode* pTextNd = aSttIdx.GetNode().GetTextNode();
+ OSL_ENSURE( pTextNd, "Only add TextNodes to the Table" );
+
+ if( !nLines && 0x0b == cCh )
+ {
+ cCh = 0x09;
+
+ // Get the separator's position from the first Node, in order for the Boxes to be set accordingly
+ SwTextFrameInfo aFInfo( static_cast<SwTextFrame*>(pTextNd->getLayoutFrame( pTextNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout() )) );
+ if( aFInfo.IsOneLine() ) // only makes sense in this case
+ {
+ OUString const& rText(pTextNd->GetText());
+ for (sal_Int32 nChPos = 0; nChPos < rText.getLength(); ++nChPos)
+ {
+ if (rText[nChPos] == cCh)
+ {
+ // sw_redlinehide: no idea if this makes any sense...
+ TextFrameIndex const nPos(aFInfo.GetFrame()->MapModelToView(pTextNd, nChPos));
+ aPosArr.push_back( o3tl::narrowing<sal_uInt16>(
+ aFInfo.GetCharPos(nPos+TextFrameIndex(1), false)) );
+ }
+ }
+
+ aPosArr.push_back(
+ o3tl::narrowing<sal_uInt16>(aFInfo.GetFrame()->IsVertical() ?
+ aFInfo.GetFrame()->getFramePrintArea().Bottom() :
+ aFInfo.GetFrame()->getFramePrintArea().Right()) );
+
+ }
+ }
+
+ lcl_RemoveBreaks(*pTextNd, (0 == nLines) ? pTableFormat : nullptr);
+
+ // Set the TableNode as StartNode for all TextNodes in the Table
+ pTextNd->m_pStartOfSection = pTableNd;
+
+ SwTableLine* pLine = new SwTableLine( pLineFormat, 1, nullptr );
+ rTable.GetTabLines().insert(rTable.GetTabLines().begin() + nLines, pLine);
+
+ SwStartNode* pSttNd;
+ SwPosition aCntPos( aSttIdx, SwIndex( pTextNd ));
+
+ const std::shared_ptr< sw::mark::ContentIdxStore> pContentStore(sw::mark::ContentIdxStore::Create());
+ pContentStore->Save(rDoc, aSttIdx.GetIndex(), SAL_MAX_INT32);
+
+ if( T2T_PARA != cCh )
+ {
+ for (sal_Int32 nChPos = 0; nChPos < pTextNd->GetText().getLength();)
+ {
+ if (pTextNd->GetText()[nChPos] == cCh)
+ {
+ aCntPos.nContent = nChPos;
+ std::function<void (SwTextNode *, sw::mark::RestoreMode, bool)> restoreFunc(
+ [&](SwTextNode *const pNewNode, sw::mark::RestoreMode const eMode, bool)
+ {
+ if (!pContentStore->Empty())
+ {
+ pContentStore->Restore(*pNewNode, nChPos, nChPos + 1, eMode);
+ }
+ });
+ SwContentNode *const pNewNd =
+ pTextNd->SplitContentNode(aCntPos, &restoreFunc);
+
+ // Delete separator and correct search string
+ pTextNd->EraseText( aCntPos.nContent, 1 );
+ nChPos = 0;
+
+ // Set the TableNode as StartNode for all TextNodes in the Table
+ const SwNodeIndex aTmpIdx( aCntPos.nNode, -1 );
+ pSttNd = new SwStartNode( aTmpIdx, SwNodeType::Start,
+ SwTableBoxStartNode );
+ new SwEndNode( aCntPos.nNode, *pSttNd );
+ pNewNd->m_pStartOfSection = pSttNd;
+
+ // Assign Section to the Box
+ pBox = new SwTableBox( pBoxFormat, *pSttNd, pLine );
+ pLine->GetTabBoxes().insert( pLine->GetTabBoxes().begin() + nBoxes++, pBox );
+ }
+ else
+ {
+ ++nChPos;
+ }
+ }
+ }
+
+ // Now for the last substring
+ if( !pContentStore->Empty())
+ pContentStore->Restore( *pTextNd, pTextNd->GetText().getLength(), pTextNd->GetText().getLength()+1 );
+
+ pSttNd = new SwStartNode( aCntPos.nNode, SwNodeType::Start, SwTableBoxStartNode );
+ const SwNodeIndex aTmpIdx( aCntPos.nNode, 1 );
+ new SwEndNode( aTmpIdx, *pSttNd );
+ pTextNd->m_pStartOfSection = pSttNd;
+
+ pBox = new SwTableBox( pBoxFormat, *pSttNd, pLine );
+ pLine->GetTabBoxes().insert( pLine->GetTabBoxes().begin() + nBoxes++, pBox );
+ if( nMaxBoxes < nBoxes )
+ nMaxBoxes = nBoxes;
+ }
+
+ lcl_BalanceTable(rTable, nMaxBoxes, *pTableNd, *pBoxFormat, *pTextColl,
+ pUndo, &aPosArr);
+ lcl_SetTableBoxWidths(rTable, nMaxBoxes, *pBoxFormat, rDoc, &aPosArr);
+
+ return pTableNd;
+}
+
+const SwTable* SwDoc::TextToTable( const std::vector< std::vector<SwNodeRange> >& rTableNodes )
+{
+ if (rTableNodes.empty())
+ return nullptr;
+
+ const std::vector<SwNodeRange>& rFirstRange = *rTableNodes.begin();
+
+ if (rFirstRange.empty())
+ return nullptr;
+
+ const std::vector<SwNodeRange>& rLastRange = *rTableNodes.rbegin();
+
+ if (rLastRange.empty())
+ return nullptr;
+
+ /* Save first node in the selection if it is a content node. */
+ SwContentNode * pSttContentNd = rFirstRange.begin()->aStart.GetNode().GetContentNode();
+
+ const SwNodeRange& rStartRange = *rFirstRange.begin();
+ const SwNodeRange& rEndRange = *rLastRange.rbegin();
+
+ //!!! not necessarily TextNodes !!!
+ SwPaM aOriginal( rStartRange.aStart, rEndRange.aEnd );
+ const SwPosition *pStt = aOriginal.GetMark();
+ const SwPosition *pEnd = aOriginal.GetPoint();
+
+ bool const bUndo(GetIDocumentUndoRedo().DoesUndo());
+ if (bUndo)
+ {
+ // Do not add splitting the TextNode to the Undo history
+ GetIDocumentUndoRedo().DoUndo(false);
+ }
+
+ ::PaMCorrAbs( aOriginal, *pEnd );
+
+ // make sure that the range is on Node Edges
+ SwNodeRange aRg( pStt->nNode, pEnd->nNode );
+ if( pStt->nContent.GetIndex() )
+ getIDocumentContentOperations().SplitNode( *pStt, false );
+
+ bool bEndContent = 0 != pEnd->nContent.GetIndex();
+
+ // Do not split at the End of a Line (except at the End of the Doc)
+ if( bEndContent )
+ {
+ if( pEnd->nNode.GetNode().GetContentNode()->Len() != pEnd->nContent.GetIndex()
+ || pEnd->nNode.GetIndex() >= GetNodes().GetEndOfContent().GetIndex()-1 )
+ {
+ getIDocumentContentOperations().SplitNode( *pEnd, false );
+ --const_cast<SwNodeIndex&>(pEnd->nNode);
+ const_cast<SwIndex&>(pEnd->nContent).Assign(
+ pEnd->nNode.GetNode().GetContentNode(), 0 );
+ // A Node and at the End?
+ if( pStt->nNode.GetIndex() >= pEnd->nNode.GetIndex() )
+ --aRg.aStart;
+ }
+ else
+ ++aRg.aEnd;
+ }
+
+ assert(aRg.aEnd == pEnd->nNode);
+ assert(aRg.aStart == pStt->nNode);
+ if( aRg.aEnd.GetIndex() == aRg.aStart.GetIndex() )
+ {
+ OSL_FAIL( "empty range" );
+ ++aRg.aEnd;
+ }
+
+
+ {
+ // TODO: this is not Undo-able - only good enough for file import
+ IDocumentRedlineAccess & rIDRA(getIDocumentRedlineAccess());
+ SwNodeIndex const prev(rTableNodes.begin()->begin()->aStart, -1);
+ SwNodeIndex const* pPrev(&prev);
+ // pPrev could point to non-textnode now
+ for (const auto& rRow : rTableNodes)
+ {
+ for (const auto& rCell : rRow)
+ {
+ assert(SwNodeIndex(*pPrev, +1) == rCell.aStart);
+ SwPaM pam(rCell.aStart, 0, *pPrev,
+ (pPrev->GetNode().IsContentNode())
+ ? pPrev->GetNode().GetContentNode()->Len() : 0);
+ rIDRA.SplitRedline(pam);
+ pPrev = &rCell.aEnd;
+ }
+ }
+ // another one to break between last cell and node after table
+ SwPaM pam(SwNodeIndex(*pPrev, +1), 0, *pPrev,
+ (pPrev->GetNode().IsContentNode())
+ ? pPrev->GetNode().GetContentNode()->Len() : 0);
+ rIDRA.SplitRedline(pam);
+ }
+
+ // We always use Upper to insert the Table
+ SwNode2LayoutSaveUpperFrames aNode2Layout( aRg.aStart.GetNode() );
+
+ GetIDocumentUndoRedo().DoUndo(bUndo);
+
+ // Create the Box/Line/Table construct
+ SwTableBoxFormat* pBoxFormat = MakeTableBoxFormat();
+ SwTableLineFormat* pLineFormat = MakeTableLineFormat();
+ SwTableFormat* pTableFormat = MakeTableFrameFormat( GetUniqueTableName(), GetDfltFrameFormat() );
+
+ // All Lines have a left-to-right Fill Order
+ pLineFormat->SetFormatAttr( SwFormatFillOrder( ATT_LEFT_TO_RIGHT ));
+ // The Table's SSize is USHRT_MAX
+ pTableFormat->SetFormatAttr( SwFormatFrameSize( SwFrameSize::Variable, USHRT_MAX ));
+
+ /* If the first node in the selection is a context node and if it
+ has an item FRAMEDIR set (no default) propagate the item to the
+ replacing table. */
+ if (pSttContentNd)
+ {
+ const SwAttrSet & aNdSet = pSttContentNd->GetSwAttrSet();
+ if (const SvxFrameDirectionItem* pItem = aNdSet.GetItemIfSet( RES_FRAMEDIR ))
+ {
+ pTableFormat->SetFormatAttr( *pItem );
+ }
+ }
+
+ //Resolves: tdf#87977, tdf#78599, disable broadcasting modifications
+ //until after RegisterToFormat is completed
+ bool bEnableSetModified = getIDocumentState().IsEnableSetModified();
+ getIDocumentState().SetEnableSetModified(false);
+
+ SwTableNode* pTableNd = GetNodes().TextToTable(
+ rTableNodes, pTableFormat, pLineFormat, pBoxFormat );
+
+ SwTable& rNdTable = pTableNd->GetTable();
+ rNdTable.RegisterToFormat(*pTableFormat);
+
+ if( !pBoxFormat->HasWriterListeners() )
+ {
+ // The Box's Formats already have the right size, we must only set
+ // the right Border/AutoFormat.
+ pTableFormat->SetFormatAttr( pBoxFormat->GetFrameSize() );
+ delete pBoxFormat;
+ }
+
+ SwNodeOffset nIdx = pTableNd->GetIndex();
+ aNode2Layout.RestoreUpperFrames( GetNodes(), nIdx, nIdx + 1 );
+
+ getIDocumentState().SetEnableSetModified(bEnableSetModified);
+ getIDocumentState().SetModified();
+ getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, SwNodeOffset(0) );
+ return &rNdTable;
+}
+
+std::unique_ptr<SwNodeRange> SwNodes::ExpandRangeForTableBox(const SwNodeRange & rRange)
+{
+ bool bChanged = false;
+
+ SwNodeIndex aNewStart = rRange.aStart;
+ SwNodeIndex aNewEnd = rRange.aEnd;
+
+ SwNodeIndex aEndIndex = rRange.aEnd;
+ SwNodeIndex aIndex = rRange.aStart;
+
+ while (aIndex < aEndIndex)
+ {
+ SwNode& rNode = aIndex.GetNode();
+
+ if (rNode.IsStartNode())
+ {
+ // advance aIndex to the end node of this start node
+ SwNode * pEndNode = rNode.EndOfSectionNode();
+ aIndex = *pEndNode;
+
+ if (aIndex > aNewEnd)
+ {
+ aNewEnd = aIndex;
+ bChanged = true;
+ }
+ }
+ else if (rNode.IsEndNode())
+ {
+ SwNode * pStartNode = rNode.StartOfSectionNode();
+ SwNodeIndex aStartIndex = *pStartNode;
+
+ if (aStartIndex < aNewStart)
+ {
+ aNewStart = aStartIndex;
+ bChanged = true;
+ }
+ }
+
+ if (aIndex < aEndIndex)
+ ++aIndex;
+ }
+
+ SwNode * pNode = &aIndex.GetNode();
+ while (pNode->IsEndNode() && aIndex < Count() - 1)
+ {
+ SwNode * pStartNode = pNode->StartOfSectionNode();
+ SwNodeIndex aStartIndex(*pStartNode);
+ aNewStart = aStartIndex;
+ aNewEnd = aIndex;
+ bChanged = true;
+
+ ++aIndex;
+ pNode = &aIndex.GetNode();
+ }
+
+ std::unique_ptr<SwNodeRange> pResult;
+ if (bChanged)
+ pResult.reset(new SwNodeRange(aNewStart, aNewEnd));
+ return pResult;
+}
+
+static void
+lcl_SetTableBoxWidths2(SwTable & rTable, size_t const nMaxBoxes,
+ SwTableBoxFormat & rBoxFormat, SwDoc & rDoc)
+{
+ // rhbz#820283, fdo#55462: set default box widths so table width is covered
+ SwTableLines & rLines = rTable.GetTabLines();
+ for (size_t nTmpLine = 0; nTmpLine < rLines.size(); ++nTmpLine)
+ {
+ SwTableBoxes & rBoxes = rLines[nTmpLine]->GetTabBoxes();
+ assert(!rBoxes.empty()); // ensured by convertToTable
+ size_t const nMissing = nMaxBoxes - rBoxes.size();
+ if (nMissing)
+ {
+ // default width for box at the end of an incomplete line
+ SwTableBoxFormat *const pNewFormat = rDoc.MakeTableBoxFormat();
+ size_t nWidth = nMaxBoxes ? USHRT_MAX / nMaxBoxes : USHRT_MAX;
+ pNewFormat->SetFormatAttr( SwFormatFrameSize(SwFrameSize::Variable,
+ nWidth * (nMissing + 1)) );
+ pNewFormat->Add(rBoxes.back());
+ }
+ }
+ size_t nWidth = nMaxBoxes ? USHRT_MAX / nMaxBoxes : USHRT_MAX;
+ // default width for all boxes not at the end of an incomplete line
+ rBoxFormat.SetFormatAttr(SwFormatFrameSize(SwFrameSize::Variable, nWidth));
+}
+
+SwTableNode* SwNodes::TextToTable( const SwNodes::TableRanges_t & rTableNodes,
+ SwTableFormat* pTableFormat,
+ SwTableLineFormat* pLineFormat,
+ SwTableBoxFormat* pBoxFormat )
+{
+ if( rTableNodes.empty() )
+ return nullptr;
+
+ SwTableNode * pTableNd = new SwTableNode( rTableNodes.begin()->begin()->aStart );
+ //insert the end node after the last text node
+ SwNodeIndex aInsertIndex( rTableNodes.rbegin()->rbegin()->aEnd );
+ ++aInsertIndex;
+
+ //!! ownership will be transferred in c-tor to SwNodes array.
+ //!! Thus no real problem here...
+ new SwEndNode( aInsertIndex, *pTableNd );
+
+ SwDoc& rDoc = GetDoc();
+ SwTable& rTable = pTableNd->GetTable();
+ SwTableBox* pBox;
+ sal_uInt16 nLines, nMaxBoxes = 0;
+
+ SwNodeIndex aNodeIndex = rTableNodes.begin()->begin()->aStart;
+ // delete frames of all contained content nodes
+ for( nLines = 0; aNodeIndex <= rTableNodes.rbegin()->rbegin()->aEnd; ++aNodeIndex,++nLines )
+ {
+ SwNode& rNode = aNodeIndex.GetNode();
+ if( rNode.IsContentNode() )
+ {
+ lcl_RemoveBreaks(static_cast<SwContentNode&>(rNode),
+ (0 == nLines) ? pTableFormat : nullptr);
+ }
+ }
+
+ nLines = 0;
+ for( const auto& rRow : rTableNodes )
+ {
+ sal_uInt16 nBoxes = 0;
+ SwTableLine* pLine = new SwTableLine( pLineFormat, 1, nullptr );
+ rTable.GetTabLines().insert(rTable.GetTabLines().begin() + nLines, pLine);
+
+ for( const auto& rCell : rRow )
+ {
+ const SwNodeIndex aTmpIdx( rCell.aStart,0 );
+
+ SwNodeIndex aCellEndIdx(rCell.aEnd);
+ ++aCellEndIdx;
+ SwStartNode* pSttNd = new SwStartNode( aTmpIdx, SwNodeType::Start,
+ SwTableBoxStartNode );
+
+ // Quotation of http://nabble.documentfoundation.org/Some-strange-lines-by-taking-a-look-at-the-bt-of-fdo-51916-tp3994561p3994639.html
+ // SwNode's constructor adds itself to the same SwNodes array as the other node (pSttNd).
+ // So this statement is only executed for the side-effect.
+ new SwEndNode( aCellEndIdx, *pSttNd );
+
+ //set the start node on all node of the current cell
+ SwNodeIndex aCellNodeIdx = rCell.aStart;
+ for(;aCellNodeIdx <= rCell.aEnd; ++aCellNodeIdx )
+ {
+ aCellNodeIdx.GetNode().m_pStartOfSection = pSttNd;
+ //skip start/end node pairs
+ if( aCellNodeIdx.GetNode().IsStartNode() )
+ aCellNodeIdx.Assign(*aCellNodeIdx.GetNode().EndOfSectionNode());
+ }
+
+ // assign Section to the Box
+ pBox = new SwTableBox( pBoxFormat, *pSttNd, pLine );
+ pLine->GetTabBoxes().insert( pLine->GetTabBoxes().begin() + nBoxes++, pBox );
+ }
+ if( nMaxBoxes < nBoxes )
+ nMaxBoxes = nBoxes;
+
+ nLines++;
+ }
+
+ lcl_SetTableBoxWidths2(rTable, nMaxBoxes, *pBoxFormat, rDoc);
+
+ return pTableNd;
+}
+
+/**
+ * Table to Text
+ */
+bool SwDoc::TableToText( const SwTableNode* pTableNd, sal_Unicode cCh )
+{
+ if( !pTableNd )
+ return false;
+
+ // #i34471#
+ // If this is triggered by SwUndoTableToText::Repeat() nobody ever deleted
+ // the table cursor.
+ SwEditShell* pESh = GetEditShell();
+ if (pESh && pESh->IsTableMode())
+ pESh->ClearMark();
+
+ SwNodeRange aRg( *pTableNd, SwNodeOffset(0), *pTableNd->EndOfSectionNode() );
+ std::unique_ptr<SwUndoTableToText> pUndo;
+ SwNodeRange* pUndoRg = nullptr;
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().ClearRedo();
+ pUndoRg = new SwNodeRange( aRg.aStart, SwNodeOffset(-1), aRg.aEnd, SwNodeOffset(+1) );
+ pUndo.reset(new SwUndoTableToText( pTableNd->GetTable(), cCh ));
+ }
+
+ SwTableFormulaUpdate aMsgHint( &pTableNd->GetTable() );
+ aMsgHint.m_eFlags = TBL_BOXNAME;
+ getIDocumentFieldsAccess().UpdateTableFields( &aMsgHint );
+
+ bool bRet = GetNodes().TableToText( aRg, cCh, pUndo.get() );
+ if( pUndoRg )
+ {
+ ++pUndoRg->aStart;
+ --pUndoRg->aEnd;
+ pUndo->SetRange( *pUndoRg );
+ GetIDocumentUndoRedo().AppendUndo(std::move(pUndo));
+ delete pUndoRg;
+ }
+
+ if( bRet )
+ getIDocumentState().SetModified();
+
+ return bRet;
+}
+
+namespace {
+
+/**
+ * Use the ForEach method from PtrArray to recreate Text from a Table.
+ * The Boxes can also contain Lines!
+ */
+struct DelTabPara
+{
+ SwTextNode* pLastNd;
+ SwNodes& rNds;
+ SwUndoTableToText* pUndo;
+ sal_Unicode cCh;
+
+ DelTabPara( SwNodes& rNodes, sal_Unicode cChar, SwUndoTableToText* pU ) :
+ pLastNd(nullptr), rNds( rNodes ), pUndo( pU ), cCh( cChar ) {}
+};
+
+}
+
+// Forward declare so that the Lines and Boxes can use recursion
+static void lcl_DelBox( SwTableBox* pBox, DelTabPara* pDelPara );
+
+static void lcl_DelLine( SwTableLine* pLine, DelTabPara* pPara )
+{
+ assert(pPara && "The parameters are missing!");
+ DelTabPara aPara( *pPara );
+ for( auto& rpBox : pLine->GetTabBoxes() )
+ lcl_DelBox(rpBox, &aPara );
+ if( pLine->GetUpper() ) // Is there a parent Box?
+ // Return the last TextNode
+ pPara->pLastNd = aPara.pLastNd;
+}
+
+static void lcl_DelBox( SwTableBox* pBox, DelTabPara* pDelPara )
+{
+ assert(pDelPara && "The parameters are missing");
+
+ // Delete the Box's Lines
+ if( !pBox->GetTabLines().empty() )
+ {
+ for( SwTableLine* pLine : pBox->GetTabLines() )
+ lcl_DelLine( pLine, pDelPara );
+ }
+ else
+ {
+ SwDoc& rDoc = pDelPara->rNds.GetDoc();
+ SwNodeRange aDelRg( *pBox->GetSttNd(), SwNodeOffset(0),
+ *pBox->GetSttNd()->EndOfSectionNode() );
+ // Delete the Section
+ pDelPara->rNds.SectionUp( &aDelRg );
+ const SwTextNode* pCurTextNd = nullptr;
+ if (T2T_PARA != pDelPara->cCh && pDelPara->pLastNd)
+ pCurTextNd = aDelRg.aStart.GetNode().GetTextNode();
+ if (nullptr != pCurTextNd)
+ {
+ // Join the current text node with the last from the previous box if possible
+ SwNodeOffset nNdIdx = aDelRg.aStart.GetIndex();
+ --aDelRg.aStart;
+ if( pDelPara->pLastNd == &aDelRg.aStart.GetNode() )
+ {
+ // Inserting the separator
+ SwIndex aCntIdx( pDelPara->pLastNd,
+ pDelPara->pLastNd->GetText().getLength());
+ pDelPara->pLastNd->InsertText( OUString(pDelPara->cCh), aCntIdx,
+ SwInsertFlags::EMPTYEXPAND );
+ if( pDelPara->pUndo )
+ pDelPara->pUndo->AddBoxPos( rDoc, nNdIdx, aDelRg.aEnd.GetIndex(),
+ aCntIdx.GetIndex() );
+
+ const std::shared_ptr<sw::mark::ContentIdxStore> pContentStore(sw::mark::ContentIdxStore::Create());
+ const sal_Int32 nOldTextLen = aCntIdx.GetIndex();
+ pContentStore->Save(rDoc, nNdIdx, SAL_MAX_INT32);
+
+ pDelPara->pLastNd->JoinNext();
+
+ if( !pContentStore->Empty() )
+ pContentStore->Restore( rDoc, pDelPara->pLastNd->GetIndex(), nOldTextLen );
+ }
+ else if( pDelPara->pUndo )
+ {
+ ++aDelRg.aStart;
+ pDelPara->pUndo->AddBoxPos( rDoc, nNdIdx, aDelRg.aEnd.GetIndex() );
+ }
+ }
+ else if( pDelPara->pUndo )
+ pDelPara->pUndo->AddBoxPos( rDoc, aDelRg.aStart.GetIndex(), aDelRg.aEnd.GetIndex() );
+ --aDelRg.aEnd;
+ pDelPara->pLastNd = aDelRg.aEnd.GetNode().GetTextNode();
+
+ // Do not take over the NumberFormatting's adjustment
+ if( pDelPara->pLastNd && pDelPara->pLastNd->HasSwAttrSet() )
+ pDelPara->pLastNd->ResetAttr( RES_PARATR_ADJUST );
+ }
+}
+
+bool SwNodes::TableToText( const SwNodeRange& rRange, sal_Unicode cCh,
+ SwUndoTableToText* pUndo )
+{
+ // Is a Table selected?
+ if (rRange.aStart.GetIndex() >= rRange.aEnd.GetIndex())
+ return false;
+ SwTableNode *const pTableNd(rRange.aStart.GetNode().GetTableNode());
+ if (nullptr == pTableNd ||
+ &rRange.aEnd.GetNode() != pTableNd->EndOfSectionNode() )
+ return false;
+
+ // If the Table was alone in a Section, create the Frames via the Table's Upper
+ SwNode2LayoutSaveUpperFrames * pNode2Layout = nullptr;
+ SwNodeIndex aFrameIdx( rRange.aStart );
+ SwNode* pFrameNd = FindPrvNxtFrameNode( aFrameIdx, &rRange.aEnd.GetNode() );
+ if( !pFrameNd )
+ // Collect all Uppers
+ pNode2Layout = new SwNode2LayoutSaveUpperFrames(*pTableNd);
+
+ // Delete the Frames
+ pTableNd->DelFrames();
+
+ // "Delete" the Table and merge all Lines/Boxes
+ DelTabPara aDelPara( *this, cCh, pUndo );
+ for( SwTableLine *pLine : pTableNd->m_pTable->GetTabLines() )
+ lcl_DelLine( pLine, &aDelPara );
+
+ // We just created a TextNode with fitting separator for every TableLine.
+ // Now we only need to delete the TableSection and create the Frames for the
+ // new TextNode.
+ SwNodeRange aDelRg( rRange.aStart, rRange.aEnd );
+
+ // If the Table has PageDesc/Break Attributes, carry them over to the
+ // first Text Node
+ {
+ // What about UNDO?
+ const SfxItemSet& rTableSet = pTableNd->m_pTable->GetFrameFormat()->GetAttrSet();
+ const SvxFormatBreakItem* pBreak = rTableSet.GetItemIfSet( RES_BREAK, false );
+ const SwFormatPageDesc* pDesc = rTableSet.GetItemIfSet( RES_PAGEDESC, false );
+
+ if( pBreak || pDesc )
+ {
+ SwNodeIndex aIdx( *pTableNd );
+ SwContentNode* pCNd = GoNext( &aIdx );
+ if( pBreak )
+ pCNd->SetAttr( *pBreak );
+ if( pDesc )
+ pCNd->SetAttr( *pDesc );
+ }
+ }
+
+ SectionUp( &aDelRg ); // Delete this Section and by that the Table
+ // #i28006#
+ SwNodeOffset nStt = aDelRg.aStart.GetIndex(), nEnd = aDelRg.aEnd.GetIndex();
+ if( !pFrameNd )
+ {
+ pNode2Layout->RestoreUpperFrames( *this,
+ aDelRg.aStart.GetIndex(), aDelRg.aEnd.GetIndex() );
+ delete pNode2Layout;
+ }
+ else
+ {
+ SwContentNode *pCNd;
+ SwSectionNode *pSNd;
+ while( aDelRg.aStart.GetIndex() < nEnd )
+ {
+ pCNd = aDelRg.aStart.GetNode().GetContentNode();
+ if( nullptr != pCNd )
+ {
+ if( pFrameNd->IsContentNode() )
+ static_cast<SwContentNode*>(pFrameNd)->MakeFramesForAdjacentContentNode(*pCNd);
+ else if( pFrameNd->IsTableNode() )
+ static_cast<SwTableNode*>(pFrameNd)->MakeFramesForAdjacentContentNode(aDelRg.aStart);
+ else if( pFrameNd->IsSectionNode() )
+ static_cast<SwSectionNode*>(pFrameNd)->MakeFramesForAdjacentContentNode(aDelRg.aStart);
+ pFrameNd = pCNd;
+ }
+ else
+ {
+ pSNd = aDelRg.aStart.GetNode().GetSectionNode();
+ if( pSNd )
+ {
+ if( !pSNd->GetSection().IsHidden() && !pSNd->IsContentHidden() )
+ {
+ pSNd->MakeOwnFrames(&aFrameIdx, &aDelRg.aEnd);
+ break;
+ }
+ aDelRg.aStart = *pSNd->EndOfSectionNode();
+ }
+ }
+ ++aDelRg.aStart;
+ }
+ }
+
+ // #i28006# Fly frames have to be restored even if the table was
+ // #alone in the section
+ const SwFrameFormats& rFlyArr = *GetDoc().GetSpzFrameFormats();
+ for( auto pFly : rFlyArr )
+ {
+ SwFrameFormat *const pFormat = pFly;
+ const SwFormatAnchor& rAnchor = pFormat->GetAnchor();
+ SwPosition const*const pAPos = rAnchor.GetContentAnchor();
+ if (pAPos &&
+ ((RndStdIds::FLY_AT_PARA == rAnchor.GetAnchorId()) ||
+ (RndStdIds::FLY_AT_CHAR == rAnchor.GetAnchorId())) &&
+ nStt <= pAPos->nNode.GetIndex() &&
+ pAPos->nNode.GetIndex() < nEnd )
+ {
+ pFormat->MakeFrames();
+ }
+ }
+
+ return true;
+}
+
+/**
+ * Inserting Columns/Rows
+ */
+void SwDoc::InsertCol( const SwCursor& rCursor, sal_uInt16 nCnt, bool bBehind )
+{
+ if( !::CheckSplitCells( rCursor, nCnt + 1, SwTableSearchType::Col ) )
+ return;
+
+ // Find the Boxes via the Layout
+ SwSelBoxes aBoxes;
+ ::GetTableSel( rCursor, aBoxes, SwTableSearchType::Col );
+
+ if( !aBoxes.empty() )
+ InsertCol( aBoxes, nCnt, bBehind );
+}
+
+bool SwDoc::InsertCol( const SwSelBoxes& rBoxes, sal_uInt16 nCnt, bool bBehind )
+{
+ OSL_ENSURE( !rBoxes.empty(), "No valid Box list" );
+ SwTableNode* pTableNd = const_cast<SwTableNode*>(rBoxes[0]->GetSttNd()->FindTableNode());
+ if( !pTableNd )
+ return false;
+
+ SwTable& rTable = pTableNd->GetTable();
+ if( dynamic_cast<const SwDDETable*>( &rTable) != nullptr)
+ return false;
+
+ SwTableSortBoxes aTmpLst;
+ std::unique_ptr<SwUndoTableNdsChg> pUndo;
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ pUndo.reset(new SwUndoTableNdsChg( SwUndoId::TABLE_INSCOL, rBoxes, *pTableNd,
+ 0, 0, nCnt, bBehind, false ));
+ aTmpLst.insert( rTable.GetTabSortBoxes() );
+ }
+
+ bool bRet(false);
+ {
+ ::sw::UndoGuard const undoGuard(GetIDocumentUndoRedo());
+
+ SwTableFormulaUpdate aMsgHint( &rTable );
+ aMsgHint.m_eFlags = TBL_BOXPTR;
+ getIDocumentFieldsAccess().UpdateTableFields( &aMsgHint );
+
+ bRet = rTable.InsertCol(*this, rBoxes, nCnt, bBehind);
+ if (bRet)
+ {
+ getIDocumentState().SetModified();
+ ::ClearFEShellTabCols(*this, nullptr);
+ getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, SwNodeOffset(0) );
+ }
+ }
+
+ if( pUndo && bRet )
+ {
+ pUndo->SaveNewBoxes( *pTableNd, aTmpLst );
+ GetIDocumentUndoRedo().AppendUndo( std::move(pUndo) );
+ }
+ return bRet;
+}
+
+void SwDoc::InsertRow( const SwCursor& rCursor, sal_uInt16 nCnt, bool bBehind )
+{
+ // Find the Boxes via the Layout
+ SwSelBoxes aBoxes;
+ GetTableSel( rCursor, aBoxes, SwTableSearchType::Row );
+
+ if( !aBoxes.empty() )
+ InsertRow( aBoxes, nCnt, bBehind );
+}
+
+bool SwDoc::InsertRow( const SwSelBoxes& rBoxes, sal_uInt16 nCnt, bool bBehind )
+{
+ OSL_ENSURE( !rBoxes.empty(), "No valid Box list" );
+ SwTableNode* pTableNd = const_cast<SwTableNode*>(rBoxes[0]->GetSttNd()->FindTableNode());
+ if( !pTableNd )
+ return false;
+
+ SwTable& rTable = pTableNd->GetTable();
+ if( dynamic_cast<const SwDDETable*>( &rTable) != nullptr)
+ return false;
+
+ SwTableSortBoxes aTmpLst;
+ std::unique_ptr<SwUndoTableNdsChg> pUndo;
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ pUndo.reset(new SwUndoTableNdsChg( SwUndoId::TABLE_INSROW,rBoxes, *pTableNd,
+ 0, 0, nCnt, bBehind, false ));
+ aTmpLst.insert( rTable.GetTabSortBoxes() );
+ }
+
+ bool bRet(false);
+ {
+ ::sw::UndoGuard const undoGuard(GetIDocumentUndoRedo());
+
+ SwTableFormulaUpdate aMsgHint( &rTable );
+ aMsgHint.m_eFlags = TBL_BOXPTR;
+ getIDocumentFieldsAccess().UpdateTableFields( &aMsgHint );
+
+ bRet = rTable.InsertRow( this, rBoxes, nCnt, bBehind );
+ if (bRet)
+ {
+ getIDocumentState().SetModified();
+ ::ClearFEShellTabCols(*this, nullptr);
+ getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, SwNodeOffset(0) );
+ }
+ }
+
+ if( pUndo && bRet )
+ {
+ pUndo->SaveNewBoxes( *pTableNd, aTmpLst );
+ GetIDocumentUndoRedo().AppendUndo( std::move(pUndo) );
+ }
+ return bRet;
+
+}
+
+/**
+ * Deleting Columns/Rows
+ */
+void SwDoc::DeleteRow( const SwCursor& rCursor )
+{
+ // Find the Boxes via the Layout
+ SwSelBoxes aBoxes;
+ GetTableSel( rCursor, aBoxes, SwTableSearchType::Row );
+ if( ::HasProtectedCells( aBoxes ))
+ return;
+
+ // Remove the Cursor from the to-be-deleted Section.
+ // The Cursor is placed after the table, except for
+ // - when there's another Line, we place it in that one
+ // - when a Line precedes it, we place it in that one
+ {
+ SwTableNode* pTableNd = rCursor.GetNode().FindTableNode();
+
+ if(dynamic_cast<const SwDDETable*>( & pTableNd->GetTable()) != nullptr)
+ return;
+
+ // Find all Boxes/Lines
+ FndBox_ aFndBox( nullptr, nullptr );
+ {
+ FndPara aPara( aBoxes, &aFndBox );
+ ForEach_FndLineCopyCol( pTableNd->GetTable().GetTabLines(), &aPara );
+ }
+
+ if( aFndBox.GetLines().empty() )
+ return;
+
+ if (SwEditShell* pESh = GetEditShell())
+ {
+ pESh->KillPams();
+ // FIXME: actually we should be iterating over all Shells!
+ }
+
+ FndBox_* pFndBox = &aFndBox;
+ while( 1 == pFndBox->GetLines().size() &&
+ 1 == pFndBox->GetLines().front()->GetBoxes().size() )
+ {
+ FndBox_ *const pTmp = pFndBox->GetLines().front()->GetBoxes()[0].get();
+ if( pTmp->GetBox()->GetSttNd() )
+ break; // Else it gets too far
+ pFndBox = pTmp;
+ }
+
+ SwTableLine* pDelLine = pFndBox->GetLines().back()->GetLine();
+ SwTableBox* pDelBox = pDelLine->GetTabBoxes().back();
+ while( !pDelBox->GetSttNd() )
+ {
+ SwTableLine* pLn = pDelBox->GetTabLines()[
+ pDelBox->GetTabLines().size()-1 ];
+ pDelBox = pLn->GetTabBoxes().back();
+ }
+ SwTableBox* pNextBox = pDelLine->FindNextBox( pTableNd->GetTable(),
+ pDelBox );
+ while( pNextBox &&
+ pNextBox->GetFrameFormat()->GetProtect().IsContentProtected() )
+ pNextBox = pNextBox->FindNextBox( pTableNd->GetTable(), pNextBox );
+
+ if( !pNextBox ) // No succeeding Boxes? Then take the preceding one
+ {
+ pDelLine = pFndBox->GetLines().front()->GetLine();
+ pDelBox = pDelLine->GetTabBoxes()[ 0 ];
+ while( !pDelBox->GetSttNd() )
+ pDelBox = pDelBox->GetTabLines()[0]->GetTabBoxes()[0];
+ pNextBox = pDelLine->FindPreviousBox( pTableNd->GetTable(),
+ pDelBox );
+ while( pNextBox &&
+ pNextBox->GetFrameFormat()->GetProtect().IsContentProtected() )
+ pNextBox = pNextBox->FindPreviousBox( pTableNd->GetTable(), pNextBox );
+ }
+
+ SwNodeOffset nIdx;
+ if( pNextBox ) // Place the Cursor here
+ nIdx = pNextBox->GetSttIdx() + 1;
+ else // Else after the Table
+ nIdx = pTableNd->EndOfSectionIndex() + 1;
+
+ SwNodeIndex aIdx( GetNodes(), nIdx );
+ SwContentNode* pCNd = aIdx.GetNode().GetContentNode();
+ if( !pCNd )
+ pCNd = GetNodes().GoNext( &aIdx );
+
+ if( pCNd )
+ {
+ // Change the Shell's Cursor or the one passed?
+ SwPaM* pPam = const_cast<SwPaM*>(static_cast<SwPaM const *>(&rCursor));
+ pPam->GetPoint()->nNode = aIdx;
+ pPam->GetPoint()->nContent.Assign( pCNd, 0 );
+ pPam->SetMark(); // Both want a part of it
+ pPam->DeleteMark();
+ }
+ }
+
+ // Thus delete the Rows
+ GetIDocumentUndoRedo().StartUndo(SwUndoId::ROW_DELETE, nullptr);
+ DeleteRowCol( aBoxes );
+ GetIDocumentUndoRedo().EndUndo(SwUndoId::ROW_DELETE, nullptr);
+}
+
+void SwDoc::DeleteCol( const SwCursor& rCursor )
+{
+ // Find the Boxes via the Layout
+ SwSelBoxes aBoxes;
+ GetTableSel( rCursor, aBoxes, SwTableSearchType::Col );
+ if( ::HasProtectedCells( aBoxes ))
+ return;
+
+ // The Cursors need to be removed from the to-be-deleted range.
+ // Always place them after/on top of the Table; they are always set
+ // to the old position via the document position.
+ if (SwEditShell* pESh = GetEditShell())
+ {
+ const SwNode* pNd = rCursor.GetNode().FindTableBoxStartNode();
+ pESh->ParkCursor( SwNodeIndex( *pNd ) );
+ }
+
+ // Thus delete the Columns
+ GetIDocumentUndoRedo().StartUndo(SwUndoId::COL_DELETE, nullptr);
+ DeleteRowCol(aBoxes, SwDoc::RowColMode::DeleteColumn);
+ GetIDocumentUndoRedo().EndUndo(SwUndoId::COL_DELETE, nullptr);
+}
+
+bool SwDoc::DeleteRowCol(const SwSelBoxes& rBoxes, RowColMode const eMode)
+{
+ if (!(eMode & SwDoc::RowColMode::DeleteProtected)
+ && ::HasProtectedCells(rBoxes))
+ {
+ return false;
+ }
+
+ OSL_ENSURE( !rBoxes.empty(), "No valid Box list" );
+ SwTableNode* pTableNd = const_cast<SwTableNode*>(rBoxes[0]->GetSttNd()->FindTableNode());
+ if( !pTableNd )
+ return false;
+
+ if (!(eMode & SwDoc::RowColMode::DeleteProtected)
+ && dynamic_cast<const SwDDETable*>(&pTableNd->GetTable()) != nullptr)
+ {
+ return false;
+ }
+
+ ::ClearFEShellTabCols(*this, nullptr);
+ SwSelBoxes aSelBoxes( rBoxes );
+ SwTable &rTable = pTableNd->GetTable();
+ tools::Long nMin = 0;
+ tools::Long nMax = 0;
+ if( rTable.IsNewModel() )
+ {
+ if (eMode & SwDoc::RowColMode::DeleteColumn)
+ rTable.ExpandColumnSelection( aSelBoxes, nMin, nMax );
+ else
+ rTable.FindSuperfluousRows( aSelBoxes );
+ }
+
+ // Are we deleting the whole Table?
+ const SwNodeOffset nTmpIdx1 = pTableNd->GetIndex();
+ const SwNodeOffset nTmpIdx2 = aSelBoxes.back()->GetSttNd()->EndOfSectionIndex() + 1;
+ if( pTableNd->GetTable().GetTabSortBoxes().size() == aSelBoxes.size() &&
+ aSelBoxes[0]->GetSttIdx()-1 == nTmpIdx1 &&
+ nTmpIdx2 == pTableNd->EndOfSectionIndex() )
+ {
+ bool bNewTextNd = false;
+ // Is it alone in a FlyFrame?
+ SwNodeIndex aIdx( *pTableNd, -1 );
+ const SwStartNode* pSttNd = aIdx.GetNode().GetStartNode();
+ if( pSttNd )
+ {
+ const SwNodeOffset nTableEnd = pTableNd->EndOfSectionIndex() + 1;
+ const SwNodeOffset nSectEnd = pSttNd->EndOfSectionIndex();
+ if( nTableEnd == nSectEnd )
+ {
+ if( SwFlyStartNode == pSttNd->GetStartNodeType() )
+ {
+ SwFrameFormat* pFormat = pSttNd->GetFlyFormat();
+ if( pFormat )
+ {
+ // That's the FlyFormat we're looking for
+ getIDocumentLayoutAccess().DelLayoutFormat( pFormat );
+ return true;
+ }
+ }
+ // No Fly? Thus Header or Footer: always leave a TextNode
+ // We can forget about Undo then!
+ bNewTextNd = true;
+ }
+ }
+
+ // No Fly? Then it is a Header or Footer, so keep always a TextNode
+ ++aIdx;
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().ClearRedo();
+ SwPaM aPaM( *pTableNd->EndOfSectionNode(), aIdx.GetNode() );
+
+ if( bNewTextNd )
+ {
+ const SwNodeIndex aTmpIdx( *pTableNd->EndOfSectionNode(), 1 );
+ GetNodes().MakeTextNode( aTmpIdx,
+ getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_STANDARD ) );
+ }
+
+ // Save the cursors (UNO and otherwise)
+ SwPaM aSavePaM( SwNodeIndex( *pTableNd->EndOfSectionNode() ) );
+ if( ! aSavePaM.Move( fnMoveForward, GoInNode ) )
+ {
+ *aSavePaM.GetMark() = SwPosition( *pTableNd );
+ aSavePaM.Move( fnMoveBackward, GoInNode );
+ }
+ {
+ SwPaM const tmpPaM(*pTableNd, *pTableNd->EndOfSectionNode());
+ ::PaMCorrAbs(tmpPaM, *aSavePaM.GetMark());
+ }
+
+ // Move hard PageBreaks to the succeeding Node
+ bool bSavePageBreak = false, bSavePageDesc = false;
+ SwNodeOffset nNextNd = pTableNd->EndOfSectionIndex()+1;
+ SwContentNode* pNextNd = GetNodes()[ nNextNd ]->GetContentNode();
+ if( pNextNd )
+ {
+ SwFrameFormat* pTableFormat = pTableNd->GetTable().GetFrameFormat();
+ const SfxPoolItem *pItem;
+ if( SfxItemState::SET == pTableFormat->GetItemState( RES_PAGEDESC,
+ false, &pItem ) )
+ {
+ pNextNd->SetAttr( *pItem );
+ bSavePageDesc = true;
+ }
+
+ if( SfxItemState::SET == pTableFormat->GetItemState( RES_BREAK,
+ false, &pItem ) )
+ {
+ pNextNd->SetAttr( *pItem );
+ bSavePageBreak = true;
+ }
+ }
+ std::unique_ptr<SwUndoDelete> pUndo(new SwUndoDelete(aPaM, SwDeleteFlags::Default));
+ if( bNewTextNd )
+ pUndo->SetTableDelLastNd();
+ pUndo->SetPgBrkFlags( bSavePageBreak, bSavePageDesc );
+ pUndo->SetTableName(pTableNd->GetTable().GetFrameFormat()->GetName());
+ GetIDocumentUndoRedo().AppendUndo( std::move(pUndo) );
+ }
+ else
+ {
+ if( bNewTextNd )
+ {
+ const SwNodeIndex aTmpIdx( *pTableNd->EndOfSectionNode(), 1 );
+ GetNodes().MakeTextNode( aTmpIdx,
+ getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_STANDARD ) );
+ }
+
+ // Save the cursors (UNO and otherwise)
+ SwPaM aSavePaM( SwNodeIndex( *pTableNd->EndOfSectionNode() ) );
+ if( ! aSavePaM.Move( fnMoveForward, GoInNode ) )
+ {
+ *aSavePaM.GetMark() = SwPosition( *pTableNd );
+ aSavePaM.Move( fnMoveBackward, GoInNode );
+ }
+ {
+ SwPaM const tmpPaM(*pTableNd, *pTableNd->EndOfSectionNode());
+ ::PaMCorrAbs(tmpPaM, *aSavePaM.GetMark());
+ }
+
+ // Move hard PageBreaks to the succeeding Node
+ SwContentNode* pNextNd = GetNodes()[ pTableNd->EndOfSectionIndex()+1 ]->GetContentNode();
+ if( pNextNd )
+ {
+ SwFrameFormat* pTableFormat = pTableNd->GetTable().GetFrameFormat();
+ const SfxPoolItem *pItem;
+ if( SfxItemState::SET == pTableFormat->GetItemState( RES_PAGEDESC,
+ false, &pItem ) )
+ pNextNd->SetAttr( *pItem );
+
+ if( SfxItemState::SET == pTableFormat->GetItemState( RES_BREAK,
+ false, &pItem ) )
+ pNextNd->SetAttr( *pItem );
+ }
+
+ pTableNd->DelFrames();
+ getIDocumentContentOperations().DeleteSection( pTableNd );
+ }
+
+ if (SwFEShell* pFEShell = GetDocShell()->GetFEShell())
+ pFEShell->UpdateTableStyleFormatting();
+
+ getIDocumentState().SetModified();
+ getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, SwNodeOffset(0) );
+
+ return true;
+ }
+
+ std::unique_ptr<SwUndoTableNdsChg> pUndo;
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ pUndo.reset(new SwUndoTableNdsChg( SwUndoId::TABLE_DELBOX, aSelBoxes, *pTableNd,
+ nMin, nMax, 0, false, false ));
+ }
+
+ bool bRet(false);
+ {
+ ::sw::UndoGuard const undoGuard(GetIDocumentUndoRedo());
+
+ SwTableFormulaUpdate aMsgHint( &pTableNd->GetTable() );
+ aMsgHint.m_eFlags = TBL_BOXPTR;
+ getIDocumentFieldsAccess().UpdateTableFields( &aMsgHint );
+
+ if (rTable.IsNewModel())
+ {
+ if (eMode & SwDoc::RowColMode::DeleteColumn)
+ rTable.PrepareDeleteCol( nMin, nMax );
+ rTable.FindSuperfluousRows( aSelBoxes );
+ if (pUndo)
+ pUndo->ReNewBoxes( aSelBoxes );
+ }
+ bRet = rTable.DeleteSel( this, aSelBoxes, nullptr, pUndo.get(), true, true );
+ if (bRet)
+ {
+ if (SwFEShell* pFEShell = GetDocShell()->GetFEShell())
+ pFEShell->UpdateTableStyleFormatting();
+
+ getIDocumentState().SetModified();
+ getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, SwNodeOffset(0) );
+ }
+ }
+
+ if( pUndo && bRet )
+ {
+ GetIDocumentUndoRedo().AppendUndo( std::move(pUndo) );
+ }
+
+ return bRet;
+}
+
+/**
+ * Split up/merge Boxes in the Table
+ */
+bool SwDoc::SplitTable( const SwSelBoxes& rBoxes, bool bVert, sal_uInt16 nCnt,
+ bool bSameHeight )
+{
+ OSL_ENSURE( !rBoxes.empty() && nCnt, "No valid Box list" );
+ SwTableNode* pTableNd = const_cast<SwTableNode*>(rBoxes[0]->GetSttNd()->FindTableNode());
+ if( !pTableNd )
+ return false;
+
+ SwTable& rTable = pTableNd->GetTable();
+ if( dynamic_cast<const SwDDETable*>( &rTable) != nullptr)
+ return false;
+
+ std::vector<SwNodeOffset> aNdsCnts;
+ SwTableSortBoxes aTmpLst;
+ std::unique_ptr<SwUndoTableNdsChg> pUndo;
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ pUndo.reset(new SwUndoTableNdsChg( SwUndoId::TABLE_SPLIT, rBoxes, *pTableNd, 0, 0,
+ nCnt, bVert, bSameHeight ));
+
+ aTmpLst.insert( rTable.GetTabSortBoxes() );
+ if( !bVert )
+ {
+ for (size_t n = 0; n < rBoxes.size(); ++n)
+ {
+ const SwStartNode* pSttNd = rBoxes[ n ]->GetSttNd();
+ aNdsCnts.push_back( pSttNd->EndOfSectionIndex() -
+ pSttNd->GetIndex() );
+ }
+ }
+ }
+
+ bool bRet(false);
+ {
+ ::sw::UndoGuard const undoGuard(GetIDocumentUndoRedo());
+
+ SwTableFormulaUpdate aMsgHint( &rTable );
+ aMsgHint.m_eFlags = TBL_BOXPTR;
+ getIDocumentFieldsAccess().UpdateTableFields( &aMsgHint );
+
+ if (bVert)
+ bRet = rTable.SplitCol(*this, rBoxes, nCnt);
+ else
+ bRet = rTable.SplitRow(*this, rBoxes, nCnt, bSameHeight);
+
+ if (bRet)
+ {
+ if (SwFEShell* pFEShell = GetDocShell()->GetFEShell())
+ pFEShell->UpdateTableStyleFormatting();
+
+ getIDocumentState().SetModified();
+ getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, SwNodeOffset(0) );
+ }
+ }
+
+ if( pUndo && bRet )
+ {
+ if( bVert )
+ pUndo->SaveNewBoxes( *pTableNd, aTmpLst );
+ else
+ pUndo->SaveNewBoxes( *pTableNd, aTmpLst, rBoxes, aNdsCnts );
+ GetIDocumentUndoRedo().AppendUndo( std::move(pUndo) );
+ }
+
+ return bRet;
+}
+
+TableMergeErr SwDoc::MergeTable( SwPaM& rPam )
+{
+ // Check if the current cursor's Point/Mark are inside a Table
+ SwTableNode* pTableNd = rPam.GetNode().FindTableNode();
+ if( !pTableNd )
+ return TableMergeErr::NoSelection;
+ SwTable& rTable = pTableNd->GetTable();
+ if( dynamic_cast<const SwDDETable*>( &rTable) != nullptr )
+ return TableMergeErr::NoSelection;
+ TableMergeErr nRet = TableMergeErr::NoSelection;
+ if( !rTable.IsNewModel() )
+ {
+ nRet =::CheckMergeSel( rPam );
+ if( TableMergeErr::Ok != nRet )
+ return nRet;
+ nRet = TableMergeErr::NoSelection;
+ }
+
+ // #i33394#
+ GetIDocumentUndoRedo().StartUndo( SwUndoId::TABLE_MERGE, nullptr );
+
+ RedlineFlags eOld = getIDocumentRedlineAccess().GetRedlineFlags();
+ getIDocumentRedlineAccess().SetRedlineFlags_intern(eOld | RedlineFlags::Ignore);
+
+ std::unique_ptr<SwUndoTableMerge> pUndo;
+ if (GetIDocumentUndoRedo().DoesUndo())
+ pUndo.reset(new SwUndoTableMerge( rPam ));
+
+ // Find the Boxes via the Layout
+ SwSelBoxes aBoxes;
+ SwSelBoxes aMerged;
+ SwTableBox* pMergeBox;
+
+ if( !rTable.PrepareMerge( rPam, aBoxes, aMerged, &pMergeBox, pUndo.get() ) )
+ { // No cells found to merge
+ getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
+ if( pUndo )
+ {
+ pUndo.reset();
+ SwUndoId nLastUndoId(SwUndoId::EMPTY);
+ if (GetIDocumentUndoRedo().GetLastUndoInfo(nullptr, & nLastUndoId)
+ && (SwUndoId::REDLINE == nLastUndoId))
+ {
+ // FIXME: why is this horrible cleanup necessary?
+ SwUndoRedline *const pU = dynamic_cast<SwUndoRedline*>(
+ GetUndoManager().RemoveLastUndo());
+ if (pU && pU->GetRedlSaveCount())
+ {
+ SwEditShell *const pEditShell(GetEditShell());
+ assert(pEditShell);
+ ::sw::UndoRedoContext context(*this, *pEditShell);
+ static_cast<SfxUndoAction *>(pU)->UndoWithContext(context);
+ }
+ delete pU;
+ }
+ }
+ }
+ else
+ {
+ // The PaMs need to be removed from the to-be-deleted range. Thus always place
+ // them at the end of/on top of the Table; it's always set to the old position via
+ // the Document Position.
+ // For a start remember an index for the temporary position, because we cannot
+ // access it after GetMergeSel
+ {
+ rPam.DeleteMark();
+ rPam.GetPoint()->nNode = *pMergeBox->GetSttNd();
+ rPam.GetPoint()->nContent.Assign( nullptr, 0 );
+ rPam.SetMark();
+ rPam.DeleteMark();
+
+ SwPaM* pTmp = &rPam;
+ while( &rPam != ( pTmp = pTmp->GetNext() ))
+ for( int i = 0; i < 2; ++i )
+ pTmp->GetBound( static_cast<bool>(i) ) = *rPam.GetPoint();
+
+ if (SwTableCursor* pTableCursor = dynamic_cast<SwTableCursor*>(&rPam))
+ {
+ // tdf#135098 update selection so rPam's m_SelectedBoxes is updated
+ // to not contain the soon to-be-deleted SwTableBox so if the rPam
+ // is queried via a11y it doesn't claim the deleted cell still
+ // exists
+ pTableCursor->NewTableSelection();
+ }
+ }
+
+ // Merge them
+ SwTableFormulaUpdate aMsgHint( &pTableNd->GetTable() );
+ aMsgHint.m_eFlags = TBL_BOXPTR;
+ getIDocumentFieldsAccess().UpdateTableFields( &aMsgHint );
+
+ if( pTableNd->GetTable().Merge( this, aBoxes, aMerged, pMergeBox, pUndo.get() ))
+ {
+ nRet = TableMergeErr::Ok;
+
+ getIDocumentState().SetModified();
+ getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, SwNodeOffset(0) );
+ if( pUndo )
+ {
+ GetIDocumentUndoRedo().AppendUndo( std::move(pUndo) );
+ }
+ }
+
+ rPam.GetPoint()->nNode = *pMergeBox->GetSttNd();
+ rPam.Move();
+
+ ::ClearFEShellTabCols(*this, nullptr);
+ getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
+ }
+ GetIDocumentUndoRedo().EndUndo( SwUndoId::TABLE_MERGE, nullptr );
+ return nRet;
+}
+
+SwTableNode::SwTableNode( const SwNodeIndex& rIdx )
+ : SwStartNode( rIdx, SwNodeType::Table )
+{
+ m_pTable.reset(new SwTable);
+}
+
+SwTableNode::~SwTableNode()
+{
+ // Notify UNO wrappers
+ GetTable().GetFrameFormat()->GetNotifier().Broadcast(SfxHint(SfxHintId::Dying));
+ DelFrames();
+ m_pTable->SetTableNode(this); // set this so that ~SwDDETable can read it!
+ m_pTable.reset();
+}
+
+SwTabFrame *SwTableNode::MakeFrame( SwFrame* pSib )
+{
+ return new SwTabFrame( *m_pTable, pSib );
+}
+
+/**
+ * Creates all Views from the Document for the preceding Node. The resulting ContentFrames
+ * are added to the corresponding Layout.
+ */
+void SwTableNode::MakeFramesForAdjacentContentNode(const SwNodeIndex & rIdx)
+{
+ if( !GetTable().GetFrameFormat()->HasWriterListeners()) // Do we actually have Frame?
+ return;
+
+ SwFrame *pFrame;
+ SwContentNode * pNode = rIdx.GetNode().GetContentNode();
+
+ OSL_ENSURE( pNode, "No ContentNode or CopyNode and new Node is identical");
+
+ bool bBefore = rIdx < GetIndex();
+
+ SwNode2Layout aNode2Layout( *this, rIdx.GetIndex() );
+
+ while( nullptr != (pFrame = aNode2Layout.NextFrame()) )
+ {
+ if ( ( pFrame->getRootFrame()->HasMergedParas() &&
+ !pNode->IsCreateFrameWhenHidingRedlines() ) ||
+ // tdf#153819 table deletion with change tracking:
+ // table node without frames in Hide Changes mode
+ !pFrame->GetUpper() )
+ {
+ continue;
+ }
+ SwFrame *pNew = pNode->MakeFrame( pFrame );
+ // Will the Node receive Frames before or after?
+ if ( bBefore )
+ // The new one precedes me
+ pNew->Paste( pFrame->GetUpper(), pFrame );
+ else
+ // The new one succeeds me
+ pNew->Paste( pFrame->GetUpper(), pFrame->GetNext() );
+ }
+}
+
+/**
+ * Create a TableFrame for every Shell and insert before the corresponding ContentFrame.
+ */
+void SwTableNode::MakeOwnFrames(SwNodeIndex* pIdxBehind)
+{
+ OSL_ENSURE( pIdxBehind, "No Index" );
+ *pIdxBehind = *this;
+ SwNode *pNd = GetNodes().FindPrvNxtFrameNode( *pIdxBehind, EndOfSectionNode() );
+ if( !pNd )
+ return ;
+
+ SwFrame *pFrame( nullptr );
+ SwLayoutFrame *pUpper( nullptr );
+ SwNode2Layout aNode2Layout( *pNd, GetIndex() );
+ while( nullptr != (pUpper = aNode2Layout.UpperFrame( pFrame, *this )) )
+ {
+ if (pUpper->getRootFrame()->HasMergedParas()
+ && !IsCreateFrameWhenHidingRedlines())
+ {
+ continue;
+ }
+ SwTabFrame* pNew = MakeFrame( pUpper );
+ pNew->Paste( pUpper, pFrame );
+ // #i27138#
+ // notify accessibility paragraphs objects about changed
+ // CONTENT_FLOWS_FROM/_TO relation.
+ // Relation CONTENT_FLOWS_FROM for next paragraph will change
+ // and relation CONTENT_FLOWS_TO for previous paragraph will change.
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ {
+ SwViewShell* pViewShell( pNew->getRootFrame()->GetCurrShell() );
+ if ( pViewShell && pViewShell->GetLayout() &&
+ pViewShell->GetLayout()->IsAnyShellAccessible() )
+ {
+ auto pNext = pNew->FindNextCnt( true );
+ auto pPrev = pNew->FindPrevCnt();
+ pViewShell->InvalidateAccessibleParaFlowRelation(
+ pNext ? pNext->DynCastTextFrame() : nullptr,
+ pPrev ? pPrev->DynCastTextFrame() : nullptr );
+ }
+ }
+#endif
+ pNew->RegistFlys();
+ }
+}
+
+void SwTableNode::DelFrames(SwRootFrame const*const pLayout)
+{
+ /* For a start, cut out and delete the TabFrames (which will also delete the Columns and Rows)
+ The TabFrames are attached to the FrameFormat of the SwTable.
+ We need to delete them in a more cumbersome way, for the Master to also delete the Follows. */
+
+ SwIterator<SwTabFrame,SwFormat> aIter( *(m_pTable->GetFrameFormat()) );
+ SwTabFrame *pFrame = aIter.First();
+ while ( pFrame )
+ {
+ bool bAgain = false;
+ {
+ if (!pFrame->IsFollow() && (!pLayout || pLayout == pFrame->getRootFrame()))
+ {
+ while ( pFrame->HasFollow() )
+ pFrame->JoinAndDelFollows();
+ // #i27138#
+ // notify accessibility paragraphs objects about changed
+ // CONTENT_FLOWS_FROM/_TO relation.
+ // Relation CONTENT_FLOWS_FROM for current next paragraph will change
+ // and relation CONTENT_FLOWS_TO for current previous paragraph will change.
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ {
+ SwViewShell* pViewShell( pFrame->getRootFrame()->GetCurrShell() );
+ if ( pViewShell && pViewShell->GetLayout() &&
+ pViewShell->GetLayout()->IsAnyShellAccessible() )
+ {
+ auto pNext = pFrame->FindNextCnt( true );
+ auto pPrev = pFrame->FindPrevCnt();
+ pViewShell->InvalidateAccessibleParaFlowRelation(
+ pNext ? pNext->DynCastTextFrame() : nullptr,
+ pPrev ? pPrev->DynCastTextFrame() : nullptr );
+ }
+ }
+#endif
+ if (pFrame->GetUpper())
+ pFrame->Cut();
+ SwFrame::DestroyFrame(pFrame);
+ bAgain = true;
+ }
+ }
+ pFrame = bAgain ? aIter.First() : aIter.Next();
+ }
+}
+
+void SwTableNode::SetNewTable( std::unique_ptr<SwTable> pNewTable, bool bNewFrames )
+{
+ DelFrames();
+ m_pTable->SetTableNode(this);
+ m_pTable = std::move(pNewTable);
+ if( bNewFrames )
+ {
+ SwNodeIndex aIdx( *EndOfSectionNode());
+ GetNodes().GoNext( &aIdx );
+ MakeOwnFrames(&aIdx);
+ }
+}
+
+void SwTableNode::RemoveRedlines()
+{
+ SwDoc& rDoc = GetDoc();
+ SwTable& rTable = GetTable();
+ rDoc.getIDocumentRedlineAccess().GetExtraRedlineTable().DeleteAllTableRedlines(rDoc, rTable, true, RedlineType::Any);
+}
+
+void SwTableNode::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwTableNode"));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("index"), BAD_CAST(OString::number(sal_Int32(GetIndex())).getStr()));
+
+ if (m_pTable)
+ {
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwTable"));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", m_pTable.get());
+ m_pTable->GetFrameFormat()->dumpAsXml(pWriter);
+ for (const auto& pLine : m_pTable->GetTabLines())
+ {
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwTableLine"));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", pLine);
+ pLine->GetFrameFormat()->dumpAsXml(pWriter);
+ (void)xmlTextWriterEndElement(pWriter);
+ }
+ (void)xmlTextWriterEndElement(pWriter);
+ }
+
+ // (void)xmlTextWriterEndElement(pWriter); - it is a start node, so don't end, will make xml better nested
+}
+
+void SwDoc::GetTabCols( SwTabCols &rFill, const SwCellFrame* pBoxFrame )
+{
+ OSL_ENSURE( pBoxFrame, "pBoxFrame needs to be specified!" );
+ if( !pBoxFrame )
+ return;
+
+ SwTabFrame *pTab = const_cast<SwFrame*>(static_cast<SwFrame const *>(pBoxFrame))->ImplFindTabFrame();
+ const SwTableBox* pBox = pBoxFrame->GetTabBox();
+
+ // Set fixed points, LeftMin in Document coordinates, all others relative
+ SwRectFnSet aRectFnSet(pTab);
+ const SwPageFrame* pPage = pTab->FindPageFrame();
+ const sal_uLong nLeftMin = aRectFnSet.GetLeft(pTab->getFrameArea()) -
+ aRectFnSet.GetLeft(pPage->getFrameArea());
+ const sal_uLong nRightMax = aRectFnSet.GetRight(pTab->getFrameArea()) -
+ aRectFnSet.GetLeft(pPage->getFrameArea());
+
+ rFill.SetLeftMin ( nLeftMin );
+ rFill.SetLeft ( aRectFnSet.GetLeft(pTab->getFramePrintArea()) );
+ rFill.SetRight ( aRectFnSet.GetRight(pTab->getFramePrintArea()));
+ rFill.SetRightMax( nRightMax - nLeftMin );
+
+ pTab->GetTable()->GetTabCols( rFill, pBox );
+}
+
+// Here are some little helpers used in SwDoc::GetTabRows
+
+#define ROWFUZZY 25
+
+namespace {
+
+struct FuzzyCompare
+{
+ bool operator() ( tools::Long s1, tools::Long s2 ) const;
+};
+
+}
+
+bool FuzzyCompare::operator() ( tools::Long s1, tools::Long s2 ) const
+{
+ return ( s1 < s2 && std::abs( s1 - s2 ) > ROWFUZZY );
+}
+
+static bool lcl_IsFrameInColumn( const SwCellFrame& rFrame, SwSelBoxes const & rBoxes )
+{
+ for (size_t i = 0; i < rBoxes.size(); ++i)
+ {
+ if ( rFrame.GetTabBox() == rBoxes[ i ] )
+ return true;
+ }
+
+ return false;
+}
+
+void SwDoc::GetTabRows( SwTabCols &rFill, const SwCellFrame* pBoxFrame )
+{
+ OSL_ENSURE( pBoxFrame, "GetTabRows called without pBoxFrame" );
+
+ // Make code robust:
+ if ( !pBoxFrame )
+ return;
+
+ // #i39552# Collection of the boxes of the current
+ // column has to be done at the beginning of this function, because
+ // the table may be formatted in ::GetTableSel.
+ SwDeletionChecker aDelCheck( pBoxFrame );
+
+ SwSelBoxes aBoxes;
+ const SwContentFrame* pContent = ::GetCellContent( *pBoxFrame );
+ if ( pContent && pContent->IsTextFrame() )
+ {
+ const SwPosition aPos(*static_cast<const SwTextFrame*>(pContent)->GetTextNodeFirst());
+ const SwCursor aTmpCursor( aPos, nullptr );
+ ::GetTableSel( aTmpCursor, aBoxes, SwTableSearchType::Col );
+ }
+
+ // Make code robust:
+ if ( aDelCheck.HasBeenDeleted() )
+ {
+ OSL_FAIL( "Current box has been deleted during GetTabRows()" );
+ return;
+ }
+
+ // Make code robust:
+ const SwTabFrame* pTab = pBoxFrame->FindTabFrame();
+ OSL_ENSURE( pTab, "GetTabRows called without a table" );
+ if ( !pTab )
+ return;
+
+ const SwFrame* pFrame = pTab->GetNextLayoutLeaf();
+
+ // Set fixed points, LeftMin in Document coordinates, all others relative
+ SwRectFnSet aRectFnSet(pTab);
+ const SwPageFrame* pPage = pTab->FindPageFrame();
+ const tools::Long nLeftMin = ( aRectFnSet.IsVert() ?
+ pTab->GetPrtLeft() - pPage->getFrameArea().Left() :
+ pTab->GetPrtTop() - pPage->getFrameArea().Top() );
+ const tools::Long nLeft = aRectFnSet.IsVert() ? LONG_MAX : 0;
+ const tools::Long nRight = aRectFnSet.GetHeight(pTab->getFramePrintArea());
+ const tools::Long nRightMax = aRectFnSet.IsVert() ? nRight : LONG_MAX;
+
+ rFill.SetLeftMin( nLeftMin );
+ rFill.SetLeft( nLeft );
+ rFill.SetRight( nRight );
+ rFill.SetRightMax( nRightMax );
+
+ typedef std::map< tools::Long, std::pair< tools::Long, long >, FuzzyCompare > BoundaryMap;
+ BoundaryMap aBoundaries;
+ BoundaryMap::iterator aIter;
+ std::pair< tools::Long, long > aPair;
+
+ typedef std::map< tools::Long, bool > HiddenMap;
+ HiddenMap aHidden;
+ HiddenMap::iterator aHiddenIter;
+
+ while ( pFrame && pTab->IsAnLower( pFrame ) )
+ {
+ if ( pFrame->IsCellFrame() && pFrame->FindTabFrame() == pTab )
+ {
+ // upper and lower borders of current cell frame:
+ tools::Long nUpperBorder = aRectFnSet.GetTop(pFrame->getFrameArea());
+ tools::Long nLowerBorder = aRectFnSet.GetBottom(pFrame->getFrameArea());
+
+ // get boundaries for nUpperBorder:
+ aIter = aBoundaries.find( nUpperBorder );
+ if ( aIter == aBoundaries.end() )
+ {
+ aPair.first = nUpperBorder; aPair.second = LONG_MAX;
+ aBoundaries[ nUpperBorder ] = aPair;
+ }
+
+ // get boundaries for nLowerBorder:
+ aIter = aBoundaries.find( nLowerBorder );
+ if ( aIter == aBoundaries.end() )
+ {
+ aPair.first = nUpperBorder; aPair.second = LONG_MAX;
+ }
+ else
+ {
+ nLowerBorder = (*aIter).first;
+ tools::Long nNewLowerBorderUpperBoundary = std::max( (*aIter).second.first, nUpperBorder );
+ aPair.first = nNewLowerBorderUpperBoundary; aPair.second = LONG_MAX;
+ }
+ aBoundaries[ nLowerBorder ] = aPair;
+
+ // calculate hidden flags for entry nUpperBorder/nLowerBorder:
+ tools::Long nTmpVal = nUpperBorder;
+ for ( sal_uInt8 i = 0; i < 2; ++i )
+ {
+ aHiddenIter = aHidden.find( nTmpVal );
+ if ( aHiddenIter == aHidden.end() )
+ aHidden[ nTmpVal ] = !lcl_IsFrameInColumn( *static_cast<const SwCellFrame*>(pFrame), aBoxes );
+ else
+ {
+ if ( aHidden[ nTmpVal ] &&
+ lcl_IsFrameInColumn( *static_cast<const SwCellFrame*>(pFrame), aBoxes ) )
+ aHidden[ nTmpVal ] = false;
+ }
+ nTmpVal = nLowerBorder;
+ }
+ }
+
+ pFrame = pFrame->GetNextLayoutLeaf();
+ }
+
+ // transfer calculated values from BoundaryMap and HiddenMap into rFill:
+ size_t nIdx = 0;
+ for ( const auto& rEntry : aBoundaries )
+ {
+ const tools::Long nTabTop = aRectFnSet.GetPrtTop(*pTab);
+ const tools::Long nKey = aRectFnSet.YDiff( rEntry.first, nTabTop );
+ const std::pair< tools::Long, long > aTmpPair = rEntry.second;
+ const tools::Long nFirst = aRectFnSet.YDiff( aTmpPair.first, nTabTop );
+ const tools::Long nSecond = aTmpPair.second;
+
+ aHiddenIter = aHidden.find( rEntry.first );
+ const bool bHidden = aHiddenIter != aHidden.end() && (*aHiddenIter).second;
+ rFill.Insert( nKey, nFirst, nSecond, bHidden, nIdx++ );
+ }
+
+ // delete first and last entry
+ OSL_ENSURE( rFill.Count(), "Deleting from empty vector. Fasten your seatbelts!" );
+ // #i60818# There may be only one entry in rFill. Make
+ // code robust by checking count of rFill.
+ if ( rFill.Count() ) rFill.Remove( 0 );
+ if ( rFill.Count() ) rFill.Remove( rFill.Count() - 1 );
+ rFill.SetLastRowAllowedToChange( !pTab->HasFollowFlowLine() );
+}
+
+void SwDoc::SetTabCols( const SwTabCols &rNew, bool bCurRowOnly,
+ const SwCellFrame* pBoxFrame )
+{
+ const SwTableBox* pBox = nullptr;
+ SwTabFrame *pTab = nullptr;
+
+ if( pBoxFrame )
+ {
+ pTab = const_cast<SwFrame*>(static_cast<SwFrame const *>(pBoxFrame))->ImplFindTabFrame();
+ pBox = pBoxFrame->GetTabBox();
+ }
+ else
+ {
+ OSL_ENSURE( false, "must specify pBoxFrame" );
+ return ;
+ }
+
+ // If the Table is still using relative values (USHRT_MAX)
+ // we need to switch to absolute ones.
+ SwTable& rTab = *pTab->GetTable();
+ const SwFormatFrameSize& rTableFrameSz = rTab.GetFrameFormat()->GetFrameSize();
+ SwRectFnSet aRectFnSet(pTab);
+ // #i17174# - With fix for #i9040# the shadow size is taken
+ // from the table width. Thus, add its left and right size to current table
+ // printing area width in order to get the correct table size attribute.
+ SwTwips nPrtWidth = aRectFnSet.GetWidth(pTab->getFramePrintArea());
+ {
+ SvxShadowItem aShadow( rTab.GetFrameFormat()->GetShadow() );
+ nPrtWidth += aShadow.CalcShadowSpace( SvxShadowItemSide::LEFT ) +
+ aShadow.CalcShadowSpace( SvxShadowItemSide::RIGHT );
+ }
+ if( nPrtWidth != rTableFrameSz.GetWidth() )
+ {
+ SwFormatFrameSize aSz( rTableFrameSz );
+ aSz.SetWidth( nPrtWidth );
+ rTab.GetFrameFormat()->SetFormatAttr( aSz );
+ }
+
+ SwTabCols aOld( rNew.Count() );
+
+ const SwPageFrame* pPage = pTab->FindPageFrame();
+ const sal_uLong nLeftMin = aRectFnSet.GetLeft(pTab->getFrameArea()) -
+ aRectFnSet.GetLeft(pPage->getFrameArea());
+ const sal_uLong nRightMax = aRectFnSet.GetRight(pTab->getFrameArea()) -
+ aRectFnSet.GetLeft(pPage->getFrameArea());
+
+ // Set fixed points, LeftMin in Document coordinates, all others relative
+ aOld.SetLeftMin ( nLeftMin );
+ aOld.SetLeft ( aRectFnSet.GetLeft(pTab->getFramePrintArea()) );
+ aOld.SetRight ( aRectFnSet.GetRight(pTab->getFramePrintArea()));
+ aOld.SetRightMax( nRightMax - nLeftMin );
+
+ rTab.GetTabCols( aOld, pBox );
+ SetTabCols(rTab, rNew, aOld, pBox, bCurRowOnly );
+}
+
+void SwDoc::SetTabRows( const SwTabCols &rNew, bool bCurColOnly,
+ const SwCellFrame* pBoxFrame )
+{
+ SwTabFrame *pTab = nullptr;
+
+ if( pBoxFrame )
+ {
+ pTab = const_cast<SwFrame*>(static_cast<SwFrame const *>(pBoxFrame))->ImplFindTabFrame();
+ }
+ else
+ {
+ OSL_ENSURE( false, "must specify pBoxFrame" );
+ return ;
+ }
+
+ // If the Table is still using relative values (USHRT_MAX)
+ // we need to switch to absolute ones.
+ SwRectFnSet aRectFnSet(pTab);
+ SwTabCols aOld( rNew.Count() );
+
+ // Set fixed points, LeftMin in Document coordinates, all others relative
+ const SwPageFrame* pPage = pTab->FindPageFrame();
+
+ aOld.SetRight( aRectFnSet.GetHeight(pTab->getFramePrintArea()) );
+ tools::Long nLeftMin;
+ if ( aRectFnSet.IsVert() )
+ {
+ nLeftMin = pTab->GetPrtLeft() - pPage->getFrameArea().Left();
+ aOld.SetLeft ( LONG_MAX );
+ aOld.SetRightMax( aOld.GetRight() );
+
+ }
+ else
+ {
+ nLeftMin = pTab->GetPrtTop() - pPage->getFrameArea().Top();
+ aOld.SetLeft ( 0 );
+ aOld.SetRightMax( LONG_MAX );
+ }
+ aOld.SetLeftMin ( nLeftMin );
+
+ GetTabRows( aOld, pBoxFrame );
+
+ GetIDocumentUndoRedo().StartUndo( SwUndoId::TABLE_ATTR, nullptr );
+
+ // check for differences between aOld and rNew:
+ const size_t nCount = rNew.Count();
+ const SwTable* pTable = pTab->GetTable();
+ OSL_ENSURE( pTable, "My colleague told me, this couldn't happen" );
+
+ for ( size_t i = 0; i <= nCount; ++i )
+ {
+ const size_t nIdxStt = aRectFnSet.IsVert() ? nCount - i : i - 1;
+ const size_t nIdxEnd = aRectFnSet.IsVert() ? nCount - i - 1 : i;
+
+ const tools::Long nOldRowStart = i == 0 ? 0 : aOld[ nIdxStt ];
+ const tools::Long nOldRowEnd = i == nCount ? aOld.GetRight() : aOld[ nIdxEnd ];
+ const tools::Long nOldRowHeight = nOldRowEnd - nOldRowStart;
+
+ const tools::Long nNewRowStart = i == 0 ? 0 : rNew[ nIdxStt ];
+ const tools::Long nNewRowEnd = i == nCount ? rNew.GetRight() : rNew[ nIdxEnd ];
+ const tools::Long nNewRowHeight = nNewRowEnd - nNewRowStart;
+
+ const tools::Long nDiff = nNewRowHeight - nOldRowHeight;
+ if ( std::abs( nDiff ) >= ROWFUZZY )
+ {
+ // For the old table model pTextFrame and pLine will be set for every box.
+ // For the new table model pTextFrame will be set if the box is not covered,
+ // but the pLine will be set if the box is not an overlapping box
+ // In the new table model the row height can be adjusted,
+ // when both variables are set.
+ const SwTextFrame* pTextFrame = nullptr;
+ const SwTableLine* pLine = nullptr;
+
+ // Iterate over all SwCellFrames with Bottom = nOldPos
+ const SwFrame* pFrame = pTab->GetNextLayoutLeaf();
+ while ( pFrame && pTab->IsAnLower( pFrame ) )
+ {
+ if ( pFrame->IsCellFrame() && pFrame->FindTabFrame() == pTab )
+ {
+ const tools::Long nLowerBorder = aRectFnSet.GetBottom(pFrame->getFrameArea());
+ const sal_uLong nTabTop = aRectFnSet.GetPrtTop(*pTab);
+ if ( std::abs( aRectFnSet.YInc( nTabTop, nOldRowEnd ) - nLowerBorder ) <= ROWFUZZY )
+ {
+ if ( !bCurColOnly || pFrame == pBoxFrame )
+ {
+ const SwFrame* pContent = ::GetCellContent( static_cast<const SwCellFrame&>(*pFrame) );
+
+ if ( pContent && pContent->IsTextFrame() )
+ {
+ const SwTableBox* pBox = static_cast<const SwCellFrame*>(pFrame)->GetTabBox();
+ const sal_Int32 nRowSpan = pBox->getRowSpan();
+ if( nRowSpan > 0 ) // Not overlapped
+ pTextFrame = static_cast<const SwTextFrame*>(pContent);
+ if( nRowSpan < 2 ) // Not overlapping for row height
+ pLine = pBox->GetUpper();
+ if( pLine && pTextFrame ) // always for old table model
+ {
+ // The new row height must not to be calculated from an overlapping box
+ SwFormatFrameSize aNew( pLine->GetFrameFormat()->GetFrameSize() );
+ const tools::Long nNewSize = aRectFnSet.GetHeight(pFrame->getFrameArea()) + nDiff;
+ if( nNewSize != aNew.GetHeight() )
+ {
+ aNew.SetHeight( nNewSize );
+ if ( SwFrameSize::Variable == aNew.GetHeightSizeType() )
+ aNew.SetHeightSizeType( SwFrameSize::Minimum );
+ // This position must not be in an overlapped box
+ const SwPosition aPos(*static_cast<const SwTextFrame*>(pContent)->GetTextNodeFirst());
+ const SwCursor aTmpCursor( aPos, nullptr );
+ SetRowHeight( aTmpCursor, aNew );
+ // For the new table model we're done, for the old one
+ // there might be another (sub)row to adjust...
+ if( pTable->IsNewModel() )
+ break;
+ }
+ pLine = nullptr;
+ }
+ }
+ }
+ }
+ }
+ pFrame = pFrame->GetNextLayoutLeaf();
+ }
+ }
+ }
+
+ GetIDocumentUndoRedo().EndUndo( SwUndoId::TABLE_ATTR, nullptr );
+
+ ::ClearFEShellTabCols(*this, nullptr);
+}
+
+/**
+ * Direct access for UNO
+ */
+void SwDoc::SetTabCols(SwTable& rTab, const SwTabCols &rNew, const SwTabCols &rOld,
+ const SwTableBox *pStart, bool bCurRowOnly )
+{
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().AppendUndo(
+ std::make_unique<SwUndoAttrTable>( *rTab.GetTableNode(), true ));
+ }
+ rTab.SetTabCols( rNew, rOld, pStart, bCurRowOnly );
+ ::ClearFEShellTabCols(*this, nullptr);
+ getIDocumentState().SetModified();
+}
+
+void SwDoc::SetRowsToRepeat( SwTable &rTable, sal_uInt16 nSet )
+{
+ if( nSet == rTable.GetRowsToRepeat() )
+ return;
+
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().AppendUndo(
+ std::make_unique<SwUndoTableHeadline>(rTable, rTable.GetRowsToRepeat(), nSet) );
+ }
+
+ rTable.SetRowsToRepeat(nSet);
+ const SwMsgPoolItem aChg(RES_TBLHEADLINECHG);
+ rTable.GetFrameFormat()->CallSwClientNotify(sw::LegacyModifyHint(&aChg, &aChg));
+ getIDocumentState().SetModified();
+}
+
+void SwCollectTableLineBoxes::AddToUndoHistory( const SwContentNode& rNd )
+{
+ if( m_pHistory )
+ m_pHistory->Add( rNd.GetFormatColl(), rNd.GetIndex(), SwNodeType::Text );
+}
+
+void SwCollectTableLineBoxes::AddBox( const SwTableBox& rBox )
+{
+ m_aPositionArr.push_back(m_nWidth);
+ SwTableBox* p = const_cast<SwTableBox*>(&rBox);
+ m_Boxes.push_back(p);
+ m_nWidth = m_nWidth + o3tl::narrowing<sal_uInt16>(rBox.GetFrameFormat()->GetFrameSize().GetWidth());
+}
+
+const SwTableBox* SwCollectTableLineBoxes::GetBoxOfPos( const SwTableBox& rBox )
+{
+ const SwTableBox* pRet = nullptr;
+
+ if( !m_aPositionArr.empty() )
+ {
+ std::vector<sal_uInt16>::size_type n;
+ for( n = 0; n < m_aPositionArr.size(); ++n )
+ if( m_aPositionArr[ n ] == m_nWidth )
+ break;
+ else if( m_aPositionArr[ n ] > m_nWidth )
+ {
+ if( n )
+ --n;
+ break;
+ }
+
+ if( n >= m_aPositionArr.size() )
+ --n;
+
+ m_nWidth = m_nWidth + o3tl::narrowing<sal_uInt16>(rBox.GetFrameFormat()->GetFrameSize().GetWidth());
+ pRet = m_Boxes[ n ];
+ }
+ return pRet;
+}
+
+bool SwCollectTableLineBoxes::Resize( sal_uInt16 nOffset, sal_uInt16 nOldWidth )
+{
+ if( !m_aPositionArr.empty() )
+ {
+ std::vector<sal_uInt16>::size_type n;
+ for( n = 0; n < m_aPositionArr.size(); ++n )
+ {
+ if( m_aPositionArr[ n ] == nOffset )
+ break;
+ else if( m_aPositionArr[ n ] > nOffset )
+ {
+ if( n )
+ --n;
+ break;
+ }
+ }
+
+ m_aPositionArr.erase( m_aPositionArr.begin(), m_aPositionArr.begin() + n );
+ m_Boxes.erase(m_Boxes.begin(), m_Boxes.begin() + n);
+
+ size_t nArrSize = m_aPositionArr.size();
+ if (nArrSize)
+ {
+ if (nOldWidth == 0)
+ throw o3tl::divide_by_zero();
+
+ // Adapt the positions to the new Size
+ for( n = 0; n < nArrSize; ++n )
+ {
+ sal_uLong nSize = m_nWidth;
+ nSize *= ( m_aPositionArr[ n ] - nOffset );
+ nSize /= nOldWidth;
+ m_aPositionArr[ n ] = sal_uInt16( nSize );
+ }
+ }
+ }
+ return !m_aPositionArr.empty();
+}
+
+bool sw_Line_CollectBox( const SwTableLine*& rpLine, void* pPara )
+{
+ SwCollectTableLineBoxes* pSplPara = static_cast<SwCollectTableLineBoxes*>(pPara);
+ if( pSplPara->IsGetValues() )
+ for( const auto& rpBox : const_cast<SwTableLine*>(rpLine)->GetTabBoxes() )
+ sw_Box_CollectBox(rpBox, pSplPara );
+ else
+ for( auto& rpBox : const_cast<SwTableLine*>(rpLine)->GetTabBoxes() )
+ sw_BoxSetSplitBoxFormats(rpBox, pSplPara );
+ return true;
+}
+
+void sw_Box_CollectBox( const SwTableBox* pBox, SwCollectTableLineBoxes* pSplPara )
+{
+ auto nLen = pBox->GetTabLines().size();
+ if( nLen )
+ {
+ // Continue with the actual Line
+ if( pSplPara->IsGetFromTop() )
+ nLen = 0;
+ else
+ --nLen;
+
+ const SwTableLine* pLn = pBox->GetTabLines()[ nLen ];
+ sw_Line_CollectBox( pLn, pSplPara );
+ }
+ else
+ pSplPara->AddBox( *pBox );
+}
+
+void sw_BoxSetSplitBoxFormats( SwTableBox* pBox, SwCollectTableLineBoxes* pSplPara )
+{
+ auto nLen = pBox->GetTabLines().size();
+ if( nLen )
+ {
+ // Continue with the actual Line
+ if( pSplPara->IsGetFromTop() )
+ nLen = 0;
+ else
+ --nLen;
+
+ const SwTableLine* pLn = pBox->GetTabLines()[ nLen ];
+ sw_Line_CollectBox( pLn, pSplPara );
+ }
+ else
+ {
+ const SwTableBox* pSrcBox = pSplPara->GetBoxOfPos( *pBox );
+ SwFrameFormat* pFormat = pSrcBox->GetFrameFormat();
+
+ if( SplitTable_HeadlineOption::BorderCopy == pSplPara->GetMode() )
+ {
+ const SvxBoxItem& rBoxItem = pBox->GetFrameFormat()->GetBox();
+ if( !rBoxItem.GetTop() )
+ {
+ SvxBoxItem aNew( rBoxItem );
+ aNew.SetLine( pFormat->GetBox().GetBottom(), SvxBoxItemLine::TOP );
+ if( aNew != rBoxItem )
+ pBox->ClaimFrameFormat()->SetFormatAttr( aNew );
+ }
+ }
+ else
+ {
+ SfxItemSetFixed<RES_LR_SPACE, RES_UL_SPACE,
+ RES_PROTECT, RES_PROTECT,
+ RES_VERT_ORIENT, RES_VERT_ORIENT,
+ RES_BACKGROUND, RES_SHADOW>
+ aTmpSet( pFormat->GetDoc()->GetAttrPool() );
+ aTmpSet.Put( pFormat->GetAttrSet() );
+ if( aTmpSet.Count() )
+ pBox->ClaimFrameFormat()->SetFormatAttr( aTmpSet );
+
+ if( SplitTable_HeadlineOption::BoxAttrAllCopy == pSplPara->GetMode() )
+ {
+ SwNodeIndex aIdx( *pSrcBox->GetSttNd(), 1 );
+ SwContentNode* pCNd = aIdx.GetNode().GetContentNode();
+ if( !pCNd )
+ pCNd = aIdx.GetNodes().GoNext( &aIdx );
+ aIdx = *pBox->GetSttNd();
+ SwContentNode* pDNd = aIdx.GetNodes().GoNext( &aIdx );
+
+ // If the Node is alone in the Section
+ if( SwNodeOffset(2) == pDNd->EndOfSectionIndex() -
+ pDNd->StartOfSectionIndex() )
+ {
+ pSplPara->AddToUndoHistory( *pDNd );
+ pDNd->ChgFormatColl( pCNd->GetFormatColl() );
+ }
+ }
+
+ // note conditional template
+ pBox->GetSttNd()->CheckSectionCondColl();
+ }
+ }
+}
+
+/**
+ * Splits a Table in the top-level Line which contains the Index.
+ * All succeeding top-level Lines go into a new Table/Node.
+ *
+ * @param bCalcNewSize true
+ * Calculate the new Size for both from the
+ * Boxes' Max; but only if Size is using absolute
+ * values (USHRT_MAX)
+ */
+void SwDoc::SplitTable( const SwPosition& rPos, SplitTable_HeadlineOption eHdlnMode,
+ bool bCalcNewSize )
+{
+ SwNode* pNd = &rPos.nNode.GetNode();
+ SwTableNode* pTNd = pNd->FindTableNode();
+ if( !pTNd || pNd->IsTableNode() )
+ return;
+
+ if( dynamic_cast<const SwDDETable*>( &pTNd->GetTable() ) != nullptr)
+ return;
+
+ SwTable& rTable = pTNd->GetTable();
+ rTable.SetHTMLTableLayout(std::shared_ptr<SwHTMLTableLayout>()); // Delete HTML Layout
+
+ SwTableFormulaUpdate aMsgHint( &rTable );
+
+ SwHistory aHistory;
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ aMsgHint.m_pHistory = &aHistory;
+ }
+
+ {
+ SwNodeOffset nSttIdx = pNd->FindTableBoxStartNode()->GetIndex();
+
+ // Find top-level Line
+ SwTableBox* pBox = rTable.GetTableBox( nSttIdx );
+ if( pBox )
+ {
+ SwTableLine* pLine = pBox->GetUpper();
+ while( pLine->GetUpper() )
+ pLine = pLine->GetUpper()->GetUpper();
+
+ // pLine contains the top-level Line now
+ aMsgHint.m_nSplitLine = rTable.GetTabLines().GetPos( pLine );
+ }
+
+ OUString sNewTableNm( GetUniqueTableName() );
+ aMsgHint.m_aData.pNewTableNm = &sNewTableNm;
+ aMsgHint.m_eFlags = TBL_SPLITTBL;
+ getIDocumentFieldsAccess().UpdateTableFields( &aMsgHint );
+ }
+
+ // Find Lines for the Layout update
+ FndBox_ aFndBox( nullptr, nullptr );
+ aFndBox.SetTableLines( rTable );
+ aFndBox.DelFrames( rTable );
+
+ SwTableNode* pNew = GetNodes().SplitTable( rPos.nNode, false, bCalcNewSize );
+
+ if( pNew )
+ {
+ std::unique_ptr<SwSaveRowSpan> pSaveRowSp = pNew->GetTable().CleanUpTopRowSpan( rTable.GetTabLines().size() );
+ SwUndoSplitTable* pUndo = nullptr;
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ pUndo = new SwUndoSplitTable(
+ *pNew, std::move(pSaveRowSp), eHdlnMode, bCalcNewSize);
+ GetIDocumentUndoRedo().AppendUndo(std::unique_ptr<SwUndo>(pUndo));
+ if( aHistory.Count() )
+ pUndo->SaveFormula( aHistory );
+ }
+
+ switch( eHdlnMode )
+ {
+ // Set the lower Border of the preceding Line to
+ // the upper Border of the current one
+ case SplitTable_HeadlineOption::BorderCopy:
+ {
+ SwCollectTableLineBoxes aPara( false, eHdlnMode );
+ SwTableLine* pLn = rTable.GetTabLines()[
+ rTable.GetTabLines().size() - 1 ];
+ for( const auto& rpBox : pLn->GetTabBoxes() )
+ sw_Box_CollectBox(rpBox, &aPara );
+
+ aPara.SetValues( true );
+ pLn = pNew->GetTable().GetTabLines()[ 0 ];
+ for( auto& rpBox : pLn->GetTabBoxes() )
+ sw_BoxSetSplitBoxFormats(rpBox, &aPara );
+
+ // Switch off repeating Header
+ pNew->GetTable().SetRowsToRepeat( 0 );
+ }
+ break;
+
+ // Take over the Attributes of the first Line to the new one
+ case SplitTable_HeadlineOption::BoxAttrCopy:
+ case SplitTable_HeadlineOption::BoxAttrAllCopy:
+ {
+ SwHistory* pHst = nullptr;
+ if( SplitTable_HeadlineOption::BoxAttrAllCopy == eHdlnMode && pUndo )
+ pHst = pUndo->GetHistory();
+
+ SwCollectTableLineBoxes aPara( true, eHdlnMode, pHst );
+ SwTableLine* pLn = rTable.GetTabLines()[ 0 ];
+ for( const auto& rpBox : pLn->GetTabBoxes() )
+ sw_Box_CollectBox(rpBox, &aPara );
+
+ aPara.SetValues( true );
+ pLn = pNew->GetTable().GetTabLines()[ 0 ];
+ for( auto& rpBox : pLn->GetTabBoxes() )
+ sw_BoxSetSplitBoxFormats(rpBox, &aPara );
+ }
+ break;
+
+ case SplitTable_HeadlineOption::ContentCopy:
+ rTable.CopyHeadlineIntoTable( *pNew );
+ if( pUndo )
+ pUndo->SetTableNodeOffset( pNew->GetIndex() );
+ break;
+
+ case SplitTable_HeadlineOption::NONE:
+ // Switch off repeating the Header
+ pNew->GetTable().SetRowsToRepeat( 0 );
+ break;
+ }
+
+ // And insert Frames
+ SwNodeIndex aNdIdx( *pNew->EndOfSectionNode() );
+ GetNodes().GoNext( &aNdIdx ); // To the next ContentNode
+ pNew->MakeOwnFrames( &aNdIdx );
+
+ // Insert a paragraph between the Table
+ GetNodes().MakeTextNode( SwNodeIndex( *pNew ),
+ getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_TEXT ) );
+ }
+
+ // Update Layout
+ aFndBox.MakeFrames( rTable );
+
+ // TL_CHART2: need to inform chart of probably changed cell names
+ UpdateCharts( rTable.GetFrameFormat()->GetName() );
+
+ // update table style formatting of both the tables
+ if (SwFEShell* pFEShell = GetDocShell()->GetFEShell())
+ {
+ pFEShell->UpdateTableStyleFormatting(pTNd);
+ pFEShell->UpdateTableStyleFormatting(pNew);
+ }
+
+ getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, SwNodeOffset(0) );
+}
+
+static bool lcl_ChgTableSize( SwTable& rTable )
+{
+ // The Attribute must not be set via the Modify or else all Boxes are
+ // set back to 0.
+ // So lock the Format.
+ SwFrameFormat* pFormat = rTable.GetFrameFormat();
+ SwFormatFrameSize aTableMaxSz( pFormat->GetFrameSize() );
+
+ if( USHRT_MAX == aTableMaxSz.GetWidth() )
+ return false;
+
+ bool bLocked = pFormat->IsModifyLocked();
+ pFormat->LockModify();
+
+ aTableMaxSz.SetWidth( 0 );
+
+ SwTableLines& rLns = rTable.GetTabLines();
+ for( auto pLn : rLns )
+ {
+ SwTwips nMaxLnWidth = 0;
+ SwTableBoxes& rBoxes = pLn->GetTabBoxes();
+ for( auto pBox : rBoxes )
+ nMaxLnWidth += pBox->GetFrameFormat()->GetFrameSize().GetWidth();
+
+ if( nMaxLnWidth > aTableMaxSz.GetWidth() )
+ aTableMaxSz.SetWidth( nMaxLnWidth );
+ }
+ pFormat->SetFormatAttr( aTableMaxSz );
+ if( !bLocked ) // Release the Lock if appropriate
+ pFormat->UnlockModify();
+
+ return true;
+}
+
+namespace {
+
+class SplitTable_Para
+{
+ std::map<SwFrameFormat const*, SwFrameFormat*> m_aSrcDestMap;
+ SwTableNode* m_pNewTableNode;
+ SwTable& m_rOldTable;
+
+public:
+ SplitTable_Para(SwTableNode* pNew, SwTable& rOld)
+ : m_pNewTableNode(pNew)
+ , m_rOldTable(rOld)
+ {}
+ SwFrameFormat* GetDestFormat( SwFrameFormat* pSrcFormat ) const
+ {
+ auto it = m_aSrcDestMap.find(pSrcFormat);
+ return it == m_aSrcDestMap.end() ? nullptr : it->second;
+ }
+
+ void InsertSrcDest( SwFrameFormat const * pSrcFormat, SwFrameFormat* pDestFormat )
+ {
+ m_aSrcDestMap[pSrcFormat] = pDestFormat;
+ }
+
+ void ChgBox( SwTableBox* pBox )
+ {
+ m_rOldTable.GetTabSortBoxes().erase(pBox);
+ m_pNewTableNode->GetTable().GetTabSortBoxes().insert(pBox);
+ }
+};
+
+}
+
+static void lcl_SplitTable_CpyBox( SwTableBox* pBox, SplitTable_Para* pPara );
+
+static void lcl_SplitTable_CpyLine( SwTableLine* pLn, SplitTable_Para* pPara )
+{
+ SwFrameFormat *pSrcFormat = pLn->GetFrameFormat();
+ SwTableLineFormat* pDestFormat = static_cast<SwTableLineFormat*>( pPara->GetDestFormat( pSrcFormat ) );
+ if( pDestFormat == nullptr )
+ {
+ pPara->InsertSrcDest( pSrcFormat, pLn->ClaimFrameFormat() );
+ }
+ else
+ pLn->ChgFrameFormat( pDestFormat );
+
+ for( auto& rpBox : pLn->GetTabBoxes() )
+ lcl_SplitTable_CpyBox(rpBox, pPara );
+}
+
+static void lcl_SplitTable_CpyBox( SwTableBox* pBox, SplitTable_Para* pPara )
+{
+ SwFrameFormat *pSrcFormat = pBox->GetFrameFormat();
+ SwTableBoxFormat* pDestFormat = static_cast<SwTableBoxFormat*>(pPara->GetDestFormat( pSrcFormat ));
+ if( pDestFormat == nullptr )
+ {
+ pPara->InsertSrcDest( pSrcFormat, pBox->ClaimFrameFormat() );
+ }
+ else
+ pBox->ChgFrameFormat( pDestFormat );
+
+ if( pBox->GetSttNd() )
+ pPara->ChgBox( pBox );
+ else
+ for( SwTableLine* pLine : pBox->GetTabLines() )
+ lcl_SplitTable_CpyLine( pLine, pPara );
+}
+
+SwTableNode* SwNodes::SplitTable( const SwNodeIndex& rPos, bool bAfter,
+ bool bCalcNewSize )
+{
+ SwNode* pNd = &rPos.GetNode();
+ SwTableNode* pTNd = pNd->FindTableNode();
+ if( !pTNd || pNd->IsTableNode() )
+ return nullptr;
+
+ SwNodeOffset nSttIdx = pNd->FindTableBoxStartNode()->GetIndex();
+
+ // Find this Box/top-level line
+ SwTable& rTable = pTNd->GetTable();
+ SwTableBox* pBox = rTable.GetTableBox( nSttIdx );
+ if( !pBox )
+ return nullptr;
+
+ SwTableLine* pLine = pBox->GetUpper();
+ while( pLine->GetUpper() )
+ pLine = pLine->GetUpper()->GetUpper();
+
+ // pLine now contains the top-level line
+ sal_uInt16 nLinePos = rTable.GetTabLines().GetPos( pLine );
+ if( USHRT_MAX == nLinePos ||
+ ( bAfter ? ++nLinePos >= rTable.GetTabLines().size() : !nLinePos ))
+ return nullptr; // Not found or last Line!
+
+ // Find the first Box of the succeeding Line
+ SwTableLine* pNextLine = rTable.GetTabLines()[ nLinePos ];
+ pBox = pNextLine->GetTabBoxes()[0];
+ while( !pBox->GetSttNd() )
+ pBox = pBox->GetTabLines()[0]->GetTabBoxes()[0];
+
+ // Insert an EndNode and TableNode into the Nodes Array
+ SwTableNode * pNewTableNd;
+ {
+ SwEndNode* pOldTableEndNd = pTNd->EndOfSectionNode()->GetEndNode();
+ assert(pOldTableEndNd && "Where is the EndNode?");
+
+ SwNodeIndex aIdx( *pBox->GetSttNd() );
+ new SwEndNode( aIdx, *pTNd );
+ pNewTableNd = new SwTableNode( aIdx );
+ pNewTableNd->GetTable().SetTableModel( rTable.IsNewModel() );
+
+ pOldTableEndNd->m_pStartOfSection = pNewTableNd;
+ pNewTableNd->m_pEndOfSection = pOldTableEndNd;
+
+ SwNode* pBoxNd = aIdx.GetNode().GetStartNode();
+ do {
+ OSL_ENSURE( pBoxNd->IsStartNode(), "This needs to be a StartNode!" );
+ pBoxNd->m_pStartOfSection = pNewTableNd;
+ pBoxNd = (*this)[ pBoxNd->EndOfSectionIndex() + 1 ];
+ } while( pBoxNd != pOldTableEndNd );
+ }
+
+ {
+ // Move the Lines
+ SwTable& rNewTable = pNewTableNd->GetTable();
+ rNewTable.GetTabLines().insert( rNewTable.GetTabLines().begin(),
+ rTable.GetTabLines().begin() + nLinePos, rTable.GetTabLines().end() );
+
+ /* From the back (bottom right) to the front (top left) deregister all Boxes from the
+ Chart Data Provider. The Modify event is triggered in the calling function.
+ TL_CHART2: */
+ SwChartDataProvider *pPCD = rTable.GetFrameFormat()->getIDocumentChartDataProviderAccess().GetChartDataProvider();
+ if( pPCD )
+ {
+ for (SwTableLines::size_type k = nLinePos; k < rTable.GetTabLines().size(); ++k)
+ {
+ const SwTableLines::size_type nLineIdx = (rTable.GetTabLines().size() - 1) - k + nLinePos;
+ const SwTableBoxes::size_type nBoxCnt = rTable.GetTabLines()[ nLineIdx ]->GetTabBoxes().size();
+ for (SwTableBoxes::size_type j = 0; j < nBoxCnt; ++j)
+ {
+ const SwTableBoxes::size_type nIdx = nBoxCnt - 1 - j;
+ pPCD->DeleteBox( &rTable, *rTable.GetTabLines()[ nLineIdx ]->GetTabBoxes()[nIdx] );
+ }
+ }
+ }
+
+ // Delete
+ sal_uInt16 nDeleted = rTable.GetTabLines().size() - nLinePos;
+ rTable.GetTabLines().erase( rTable.GetTabLines().begin() + nLinePos, rTable.GetTabLines().end() );
+
+ // Move the affected Boxes. Make the Formats unique and correct the StartNodes
+ SplitTable_Para aPara( pNewTableNd, rTable );
+ for( SwTableLine* pNewLine : rNewTable.GetTabLines() )
+ lcl_SplitTable_CpyLine( pNewLine, &aPara );
+ rTable.CleanUpBottomRowSpan( nDeleted );
+ }
+
+ {
+ // Copy the Table FrameFormat
+ SwFrameFormat* pOldTableFormat = rTable.GetFrameFormat();
+ SwFrameFormat* pNewTableFormat = pOldTableFormat->GetDoc()->MakeTableFrameFormat(
+ pOldTableFormat->GetDoc()->GetUniqueTableName(),
+ pOldTableFormat->GetDoc()->GetDfltFrameFormat() );
+
+ *pNewTableFormat = *pOldTableFormat;
+ pNewTableNd->GetTable().RegisterToFormat( *pNewTableFormat );
+
+ pNewTableNd->GetTable().SetTableStyleName(rTable.GetTableStyleName());
+
+ // Calculate a new Size?
+ // lcl_ChgTableSize: Only execute the second call if the first call was
+ // successful, thus has an absolute Size
+ if( bCalcNewSize && lcl_ChgTableSize( rTable ) )
+ lcl_ChgTableSize( pNewTableNd->GetTable() );
+ }
+
+ // TL_CHART2: need to inform chart of probably changed cell names
+ rTable.UpdateCharts();
+
+ return pNewTableNd; // That's it!
+}
+
+/**
+ * rPos needs to be in the Table that remains
+ *
+ * @param bWithPrev merge the current Table with the preceding
+ * or succeeding one
+ */
+bool SwDoc::MergeTable( const SwPosition& rPos, bool bWithPrev, sal_uInt16 nMode )
+{
+ SwTableNode* pTableNd = rPos.nNode.GetNode().FindTableNode(), *pDelTableNd;
+ if( !pTableNd )
+ return false;
+
+ SwNodes& rNds = GetNodes();
+ if( bWithPrev )
+ pDelTableNd = rNds[ pTableNd->GetIndex() - 1 ]->FindTableNode();
+ else
+ pDelTableNd = rNds[ pTableNd->EndOfSectionIndex() + 1 ]->GetTableNode();
+ if( !pDelTableNd )
+ return false;
+
+ if( dynamic_cast<const SwDDETable*>( &pTableNd->GetTable() ) != nullptr ||
+ dynamic_cast<const SwDDETable*>( &pDelTableNd->GetTable() ) != nullptr)
+ return false;
+
+ // Delete HTML Layout
+ pTableNd->GetTable().SetHTMLTableLayout(std::shared_ptr<SwHTMLTableLayout>());
+ pDelTableNd->GetTable().SetHTMLTableLayout(std::shared_ptr<SwHTMLTableLayout>());
+
+ // Both Tables are present; we can start
+ SwUndoMergeTable* pUndo = nullptr;
+ std::unique_ptr<SwHistory> pHistory;
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ pUndo = new SwUndoMergeTable( *pTableNd, *pDelTableNd, bWithPrev, nMode );
+ GetIDocumentUndoRedo().AppendUndo(std::unique_ptr<SwUndo>(pUndo));
+ pHistory.reset(new SwHistory);
+ }
+
+ // Adapt all "TableFormulas"
+ SwTableFormulaUpdate aMsgHint( &pTableNd->GetTable() );
+ aMsgHint.m_aData.pDelTable = &pDelTableNd->GetTable();
+ aMsgHint.m_eFlags = TBL_MERGETBL;
+ aMsgHint.m_pHistory = pHistory.get();
+ getIDocumentFieldsAccess().UpdateTableFields( &aMsgHint );
+
+ // The actual merge
+ SwNodeIndex aIdx( bWithPrev ? *pTableNd : *pDelTableNd );
+ bool bRet = rNds.MergeTable( aIdx, !bWithPrev, nMode );
+
+ if( pHistory )
+ {
+ if( pHistory->Count() )
+ pUndo->SaveFormula( *pHistory );
+ pHistory.reset();
+ }
+ if( bRet )
+ {
+ if (SwFEShell* pFEShell = GetDocShell()->GetFEShell())
+ pFEShell->UpdateTableStyleFormatting();
+
+ getIDocumentState().SetModified();
+ getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, SwNodeOffset(0) );
+ }
+ return bRet;
+}
+
+bool SwNodes::MergeTable( const SwNodeIndex& rPos, bool bWithPrev,
+ sal_uInt16 nMode )
+{
+ SwTableNode* pDelTableNd = rPos.GetNode().GetTableNode();
+ OSL_ENSURE( pDelTableNd, "Where did the TableNode go?" );
+
+ SwTableNode* pTableNd = (*this)[ rPos.GetIndex() - 1]->FindTableNode();
+ OSL_ENSURE( pTableNd, "Where did the TableNode go?" );
+
+ if( !pDelTableNd || !pTableNd )
+ return false;
+
+ pDelTableNd->DelFrames();
+
+ SwTable& rDelTable = pDelTableNd->GetTable();
+ SwTable& rTable = pTableNd->GetTable();
+
+ // Find Lines for the Layout update
+ FndBox_ aFndBox( nullptr, nullptr );
+ aFndBox.SetTableLines( rTable );
+ aFndBox.DelFrames( rTable );
+
+ // TL_CHART2:
+ // tell the charts about the table to be deleted and have them use their own data
+ GetDoc().getIDocumentChartDataProviderAccess().CreateChartInternalDataProviders( &rDelTable );
+
+ // Sync the TableFormat's Width
+ {
+ const SwFormatFrameSize& rTableSz = rTable.GetFrameFormat()->GetFrameSize();
+ const SwFormatFrameSize& rDelTableSz = rDelTable.GetFrameFormat()->GetFrameSize();
+ if( rTableSz != rDelTableSz )
+ {
+ // The needs correction
+ if( bWithPrev )
+ rDelTable.GetFrameFormat()->SetFormatAttr( rTableSz );
+ else
+ rTable.GetFrameFormat()->SetFormatAttr( rDelTableSz );
+ }
+ }
+
+ if( !bWithPrev )
+ {
+ // Transfer all Attributes of the succeeding Table to the preceding one
+ // We do this, because the succeeding one is deleted when deleting the Node
+ rTable.SetRowsToRepeat( rDelTable.GetRowsToRepeat() );
+ rTable.SetTableChgMode( rDelTable.GetTableChgMode() );
+
+ rTable.GetFrameFormat()->LockModify();
+ *rTable.GetFrameFormat() = *rDelTable.GetFrameFormat();
+ // Also switch the Name
+ rTable.GetFrameFormat()->SetName( rDelTable.GetFrameFormat()->GetName() );
+ rTable.GetFrameFormat()->UnlockModify();
+ }
+
+ // Move the Lines and Boxes
+ SwTableLines::size_type nOldSize = rTable.GetTabLines().size();
+ rTable.GetTabLines().insert( rTable.GetTabLines().begin() + nOldSize,
+ rDelTable.GetTabLines().begin(), rDelTable.GetTabLines().end() );
+ rDelTable.GetTabLines().clear();
+
+ rTable.GetTabSortBoxes().insert( rDelTable.GetTabSortBoxes() );
+ rDelTable.GetTabSortBoxes().clear();
+
+ // The preceding Table always remains, while the succeeding one is deleted
+ SwEndNode* pTableEndNd = pDelTableNd->EndOfSectionNode();
+ pTableNd->m_pEndOfSection = pTableEndNd;
+
+ SwNodeIndex aIdx( *pDelTableNd, 1 );
+
+ SwNode* pBoxNd = aIdx.GetNode().GetStartNode();
+ do {
+ OSL_ENSURE( pBoxNd->IsStartNode(), "This needs to be a StartNode!" );
+ pBoxNd->m_pStartOfSection = pTableNd;
+ pBoxNd = (*this)[ pBoxNd->EndOfSectionIndex() + 1 ];
+ } while( pBoxNd != pTableEndNd );
+ pBoxNd->m_pStartOfSection = pTableNd;
+
+ aIdx -= SwNodeOffset(2);
+ DelNodes( aIdx, SwNodeOffset(2) );
+
+ // tweak the conditional styles at the first inserted Line
+ const SwTableLine* pFirstLn = rTable.GetTabLines()[ nOldSize ];
+ if( 1 == nMode )
+ {
+ // Set Header Template in the Line and save in the History
+ // if needed for Undo!
+ }
+ sw_LineSetHeadCondColl( pFirstLn );
+
+ // Clean up the Borders
+ if( nOldSize )
+ {
+ SwGCLineBorder aPara( rTable );
+ aPara.nLinePos = --nOldSize;
+ pFirstLn = rTable.GetTabLines()[ nOldSize ];
+ sw_GC_Line_Border( pFirstLn, &aPara );
+ }
+
+ // Update Layout
+ aFndBox.MakeFrames( rTable );
+
+ return true;
+}
+
+namespace {
+
+// Use the PtrArray's ForEach method
+struct SetAFormatTabPara
+{
+ SwTableAutoFormat& rTableFormat;
+ SwUndoTableAutoFormat* pUndo;
+ sal_uInt16 nEndBox, nCurBox;
+ sal_uInt8 nAFormatLine, nAFormatBox;
+ bool bSingleRowTable;
+
+ explicit SetAFormatTabPara( const SwTableAutoFormat& rNew )
+ : rTableFormat( const_cast<SwTableAutoFormat&>(rNew) ), pUndo( nullptr ),
+ nEndBox( 0 ), nCurBox( 0 ), nAFormatLine( 0 ), nAFormatBox( 0 ), bSingleRowTable(false)
+ {}
+};
+
+}
+
+// Forward declare so that the Lines and Boxes can use recursion
+static bool lcl_SetAFormatBox(FndBox_ &, SetAFormatTabPara *pSetPara, bool bResetDirect);
+static bool lcl_SetAFormatLine(FndLine_ &, SetAFormatTabPara *pPara, bool bResetDirect);
+
+static bool lcl_SetAFormatLine(FndLine_ & rLine, SetAFormatTabPara *pPara, bool bResetDirect)
+{
+ for (auto const& it : rLine.GetBoxes())
+ {
+ lcl_SetAFormatBox(*it, pPara, bResetDirect);
+ }
+ return true;
+}
+
+static bool lcl_SetAFormatBox(FndBox_ & rBox, SetAFormatTabPara *pSetPara, bool bResetDirect)
+{
+ if (!rBox.GetUpper()->GetUpper()) // Box on first level?
+ {
+ if( !pSetPara->nCurBox )
+ pSetPara->nAFormatBox = 0;
+ else if( pSetPara->nCurBox == pSetPara->nEndBox )
+ pSetPara->nAFormatBox = 3;
+ else //Even column(1) or Odd column(2)
+ pSetPara->nAFormatBox = static_cast<sal_uInt8>(1 + ((pSetPara->nCurBox-1) & 1));
+ }
+
+ if (rBox.GetBox()->GetSttNd())
+ {
+ SwTableBox* pSetBox = rBox.GetBox();
+ if (!pSetBox->HasDirectFormatting() || bResetDirect)
+ {
+ if (bResetDirect)
+ pSetBox->SetDirectFormatting(false);
+
+ SwDoc* pDoc = pSetBox->GetFrameFormat()->GetDoc();
+ SfxItemSetFixed<RES_CHRATR_BEGIN, RES_PARATR_LIST_END-1> aCharSet(pDoc->GetAttrPool());
+ SfxItemSet aBoxSet(pDoc->GetAttrPool(), aTableBoxSetRange);
+ sal_uInt8 nPos = pSetPara->nAFormatLine * 4 + pSetPara->nAFormatBox;
+ const bool bSingleRowTable = pSetPara->bSingleRowTable;
+ const bool bSingleColTable = pSetPara->nEndBox == 0;
+ pSetPara->rTableFormat.UpdateToSet(nPos, bSingleRowTable, bSingleColTable, aCharSet, SwTableAutoFormatUpdateFlags::Char, nullptr);
+ pSetPara->rTableFormat.UpdateToSet(nPos, bSingleRowTable, bSingleColTable, aBoxSet, SwTableAutoFormatUpdateFlags::Box, pDoc->GetNumberFormatter());
+
+ if (aCharSet.Count())
+ {
+ SwNodeOffset nSttNd = pSetBox->GetSttIdx()+1;
+ SwNodeOffset nEndNd = pSetBox->GetSttNd()->EndOfSectionIndex();
+ for (; nSttNd < nEndNd; ++nSttNd)
+ {
+ SwContentNode* pNd = pDoc->GetNodes()[ nSttNd ]->GetContentNode();
+ if (pNd)
+ pNd->SetAttr(aCharSet);
+ }
+ }
+
+ if (aBoxSet.Count())
+ {
+ if (pSetPara->pUndo && SfxItemState::SET == aBoxSet.GetItemState(RES_BOXATR_FORMAT))
+ pSetPara->pUndo->SaveBoxContent( *pSetBox );
+
+ pSetBox->ClaimFrameFormat()->SetFormatAttr(aBoxSet);
+ }
+ }
+ }
+ else
+ {
+ // Not sure how this situation can occur, but apparently we have some kind of table in table.
+ // I am guessing at how to best handle singlerow in this situation.
+ const bool bOrigSingleRowTable = pSetPara->bSingleRowTable;
+ pSetPara->bSingleRowTable = rBox.GetLines().size() == 1;
+ for (auto const& rpFndLine : rBox.GetLines())
+ {
+ lcl_SetAFormatLine(*rpFndLine, pSetPara, bResetDirect);
+ }
+ pSetPara->bSingleRowTable = bOrigSingleRowTable;
+ }
+
+ if (!rBox.GetUpper()->GetUpper()) // a BaseLine
+ ++pSetPara->nCurBox;
+ return true;
+}
+
+bool SwDoc::SetTableAutoFormat(const SwSelBoxes& rBoxes, const SwTableAutoFormat& rNew, bool bResetDirect, bool const isSetStyleName)
+{
+ OSL_ENSURE( !rBoxes.empty(), "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( pTableNd->GetTable().GetTabLines(), &aPara );
+ }
+ if( aFndBox.GetLines().empty() )
+ return false;
+
+ SwTable &table = pTableNd->GetTable();
+ table.SetHTMLTableLayout(std::shared_ptr<SwHTMLTableLayout>());
+
+ FndBox_* pFndBox = &aFndBox;
+ while( 1 == pFndBox->GetLines().size() &&
+ 1 == pFndBox->GetLines().front()->GetBoxes().size())
+ {
+ pFndBox = pFndBox->GetLines().front()->GetBoxes()[0].get();
+ }
+
+ if( pFndBox->GetLines().empty() ) // One too far? (only one sel. Box)
+ pFndBox = pFndBox->GetUpper()->GetUpper();
+
+ // Disable Undo, but first store parameters
+ SwUndoTableAutoFormat* pUndo = nullptr;
+ bool const bUndo(GetIDocumentUndoRedo().DoesUndo());
+ if (bUndo)
+ {
+ pUndo = new SwUndoTableAutoFormat( *pTableNd, rNew );
+ GetIDocumentUndoRedo().AppendUndo(std::unique_ptr<SwUndo>(pUndo));
+ GetIDocumentUndoRedo().DoUndo(false);
+ }
+
+ if (isSetStyleName)
+ { // tdf#98226 do this here where undo can record it
+ pTableNd->GetTable().SetTableStyleName(rNew.GetName());
+ }
+
+ rNew.RestoreTableProperties(table);
+
+ SetAFormatTabPara aPara( rNew );
+ FndLines_t& rFLns = pFndBox->GetLines();
+ aPara.bSingleRowTable = rFLns.size() == 1;
+
+ for (FndLines_t::size_type n = 0; n < rFLns.size(); ++n)
+ {
+ FndLine_* pLine = rFLns[n].get();
+
+ // Set Upper to 0 (thus simulate BaseLine)
+ FndBox_* pSaveBox = pLine->GetUpper();
+ pLine->SetUpper( nullptr );
+
+ if( !n )
+ aPara.nAFormatLine = 0;
+ else if (static_cast<size_t>(n+1) == rFLns.size())
+ aPara.nAFormatLine = 3;
+ else
+ aPara.nAFormatLine = static_cast<sal_uInt8>(1 + ((n-1) & 1 ));
+
+ aPara.nAFormatBox = 0;
+ aPara.nCurBox = 0;
+ aPara.nEndBox = pLine->GetBoxes().size()-1;
+ aPara.pUndo = pUndo;
+ for (auto const& it : pLine->GetBoxes())
+ {
+ lcl_SetAFormatBox(*it, &aPara, bResetDirect);
+ }
+
+ pLine->SetUpper( pSaveBox );
+ }
+
+ if( pUndo )
+ {
+ GetIDocumentUndoRedo().DoUndo(bUndo);
+ }
+
+ getIDocumentState().SetModified();
+ getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, SwNodeOffset(0) );
+
+ return true;
+}
+
+/**
+ * Find out who has the Attributes
+ */
+bool SwDoc::GetTableAutoFormat( const SwSelBoxes& rBoxes, SwTableAutoFormat& rGet )
+{
+ OSL_ENSURE( !rBoxes.empty(), "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( pTableNd->GetTable().GetTabLines(), &aPara );
+ }
+ if( aFndBox.GetLines().empty() )
+ return false;
+
+ // Store table properties
+ SwTable &table = pTableNd->GetTable();
+ rGet.StoreTableProperties(table);
+
+ FndBox_* pFndBox = &aFndBox;
+ while( 1 == pFndBox->GetLines().size() &&
+ 1 == pFndBox->GetLines().front()->GetBoxes().size())
+ {
+ pFndBox = pFndBox->GetLines().front()->GetBoxes()[0].get();
+ }
+
+ if( pFndBox->GetLines().empty() ) // One too far? (only one sel. Box)
+ pFndBox = pFndBox->GetUpper()->GetUpper();
+
+ FndLines_t& rFLns = pFndBox->GetLines();
+
+ sal_uInt16 aLnArr[4];
+ aLnArr[0] = 0;
+ aLnArr[1] = 1 < rFLns.size() ? 1 : 0;
+ aLnArr[2] = 2 < rFLns.size() ? 2 : aLnArr[1];
+ aLnArr[3] = rFLns.size() - 1;
+
+ for( sal_uInt8 nLine = 0; nLine < 4; ++nLine )
+ {
+ FndLine_& rLine = *rFLns[ aLnArr[ nLine ] ];
+
+ sal_uInt16 aBoxArr[4];
+ aBoxArr[0] = 0;
+ aBoxArr[1] = 1 < rLine.GetBoxes().size() ? 1 : 0;
+ aBoxArr[2] = 2 < rLine.GetBoxes().size() ? 2 : aBoxArr[1];
+ aBoxArr[3] = rLine.GetBoxes().size() - 1;
+
+ for( sal_uInt8 nBox = 0; nBox < 4; ++nBox )
+ {
+ SwTableBox* pFBox = rLine.GetBoxes()[ aBoxArr[ nBox ] ]->GetBox();
+ // Always apply to the first ones
+ while( !pFBox->GetSttNd() )
+ pFBox = pFBox->GetTabLines()[0]->GetTabBoxes()[0];
+
+ sal_uInt8 nPos = nLine * 4 + nBox;
+ SwNodeIndex aIdx( *pFBox->GetSttNd(), 1 );
+ SwContentNode* pCNd = aIdx.GetNode().GetContentNode();
+ if( !pCNd )
+ pCNd = GetNodes().GoNext( &aIdx );
+
+ if( pCNd )
+ rGet.UpdateFromSet( nPos, pCNd->GetSwAttrSet(),
+ SwTableAutoFormatUpdateFlags::Char, nullptr );
+ rGet.UpdateFromSet( nPos, pFBox->GetFrameFormat()->GetAttrSet(),
+ SwTableAutoFormatUpdateFlags::Box,
+ GetNumberFormatter() );
+ }
+ }
+
+ return true;
+}
+
+SwTableAutoFormatTable& SwDoc::GetTableStyles()
+{
+ if (!m_pTableStyles)
+ {
+ m_pTableStyles.reset(new SwTableAutoFormatTable);
+ m_pTableStyles->Load();
+ }
+ return *m_pTableStyles;
+}
+
+OUString SwDoc::GetUniqueTableName() const
+{
+ if( IsInMailMerge())
+ {
+ OUString newName = "MailMergeTable"
+ + OStringToOUString( DateTimeToOString( DateTime( DateTime::SYSTEM )), RTL_TEXTENCODING_ASCII_US )
+ + OUString::number( mpTableFrameFormatTable->size() + 1 );
+ return newName;
+ }
+
+ const OUString aName(SwResId(STR_TABLE_DEFNAME));
+
+ const size_t nFlagSize = ( mpTableFrameFormatTable->size() / 8 ) + 2;
+
+ std::unique_ptr<sal_uInt8[]> pSetFlags( new sal_uInt8[ nFlagSize ] );
+ memset( pSetFlags.get(), 0, nFlagSize );
+
+ for( size_t n = 0; n < mpTableFrameFormatTable->size(); ++n )
+ {
+ const SwFrameFormat* pFormat = (*mpTableFrameFormatTable)[ n ];
+ if( !pFormat->IsDefault() && IsUsed( *pFormat ) &&
+ pFormat->GetName().startsWith( aName ) )
+ {
+ // Get number and set the Flag
+ const sal_Int32 nNmLen = aName.getLength();
+ size_t nNum = o3tl::toInt32(pFormat->GetName().subView( nNmLen ));
+ if( nNum-- && nNum < mpTableFrameFormatTable->size() )
+ pSetFlags[ nNum / 8 ] |= (0x01 << ( nNum & 0x07 ));
+ }
+ }
+
+ // All numbers are flagged properly, thus calculate the right number
+ size_t nNum = mpTableFrameFormatTable->size();
+ for( size_t n = 0; n < nFlagSize; ++n )
+ {
+ auto nTmp = pSetFlags[ n ];
+ if( nTmp != 0xFF )
+ {
+ // Calculate the number
+ nNum = n * 8;
+ while( nTmp & 1 )
+ {
+ ++nNum;
+ nTmp >>= 1;
+ }
+ break;
+ }
+ }
+
+ return aName + OUString::number( ++nNum );
+}
+
+SwTableFormat* SwDoc::FindTableFormatByName( const OUString& rName, bool bAll ) const
+{
+ const SwFormat* pRet = nullptr;
+ if( bAll )
+ pRet = mpTableFrameFormatTable->FindFormatByName( rName );
+ else
+ {
+ auto [it, itEnd] = mpTableFrameFormatTable->findRangeByName(rName);
+ // Only the ones set in the Doc
+ for( ; it != itEnd; ++it )
+ {
+ const SwFrameFormat* pFormat = *it;
+ if( !pFormat->IsDefault() && IsUsed( *pFormat ) &&
+ pFormat->GetName() == rName )
+ {
+ pRet = pFormat;
+ break;
+ }
+ }
+ }
+ return const_cast<SwTableFormat*>(static_cast<const SwTableFormat*>(pRet));
+}
+
+void SwDoc::SetColRowWidthHeight( SwTableBox& rCurrentBox, TableChgWidthHeightType eType,
+ SwTwips nAbsDiff, SwTwips nRelDiff )
+{
+ SwTableNode* pTableNd = const_cast<SwTableNode*>(rCurrentBox.GetSttNd()->FindTableNode());
+ std::unique_ptr<SwUndo> pUndo;
+
+ SwTableFormulaUpdate aMsgHint( &pTableNd->GetTable() );
+ aMsgHint.m_eFlags = TBL_BOXPTR;
+ getIDocumentFieldsAccess().UpdateTableFields( &aMsgHint );
+
+ bool const bUndo(GetIDocumentUndoRedo().DoesUndo());
+ bool bRet = false;
+ switch( extractPosition(eType) )
+ {
+ case TableChgWidthHeightType::ColLeft:
+ case TableChgWidthHeightType::ColRight:
+ case TableChgWidthHeightType::CellLeft:
+ case TableChgWidthHeightType::CellRight:
+ {
+ bRet = pTableNd->GetTable().SetColWidth( rCurrentBox,
+ eType, nAbsDiff, nRelDiff,
+ bUndo ? &pUndo : nullptr );
+ }
+ break;
+ case TableChgWidthHeightType::RowBottom:
+ case TableChgWidthHeightType::CellTop:
+ case TableChgWidthHeightType::CellBottom:
+ bRet = pTableNd->GetTable().SetRowHeight( rCurrentBox,
+ eType, nAbsDiff, nRelDiff,
+ bUndo ? &pUndo : nullptr );
+ break;
+ default: break;
+ }
+
+ GetIDocumentUndoRedo().DoUndo(bUndo); // SetColWidth can turn it off
+ if( pUndo )
+ {
+ GetIDocumentUndoRedo().AppendUndo( std::move(pUndo) );
+ }
+
+ if( bRet )
+ {
+ getIDocumentState().SetModified();
+ }
+}
+
+bool SwDoc::IsNumberFormat( const OUString& rString, sal_uInt32& F_Index, double& fOutNumber )
+{
+ if( rString.getLength() > 308 ) // optimization matches svl:IsNumberFormat arbitrary value
+ return false;
+
+ // remove any comment anchor marks
+ OUStringBuffer sStringBuffer(rString);
+ sal_Int32 nCommentPosition = sStringBuffer.indexOf( CH_TXTATR_INWORD );
+ while( nCommentPosition != -1 )
+ {
+ sStringBuffer.remove( nCommentPosition, 1 );
+ nCommentPosition = sStringBuffer.indexOf( CH_TXTATR_INWORD, nCommentPosition );
+ }
+
+ return GetNumberFormatter()->IsNumberFormat( sStringBuffer.makeStringAndClear(), F_Index, fOutNumber );
+}
+
+void SwDoc::ChkBoxNumFormat( SwTableBox& rBox, bool bCallUpdate )
+{
+ // Optimization: If the Box says it's Text, it remains Text
+ const SwTableBoxNumFormat* pNumFormatItem = rBox.GetFrameFormat()->GetItemIfSet( RES_BOXATR_FORMAT,
+ false );
+ if( pNumFormatItem && GetNumberFormatter()->IsTextFormat(pNumFormatItem->GetValue()) )
+ return ;
+
+ std::unique_ptr<SwUndoTableNumFormat> pUndo;
+
+ bool bIsEmptyTextNd;
+ bool bChgd = true;
+ sal_uInt32 nFormatIdx;
+ double fNumber;
+ if( rBox.HasNumContent( fNumber, nFormatIdx, bIsEmptyTextNd ) )
+ {
+ if( !rBox.IsNumberChanged() )
+ bChgd = false;
+ else
+ {
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().StartUndo( SwUndoId::TABLE_AUTOFMT, nullptr );
+ pUndo.reset(new SwUndoTableNumFormat( rBox ));
+ pUndo->SetNumFormat( nFormatIdx, fNumber );
+ }
+
+ SwTableBoxFormat* pBoxFormat = static_cast<SwTableBoxFormat*>(rBox.GetFrameFormat());
+ SfxItemSetFixed<RES_BOXATR_FORMAT, RES_BOXATR_VALUE> aBoxSet( GetAttrPool() );
+
+ bool bLockModify = true;
+ bool bSetNumberFormat = IsInsTableFormatNum();
+ const bool bForceNumberFormat = IsInsTableFormatNum() && IsInsTableChangeNumFormat();
+
+ // if the user forced a number format in this cell previously,
+ // keep it, unless the user set that she wants the full number
+ // format recognition
+ if( pNumFormatItem && !bForceNumberFormat )
+ {
+ sal_uLong nOldNumFormat = pNumFormatItem->GetValue();
+ SvNumberFormatter* pNumFormatr = GetNumberFormatter();
+
+ SvNumFormatType nFormatType = pNumFormatr->GetType( nFormatIdx );
+ if( nFormatType == pNumFormatr->GetType( nOldNumFormat ) || SvNumFormatType::NUMBER == nFormatType )
+ {
+ // Current and specified NumFormat match
+ // -> keep old Format
+ nFormatIdx = nOldNumFormat;
+ bSetNumberFormat = true;
+ }
+ else
+ {
+ // Current and specified NumFormat do not match
+ // -> insert as Text
+ bLockModify = bSetNumberFormat = false;
+ }
+ }
+
+ if( bSetNumberFormat || bForceNumberFormat )
+ {
+ pBoxFormat = static_cast<SwTableBoxFormat*>(rBox.ClaimFrameFormat());
+
+ aBoxSet.Put( SwTableBoxValue( fNumber ));
+ aBoxSet.Put( SwTableBoxNumFormat( nFormatIdx ));
+ }
+
+ // It's not enough to only reset the Formula.
+ // Make sure that the Text is formatted accordingly
+ if( !bSetNumberFormat && !bIsEmptyTextNd && pNumFormatItem )
+ {
+ // Just resetting Attributes is not enough
+ // Make sure that the Text is formatted accordingly
+ pBoxFormat->SetFormatAttr( *GetDfltAttr( RES_BOXATR_FORMAT ));
+ }
+
+ if( bLockModify ) pBoxFormat->LockModify();
+ pBoxFormat->ResetFormatAttr( RES_BOXATR_FORMAT, RES_BOXATR_VALUE );
+ if( bLockModify ) pBoxFormat->UnlockModify();
+
+ if( bSetNumberFormat )
+ pBoxFormat->SetFormatAttr( aBoxSet );
+ }
+ }
+ else
+ {
+ // It's not a number
+ SwTableBoxFormat* pBoxFormat = static_cast<SwTableBoxFormat*>(rBox.GetFrameFormat());
+ if( SfxItemState::SET == pBoxFormat->GetItemState( RES_BOXATR_FORMAT, false ) ||
+ SfxItemState::SET == pBoxFormat->GetItemState( RES_BOXATR_VALUE, false ) )
+ {
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().StartUndo( SwUndoId::TABLE_AUTOFMT, nullptr );
+ pUndo.reset(new SwUndoTableNumFormat( rBox ));
+ }
+
+ pBoxFormat = static_cast<SwTableBoxFormat*>(rBox.ClaimFrameFormat());
+
+ // Remove all number formats
+ sal_uInt16 nWhich1 = RES_BOXATR_FORMULA;
+ if( !bIsEmptyTextNd )
+ {
+ nWhich1 = RES_BOXATR_FORMAT;
+
+ // Just resetting Attributes is not enough
+ // Make sure that the Text is formatted accordingly
+ pBoxFormat->SetFormatAttr( *GetDfltAttr( nWhich1 ));
+ }
+ pBoxFormat->ResetFormatAttr( nWhich1, RES_BOXATR_VALUE );
+ }
+ else
+ bChgd = false;
+ }
+
+ if( !bChgd )
+ return;
+
+ if( pUndo )
+ {
+ pUndo->SetBox( rBox );
+ GetIDocumentUndoRedo().AppendUndo(std::move(pUndo));
+ GetIDocumentUndoRedo().EndUndo( SwUndoId::END, nullptr );
+ }
+
+ const SwTableNode* pTableNd = rBox.GetSttNd()->FindTableNode();
+ if( bCallUpdate )
+ {
+ SwTableFormulaUpdate aTableUpdate( &pTableNd->GetTable() );
+ getIDocumentFieldsAccess().UpdateTableFields( &aTableUpdate );
+
+ // TL_CHART2: update charts (when cursor leaves cell and
+ // automatic update is enabled)
+ if (AUTOUPD_FIELD_AND_CHARTS == GetDocumentSettingManager().getFieldUpdateFlags(true))
+ pTableNd->GetTable().UpdateCharts();
+ }
+ getIDocumentState().SetModified();
+}
+
+void SwDoc::SetTableBoxFormulaAttrs( SwTableBox& rBox, const SfxItemSet& rSet )
+{
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().AppendUndo( std::make_unique<SwUndoTableNumFormat>(rBox, &rSet) );
+ }
+
+ SwFrameFormat* pBoxFormat = rBox.ClaimFrameFormat();
+ if( SfxItemState::SET == rSet.GetItemState( RES_BOXATR_FORMULA ))
+ {
+ pBoxFormat->LockModify();
+ pBoxFormat->ResetFormatAttr( RES_BOXATR_VALUE );
+ pBoxFormat->UnlockModify();
+ }
+ else if( SfxItemState::SET == rSet.GetItemState( RES_BOXATR_VALUE ))
+ {
+ pBoxFormat->LockModify();
+ pBoxFormat->ResetFormatAttr( RES_BOXATR_FORMULA );
+ pBoxFormat->UnlockModify();
+ }
+ pBoxFormat->SetFormatAttr( rSet );
+ getIDocumentState().SetModified();
+}
+
+void SwDoc::ClearLineNumAttrs( SwPosition const & rPos )
+{
+ SwPaM aPam(rPos);
+ aPam.Move(fnMoveBackward);
+ SwContentNode *pNode = aPam.GetContentNode();
+ if ( nullptr == pNode )
+ return ;
+ if( !pNode->IsTextNode() )
+ return;
+
+ SwTextNode * pTextNode = pNode->GetTextNode();
+ if (!(pTextNode && pTextNode->IsNumbered()
+ && pTextNode->GetText().isEmpty()))
+ return;
+
+ SfxItemSetFixed<RES_PARATR_BEGIN, RES_PARATR_END - 1>
+ rSet( pTextNode->GetDoc().GetAttrPool() );
+ pTextNode->SwContentNode::GetAttr( rSet );
+ const SfxStringItem* pFormatItem = rSet.GetItemIfSet( RES_PARATR_NUMRULE, false );
+ if ( !pFormatItem )
+ return;
+
+ SwUndoDelNum * pUndo;
+ if( GetIDocumentUndoRedo().DoesUndo() )
+ {
+ GetIDocumentUndoRedo().ClearRedo();
+ pUndo = new SwUndoDelNum( aPam );
+ GetIDocumentUndoRedo().AppendUndo( std::unique_ptr<SwUndo>(pUndo) );
+ }
+ else
+ pUndo = nullptr;
+ SwRegHistory aRegH( pUndo ? pUndo->GetHistory() : nullptr );
+ aRegH.RegisterInModify( pTextNode , *pTextNode );
+ if ( pUndo )
+ pUndo->AddNode( *pTextNode );
+ std::unique_ptr<SfxStringItem> pNewItem(pFormatItem->Clone());
+ pNewItem->SetValue(OUString());
+ rSet.Put( std::move(pNewItem) );
+ pTextNode->SetAttr( rSet );
+}
+
+void SwDoc::ClearBoxNumAttrs( const SwNodeIndex& rNode )
+{
+ SwStartNode* pSttNd = rNode.GetNode().FindSttNodeByType( SwTableBoxStartNode );
+ if( nullptr == pSttNd ||
+ SwNodeOffset(2) != pSttNd->EndOfSectionIndex() - pSttNd->GetIndex())
+ return;
+
+ SwTableBox* pBox = pSttNd->FindTableNode()->GetTable().
+ GetTableBox( pSttNd->GetIndex() );
+
+ const SfxItemSet& rSet = pBox->GetFrameFormat()->GetAttrSet();
+ const SwTableBoxNumFormat* pFormatItem = rSet.GetItemIfSet( RES_BOXATR_FORMAT, false );
+ if( !pFormatItem ||
+ SfxItemState::SET == rSet.GetItemState( RES_BOXATR_FORMULA, false ) ||
+ SfxItemState::SET == rSet.GetItemState( RES_BOXATR_VALUE, false ))
+ return;
+
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().AppendUndo(std::make_unique<SwUndoTableNumFormat>(*pBox));
+ }
+
+ SwFrameFormat* pBoxFormat = pBox->ClaimFrameFormat();
+
+ // Keep TextFormats!
+ sal_uInt16 nWhich1 = RES_BOXATR_FORMAT;
+ if( pFormatItem && GetNumberFormatter()->IsTextFormat(
+ pFormatItem->GetValue() ))
+ nWhich1 = RES_BOXATR_FORMULA;
+ else
+ // Just resetting Attributes is not enough
+ // Make sure that the Text is formatted accordingly
+ pBoxFormat->SetFormatAttr( *GetDfltAttr( RES_BOXATR_FORMAT ));
+
+ pBoxFormat->ResetFormatAttr( nWhich1, RES_BOXATR_VALUE );
+ getIDocumentState().SetModified();
+}
+
+/**
+ * Copies a Table from the same or another Doc into itself
+ * We create a new Table or an existing one is filled with the Content.
+ * We either fill in the Content from a certain Box or a certain TableSelection
+ *
+ * This method is called by edglss.cxx/fecopy.cxx
+ */
+bool SwDoc::InsCopyOfTable( SwPosition& rInsPos, const SwSelBoxes& rBoxes,
+ const SwTable* pCpyTable, bool bCpyName, bool bCorrPos, const OUString& rStyleName )
+{
+ bool bRet;
+
+ const SwTableNode* pSrcTableNd = pCpyTable
+ ? pCpyTable->GetTableNode()
+ : rBoxes[ 0 ]->GetSttNd()->FindTableNode();
+
+ SwTableNode * pInsTableNd = rInsPos.nNode.GetNode().FindTableNode();
+
+ bool const bUndo( GetIDocumentUndoRedo().DoesUndo() );
+ if( !pCpyTable && !pInsTableNd )
+ {
+ std::unique_ptr<SwUndoCpyTable> pUndo;
+ if (bUndo)
+ {
+ GetIDocumentUndoRedo().ClearRedo();
+ pUndo.reset(new SwUndoCpyTable(*this));
+ }
+
+ {
+ ::sw::UndoGuard const undoGuard(GetIDocumentUndoRedo());
+ bRet = pSrcTableNd->GetTable().MakeCopy( *this, rInsPos, rBoxes,
+ bCpyName, rStyleName );
+ }
+
+ if( pUndo && bRet )
+ {
+ pInsTableNd = GetNodes()[ rInsPos.nNode.GetIndex() - 1 ]->FindTableNode();
+
+ pUndo->SetTableSttIdx( pInsTableNd->GetIndex() );
+ GetIDocumentUndoRedo().AppendUndo( std::move(pUndo) );
+ }
+ }
+ else
+ {
+ RedlineFlags eOld = getIDocumentRedlineAccess().GetRedlineFlags();
+ if( getIDocumentRedlineAccess().IsRedlineOn() )
+ getIDocumentRedlineAccess().SetRedlineFlags( RedlineFlags::On |
+ RedlineFlags::ShowInsert |
+ RedlineFlags::ShowDelete );
+
+ std::unique_ptr<SwUndoTableCpyTable> pUndo;
+ if (bUndo)
+ {
+ GetIDocumentUndoRedo().ClearRedo();
+ pUndo.reset(new SwUndoTableCpyTable(*this));
+ GetIDocumentUndoRedo().DoUndo(false);
+ }
+
+ rtl::Reference<SwDoc> xCpyDoc(&const_cast<SwDoc&>(pSrcTableNd->GetDoc()));
+ bool bDelCpyDoc = xCpyDoc == this;
+
+ if( bDelCpyDoc )
+ {
+ // Copy the Table into a temporary Doc
+ xCpyDoc = new SwDoc;
+
+ SwPosition aPos( SwNodeIndex( xCpyDoc->GetNodes().GetEndOfContent() ));
+ if( !pSrcTableNd->GetTable().MakeCopy( *xCpyDoc, aPos, rBoxes, true ))
+ {
+ xCpyDoc.clear();
+
+ if( pUndo )
+ {
+ GetIDocumentUndoRedo().DoUndo(bUndo);
+ }
+ return false;
+ }
+ aPos.nNode -= SwNodeOffset(1); // Set to the Table's EndNode
+ pSrcTableNd = aPos.nNode.GetNode().FindTableNode();
+ }
+
+ const SwStartNode* pSttNd = rInsPos.nNode.GetNode().FindTableBoxStartNode();
+
+ rInsPos.nContent.Assign( nullptr, 0 );
+
+ // no complex into complex, but copy into or from new model is welcome
+ if( ( !pSrcTableNd->GetTable().IsTableComplex() || pInsTableNd->GetTable().IsNewModel() )
+ && ( bDelCpyDoc || !rBoxes.empty() ) )
+ {
+ // Copy the Table "relatively"
+ const SwSelBoxes* pBoxes;
+ SwSelBoxes aBoxes;
+
+ if( bDelCpyDoc )
+ {
+ SwTableBox* pBox = pInsTableNd->GetTable().GetTableBox(
+ pSttNd->GetIndex() );
+ OSL_ENSURE( pBox, "Box is not in this Table" );
+ aBoxes.insert( pBox );
+ pBoxes = &aBoxes;
+ }
+ else
+ pBoxes = &rBoxes;
+
+ // Copy Table to the selected Lines
+ bRet = pInsTableNd->GetTable().InsTable( pSrcTableNd->GetTable(),
+ *pBoxes, pUndo.get() );
+ }
+ else
+ {
+ SwNodeIndex aNdIdx( *pSttNd, 1 );
+ bRet = pInsTableNd->GetTable().InsTable( pSrcTableNd->GetTable(),
+ aNdIdx, pUndo.get() );
+ }
+
+ xCpyDoc.clear();
+
+ if( pUndo )
+ {
+ // If the Table could not be copied, delete the Undo object
+ GetIDocumentUndoRedo().DoUndo(bUndo);
+ if( bRet || !pUndo->IsEmpty() )
+ {
+ GetIDocumentUndoRedo().AppendUndo(std::move(pUndo));
+ }
+ }
+
+ if( bCorrPos )
+ {
+ rInsPos.nNode = *pSttNd;
+ rInsPos.nContent.Assign( GetNodes().GoNext( &rInsPos.nNode ), 0 );
+ }
+ getIDocumentRedlineAccess().SetRedlineFlags( eOld );
+ }
+
+ if( bRet )
+ {
+ getIDocumentState().SetModified();
+ getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, SwNodeOffset(0) );
+ }
+ return bRet;
+}
+
+bool SwDoc::UnProtectTableCells( SwTable& rTable )
+{
+ bool bChgd = false;
+ std::unique_ptr<SwUndoAttrTable> pUndo;
+ if (GetIDocumentUndoRedo().DoesUndo())
+ pUndo.reset(new SwUndoAttrTable( *rTable.GetTableNode() ));
+
+ SwTableSortBoxes& rSrtBox = rTable.GetTabSortBoxes();
+ for (size_t i = rSrtBox.size(); i; )
+ {
+ SwFrameFormat *pBoxFormat = rSrtBox[ --i ]->GetFrameFormat();
+ if( pBoxFormat->GetProtect().IsContentProtected() )
+ {
+ pBoxFormat->ResetFormatAttr( RES_PROTECT );
+ bChgd = true;
+ }
+ }
+
+ if( pUndo && bChgd )
+ GetIDocumentUndoRedo().AppendUndo( std::move(pUndo) );
+ return bChgd;
+}
+
+void SwDoc::UnProtectCells( const OUString& rName )
+{
+ SwTableFormat* pFormat = FindTableFormatByName( rName );
+ if( pFormat )
+ {
+ bool bChgd = UnProtectTableCells( *SwTable::FindTable( pFormat ) );
+ if( bChgd )
+ getIDocumentState().SetModified();
+ }
+}
+
+bool SwDoc::UnProtectCells( const SwSelBoxes& rBoxes )
+{
+ bool bChgd = false;
+ if( !rBoxes.empty() )
+ {
+ std::unique_ptr<SwUndoAttrTable> pUndo;
+ if (GetIDocumentUndoRedo().DoesUndo())
+ pUndo.reset(new SwUndoAttrTable( *rBoxes[0]->GetSttNd()->FindTableNode() ));
+
+ std::map<SwFrameFormat*, SwTableBoxFormat*> aFormatsMap;
+ for (size_t i = rBoxes.size(); i; )
+ {
+ SwTableBox* pBox = rBoxes[ --i ];
+ SwFrameFormat* pBoxFormat = pBox->GetFrameFormat();
+ if( pBoxFormat->GetProtect().IsContentProtected() )
+ {
+ std::map<SwFrameFormat*, SwTableBoxFormat*>::const_iterator const it =
+ aFormatsMap.find(pBoxFormat);
+ if (aFormatsMap.end() != it)
+ pBox->ChgFrameFormat(it->second);
+ else
+ {
+ SwTableBoxFormat *const pNewBoxFormat(
+ static_cast<SwTableBoxFormat*>(pBox->ClaimFrameFormat()));
+ pNewBoxFormat->ResetFormatAttr( RES_PROTECT );
+ aFormatsMap.insert(std::make_pair(pBoxFormat, pNewBoxFormat));
+ }
+ bChgd = true;
+ }
+ }
+
+ if( pUndo && bChgd )
+ GetIDocumentUndoRedo().AppendUndo( std::move(pUndo) );
+ }
+ return bChgd;
+}
+
+void SwDoc::UnProtectTables( const SwPaM& rPam )
+{
+ GetIDocumentUndoRedo().StartUndo(SwUndoId::EMPTY, nullptr);
+
+ bool bChgd = false, bHasSel = rPam.HasMark() ||
+ rPam.GetNext() != &rPam;
+ SwFrameFormats& rFormats = *GetTableFrameFormats();
+ SwTable* pTable;
+ const SwTableNode* pTableNd;
+ for( auto n = rFormats.size(); n ; )
+ if( nullptr != (pTable = SwTable::FindTable( rFormats[ --n ] )) &&
+ nullptr != (pTableNd = pTable->GetTableNode() ) &&
+ pTableNd->GetNodes().IsDocNodes() )
+ {
+ SwNodeOffset nTableIdx = pTableNd->GetIndex();
+
+ // Check whether the Table is within the Selection
+ if( bHasSel )
+ {
+ bool bFound = false;
+ SwPaM* pTmp = const_cast<SwPaM*>(&rPam);
+ do {
+ const SwPosition *pStt = pTmp->Start(),
+ *pEnd = pTmp->End();
+ bFound = pStt->nNode.GetIndex() < nTableIdx &&
+ nTableIdx < pEnd->nNode.GetIndex();
+
+ } while( !bFound && &rPam != ( pTmp = pTmp->GetNext() ) );
+ if( !bFound )
+ continue; // Continue searching
+ }
+
+ // Lift the protection
+ bChgd |= UnProtectTableCells( *pTable );
+ }
+
+ GetIDocumentUndoRedo().EndUndo(SwUndoId::EMPTY, nullptr);
+ if( bChgd )
+ getIDocumentState().SetModified();
+}
+
+bool SwDoc::HasTableAnyProtection( const SwPosition* pPos,
+ const OUString* pTableName,
+ bool* pFullTableProtection )
+{
+ bool bHasProtection = false;
+ SwTable* pTable = nullptr;
+ if( pTableName )
+ pTable = SwTable::FindTable( FindTableFormatByName( *pTableName ) );
+ else if( pPos )
+ {
+ SwTableNode* pTableNd = pPos->nNode.GetNode().FindTableNode();
+ if( pTableNd )
+ pTable = &pTableNd->GetTable();
+ }
+
+ if( pTable )
+ {
+ SwTableSortBoxes& rSrtBox = pTable->GetTabSortBoxes();
+ for (size_t i = rSrtBox.size(); i; )
+ {
+ SwFrameFormat *pBoxFormat = rSrtBox[ --i ]->GetFrameFormat();
+ if( pBoxFormat->GetProtect().IsContentProtected() )
+ {
+ if( !bHasProtection )
+ {
+ bHasProtection = true;
+ if( !pFullTableProtection )
+ break;
+ *pFullTableProtection = true;
+ }
+ }
+ else if( bHasProtection && pFullTableProtection )
+ {
+ *pFullTableProtection = false;
+ break;
+ }
+ }
+ }
+ return bHasProtection;
+}
+
+SwTableAutoFormat* SwDoc::MakeTableStyle(const OUString& rName, bool bBroadcast)
+{
+ SwTableAutoFormat aTableFormat(rName);
+ GetTableStyles().AddAutoFormat(aTableFormat);
+ SwTableAutoFormat* pTableFormat = GetTableStyles().FindAutoFormat(rName);
+
+ getIDocumentState().SetModified();
+
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().AppendUndo(
+ std::make_unique<SwUndoTableStyleMake>(rName, *this));
+ }
+
+ if (bBroadcast)
+ BroadcastStyleOperation(rName, SfxStyleFamily::Table, SfxHintId::StyleSheetCreated);
+
+ return pTableFormat;
+}
+
+std::unique_ptr<SwTableAutoFormat> SwDoc::DelTableStyle(const OUString& rName, bool bBroadcast)
+{
+ if (bBroadcast)
+ BroadcastStyleOperation(rName, SfxStyleFamily::Table, SfxHintId::StyleSheetErased);
+
+ std::unique_ptr<SwTableAutoFormat> pReleasedFormat = GetTableStyles().ReleaseAutoFormat(rName);
+
+ std::vector<SwTable*> vAffectedTables;
+ if (pReleasedFormat)
+ {
+ 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() == pReleasedFormat->GetName())
+ {
+ pTable->SetTableStyleName("");
+ vAffectedTables.push_back(pTable);
+ }
+ }
+
+ getIDocumentState().SetModified();
+
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().AppendUndo(
+ std::make_unique<SwUndoTableStyleDelete>(std::move(pReleasedFormat), std::move(vAffectedTables), *this));
+ }
+ }
+
+ return pReleasedFormat;
+}
+
+void SwDoc::ChgTableStyle(const OUString& rName, const SwTableAutoFormat& rNewFormat)
+{
+ SwTableAutoFormat* pFormat = GetTableStyles().FindAutoFormat(rName);
+ if (!pFormat)
+ return;
+
+ SwTableAutoFormat aOldFormat = *pFormat;
+ *pFormat = rNewFormat;
+ pFormat->SetName(rName);
+
+ 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() == rName)
+ if (SwFEShell* pFEShell = GetDocShell()->GetFEShell())
+ pFEShell->UpdateTableStyleFormatting(pTable->GetTableNode());
+ }
+
+ getIDocumentState().SetModified();
+
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().AppendUndo(
+ std::make_unique<SwUndoTableStyleUpdate>(*pFormat, aOldFormat, *this));
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/docnode/ndtbl1.cxx b/sw/source/core/docnode/ndtbl1.cxx
new file mode 100644
index 000000000..fb1b59a40
--- /dev/null
+++ b/sw/source/core/docnode/ndtbl1.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 <hintids.hxx>
+#include <editeng/boxitem.hxx>
+#include <editeng/brushitem.hxx>
+#include <editeng/frmdiritem.hxx>
+#include <fesh.hxx>
+#include <fmtornt.hxx>
+#include <fmtfsize.hxx>
+#include <fmtrowsplt.hxx>
+#include <tabcol.hxx>
+#include <frmatr.hxx>
+#include <cellfrm.hxx>
+#include <tabfrm.hxx>
+#include <cntfrm.hxx>
+#include <txtfrm.hxx>
+#include <svx/svxids.hrc>
+#include <doc.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <IDocumentState.hxx>
+#include <IDocumentContentOperations.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <pam.hxx>
+#include <swcrsr.hxx>
+#include <viscrs.hxx>
+#include <swtable.hxx>
+#include <htmltbl.hxx>
+#include <tblsel.hxx>
+#include <swtblfmt.hxx>
+#include <ndindex.hxx>
+#include <undobj.hxx>
+#include <calbck.hxx>
+#include <UndoTable.hxx>
+#include <o3tl/enumrange.hxx>
+#include <o3tl/safeint.hxx>
+#include <osl/diagnose.h>
+#include <redline.hxx>
+
+using ::editeng::SvxBorderLine;
+using namespace ::com::sun::star;
+
+// See swtable.cxx too
+#define COLFUZZY 20L
+
+static bool IsSame( tools::Long nA, tools::Long nB ) { return std::abs(nA-nB) <= COLFUZZY; }
+
+namespace {
+
+// SwTableLine::ChgFrameFormat may delete old format which doesn't have writer listeners anymore.
+// This may invalidate my pointers, and lead to use-after-free. For this reason, I register myself
+// as a writer listener for the old format here, and take care to delete formats without listeners
+// in my own dtor.
+class SwTableFormatCmp : public SwClient
+{
+public:
+ SwTableFormatCmp( SwFrameFormat *pOld, SwFrameFormat *pNew, sal_Int16 nType );
+ ~SwTableFormatCmp() override;
+
+ static SwFrameFormat* FindNewFormat(std::vector<std::unique_ptr<SwTableFormatCmp>>& rArr,
+ SwFrameFormat const* pOld, sal_Int16 nType);
+
+private:
+ SwFrameFormat *m_pOld, *m_pNew;
+ sal_Int16 m_nType;
+};
+
+}
+
+SwTableFormatCmp::SwTableFormatCmp(SwFrameFormat* pO, SwFrameFormat* pN, sal_Int16 nT)
+ : m_pOld(pO)
+ , m_pNew(pN)
+ , m_nType(nT)
+{
+ if (m_pOld)
+ m_pOld->Add(this);
+}
+
+SwTableFormatCmp::~SwTableFormatCmp()
+{
+ if (m_pOld)
+ {
+ m_pOld->Remove(this);
+ if (!m_pOld->HasWriterListeners())
+ delete m_pOld;
+ }
+}
+
+// static
+SwFrameFormat* SwTableFormatCmp::FindNewFormat(std::vector<std::unique_ptr<SwTableFormatCmp>>& rArr,
+ SwFrameFormat const* pOld, sal_Int16 nType)
+{
+ for (const auto& pCmp : rArr)
+ {
+ if (pCmp->m_pOld == pOld && pCmp->m_nType == nType)
+ return pCmp->m_pNew;
+ }
+ return nullptr;
+}
+
+static void lcl_GetStartEndCell( const SwCursor& rCursor,
+ SwLayoutFrame *&prStart, SwLayoutFrame *&prEnd )
+{
+ OSL_ENSURE( rCursor.GetContentNode() && rCursor.GetContentNode( false ),
+ "Tab selection not at ContentNode" );
+
+ Point aPtPos, aMkPos;
+ const SwShellCursor* pShCursor = dynamic_cast<const SwShellCursor*>(&rCursor);
+ if( pShCursor )
+ {
+ aPtPos = pShCursor->GetPtPos();
+ aMkPos = pShCursor->GetMkPos();
+ }
+
+ // Robust:
+ SwContentNode* pPointNd = rCursor.GetContentNode();
+ SwContentNode* pMarkNd = rCursor.GetContentNode(false);
+
+ std::pair<Point, bool> tmp(aPtPos, true);
+ SwFrame *const pPointFrame = pPointNd ? pPointNd->getLayoutFrame(pPointNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(), nullptr, &tmp) : nullptr;
+ tmp.first = aMkPos;
+ SwFrame *const pMarkFrame = pMarkNd ? pMarkNd->getLayoutFrame(pMarkNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(), nullptr, &tmp) : nullptr;
+
+ prStart = pPointFrame ? pPointFrame->GetUpper() : nullptr;
+ prEnd = pMarkFrame ? pMarkFrame->GetUpper() : nullptr;
+}
+
+static bool lcl_GetBoxSel( const SwCursor& rCursor, SwSelBoxes& rBoxes,
+ bool bAllCursor = false )
+{
+ const SwTableCursor* pTableCursor =
+ dynamic_cast<const SwTableCursor*>(&rCursor);
+ if( pTableCursor )
+ ::GetTableSelCrs( *pTableCursor, rBoxes );
+ else
+ {
+ const SwPaM *pCurPam = &rCursor, *pSttPam = pCurPam;
+ do {
+ const SwNode* pNd = pCurPam->GetNode().FindTableBoxStartNode();
+ if( pNd )
+ {
+ SwTableBox* pBox = const_cast<SwTableBox*>(pNd->FindTableNode()->GetTable().
+ GetTableBox( pNd->GetIndex() ));
+ rBoxes.insert( pBox );
+ }
+ } while( bAllCursor &&
+ pSttPam != ( pCurPam = pCurPam->GetNext()) );
+ }
+ return !rBoxes.empty();
+}
+
+static void InsertLine( std::vector<SwTableLine*>& rLineArr, SwTableLine* pLine )
+{
+ if( rLineArr.end() == std::find( rLineArr.begin(), rLineArr.end(), pLine ) )
+ rLineArr.push_back( pLine );
+}
+
+static bool lcl_IsAnLower( const SwTableLine *pLine, const SwTableLine *pAssumed )
+{
+ const SwTableLine *pTmp = pAssumed->GetUpper() ?
+ pAssumed->GetUpper()->GetUpper() : nullptr;
+ while ( pTmp )
+ {
+ if ( pTmp == pLine )
+ return true;
+ pTmp = pTmp->GetUpper() ? pTmp->GetUpper()->GetUpper() : nullptr;
+ }
+ return false;
+}
+
+namespace {
+
+struct LinesAndTable
+{
+ std::vector<SwTableLine*> &m_rLines;
+ const SwTable &m_rTable;
+ bool m_bInsertLines;
+
+ LinesAndTable(std::vector<SwTableLine*> &rL, const SwTable &rTable) :
+ m_rLines(rL), m_rTable(rTable), m_bInsertLines(true) {}
+};
+
+}
+
+static bool FindLine_( FndLine_ & rLine, LinesAndTable* pPara );
+
+static bool FindBox_( FndBox_ & rBox, LinesAndTable* pPara )
+{
+ if (!rBox.GetLines().empty())
+ {
+ pPara->m_bInsertLines = true;
+ for (auto const& rpFndLine : rBox.GetLines())
+ {
+ FindLine_(*rpFndLine, pPara);
+ }
+
+ if (pPara->m_bInsertLines)
+ {
+ const SwTableLines &rLines = (rBox.GetBox())
+ ? rBox.GetBox()->GetTabLines()
+ : pPara->m_rTable.GetTabLines();
+ if (rBox.GetLines().size() == rLines.size())
+ {
+ for ( auto pLine : rLines )
+ ::InsertLine(pPara->m_rLines, pLine);
+ }
+ else
+ pPara->m_bInsertLines = false;
+ }
+ }
+ else if (rBox.GetBox())
+ {
+ ::InsertLine(pPara->m_rLines, rBox.GetBox()->GetUpper());
+ }
+ return true;
+}
+
+bool FindLine_( FndLine_& rLine, LinesAndTable* pPara )
+{
+ for (auto const& it : rLine.GetBoxes())
+ {
+ FindBox_(*it, pPara);
+ }
+ return true;
+}
+
+static void lcl_CollectLines( std::vector<SwTableLine*> &rArr, const SwCursor& rCursor, bool bRemoveLines )
+{
+ // Collect the selected Boxes first
+ SwSelBoxes aBoxes;
+ if( !::lcl_GetBoxSel( rCursor, aBoxes ))
+ return ;
+
+ // Copy the selected structure
+ const SwTable &rTable = aBoxes[0]->GetSttNd()->FindTableNode()->GetTable();
+ LinesAndTable aPara( rArr, rTable );
+ FndBox_ aFndBox( nullptr, nullptr );
+ {
+ FndPara aTmpPara( aBoxes, &aFndBox );
+ ForEach_FndLineCopyCol( const_cast<SwTableLines&>(rTable.GetTabLines()), &aTmpPara );
+ }
+
+ // Collect the Lines which only contain selected Boxes
+ ::FindBox_(aFndBox, &aPara);
+
+ // Remove lines, that have a common superordinate row.
+ // (Not for row split)
+ if ( !bRemoveLines )
+ return;
+
+ for ( std::vector<SwTableLine*>::size_type i = 0; i < rArr.size(); ++i )
+ {
+ SwTableLine *pUpLine = rArr[i];
+ for ( std::vector<SwTableLine*>::size_type k = 0; k < rArr.size(); ++k )
+ {
+ if ( k != i && ::lcl_IsAnLower( pUpLine, rArr[k] ) )
+ {
+ rArr.erase( rArr.begin() + k );
+ if ( k <= i )
+ --i;
+ --k;
+ }
+ }
+ }
+}
+
+static void lcl_ProcessRowAttr(std::vector<std::unique_ptr<SwTableFormatCmp>>& rFormatCmp,
+ SwTableLine* pLine, const SfxPoolItem& rNew)
+{
+ SwFrameFormat *pNewFormat = SwTableFormatCmp::FindNewFormat( rFormatCmp, pLine->GetFrameFormat(), 0 );
+ if ( nullptr != pNewFormat )
+ pLine->ChgFrameFormat( static_cast<SwTableLineFormat*>(pNewFormat) );
+ else
+ {
+ SwFrameFormat *pOld = pLine->GetFrameFormat();
+ SwFrameFormat *pNew = pLine->ClaimFrameFormat();
+ pNew->SetFormatAttr( rNew );
+ rFormatCmp.push_back(std::make_unique<SwTableFormatCmp>(pOld, pNew, 0));
+ }
+}
+
+static void lcl_ProcessBoxSize(std::vector<std::unique_ptr<SwTableFormatCmp>>& rFormatCmp,
+ SwTableBox* pBox, const SwFormatFrameSize& rNew);
+
+static void lcl_ProcessRowSize(std::vector<std::unique_ptr<SwTableFormatCmp>>& rFormatCmp,
+ SwTableLine* pLine, const SwFormatFrameSize& rNew)
+{
+ lcl_ProcessRowAttr( rFormatCmp, pLine, rNew );
+ SwTableBoxes &rBoxes = pLine->GetTabBoxes();
+ for ( auto pBox : rBoxes )
+ ::lcl_ProcessBoxSize( rFormatCmp, pBox, rNew );
+}
+
+static void lcl_ProcessBoxSize(std::vector<std::unique_ptr<SwTableFormatCmp>>& rFormatCmp,
+ SwTableBox* pBox, const SwFormatFrameSize& rNew)
+{
+ SwTableLines &rLines = pBox->GetTabLines();
+ if ( !rLines.empty() )
+ {
+ SwFormatFrameSize aSz( rNew );
+ aSz.SetHeight( rNew.GetHeight() ? rNew.GetHeight() / rLines.size() : 0 );
+ for ( auto pLine : rLines )
+ ::lcl_ProcessRowSize( rFormatCmp, pLine, aSz );
+ }
+}
+
+void SwDoc::SetRowSplit( const SwCursor& rCursor, const SwFormatRowSplit &rNew )
+{
+ SwTableNode* pTableNd = rCursor.GetPoint()->nNode.GetNode().FindTableNode();
+ if( !pTableNd )
+ return;
+
+ std::vector<SwTableLine*> aRowArr; // For Lines collecting
+ ::lcl_CollectLines( aRowArr, rCursor, false );
+
+ if( aRowArr.empty() )
+ return;
+
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().AppendUndo(std::make_unique<SwUndoAttrTable>(*pTableNd));
+ }
+
+ std::vector<std::unique_ptr<SwTableFormatCmp>> aFormatCmp;
+ aFormatCmp.reserve( std::max( 255, static_cast<int>(aRowArr.size()) ) );
+
+ for( auto pLn : aRowArr )
+ ::lcl_ProcessRowAttr( aFormatCmp, pLn, rNew );
+
+ getIDocumentState().SetModified();
+}
+
+std::unique_ptr<SwFormatRowSplit> SwDoc::GetRowSplit( const SwCursor& rCursor )
+{
+ SwTableNode* pTableNd = rCursor.GetPoint()->nNode.GetNode().FindTableNode();
+ if( !pTableNd )
+ return nullptr;
+
+ std::vector<SwTableLine*> aRowArr; // For Lines collecting
+ ::lcl_CollectLines( aRowArr, rCursor, false );
+
+ if( aRowArr.empty() )
+ return nullptr;
+
+ SwFormatRowSplit* pSz = &const_cast<SwFormatRowSplit&>(aRowArr[0]->GetFrameFormat()->GetRowSplit());
+
+ for ( auto pLn : aRowArr )
+ {
+ if ( pSz->GetValue() != pLn->GetFrameFormat()->GetRowSplit().GetValue() )
+ {
+ return nullptr;
+ }
+ }
+ return std::make_unique<SwFormatRowSplit>( *pSz );
+}
+
+/* Class: SwDoc
+ * Methods: SetRowHeight(), GetRowHeight()
+ *
+ * The line height is calculated from the Selection.
+ * Starting with every Cell within the Selection, all Cells are iterated
+ * through in an upwards fashion.
+ *
+ * The topmost Line gets the requested value, all Lines below it get
+ * a respective value that is calculated from the relation of the old and
+ * new size of the topmost Line in the lower line's own size.
+ *
+ * All changed Lines may get an own FrameFormat.
+ * Of course we can only touch every Line once.
+ */
+
+void SwDoc::SetRowHeight( const SwCursor& rCursor, const SwFormatFrameSize &rNew )
+{
+ SwTableNode* pTableNd = rCursor.GetPoint()->nNode.GetNode().FindTableNode();
+ if( !pTableNd )
+ return;
+
+ std::vector<SwTableLine*> aRowArr; // For Lines collecting
+ ::lcl_CollectLines( aRowArr, rCursor, true );
+
+ if( aRowArr.empty() )
+ return;
+
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().AppendUndo(std::make_unique<SwUndoAttrTable>(*pTableNd));
+ }
+
+ std::vector<std::unique_ptr<SwTableFormatCmp>> aFormatCmp;
+ aFormatCmp.reserve( std::max( 255, static_cast<int>(aRowArr.size()) ) );
+ for ( auto pLn : aRowArr )
+ ::lcl_ProcessRowSize( aFormatCmp, pLn, rNew );
+
+ getIDocumentState().SetModified();
+}
+
+std::unique_ptr<SwFormatFrameSize> SwDoc::GetRowHeight( const SwCursor& rCursor )
+{
+ SwTableNode* pTableNd = rCursor.GetPoint()->nNode.GetNode().FindTableNode();
+ if( !pTableNd )
+ return nullptr;
+
+ std::vector<SwTableLine*> aRowArr; // For Lines collecting
+ ::lcl_CollectLines( aRowArr, rCursor, true );
+
+ if( aRowArr.empty() )
+ return nullptr;
+
+ SwFormatFrameSize* pSz = &const_cast<SwFormatFrameSize&>(aRowArr[0]->GetFrameFormat()->GetFrameSize());
+
+ for ( auto pLn : aRowArr )
+ {
+ if ( *pSz != pLn->GetFrameFormat()->GetFrameSize() )
+ return nullptr;
+ }
+ return std::make_unique<SwFormatFrameSize>( *pSz );
+}
+
+bool SwDoc::BalanceRowHeight( const SwCursor& rCursor, bool bTstOnly, const bool bOptimize )
+{
+ bool bRet = false;
+ SwTableNode* pTableNd = rCursor.GetPoint()->nNode.GetNode().FindTableNode();
+ if( pTableNd )
+ {
+ std::vector<SwTableLine*> aRowArr; // For Lines collecting
+ ::lcl_CollectLines( aRowArr, rCursor, true );
+
+ if( 1 < aRowArr.size() )
+ {
+ if( !bTstOnly )
+ {
+ tools::Long nHeight = 0;
+ sal_Int32 nTotalHeight = 0;
+ for ( auto pLn : aRowArr )
+ {
+ if (bOptimize)
+ nHeight = 0;
+ SwIterator<SwFrame,SwFormat> aIter( *pLn->GetFrameFormat() );
+ SwFrame* pFrame = aIter.First();
+ while ( pFrame )
+ {
+ nHeight = std::max( nHeight, pFrame->getFrameArea().Height() );
+ pFrame = aIter.Next();
+ }
+ nTotalHeight += nHeight;
+ }
+
+ if ( bOptimize )
+ nHeight = nTotalHeight / aRowArr.size();
+
+ SwFormatFrameSize aNew( SwFrameSize::Minimum, 0, nHeight );
+
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().AppendUndo(
+ std::make_unique<SwUndoAttrTable>(*pTableNd));
+ }
+
+ std::vector<std::unique_ptr<SwTableFormatCmp>> aFormatCmp;
+ aFormatCmp.reserve( std::max( 255, static_cast<int>(aRowArr.size()) ) );
+ for( auto pLn : aRowArr )
+ ::lcl_ProcessRowSize( aFormatCmp, pLn, aNew );
+
+ getIDocumentState().SetModified();
+ }
+ bRet = true;
+ }
+ }
+ return bRet;
+}
+
+void SwDoc::SetRowBackground( const SwCursor& rCursor, const SvxBrushItem &rNew )
+{
+ SwTableNode* pTableNd = rCursor.GetPoint()->nNode.GetNode().FindTableNode();
+ if( !pTableNd )
+ return;
+
+ std::vector<SwTableLine*> aRowArr; // For Lines collecting
+ ::lcl_CollectLines( aRowArr, rCursor, true );
+
+ if( aRowArr.empty() )
+ return;
+
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().AppendUndo(std::make_unique<SwUndoAttrTable>(*pTableNd));
+ }
+
+ std::vector<std::unique_ptr<SwTableFormatCmp>> aFormatCmp;
+ aFormatCmp.reserve( std::max( 255, static_cast<int>(aRowArr.size()) ) );
+
+ for( auto pLn : aRowArr )
+ ::lcl_ProcessRowAttr( aFormatCmp, pLn, rNew );
+
+ getIDocumentState().SetModified();
+}
+
+bool SwDoc::GetRowBackground( const SwCursor& rCursor, std::unique_ptr<SvxBrushItem>& rToFill )
+{
+ bool bRet = false;
+ SwTableNode* pTableNd = rCursor.GetPoint()->nNode.GetNode().FindTableNode();
+ if( pTableNd )
+ {
+ std::vector<SwTableLine*> aRowArr; // For Lines collecting
+ ::lcl_CollectLines( aRowArr, rCursor, true );
+
+ if( !aRowArr.empty() )
+ {
+ rToFill = aRowArr[0]->GetFrameFormat()->makeBackgroundBrushItem();
+
+ bRet = true;
+ for ( std::vector<SwTableLine*>::size_type i = 1; i < aRowArr.size(); ++i )
+ {
+ std::unique_ptr<SvxBrushItem> aAlternative(aRowArr[i]->GetFrameFormat()->makeBackgroundBrushItem());
+
+ if ( *rToFill != *aAlternative )
+ {
+ bRet = false;
+ break;
+ }
+ }
+ }
+ }
+ return bRet;
+}
+
+// has a table row, which is not a tracked deletion
+bool SwDoc::HasRowNotTracked( const SwCursor& rCursor )
+{
+ SwTableNode* pTableNd = rCursor.GetPoint()->nNode.GetNode().FindTableNode();
+ if( !pTableNd )
+ return false;
+
+ std::vector<SwTableLine*> aRowArr; // For Lines collecting
+ ::lcl_CollectLines( aRowArr, rCursor, true );
+
+ if( aRowArr.empty() )
+ return false;
+
+ SwRedlineTable::size_type nRedlinePos = 0;
+ SwDoc* pDoc = aRowArr[0]->GetFrameFormat()->GetDoc();
+ const IDocumentRedlineAccess& rIDRA = pDoc->getIDocumentRedlineAccess();
+
+ for( auto pLn : aRowArr )
+ {
+ auto pHasTextChangesOnlyProp = pLn->GetFrameFormat()->GetAttrSet().GetItem<SvxPrintItem>(RES_PRINT);
+ if ( !pHasTextChangesOnlyProp || pHasTextChangesOnlyProp->GetValue() )
+ // there is a not tracked row in the table selection
+ return true;
+
+ // tdf#150666 examine tracked row: it's possible to delete a tracked insertion
+ SwRedlineTable::size_type nPos = pLn->UpdateTextChangesOnly(nRedlinePos);
+ if ( nPos != SwRedlineTable::npos )
+ {
+ const SwRedlineTable& aRedlineTable = rIDRA.GetRedlineTable();
+ SwRangeRedline* pTmp = aRedlineTable[ nPos ];
+ if ( RedlineType::Insert == pTmp->GetType() )
+ return true;
+ }
+ }
+ return false;
+}
+
+void SwDoc::SetRowNotTracked( const SwCursor& rCursor,
+ const SvxPrintItem &rNew, bool bAll, bool bIns )
+{
+ SwTableNode* pTableNd = rCursor.GetPoint()->nNode.GetNode().FindTableNode();
+ if( !pTableNd )
+ return;
+
+ std::vector<SwTableLine*> aRowArr; // For Lines collecting
+ if ( bAll )
+ {
+ const SwTableLines &rLines = pTableNd->GetTable().GetTabLines();
+ aRowArr.insert(aRowArr.end(), rLines.begin(), rLines.end());
+ }
+ else
+ ::lcl_CollectLines( aRowArr, rCursor, true );
+
+ if( aRowArr.empty() )
+ return;
+
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().AppendUndo(std::make_unique<SwUndoAttrTable>(*pTableNd));
+ }
+
+ bool bInsertDummy = !bAll && !bIns &&
+ // HasTextChangesOnly == false, i.e. a tracked row change (deletion, if bIns == false)
+ !rNew.GetValue();
+ std::vector<std::unique_ptr<SwTableFormatCmp>> aFormatCmp;
+ aFormatCmp.reserve( std::max( 255, static_cast<int>(aRowArr.size()) ) );
+
+ SwRedlineTable::size_type nRedlinePos = 0;
+ for( auto pLn : aRowArr )
+ {
+ // tdf#150666 deleting row insertion from the same author needs special handling,
+ // because removing redlines of the author can result an empty line,
+ // which doesn't contain any redline for the tracked row
+ bool bDeletionOfOwnRowInsertion = false;
+ if ( bInsertDummy )
+ {
+ SwRedlineTable::size_type nPos = pLn->UpdateTextChangesOnly(nRedlinePos);
+ if ( nPos != SwRedlineTable::npos )
+ {
+ SwDoc* pDoc = pLn->GetFrameFormat()->GetDoc();
+ IDocumentRedlineAccess& rIDRA = pDoc->getIDocumentRedlineAccess();
+ const SwRedlineTable& aRedlineTable = rIDRA.GetRedlineTable();
+ SwRangeRedline* pTmp = aRedlineTable[ nPos ];
+ if ( RedlineType::Insert == pTmp->GetType() &&
+ rIDRA.GetRedlineAuthor() == pTmp->GetRedlineData().GetAuthor() &&
+ pTmp->GetText()[0] == CH_TXT_TRACKED_DUMMY_CHAR )
+ {
+ bDeletionOfOwnRowInsertion = true;
+ }
+ }
+ }
+
+ ::lcl_ProcessRowAttr( aFormatCmp, pLn, rNew );
+ // as a workaround for the rows without text content,
+ // add a redline with invisible text CH_TXT_TRACKED_DUMMY_CHAR
+ // (unless the table is part of a bigger deletion, where the
+ // new redline can cause a problem)
+ if ( bInsertDummy && (pLn->IsEmpty() || bDeletionOfOwnRowInsertion ) )
+ {
+ ::sw::UndoGuard const undoGuard(GetIDocumentUndoRedo());
+ SwNodeIndex aInsPos( *(pLn->GetTabBoxes()[0]->GetSttNd()), 1 );
+ RedlineFlags eOld = getIDocumentRedlineAccess().GetRedlineFlags();
+ getIDocumentRedlineAccess().SetRedlineFlags_intern(RedlineFlags::NONE);
+ SwPaM aPaM(aInsPos);
+ getIDocumentContentOperations().InsertString( aPaM,
+ OUStringChar(CH_TXT_TRACKED_DUMMY_CHAR) );
+ aPaM.SetMark();
+ aPaM.GetMark()->nContent.Assign(aPaM.GetContentNode(), 0);
+ getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
+ getIDocumentContentOperations().DeleteAndJoin( aPaM );
+ }
+ }
+
+ getIDocumentState().SetModified();
+}
+
+static void InsertCell( std::vector<SwCellFrame*>& rCellArr, SwCellFrame* pCellFrame )
+{
+ if( rCellArr.end() == std::find( rCellArr.begin(), rCellArr.end(), pCellFrame ) )
+ rCellArr.push_back( pCellFrame );
+}
+
+static void lcl_CollectCells( std::vector<SwCellFrame*> &rArr, const SwRect &rUnion,
+ SwTabFrame *pTab )
+{
+ SwLayoutFrame *pCell = pTab->FirstCell();
+ do
+ {
+ // If the Cell contains a CellFrame, we need to use it
+ // in order to get to the Cell
+ while ( !pCell->IsCellFrame() )
+ pCell = pCell->GetUpper();
+ OSL_ENSURE( pCell, "Frame is not a Cell" );
+ if ( rUnion.Overlaps( pCell->getFrameArea() ) )
+ ::InsertCell( rArr, static_cast<SwCellFrame*>(pCell) );
+
+ // Make sure the Cell is left (Areas)
+ SwLayoutFrame *pTmp = pCell;
+ do
+ { pTmp = pTmp->GetNextLayoutLeaf();
+ } while ( pCell->IsAnLower( pTmp ) );
+ pCell = pTmp;
+ } while( pCell && pTab->IsAnLower( pCell ) );
+}
+
+void SwDoc::SetTabBorders( const SwCursor& rCursor, const SfxItemSet& rSet )
+{
+ SwContentNode* pCntNd = rCursor.GetPoint()->nNode.GetNode().GetContentNode();
+ SwTableNode* pTableNd = pCntNd ? pCntNd->FindTableNode() : nullptr;
+ if( !pTableNd )
+ return ;
+
+ SwLayoutFrame *pStart, *pEnd;
+ ::lcl_GetStartEndCell( rCursor, pStart, pEnd );
+
+ SwSelUnions aUnions;
+ ::MakeSelUnions( aUnions, pStart, pEnd );
+
+ if( aUnions.empty() )
+ return;
+
+ SwTable& rTable = pTableNd->GetTable();
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().AppendUndo( std::make_unique<SwUndoAttrTable>(*pTableNd) );
+ }
+
+ std::vector<std::unique_ptr<SwTableFormatCmp>> aFormatCmp;
+ aFormatCmp.reserve( 255 );
+ const SvxBoxItem* pSetBox;
+ const SvxBoxInfoItem *pSetBoxInfo;
+
+ const SvxBorderLine* pLeft = nullptr;
+ const SvxBorderLine* pRight = nullptr;
+ const SvxBorderLine* pTop = nullptr;
+ const SvxBorderLine* pBottom = nullptr;
+ const SvxBorderLine* pHori = nullptr;
+ const SvxBorderLine* pVert = nullptr;
+ bool bHoriValid = true, bVertValid = true,
+ bTopValid = true, bBottomValid = true,
+ bLeftValid = true, bRightValid = true;
+
+ // The Flags in the BoxInfo Item decide whether a BorderLine is valid!
+ pSetBoxInfo = rSet.GetItemIfSet( SID_ATTR_BORDER_INNER, false );
+ if( pSetBoxInfo )
+ {
+ pHori = pSetBoxInfo->GetHori();
+ pVert = pSetBoxInfo->GetVert();
+
+ bHoriValid = pSetBoxInfo->IsValid(SvxBoxInfoItemValidFlags::HORI);
+ bVertValid = pSetBoxInfo->IsValid(SvxBoxInfoItemValidFlags::VERT);
+
+ // Do we want to evaluate these?
+ bTopValid = pSetBoxInfo->IsValid(SvxBoxInfoItemValidFlags::TOP);
+ bBottomValid = pSetBoxInfo->IsValid(SvxBoxInfoItemValidFlags::BOTTOM);
+ bLeftValid = pSetBoxInfo->IsValid(SvxBoxInfoItemValidFlags::LEFT);
+ bRightValid = pSetBoxInfo->IsValid(SvxBoxInfoItemValidFlags::RIGHT);
+ }
+
+ pSetBox = rSet.GetItemIfSet( RES_BOX, false );
+ if( pSetBox )
+ {
+ pLeft = pSetBox->GetLeft();
+ pRight = pSetBox->GetRight();
+ pTop = pSetBox->GetTop();
+ pBottom = pSetBox->GetBottom();
+ }
+ else
+ {
+ // Not set, thus not valid values
+ bTopValid = bBottomValid = bLeftValid = bRightValid = false;
+ pSetBox = nullptr;
+ }
+
+ bool bFirst = true;
+ for ( SwSelUnions::size_type i = 0; i < aUnions.size(); ++i )
+ {
+ SwSelUnion *pUnion = &aUnions[i];
+ SwTabFrame *pTab = pUnion->GetTable();
+ const SwRect &rUnion = pUnion->GetUnion();
+ const bool bLast = (i == aUnions.size() - 1);
+
+ std::vector<SwCellFrame*> aCellArr;
+ aCellArr.reserve( 255 );
+ ::lcl_CollectCells( aCellArr, pUnion->GetUnion(), pTab );
+
+ // All Cell Borders that match the UnionRect or extend it are
+ // Outer Borders. All others are Inner Borders.
+
+ // New: The Outer Borders can, depending on whether it's a
+ // Start/Middle/Follow Table (for Selection via FollowTabs),
+ // also not be Outer Borders.
+ // Outer Borders are set on the left, right, at the top and at the bottom.
+ // Inner Borders are only set at the top and on the left.
+ for ( auto pCell : aCellArr )
+ {
+ const bool bVert = pTab->IsVertical();
+ const bool bRTL = pTab->IsRightToLeft();
+ bool bTopOver, bLeftOver, bRightOver, bBottomOver;
+ if ( bVert )
+ {
+ bTopOver = pCell->getFrameArea().Right() >= rUnion.Right();
+ bLeftOver = pCell->getFrameArea().Top() <= rUnion.Top();
+ bRightOver = pCell->getFrameArea().Bottom() >= rUnion.Bottom();
+ bBottomOver = pCell->getFrameArea().Left() <= rUnion.Left();
+ }
+ else
+ {
+ bTopOver = pCell->getFrameArea().Top() <= rUnion.Top();
+ bLeftOver = pCell->getFrameArea().Left() <= rUnion.Left();
+ bRightOver = pCell->getFrameArea().Right() >= rUnion.Right();
+ bBottomOver = pCell->getFrameArea().Bottom() >= rUnion.Bottom();
+ }
+
+ if ( bRTL )
+ {
+ bool bTmp = bRightOver;
+ bRightOver = bLeftOver;
+ bLeftOver = bTmp;
+ }
+
+ // Do not set anything by default in HeadlineRepeats
+ if ( pTab->IsFollow() &&
+ ( pTab->IsInHeadline( *pCell ) ||
+ // Same holds for follow flow rows
+ pCell->IsInFollowFlowRow() ) )
+ continue;
+
+ SvxBoxItem aBox( pCell->GetFormat()->GetBox() );
+
+ sal_Int16 nType = 0;
+
+ // Top Border
+ if( bTopValid )
+ {
+ if ( bFirst && bTopOver )
+ {
+ aBox.SetLine( pTop, SvxBoxItemLine::TOP );
+ nType |= 0x0001;
+ }
+ else if ( bHoriValid )
+ {
+ aBox.SetLine( nullptr, SvxBoxItemLine::TOP );
+ nType |= 0x0002;
+ }
+ }
+
+ // Fix fdo#62470 correct the input for RTL table
+ if (bRTL)
+ {
+ if( bLeftOver && bRightOver)
+ {
+ if ( bLeftValid )
+ {
+ aBox.SetLine( pLeft, SvxBoxItemLine::RIGHT );
+ nType |= 0x0010;
+ }
+ if ( bRightValid )
+ {
+ aBox.SetLine( pRight, SvxBoxItemLine::LEFT );
+ nType |= 0x0004;
+ }
+ }
+ else
+ {
+ if ( bLeftValid )
+ {
+ aBox.SetLine( bRightOver ? pLeft : nullptr, SvxBoxItemLine::RIGHT );
+ if (bVertValid)
+ nType |= 0x0020;
+ else
+ nType |= 0x0010;
+ }
+ if ( bLeftOver )
+ {
+ if ( bRightValid )
+ {
+ aBox.SetLine( pRight, SvxBoxItemLine::LEFT );
+ nType |= 0x0004;
+ }
+ }
+ else if ( bVertValid )
+ {
+ aBox.SetLine( pVert, SvxBoxItemLine::LEFT );
+ nType |= 0x0008;
+ }
+ }
+ }
+ else
+ {
+ // Left Border
+ if ( bLeftOver )
+ {
+ if( bLeftValid )
+ {
+ aBox.SetLine( pLeft, SvxBoxItemLine::LEFT );
+ nType |= 0x0004;
+ }
+ }
+ else if( bVertValid )
+ {
+ aBox.SetLine( pVert, SvxBoxItemLine::LEFT );
+ nType |= 0x0008;
+ }
+
+ // Right Border
+ if( bRightValid )
+ {
+ if ( bRightOver )
+ {
+ aBox.SetLine( pRight, SvxBoxItemLine::RIGHT );
+ nType |= 0x0010;
+ }
+ else if ( bVertValid )
+ {
+ aBox.SetLine( nullptr, SvxBoxItemLine::RIGHT );
+ nType |= 0x0020;
+ }
+ }
+ }
+
+ // Bottom Border
+ if ( bLast && bBottomOver )
+ {
+ if( bBottomValid )
+ {
+ aBox.SetLine( pBottom, SvxBoxItemLine::BOTTOM );
+ nType |= 0x0040;
+ }
+ }
+ else if( bHoriValid )
+ {
+ aBox.SetLine( pHori, SvxBoxItemLine::BOTTOM );
+ nType |= 0x0080;
+ }
+
+ if( pSetBox )
+ {
+ for( SvxBoxItemLine k : o3tl::enumrange<SvxBoxItemLine>() )
+ aBox.SetDistance( pSetBox->GetDistance( k ), k );
+ }
+
+ SwTableBox *pBox = const_cast<SwTableBox*>(pCell->GetTabBox());
+ SwFrameFormat *pNewFormat = SwTableFormatCmp::FindNewFormat( aFormatCmp, pBox->GetFrameFormat(), nType );
+ if ( nullptr != pNewFormat )
+ pBox->ChgFrameFormat( static_cast<SwTableBoxFormat*>(pNewFormat) );
+ else
+ {
+ SwFrameFormat *pOld = pBox->GetFrameFormat();
+ SwFrameFormat *pNew = pBox->ClaimFrameFormat();
+ pNew->SetFormatAttr( aBox );
+ aFormatCmp.push_back(std::make_unique<SwTableFormatCmp>(pOld, pNew, nType));
+ }
+ }
+
+ bFirst = false;
+ }
+
+ SwHTMLTableLayout *pTableLayout = rTable.GetHTMLTableLayout();
+ if( pTableLayout )
+ {
+ SwContentFrame* pFrame = rCursor.GetContentNode()->getLayoutFrame( rCursor.GetContentNode()->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout() );
+ SwTabFrame* pTabFrame = pFrame->ImplFindTabFrame();
+
+ pTableLayout->BordersChanged(
+ pTableLayout->GetBrowseWidthByTabFrame( *pTabFrame ) );
+ }
+ ::ClearFEShellTabCols(*this, nullptr);
+ getIDocumentState().SetModified();
+}
+
+static void lcl_SetLineStyle( SvxBorderLine *pToSet,
+ const Color *pColor, const SvxBorderLine *pBorderLine)
+{
+ if ( pBorderLine )
+ {
+ if ( !pColor )
+ {
+ Color aTmp( pToSet->GetColor() );
+ *pToSet = *pBorderLine;
+ pToSet->SetColor( aTmp );
+ }
+ else
+ *pToSet = *pBorderLine;
+ }
+ if ( pColor )
+ pToSet->SetColor( *pColor );
+}
+
+void SwDoc::SetTabLineStyle( const SwCursor& rCursor,
+ const Color* pColor, bool bSetLine,
+ const SvxBorderLine* pBorderLine )
+{
+ SwContentNode* pCntNd = rCursor.GetPoint()->nNode.GetNode().GetContentNode();
+ SwTableNode* pTableNd = pCntNd ? pCntNd->FindTableNode() : nullptr;
+ if( !pTableNd )
+ return ;
+
+ SwLayoutFrame *pStart, *pEnd;
+ ::lcl_GetStartEndCell( rCursor, pStart, pEnd );
+
+ SwSelUnions aUnions;
+ ::MakeSelUnions( aUnions, pStart, pEnd );
+
+ if( aUnions.empty() )
+ return;
+
+ SwTable& rTable = pTableNd->GetTable();
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().AppendUndo(std::make_unique<SwUndoAttrTable>(*pTableNd));
+ }
+
+ SvxBorderLine aDefaultBorder(pBorderLine ? *pBorderLine
+ : SvxBorderLine(pColor, SvxBorderLineWidth::VeryThin));
+ if (pColor && pBorderLine)
+ aDefaultBorder.SetColor(*pColor);
+
+ for( auto &rU : aUnions )
+ {
+ SwSelUnion *pUnion = &rU;
+ SwTabFrame *pTab = pUnion->GetTable();
+ std::vector<SwCellFrame*> aCellArr;
+ aCellArr.reserve( 255 );
+ ::lcl_CollectCells( aCellArr, pUnion->GetUnion(), pTab );
+
+ for ( auto pCell : aCellArr )
+ {
+ // Do not set anything by default in HeadlineRepeats
+ if ( pTab->IsFollow() && pTab->IsInHeadline( *pCell ) )
+ continue;
+
+ const_cast<SwTableBox*>(pCell->GetTabBox())->ClaimFrameFormat();
+ SwFrameFormat *pFormat = pCell->GetFormat();
+ std::unique_ptr<SvxBoxItem> aBox(pFormat->GetBox().Clone());
+
+ SvxBorderLine* pTop = const_cast<SvxBorderLine*>(aBox->GetTop());
+ SvxBorderLine* pBot = const_cast<SvxBorderLine*>(aBox->GetBottom());
+ SvxBorderLine* pLeft = const_cast<SvxBorderLine*>(aBox->GetLeft());
+ SvxBorderLine* pRight = const_cast<SvxBorderLine*>(aBox->GetRight());
+
+ if ( !pBorderLine && bSetLine )
+ {
+ aBox.reset(::GetDfltAttr(RES_BOX)->Clone());
+ }
+ else if ((pColor || pBorderLine) && !pTop && !pBot && !pLeft && !pRight)
+ {
+ aBox->SetLine(&aDefaultBorder, SvxBoxItemLine::TOP);
+ aBox->SetLine(&aDefaultBorder, SvxBoxItemLine::BOTTOM);
+ aBox->SetLine(&aDefaultBorder, SvxBoxItemLine::LEFT);
+ aBox->SetLine(&aDefaultBorder, SvxBoxItemLine::RIGHT);
+ }
+ else
+ {
+ if (pTop)
+ ::lcl_SetLineStyle(pTop, pColor, pBorderLine);
+ if (pBot)
+ ::lcl_SetLineStyle(pBot, pColor, pBorderLine);
+ if (pLeft)
+ ::lcl_SetLineStyle(pLeft, pColor, pBorderLine);
+ if (pRight)
+ ::lcl_SetLineStyle(pRight, pColor, pBorderLine);
+ }
+ pFormat->SetFormatAttr( *aBox );
+ }
+ }
+
+ SwHTMLTableLayout *pTableLayout = rTable.GetHTMLTableLayout();
+ if( pTableLayout )
+ {
+ SwContentFrame* pFrame = rCursor.GetContentNode()->getLayoutFrame( rCursor.GetContentNode()->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout() );
+ SwTabFrame* pTabFrame = pFrame->ImplFindTabFrame();
+
+ pTableLayout->BordersChanged(
+ pTableLayout->GetBrowseWidthByTabFrame( *pTabFrame ) );
+ }
+ ::ClearFEShellTabCols(*this, nullptr);
+ getIDocumentState().SetModified();
+}
+
+void SwDoc::GetTabBorders( const SwCursor& rCursor, SfxItemSet& rSet )
+{
+ SwContentNode* pCntNd = rCursor.GetPoint()->nNode.GetNode().GetContentNode();
+ SwTableNode* pTableNd = pCntNd ? pCntNd->FindTableNode() : nullptr;
+ if( !pTableNd )
+ return ;
+
+ SwLayoutFrame *pStart, *pEnd;
+ ::lcl_GetStartEndCell( rCursor, pStart, pEnd );
+
+ SwSelUnions aUnions;
+ ::MakeSelUnions( aUnions, pStart, pEnd );
+
+ if( aUnions.empty() )
+ return;
+
+ SvxBoxItem aSetBox ( rSet.Get(RES_BOX ) );
+ SvxBoxInfoItem aSetBoxInfo( rSet.Get(SID_ATTR_BORDER_INNER) );
+
+ bool bTopSet = false,
+ bBottomSet = false,
+ bLeftSet = false,
+ bRightSet = false,
+ bHoriSet = false,
+ bVertSet = false,
+ bDistanceSet = false,
+ bRTLTab = false;
+
+ aSetBoxInfo.ResetFlags();
+
+ for ( SwSelUnions::size_type i = 0; i < aUnions.size(); ++i )
+ {
+ SwSelUnion *pUnion = &aUnions[i];
+ const SwTabFrame *pTab = pUnion->GetTable();
+ const SwRect &rUnion = pUnion->GetUnion();
+ const bool bFirst = i == 0;
+ const bool bLast = (i == aUnions.size() - 1);
+
+ std::vector<SwCellFrame*> aCellArr;
+ aCellArr.reserve(255);
+ ::lcl_CollectCells( aCellArr, rUnion, const_cast<SwTabFrame*>(pTab) );
+
+ for ( auto pCell : aCellArr )
+ {
+ const bool bVert = pTab->IsVertical();
+ const bool bRTL = bRTLTab = pTab->IsRightToLeft();
+ bool bTopOver, bLeftOver, bRightOver, bBottomOver;
+ if ( bVert )
+ {
+ bTopOver = pCell->getFrameArea().Right() >= rUnion.Right();
+ bLeftOver = pCell->getFrameArea().Top() <= rUnion.Top();
+ bRightOver = pCell->getFrameArea().Bottom() >= rUnion.Bottom();
+ bBottomOver = pCell->getFrameArea().Left() <= rUnion.Left();
+ }
+ else
+ {
+ bTopOver = pCell->getFrameArea().Top() <= rUnion.Top();
+ bLeftOver = pCell->getFrameArea().Left() <= rUnion.Left();
+ bRightOver = pCell->getFrameArea().Right() >= rUnion.Right();
+ bBottomOver = pCell->getFrameArea().Bottom() >= rUnion.Bottom();
+ }
+
+ if ( bRTL )
+ {
+ bool bTmp = bRightOver;
+ bRightOver = bLeftOver;
+ bLeftOver = bTmp;
+ }
+
+ const SwFrameFormat *pFormat = pCell->GetFormat();
+ const SvxBoxItem &rBox = pFormat->GetBox();
+
+ // Top Border
+ if ( bFirst && bTopOver )
+ {
+ if (aSetBoxInfo.IsValid(SvxBoxInfoItemValidFlags::TOP))
+ {
+ if ( !bTopSet )
+ { bTopSet = true;
+ aSetBox.SetLine( rBox.GetTop(), SvxBoxItemLine::TOP );
+ }
+ else if ((aSetBox.GetTop() && rBox.GetTop() &&
+ (*aSetBox.GetTop() != *rBox.GetTop())) ||
+ ((!aSetBox.GetTop()) != (!rBox.GetTop()))) // != expression is true, if one and only one of the two pointers is !0
+ {
+ aSetBoxInfo.SetValid(SvxBoxInfoItemValidFlags::TOP, false );
+ aSetBox.SetLine( nullptr, SvxBoxItemLine::TOP );
+ }
+ }
+ }
+
+ // Left Border
+ if ( bLeftOver )
+ {
+ if (aSetBoxInfo.IsValid(SvxBoxInfoItemValidFlags::LEFT))
+ {
+ if ( !bLeftSet )
+ { bLeftSet = true;
+ aSetBox.SetLine( rBox.GetLeft(), SvxBoxItemLine::LEFT );
+ }
+ else if ((aSetBox.GetLeft() && rBox.GetLeft() &&
+ (*aSetBox.GetLeft() != *rBox.GetLeft())) ||
+ ((!aSetBox.GetLeft()) != (!rBox.GetLeft())))
+ {
+ aSetBoxInfo.SetValid(SvxBoxInfoItemValidFlags::LEFT, false );
+ aSetBox.SetLine( nullptr, SvxBoxItemLine::LEFT );
+ }
+ }
+ }
+ else
+ {
+ if (aSetBoxInfo.IsValid(SvxBoxInfoItemValidFlags::VERT))
+ {
+ if ( !bVertSet )
+ { bVertSet = true;
+ aSetBoxInfo.SetLine( rBox.GetLeft(), SvxBoxInfoItemLine::VERT );
+ }
+ else if ((aSetBoxInfo.GetVert() && rBox.GetLeft() &&
+ (*aSetBoxInfo.GetVert() != *rBox.GetLeft())) ||
+ ((!aSetBoxInfo.GetVert()) != (!rBox.GetLeft())))
+ { aSetBoxInfo.SetValid( SvxBoxInfoItemValidFlags::VERT, false );
+ aSetBoxInfo.SetLine( nullptr, SvxBoxInfoItemLine::VERT );
+ }
+ }
+ }
+
+ // Right Border
+ if ( aSetBoxInfo.IsValid(SvxBoxInfoItemValidFlags::RIGHT) && bRightOver )
+ {
+ if ( !bRightSet )
+ { bRightSet = true;
+ aSetBox.SetLine( rBox.GetRight(), SvxBoxItemLine::RIGHT );
+ }
+ else if ((aSetBox.GetRight() && rBox.GetRight() &&
+ (*aSetBox.GetRight() != *rBox.GetRight())) ||
+ (!aSetBox.GetRight() != !rBox.GetRight()))
+ { aSetBoxInfo.SetValid( SvxBoxInfoItemValidFlags::RIGHT, false );
+ aSetBox.SetLine( nullptr, SvxBoxItemLine::RIGHT );
+ }
+ }
+
+ // Bottom Border
+ if ( bLast && bBottomOver )
+ {
+ if ( aSetBoxInfo.IsValid(SvxBoxInfoItemValidFlags::BOTTOM) )
+ {
+ if ( !bBottomSet )
+ { bBottomSet = true;
+ aSetBox.SetLine( rBox.GetBottom(), SvxBoxItemLine::BOTTOM );
+ }
+ else if ((aSetBox.GetBottom() && rBox.GetBottom() &&
+ (*aSetBox.GetBottom() != *rBox.GetBottom())) ||
+ (!aSetBox.GetBottom() != !rBox.GetBottom()))
+ { aSetBoxInfo.SetValid( SvxBoxInfoItemValidFlags::BOTTOM, false );
+ aSetBox.SetLine( nullptr, SvxBoxItemLine::BOTTOM );
+ }
+ }
+ }
+ // In all Lines, except for the last one, the horizontal Line
+ // is taken from the Bottom Line.
+ else
+ {
+ if (aSetBoxInfo.IsValid(SvxBoxInfoItemValidFlags::HORI))
+ {
+ if ( !bHoriSet )
+ { bHoriSet = true;
+ aSetBoxInfo.SetLine( rBox.GetBottom(), SvxBoxInfoItemLine::HORI );
+ }
+ else if ((aSetBoxInfo.GetHori() && rBox.GetBottom() &&
+ (*aSetBoxInfo.GetHori() != *rBox.GetBottom())) ||
+ ((!aSetBoxInfo.GetHori()) != (!rBox.GetBottom())))
+ {
+ aSetBoxInfo.SetValid( SvxBoxInfoItemValidFlags::HORI, false );
+ aSetBoxInfo.SetLine( nullptr, SvxBoxInfoItemLine::HORI );
+ }
+ }
+ }
+
+ // Distance to text
+ if (aSetBoxInfo.IsValid(SvxBoxInfoItemValidFlags::DISTANCE))
+ {
+ if( !bDistanceSet ) // Set on first iteration
+ {
+ bDistanceSet = true;
+ for( SvxBoxItemLine k : o3tl::enumrange<SvxBoxItemLine>() )
+ aSetBox.SetDistance( rBox.GetDistance( k ), k );
+ }
+ else
+ {
+ for( SvxBoxItemLine k : o3tl::enumrange<SvxBoxItemLine>() )
+ if( aSetBox.GetDistance( k ) !=
+ rBox.GetDistance( k ) )
+ {
+ aSetBoxInfo.SetValid( SvxBoxInfoItemValidFlags::DISTANCE, false );
+ aSetBox.SetAllDistances(0);
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ // fdo#62470 fix the reading for table format.
+ if ( bRTLTab )
+ {
+ SvxBoxItem aTempBox ( rSet.Get(RES_BOX ) );
+ SvxBoxInfoItem aTempBoxInfo( rSet.Get(SID_ATTR_BORDER_INNER) );
+
+ aTempBox.SetLine( aSetBox.GetRight(), SvxBoxItemLine::RIGHT);
+ aSetBox.SetLine( aSetBox.GetLeft(), SvxBoxItemLine::RIGHT);
+ aSetBox.SetLine( aTempBox.GetRight(), SvxBoxItemLine::LEFT);
+
+ aTempBoxInfo.SetValid( SvxBoxInfoItemValidFlags::LEFT, aSetBoxInfo.IsValid(SvxBoxInfoItemValidFlags::LEFT) );
+ aSetBoxInfo.SetValid( SvxBoxInfoItemValidFlags::LEFT, aSetBoxInfo.IsValid(SvxBoxInfoItemValidFlags::RIGHT) );
+ aSetBoxInfo.SetValid( SvxBoxInfoItemValidFlags::RIGHT, aTempBoxInfo.IsValid(SvxBoxInfoItemValidFlags::LEFT) );
+ }
+
+ rSet.Put( aSetBox );
+ rSet.Put( aSetBoxInfo );
+}
+
+void SwDoc::SetBoxAttr( const SwCursor& rCursor, const SfxPoolItem &rNew )
+{
+ SwTableNode* pTableNd = rCursor.GetPoint()->nNode.GetNode().FindTableNode();
+ SwSelBoxes aBoxes;
+ if( !(pTableNd && ::lcl_GetBoxSel( rCursor, aBoxes, true )) )
+ return;
+
+ SwTable& rTable = pTableNd->GetTable();
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().AppendUndo( std::make_unique<SwUndoAttrTable>(*pTableNd) );
+ }
+
+ std::vector<std::unique_ptr<SwTableFormatCmp>> aFormatCmp;
+ aFormatCmp.reserve(std::max<size_t>(255, aBoxes.size()));
+ for (size_t i = 0; i < aBoxes.size(); ++i)
+ {
+ SwTableBox *pBox = aBoxes[i];
+
+ SwFrameFormat *pNewFormat = SwTableFormatCmp::FindNewFormat( aFormatCmp, pBox->GetFrameFormat(), 0 );
+ if ( nullptr != pNewFormat )
+ pBox->ChgFrameFormat( static_cast<SwTableBoxFormat*>(pNewFormat) );
+ else
+ {
+ SwFrameFormat *pOld = pBox->GetFrameFormat();
+ SwFrameFormat *pNew = pBox->ClaimFrameFormat();
+ pNew->SetFormatAttr( rNew );
+ aFormatCmp.push_back(std::make_unique<SwTableFormatCmp>(pOld, pNew, 0));
+ }
+
+ pBox->SetDirectFormatting(true);
+ }
+
+ SwHTMLTableLayout *pTableLayout = rTable.GetHTMLTableLayout();
+ if( pTableLayout )
+ {
+ SwContentFrame* pFrame = rCursor.GetContentNode()->getLayoutFrame( rCursor.GetContentNode()->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout() );
+ SwTabFrame* pTabFrame = pFrame->ImplFindTabFrame();
+
+ pTableLayout->Resize(
+ pTableLayout->GetBrowseWidthByTabFrame( *pTabFrame ), true );
+ }
+ getIDocumentState().SetModified();
+}
+
+bool SwDoc::GetBoxAttr( const SwCursor& rCursor, std::unique_ptr<SfxPoolItem>& rToFill )
+{
+ // tdf#144843 calling GetBoxAttr *requires* object
+ assert(rToFill && "requires object here");
+ bool bRet = false;
+ SwTableNode* pTableNd = rCursor.GetPoint()->nNode.GetNode().FindTableNode();
+ SwSelBoxes aBoxes;
+ if( pTableNd && lcl_GetBoxSel( rCursor, aBoxes ))
+ {
+ bRet = true;
+ bool bOneFound = false;
+ const sal_uInt16 nWhich = rToFill->Which();
+ for (size_t i = 0; i < aBoxes.size(); ++i)
+ {
+ switch ( nWhich )
+ {
+ case RES_BACKGROUND:
+ {
+ std::unique_ptr<SvxBrushItem> xBack =
+ aBoxes[i]->GetFrameFormat()->makeBackgroundBrushItem();
+ if( !bOneFound )
+ {
+ rToFill = std::move(xBack);
+ bOneFound = true;
+ }
+ else if( *rToFill != *xBack )
+ bRet = false;
+ }
+ break;
+
+ case RES_FRAMEDIR:
+ {
+ const SvxFrameDirectionItem& rDir =
+ aBoxes[i]->GetFrameFormat()->GetFrameDir();
+ if( !bOneFound )
+ {
+ rToFill.reset(rDir.Clone());
+ bOneFound = true;
+ }
+ else if( rToFill && *rToFill != rDir )
+ bRet = false;
+ }
+ break;
+ case RES_VERT_ORIENT:
+ {
+ const SwFormatVertOrient& rOrient =
+ aBoxes[i]->GetFrameFormat()->GetVertOrient();
+ if( !bOneFound )
+ {
+ rToFill.reset(rOrient.Clone());
+ bOneFound = true;
+ }
+ else if( rToFill && *rToFill != rOrient )
+ bRet = false;
+ }
+ break;
+ }
+
+ if ( !bRet )
+ break;
+ }
+ }
+ return bRet;
+}
+
+void SwDoc::SetBoxAlign( const SwCursor& rCursor, sal_uInt16 nAlign )
+{
+ OSL_ENSURE( nAlign == text::VertOrientation::NONE ||
+ nAlign == text::VertOrientation::CENTER ||
+ nAlign == text::VertOrientation::BOTTOM, "Wrong alignment" );
+ SwFormatVertOrient aVertOri( 0, nAlign );
+ SetBoxAttr( rCursor, aVertOri );
+}
+
+sal_uInt16 SwDoc::GetBoxAlign( const SwCursor& rCursor )
+{
+ sal_uInt16 nAlign = USHRT_MAX;
+ SwTableNode* pTableNd = rCursor.GetPoint()->nNode.GetNode().FindTableNode();
+ SwSelBoxes aBoxes;
+ if( pTableNd && ::lcl_GetBoxSel( rCursor, aBoxes ))
+ {
+ for (size_t i = 0; i < aBoxes.size(); ++i)
+ {
+ const SwFormatVertOrient &rOri =
+ aBoxes[i]->GetFrameFormat()->GetVertOrient();
+ if( USHRT_MAX == nAlign )
+ nAlign = o3tl::narrowing<sal_uInt16>(rOri.GetVertOrient());
+ else if( rOri.GetVertOrient() != nAlign )
+ {
+ nAlign = USHRT_MAX;
+ break;
+ }
+ }
+ }
+ return nAlign;
+}
+
+static sal_uInt16 lcl_CalcCellFit( const SwLayoutFrame *pCell )
+{
+ SwTwips nRet = 0;
+ const SwFrame *pFrame = pCell->Lower(); // The whole Line
+ SwRectFnSet aRectFnSet(pCell);
+ while ( pFrame )
+ {
+ const SwTwips nAdd = aRectFnSet.GetWidth(pFrame->getFrameArea()) -
+ aRectFnSet.GetWidth(pFrame->getFramePrintArea());
+
+ // pFrame does not necessarily have to be a SwTextFrame!
+ const SwTwips nCalcFitToContent = pFrame->IsTextFrame() ?
+ const_cast<SwTextFrame*>(static_cast<const SwTextFrame*>(pFrame))->CalcFitToContent() :
+ aRectFnSet.GetWidth(pFrame->getFramePrintArea());
+
+ nRet = std::max( nRet, nCalcFitToContent + nAdd );
+ pFrame = pFrame->GetNext();
+ }
+ // Surrounding border as well as left and Right Border also need to be respected
+ nRet += aRectFnSet.GetWidth(pCell->getFrameArea()) -
+ aRectFnSet.GetWidth(pCell->getFramePrintArea());
+
+ // To compensate for the accuracy of calculation later on in SwTable::SetTabCols
+ // we keep adding up a little.
+ nRet += COLFUZZY;
+ return o3tl::narrowing<sal_uInt16>(std::max( SwTwips(MINLAY), nRet ));
+}
+
+/* The Line is within the Selection but not outlined by the TabCols.
+ *
+ * That means that the Line has been "split" by other Cells due to the
+ * two-dimensional representation used. Thus, we have to distribute the cell's
+ * default or minimum value amongst the Cell it has been split by.
+ *
+ * First, we collect the Columns (not the Column separators) which overlap
+ * with the Cell. We then distribute the desired value according to the
+ * amount of overlapping amongst the Cells.
+ *
+ * A Cell's default value stays the same if it already has a larger value than
+ * the desired one. It's overwritten if it's smaller.
+ */
+static void lcl_CalcSubColValues( std::vector<sal_uInt16> &rToFill, const SwTabCols &rCols,
+ const SwLayoutFrame *pCell, const SwLayoutFrame *pTab,
+ bool bWishValues )
+{
+ const sal_uInt16 nWish = bWishValues ?
+ ::lcl_CalcCellFit( pCell ) :
+ MINLAY + sal_uInt16(pCell->getFrameArea().Width() - pCell->getFramePrintArea().Width());
+
+ SwRectFnSet aRectFnSet(pTab);
+
+ for ( size_t i = 0 ; i <= rCols.Count(); ++i )
+ {
+ tools::Long nColLeft = i == 0 ? rCols.GetLeft() : rCols[i-1];
+ tools::Long nColRight = i == rCols.Count() ? rCols.GetRight() : rCols[i];
+ nColLeft += rCols.GetLeftMin();
+ nColRight += rCols.GetLeftMin();
+
+ // Adapt values to the proportions of the Table (Follows)
+ if ( rCols.GetLeftMin() != aRectFnSet.GetLeft(pTab->getFrameArea()) )
+ {
+ const tools::Long nDiff = aRectFnSet.GetLeft(pTab->getFrameArea()) - rCols.GetLeftMin();
+ nColLeft += nDiff;
+ nColRight += nDiff;
+ }
+ const tools::Long nCellLeft = aRectFnSet.GetLeft(pCell->getFrameArea());
+ const tools::Long nCellRight = aRectFnSet.GetRight(pCell->getFrameArea());
+
+ // Calculate overlapping value
+ tools::Long nWidth = 0;
+ if ( nColLeft <= nCellLeft && nColRight >= (nCellLeft+COLFUZZY) )
+ nWidth = nColRight - nCellLeft;
+ else if ( nColLeft <= (nCellRight-COLFUZZY) && nColRight >= nCellRight )
+ nWidth = nCellRight - nColLeft;
+ else if ( nColLeft >= nCellLeft && nColRight <= nCellRight )
+ nWidth = nColRight - nColLeft;
+ if ( nWidth && pCell->getFrameArea().Width() )
+ {
+ tools::Long nTmp = nWidth * nWish / pCell->getFrameArea().Width();
+ if ( o3tl::make_unsigned(nTmp) > rToFill[i] )
+ rToFill[i] = sal_uInt16(nTmp);
+ }
+ }
+}
+
+/**
+ * Retrieves new values to set the TabCols.
+ *
+ * We do not iterate over the TabCols' entries, but over the gaps that describe Cells.
+ * We set TabCol entries for which we did not calculate Cells to 0.
+ *
+ * @param bWishValues == true: We calculate the desired value of all affected
+ * Cells for the current Selection/current Cell.
+ * If more Cells are within a Column, the highest
+ * desired value is returned.
+ * We set TabCol entries for which we did not calculate
+ * Cells to 0.
+ *
+ * @param bWishValues == false: The Selection is expanded vertically.
+ * We calculate the minimum value for every
+ * Column in the TabCols that intersects with the
+ * Selection.
+ */
+static void lcl_CalcColValues( std::vector<sal_uInt16> &rToFill, const SwTabCols &rCols,
+ const SwLayoutFrame *pStart, const SwLayoutFrame *pEnd,
+ bool bWishValues )
+{
+ SwSelUnions aUnions;
+ ::MakeSelUnions( aUnions, pStart, pEnd,
+ bWishValues ? SwTableSearchType::NONE : SwTableSearchType::Col );
+
+ for ( auto &rU : aUnions )
+ {
+ SwSelUnion *pSelUnion = &rU;
+ const SwTabFrame *pTab = pSelUnion->GetTable();
+ const SwRect &rUnion = pSelUnion->GetUnion();
+
+ SwRectFnSet aRectFnSet(pTab);
+ bool bRTL = pTab->IsRightToLeft();
+
+ const SwLayoutFrame *pCell = pTab->FirstCell();
+ if (!pCell)
+ continue;
+ do
+ {
+ if ( pCell->IsCellFrame() && pCell->FindTabFrame() == pTab && ::IsFrameInTableSel( rUnion, pCell ) )
+ {
+ const tools::Long nCLeft = aRectFnSet.GetLeft(pCell->getFrameArea());
+ const tools::Long nCRight = aRectFnSet.GetRight(pCell->getFrameArea());
+
+ bool bNotInCols = true;
+
+ for ( size_t i = 0; i <= rCols.Count(); ++i )
+ {
+ sal_uInt16 nFit = rToFill[i];
+ tools::Long nColLeft = i == 0 ? rCols.GetLeft() : rCols[i-1];
+ tools::Long nColRight = i == rCols.Count() ? rCols.GetRight() : rCols[i];
+
+ if ( bRTL )
+ {
+ tools::Long nTmpRight = nColRight;
+ nColRight = rCols.GetRight() - nColLeft;
+ nColLeft = rCols.GetRight() - nTmpRight;
+ }
+
+ nColLeft += rCols.GetLeftMin();
+ nColRight += rCols.GetLeftMin();
+
+ // Adapt values to the proportions of the Table (Follows)
+ tools::Long nLeftA = nColLeft;
+ tools::Long nRightA = nColRight;
+ if ( rCols.GetLeftMin() != sal_uInt16(aRectFnSet.GetLeft(pTab->getFrameArea())) )
+ {
+ const tools::Long nDiff = aRectFnSet.GetLeft(pTab->getFrameArea()) - rCols.GetLeftMin();
+ nLeftA += nDiff;
+ nRightA += nDiff;
+ }
+
+ // We don't want to take a too close look
+ if ( ::IsSame(nCLeft, nLeftA) && ::IsSame(nCRight, nRightA))
+ {
+ bNotInCols = false;
+ if ( bWishValues )
+ {
+ const sal_uInt16 nWish = ::lcl_CalcCellFit( pCell );
+ if ( nWish > nFit )
+ nFit = nWish;
+ }
+ else
+ { const sal_uInt16 nMin = MINLAY + sal_uInt16(pCell->getFrameArea().Width() -
+ pCell->getFramePrintArea().Width());
+ if ( !nFit || nMin < nFit )
+ nFit = nMin;
+ }
+ if ( rToFill[i] < nFit )
+ rToFill[i] = nFit;
+ }
+ }
+ if ( bNotInCols )
+ ::lcl_CalcSubColValues( rToFill, rCols, pCell, pTab, bWishValues );
+ }
+ do {
+ pCell = pCell->GetNextLayoutLeaf();
+ } while( pCell && pCell->getFrameArea().Width() == 0 );
+ } while ( pCell && pTab->IsAnLower( pCell ) );
+ }
+}
+
+void SwDoc::AdjustCellWidth( const SwCursor& rCursor,
+ const bool bBalance,
+ const bool bNoShrink )
+{
+ // Check whether the current Cursor has it's Point/Mark in a Table
+ SwContentNode* pCntNd = rCursor.GetPoint()->nNode.GetNode().GetContentNode();
+ SwTableNode* pTableNd = pCntNd ? pCntNd->FindTableNode() : nullptr;
+ if( !pTableNd )
+ return ;
+
+ SwLayoutFrame *pStart, *pEnd;
+ ::lcl_GetStartEndCell( rCursor, pStart, pEnd );
+
+ // Collect TabCols; we reset the Table with them
+ SwFrame* pBoxFrame = pStart;
+ while( pBoxFrame && !pBoxFrame->IsCellFrame() )
+ pBoxFrame = pBoxFrame->GetUpper();
+
+ if ( !pBoxFrame )
+ return; // Robust
+
+ SwTabCols aTabCols;
+ GetTabCols( aTabCols, static_cast<SwCellFrame*>(pBoxFrame) );
+
+ if ( ! aTabCols.Count() )
+ return;
+
+ std::vector<sal_uInt16> aWish(aTabCols.Count() + 1);
+ std::vector<sal_uInt16> aMins(aTabCols.Count() + 1);
+
+ ::lcl_CalcColValues( aWish, aTabCols, pStart, pEnd, /*bWishValues=*/true );
+
+ // It's more robust if we calculate the minimum values for the whole Table
+ const SwTabFrame *pTab = pStart->ImplFindTabFrame();
+ pStart = const_cast<SwLayoutFrame*>(static_cast<SwLayoutFrame const *>(pTab->FirstCell()));
+ pEnd = const_cast<SwLayoutFrame*>(pTab->FindLastContentOrTable()->GetUpper());
+ while( !pEnd->IsCellFrame() )
+ pEnd = pEnd->GetUpper();
+ ::lcl_CalcColValues( aMins, aTabCols, pStart, pEnd, /*bWishValues=*/false );
+
+ sal_uInt16 nSelectedWidth = 0, nCols = 0;
+ float fTotalWish = 0;
+ if ( bBalance || bNoShrink )
+ {
+ // Find the combined size of the selected columns
+ for ( size_t i = 0; i <= aTabCols.Count(); ++i )
+ {
+ if ( aWish[i] )
+ {
+ if ( i == 0 )
+ nSelectedWidth += aTabCols[i] - aTabCols.GetLeft();
+ else if ( i == aTabCols.Count() )
+ nSelectedWidth += aTabCols.GetRight() - aTabCols[i-1];
+ else
+ nSelectedWidth += aTabCols[i] - aTabCols[i-1];
+ ++nCols;
+ }
+ fTotalWish += aWish[i];
+ }
+ assert(nCols);
+ const sal_uInt16 nEqualWidth = nCols ? nSelectedWidth / nCols : 0;
+ // bBalance: Distribute the width evenly
+ for (sal_uInt16 & rn : aWish)
+ if ( rn && bBalance )
+ rn = nEqualWidth;
+ }
+
+ const tools::Long nOldRight = aTabCols.GetRight();
+
+ // In order to make the implementation easier, but still use the available
+ // space properly, we do this twice.
+
+ // The problem: The first column is getting wider, the others get slimmer
+ // only afterwards.
+ // The first column's desired width would be discarded as it would cause
+ // the Table's width to exceed the maximum width.
+ const sal_uInt16 nEqualWidth = (aTabCols.GetRight() - aTabCols.GetLeft()) / (aTabCols.Count() + 1);
+ const sal_Int16 nTablePadding = nSelectedWidth - fTotalWish;
+ for ( int k = 0; k < 2; ++k )
+ {
+ for ( size_t i = 0; i <= aTabCols.Count(); ++i )
+ {
+ // bNoShrink: distribute excess space proportionately on pass 2.
+ if ( bNoShrink && k && nTablePadding > 0 && fTotalWish > 0 )
+ aWish[i] += round( aWish[i] / fTotalWish * nTablePadding );
+
+ // First pass is primarily a shrink pass. Give all columns a chance
+ // to grow by requesting the maximum width as "balanced".
+ // Second pass is a first-come, first-served chance to max out.
+ int nDiff = k ? aWish[i] : std::min(aWish[i], nEqualWidth);
+ if ( nDiff )
+ {
+ int nMin = aMins[i];
+ if ( nMin > nDiff )
+ nDiff = nMin;
+
+ if ( i == 0 )
+ {
+ if( aTabCols.Count() )
+ nDiff -= aTabCols[0] - aTabCols.GetLeft();
+ else
+ nDiff -= aTabCols.GetRight() - aTabCols.GetLeft();
+ }
+ else if ( i == aTabCols.Count() )
+ nDiff -= aTabCols.GetRight() - aTabCols[i-1];
+ else
+ nDiff -= aTabCols[i] - aTabCols[i-1];
+
+ tools::Long nTabRight = aTabCols.GetRight() + nDiff;
+ const tools::Long nMaxRight = std::max(aTabCols.GetRightMax(), nOldRight);
+
+ // If the Table would become (or is already) too wide,
+ // restrict the column growth to the allowed maximum.
+ if (!bBalance && nTabRight > nMaxRight)
+ {
+ const tools::Long nTmpD = nTabRight - nMaxRight;
+ nDiff -= nTmpD;
+ nTabRight -= nTmpD;
+ }
+
+ // all the remaining columns need to be shifted by the same amount
+ for ( size_t i2 = i; i2 < aTabCols.Count(); ++i2 )
+ aTabCols[i2] += nDiff;
+ aTabCols.SetRight( nTabRight );
+ }
+ }
+ }
+
+ const tools::Long nNewRight = aTabCols.GetRight();
+
+ SwFrameFormat *pFormat = pTableNd->GetTable().GetFrameFormat();
+ const sal_Int16 nOriHori = pFormat->GetHoriOrient().GetHoriOrient();
+
+ // We can leave the "real" work to the SwTable now
+ SetTabCols( aTabCols, false, static_cast<SwCellFrame*>(pBoxFrame) );
+
+ // Alignment might have been changed in SetTabCols; restore old value
+ const SwFormatHoriOrient &rHori = pFormat->GetHoriOrient();
+ SwFormatHoriOrient aHori( rHori );
+ if ( aHori.GetHoriOrient() != nOriHori )
+ {
+ aHori.SetHoriOrient( nOriHori );
+ pFormat->SetFormatAttr( aHori );
+ }
+
+ // We switch to left-adjusted for automatic width
+ // We adjust the right border for Border attributes
+ if( !bBalance && nNewRight < nOldRight )
+ {
+ if( aHori.GetHoriOrient() == text::HoriOrientation::FULL )
+ {
+ aHori.SetHoriOrient( text::HoriOrientation::LEFT );
+ pFormat->SetFormatAttr( aHori );
+ }
+ }
+
+ getIDocumentState().SetModified();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/docnode/node.cxx b/sw/source/core/docnode/node.cxx
new file mode 100644
index 000000000..d95b18cee
--- /dev/null
+++ b/sw/source/core/docnode/node.cxx
@@ -0,0 +1,2138 @@
+/* -*- 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_wasm_strip.h>
+
+#include <hintids.hxx>
+#include <editeng/protitem.hxx>
+#include <osl/diagnose.h>
+#include <tools/gen.hxx>
+#include <com/sun/star/i18n/CharacterIteratorMode.hpp>
+#include <com/sun/star/i18n/XBreakIterator.hpp>
+#include <fmtcntnt.hxx>
+#include <fmtanchr.hxx>
+#include <frmfmt.hxx>
+#include <txtftn.hxx>
+#include <ftnfrm.hxx>
+#include <doc.hxx>
+#include <node.hxx>
+#include <ndindex.hxx>
+#include <numrule.hxx>
+#include <swtable.hxx>
+#include <ndtxt.hxx>
+#include <pam.hxx>
+#include <section.hxx>
+#include <cntfrm.hxx>
+#include <flyfrm.hxx>
+#include <txtfrm.hxx>
+#include <tabfrm.hxx>
+#include <viewsh.hxx>
+#include <paratr.hxx>
+#include <ftnidx.hxx>
+#include <fmtftn.hxx>
+#include <fmthdft.hxx>
+#include <frmatr.hxx>
+#include <fmtautofmt.hxx>
+#include <frmtool.hxx>
+#include <pagefrm.hxx>
+#include <node2lay.hxx>
+#include <pagedesc.hxx>
+#include <fmtpdsc.hxx>
+#include <breakit.hxx>
+#include <SwStyleNameMapper.hxx>
+#include <scriptinfo.hxx>
+#include <rootfrm.hxx>
+#include <istyleaccess.hxx>
+#include <IDocumentListItems.hxx>
+#include <DocumentSettingManager.hxx>
+#include <IDocumentLinksAdministration.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <calbck.hxx>
+#include <ndole.hxx>
+#include <memory>
+#include <swcrsr.hxx>
+#include <hints.hxx>
+#include <frameformats.hxx>
+#ifdef DBG_UTIL
+#include <sal/backtrace.hxx>
+#endif
+
+using namespace ::com::sun::star::i18n;
+
+
+/*
+ * Some local helper functions for the attribute set handle of a content node.
+ * Since the attribute set of a content node may not be modified directly,
+ * we always have to create a new SwAttrSet, do the modifications, and get
+ * a new handle from the style access
+ */
+
+namespace AttrSetHandleHelper
+{
+
+static void GetNewAutoStyle( std::shared_ptr<const SfxItemSet>& rpAttrSet,
+ const SwContentNode& rNode,
+ SwAttrSet const & rNewAttrSet )
+{
+ const SwAttrSet* pAttrSet = static_cast<const SwAttrSet*>(rpAttrSet.get());
+ if( rNode.GetModifyAtAttr() )
+ const_cast<SwAttrSet*>(pAttrSet)->SetModifyAtAttr( nullptr );
+ IStyleAccess& rSA = pAttrSet->GetPool()->GetDoc()->GetIStyleAccess();
+ rpAttrSet = rSA.getAutomaticStyle( rNewAttrSet, rNode.IsTextNode() ?
+ IStyleAccess::AUTO_STYLE_PARA :
+ IStyleAccess::AUTO_STYLE_NOTXT );
+ const bool bSetModifyAtAttr = const_cast<SwAttrSet*>(static_cast<const SwAttrSet*>(rpAttrSet.get()))->SetModifyAtAttr( &rNode );
+ rNode.SetModifyAtAttr( bSetModifyAtAttr );
+}
+
+static void SetParent( std::shared_ptr<const SfxItemSet>& rpAttrSet,
+ const SwContentNode& rNode,
+ const SwFormat* pParentFormat,
+ const SwFormat* pConditionalFormat )
+{
+ const SwAttrSet* pAttrSet = static_cast<const SwAttrSet*>(rpAttrSet.get());
+ OSL_ENSURE( pAttrSet, "no SwAttrSet" );
+ OSL_ENSURE( pParentFormat || !pConditionalFormat, "ConditionalFormat without ParentFormat?" );
+
+ const SwAttrSet* pParentSet = pParentFormat ? &pParentFormat->GetAttrSet() : nullptr;
+
+ if ( pParentSet == pAttrSet->GetParent() )
+ return;
+
+ SwAttrSet aNewSet( *pAttrSet );
+ aNewSet.SetParent( pParentSet );
+ aNewSet.ClearItem( RES_FRMATR_STYLE_NAME );
+ aNewSet.ClearItem( RES_FRMATR_CONDITIONAL_STYLE_NAME );
+
+ if ( pParentFormat )
+ {
+ OUString sVal;
+ SwStyleNameMapper::FillProgName( pParentFormat->GetName(), sVal, SwGetPoolIdFromName::TxtColl );
+ const SfxStringItem aAnyFormatColl( RES_FRMATR_STYLE_NAME, sVal );
+ aNewSet.Put( aAnyFormatColl );
+
+ if ( pConditionalFormat != pParentFormat )
+ SwStyleNameMapper::FillProgName( pConditionalFormat->GetName(), sVal, SwGetPoolIdFromName::TxtColl );
+
+ const SfxStringItem aFormatColl( RES_FRMATR_CONDITIONAL_STYLE_NAME, sVal );
+ aNewSet.Put( aFormatColl );
+ }
+
+ GetNewAutoStyle( rpAttrSet, rNode, aNewSet );
+}
+
+static const SfxPoolItem* Put( std::shared_ptr<const SfxItemSet>& rpAttrSet,
+ const SwContentNode& rNode,
+ const SfxPoolItem& rAttr )
+{
+ SwAttrSet aNewSet( static_cast<const SwAttrSet&>(*rpAttrSet) );
+ const SfxPoolItem* pRet = aNewSet.Put( rAttr );
+ if ( pRet )
+ GetNewAutoStyle( rpAttrSet, rNode, aNewSet );
+ return pRet;
+}
+
+static bool Put( std::shared_ptr<const SfxItemSet>& rpAttrSet, const SwContentNode& rNode,
+ const SfxItemSet& rSet )
+{
+ SwAttrSet aNewSet( static_cast<const SwAttrSet&>(*rpAttrSet) );
+
+ // #i76273# Robust
+ std::optional<SfxItemSetFixed<RES_FRMATR_STYLE_NAME, RES_FRMATR_CONDITIONAL_STYLE_NAME>> pStyleNames;
+ if ( SfxItemState::SET == rSet.GetItemState( RES_FRMATR_STYLE_NAME, false ) )
+ {
+ pStyleNames.emplace( *aNewSet.GetPool() );
+ pStyleNames->Put( aNewSet );
+ }
+
+ const bool bRet = aNewSet.Put( rSet );
+
+ // #i76273# Robust
+ if ( pStyleNames )
+ {
+ aNewSet.Put( *pStyleNames );
+ }
+
+ if ( bRet )
+ GetNewAutoStyle( rpAttrSet, rNode, aNewSet );
+
+ return bRet;
+}
+
+static bool Put_BC( std::shared_ptr<const SfxItemSet>& rpAttrSet,
+ const SwContentNode& rNode, const SfxPoolItem& rAttr,
+ SwAttrSet* pOld, SwAttrSet* pNew )
+{
+ SwAttrSet aNewSet( static_cast<const SwAttrSet&>(*rpAttrSet) );
+
+ // for a correct broadcast, we need to do a SetModifyAtAttr with the items
+ // from aNewSet. The 'regular' SetModifyAtAttr is done in GetNewAutoStyle
+ if( rNode.GetModifyAtAttr() )
+ aNewSet.SetModifyAtAttr( &rNode );
+
+ const bool bRet = aNewSet.Put_BC( rAttr, pOld, pNew );
+
+ if ( bRet )
+ GetNewAutoStyle( rpAttrSet, rNode, aNewSet );
+
+ return bRet;
+}
+
+static bool Put_BC( std::shared_ptr<const SfxItemSet>& rpAttrSet,
+ const SwContentNode& rNode, const SfxItemSet& rSet,
+ SwAttrSet* pOld, SwAttrSet* pNew )
+{
+ SwAttrSet aNewSet( static_cast<const SwAttrSet&>(*rpAttrSet) );
+
+ // #i76273# Robust
+ std::optional<SfxItemSetFixed<RES_FRMATR_STYLE_NAME, RES_FRMATR_CONDITIONAL_STYLE_NAME>> pStyleNames;
+ if ( SfxItemState::SET == rSet.GetItemState( RES_FRMATR_STYLE_NAME, false ) )
+ {
+ pStyleNames.emplace( *aNewSet.GetPool() );
+ pStyleNames->Put( aNewSet );
+ }
+
+ // for a correct broadcast, we need to do a SetModifyAtAttr with the items
+ // from aNewSet. The 'regular' SetModifyAtAttr is done in GetNewAutoStyle
+ if( rNode.GetModifyAtAttr() )
+ aNewSet.SetModifyAtAttr( &rNode );
+
+ const bool bRet = aNewSet.Put_BC( rSet, pOld, pNew );
+
+ // #i76273# Robust
+ if ( pStyleNames )
+ {
+ aNewSet.Put( *pStyleNames );
+ }
+
+ if ( bRet )
+ GetNewAutoStyle( rpAttrSet, rNode, aNewSet );
+
+ return bRet;
+}
+
+static sal_uInt16 ClearItem_BC( std::shared_ptr<const SfxItemSet>& rpAttrSet,
+ const SwContentNode& rNode, sal_uInt16 nWhich,
+ SwAttrSet* pOld, SwAttrSet* pNew )
+{
+ SwAttrSet aNewSet( static_cast<const SwAttrSet&>(*rpAttrSet) );
+ if( rNode.GetModifyAtAttr() )
+ aNewSet.SetModifyAtAttr( &rNode );
+ const sal_uInt16 nRet = aNewSet.ClearItem_BC( nWhich, pOld, pNew );
+ if ( nRet )
+ GetNewAutoStyle( rpAttrSet, rNode, aNewSet );
+ return nRet;
+}
+
+static sal_uInt16 ClearItem_BC( std::shared_ptr<const SfxItemSet>& rpAttrSet,
+ const SwContentNode& rNode,
+ sal_uInt16 nWhich1, sal_uInt16 nWhich2,
+ SwAttrSet* pOld, SwAttrSet* pNew )
+{
+ SwAttrSet aNewSet( static_cast<const SwAttrSet&>(*rpAttrSet) );
+ if( rNode.GetModifyAtAttr() )
+ aNewSet.SetModifyAtAttr( &rNode );
+ const sal_uInt16 nRet = aNewSet.ClearItem_BC( nWhich1, nWhich2, pOld, pNew );
+ if ( nRet )
+ GetNewAutoStyle( rpAttrSet, rNode, aNewSet );
+ return nRet;
+}
+
+}
+
+/** Returns the section level at the position given by aIndex.
+ *
+ * We use the following logic:
+ * S = Start, E = End, C = ContentNode
+ * Level 0 = E
+ * 1 = S E
+ * 2 = SC
+ *
+ * All EndNodes of the BaseSection have level 0
+ * All StartNodes of the BaseSection have level 1
+ */
+sal_uInt16 SwNode::GetSectionLevel() const
+{
+ // EndNode of a BaseSection? They are always 0!
+ if( IsEndNode() && SwNodeOffset(0) == m_pStartOfSection->StartOfSectionIndex() )
+ return 0;
+
+ sal_uInt16 nLevel;
+ const SwNode* pNode = IsStartNode() ? this : m_pStartOfSection;
+ for( nLevel = 1; SwNodeOffset(0) != pNode->StartOfSectionIndex(); ++nLevel )
+ pNode = pNode->m_pStartOfSection;
+ return IsEndNode() ? nLevel-1 : nLevel;
+}
+
+#ifdef DBG_UTIL
+tools::Long SwNode::s_nSerial = 0;
+#endif
+
+SwNode::SwNode( const SwNodeIndex &rWhere, const SwNodeType nNdType )
+ : m_nNodeType( nNdType )
+ , m_nAFormatNumLvl( 0 )
+ , m_bIgnoreDontExpand( false)
+ , m_eMerge(Merge::None)
+#ifdef DBG_UTIL
+ , m_nSerial( s_nSerial++)
+#endif
+ , m_pStartOfSection( nullptr )
+{
+ if( !rWhere.GetIndex() )
+ return;
+
+ SwNodes& rNodes = const_cast<SwNodes&> (rWhere.GetNodes());
+ SwNode* pNd = rNodes[ rWhere.GetIndex() -1 ];
+ rNodes.InsertNode( this, rWhere );
+ m_pStartOfSection = pNd->GetStartNode();
+ if( nullptr == m_pStartOfSection )
+ {
+ m_pStartOfSection = pNd->m_pStartOfSection;
+ if( pNd->GetEndNode() ) // Skip EndNode ? Section
+ {
+ pNd = m_pStartOfSection;
+ m_pStartOfSection = pNd->m_pStartOfSection;
+ }
+ }
+}
+
+/** Inserts a node into the rNodes array at the rWhere position
+ *
+ * @param rNodes the variable array in that the node will be inserted
+ * @param nPos position within the array where the node will be inserted
+ * @param nNdType the type of node to insert
+ */
+SwNode::SwNode( SwNodes& rNodes, SwNodeOffset nPos, const SwNodeType nNdType )
+ : m_nNodeType( nNdType )
+ , m_nAFormatNumLvl( 0 )
+ , m_bIgnoreDontExpand( false)
+ , m_eMerge(Merge::None)
+#ifdef DBG_UTIL
+ , m_nSerial( s_nSerial++)
+#endif
+ , m_pStartOfSection( nullptr )
+{
+ if( !nPos )
+ return;
+
+ SwNode* pNd = rNodes[ nPos - 1 ];
+ rNodes.InsertNode( this, nPos );
+ m_pStartOfSection = pNd->GetStartNode();
+ if( nullptr == m_pStartOfSection )
+ {
+ m_pStartOfSection = pNd->m_pStartOfSection;
+ if( pNd->GetEndNode() ) // Skip EndNode ? Section!
+ {
+ pNd = m_pStartOfSection;
+ m_pStartOfSection = pNd->m_pStartOfSection;
+ }
+ }
+}
+
+SwNode::~SwNode()
+{
+ assert(m_aAnchoredFlys.empty() || GetDoc().IsInDtor()); // must all be deleted
+ InvalidateInSwCache(RES_OBJECTDYING);
+ assert(!IsInCache());
+}
+
+/// Find the TableNode in which it is located.
+/// If we're not in a table: return 0
+SwTableNode* SwNode::FindTableNode()
+{
+ if( IsTableNode() )
+ return GetTableNode();
+ SwStartNode* pTmp = m_pStartOfSection;
+ while( !pTmp->IsTableNode() && pTmp->GetIndex() )
+ pTmp = pTmp->m_pStartOfSection;
+ return pTmp->GetTableNode();
+}
+
+/// Is the node located in the visible area of the Shell?
+bool SwNode::IsInVisibleArea( SwViewShell const * pSh ) const
+{
+ bool bRet = false;
+ const SwContentNode* pNd;
+
+ if( SwNodeType::Start & m_nNodeType )
+ {
+ SwNodeIndex aIdx( *this );
+ pNd = GetNodes().GoNext( &aIdx );
+ }
+ else if( SwNodeType::End & m_nNodeType )
+ {
+ SwNodeIndex aIdx( *EndOfSectionNode() );
+ pNd = SwNodes::GoPrevious( &aIdx );
+ }
+ else
+ pNd = GetContentNode();
+
+ if( !pSh )
+ // Get the Shell from the Doc
+ pSh = GetDoc().getIDocumentLayoutAccess().GetCurrentViewShell();
+
+ if( pSh )
+ {
+ const SwFrame* pFrame;
+ if (pNd && nullptr != (pFrame = pNd->getLayoutFrame(pSh->GetLayout(), nullptr, nullptr)))
+ {
+
+ if ( pFrame->IsInTab() )
+ pFrame = pFrame->FindTabFrame();
+
+ if( !pFrame->isFrameAreaDefinitionValid() )
+ {
+ do
+ {
+ pFrame = pFrame->FindPrev();
+ }
+ while ( pFrame && !pFrame->isFrameAreaDefinitionValid() );
+ }
+
+ if( !pFrame || pSh->VisArea().Overlaps( pFrame->getFrameArea() ) )
+ bRet = true;
+ }
+ }
+
+ return bRet;
+}
+
+bool SwNode::IsInProtectSect() const
+{
+ const SwNode* pNd = SwNodeType::Section == m_nNodeType ? m_pStartOfSection : this;
+ const SwSectionNode* pSectNd = pNd->FindSectionNode();
+ return pSectNd && pSectNd->GetSection().IsProtectFlag();
+}
+
+/// Does the node contain anything protected?
+/// I.e.: Area/Frame/Table rows/... including the Anchor for
+/// Frames/Footnotes/...
+bool SwNode::IsProtect() const
+{
+ const SwNode* pNd = SwNodeType::Section == m_nNodeType ? m_pStartOfSection : this;
+ const SwStartNode* pSttNd = pNd->FindSectionNode();
+ if( pSttNd && static_cast<const SwSectionNode*>(pSttNd)->GetSection().IsProtectFlag() )
+ return true;
+
+ pSttNd = FindTableBoxStartNode();
+ if( nullptr != pSttNd )
+ {
+ SwContentFrame* pCFrame;
+ if( IsContentNode() && nullptr != (pCFrame = static_cast<const SwContentNode*>(this)->getLayoutFrame( GetDoc().getIDocumentLayoutAccess().GetCurrentLayout() ) ))
+ return pCFrame->IsProtected();
+
+ const SwTableBox* pBox = pSttNd->FindTableNode()->GetTable().
+ GetTableBox( pSttNd->GetIndex() );
+ //Robust #149568
+ if( pBox && pBox->GetFrameFormat()->GetProtect().IsContentProtected() )
+ return true;
+ }
+
+ SwFrameFormat* pFlyFormat = GetFlyFormat();
+ if( pFlyFormat )
+ {
+ if (pFlyFormat->GetProtect().IsContentProtected())
+ return true;
+ const SwFormatAnchor& rAnchor = pFlyFormat->GetAnchor();
+ const SwPosition* pAnchorPos = rAnchor.GetContentAnchor();
+ if (!pAnchorPos)
+ return false;
+ const SwNode& rAnchorNd = pAnchorPos->nNode.GetNode();
+ return &rAnchorNd != this && rAnchorNd.IsProtect();
+ }
+
+ pSttNd = FindFootnoteStartNode();
+ if( nullptr != pSttNd )
+ {
+ const SwTextFootnote* pTFootnote = GetDoc().GetFootnoteIdxs().SeekEntry(
+ SwNodeIndex( *pSttNd ) );
+ if( pTFootnote )
+ return pTFootnote->GetTextNode().IsProtect();
+ }
+
+ return false;
+}
+
+/// Find the PageDesc that is used to format this node. If the Layout is available,
+/// we search through that. Else we can only do it the hard way by searching onwards through the nodes.
+const SwPageDesc* SwNode::FindPageDesc( SwNodeOffset* pPgDescNdIdx ) const
+{
+ if ( !GetNodes().IsDocNodes() )
+ {
+ return nullptr;
+ }
+
+ const SwPageDesc* pPgDesc = nullptr;
+
+ const SwContentNode* pNode;
+ if( SwNodeType::Start & m_nNodeType )
+ {
+ SwNodeIndex aIdx( *this );
+ pNode = GetNodes().GoNext( &aIdx );
+ }
+ else if( SwNodeType::End & m_nNodeType )
+ {
+ SwNodeIndex aIdx( *EndOfSectionNode() );
+ pNode = SwNodes::GoPrevious( &aIdx );
+ }
+ else
+ {
+ pNode = GetContentNode();
+ if( pNode )
+ pPgDesc = pNode->GetAttr( RES_PAGEDESC ).GetPageDesc();
+ }
+
+ // Are we going through the layout?
+ if( !pPgDesc )
+ {
+ const SwFrame* pFrame;
+ const SwPageFrame* pPage;
+ if (pNode && nullptr != (pFrame = pNode->getLayoutFrame(pNode->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(), nullptr, nullptr)) &&
+ nullptr != ( pPage = pFrame->FindPageFrame() ) )
+ {
+ pPgDesc = pPage->GetPageDesc();
+ if ( pPgDescNdIdx )
+ {
+ *pPgDescNdIdx = pNode->GetIndex();
+ }
+ }
+ }
+
+ if( !pPgDesc )
+ {
+ // Thus via the nodes array
+ const SwDoc& rDoc = GetDoc();
+ const SwNode* pNd = this;
+ const SwStartNode* pSttNd;
+ if( pNd->GetIndex() < GetNodes().GetEndOfExtras().GetIndex() &&
+ nullptr != ( pSttNd = pNd->FindFlyStartNode() ) )
+ {
+ // Find the right Anchor first
+ const SwFrameFormat* pFormat = nullptr;
+ const SwFrameFormats& rFormats = *rDoc.GetSpzFrameFormats();
+
+ for( size_t n = 0; n < rFormats.size(); ++n )
+ {
+ const SwFrameFormat* pFrameFormat = rFormats[ n ];
+ const SwFormatContent& rContent = pFrameFormat->GetContent();
+ if( rContent.GetContentIdx() &&
+ &rContent.GetContentIdx()->GetNode() == static_cast<SwNode const *>(pSttNd) )
+ {
+ pFormat = pFrameFormat;
+ break;
+ }
+ }
+
+ if( pFormat )
+ {
+ const SwFormatAnchor* pAnchor = &pFormat->GetAnchor();
+ if ((RndStdIds::FLY_AT_PAGE != pAnchor->GetAnchorId()) &&
+ pAnchor->GetContentAnchor() )
+ {
+ pNd = &pAnchor->GetContentAnchor()->nNode.GetNode();
+ const SwNode* pFlyNd = pNd->FindFlyStartNode();
+ while( pFlyNd )
+ {
+ // Get up through the Anchor
+ size_t n;
+ for( n = 0; n < rFormats.size(); ++n )
+ {
+ const SwFrameFormat* pFrameFormat = rFormats[ n ];
+ const SwNodeIndex* pIdx = pFrameFormat->GetContent().
+ GetContentIdx();
+ if( pIdx && pFlyNd == &pIdx->GetNode() )
+ {
+ if( pFormat == pFrameFormat )
+ {
+ pNd = pFlyNd;
+ pFlyNd = nullptr;
+ break;
+ }
+ pAnchor = &pFrameFormat->GetAnchor();
+ if ((RndStdIds::FLY_AT_PAGE == pAnchor->GetAnchorId()) ||
+ !pAnchor->GetContentAnchor() )
+ {
+ pFlyNd = nullptr;
+ break;
+ }
+
+ pFlyNd = pAnchor->GetContentAnchor()->nNode.
+ GetNode().FindFlyStartNode();
+ break;
+ }
+ }
+ if( n >= rFormats.size() )
+ {
+ OSL_ENSURE( false, "FlySection, but no Format found" );
+ return nullptr;
+ }
+ }
+ }
+ }
+ // pNd should now contain the correct Anchor or it's still this
+ }
+
+ if( pNd->GetIndex() < GetNodes().GetEndOfExtras().GetIndex() )
+ {
+ if( pNd->GetIndex() > GetNodes().GetEndOfAutotext().GetIndex() )
+ {
+ pPgDesc = &rDoc.GetPageDesc( 0 );
+ pNd = nullptr;
+ }
+ else
+ {
+ // Find the Body text node
+ if( nullptr != ( pSttNd = pNd->FindHeaderStartNode() ) ||
+ nullptr != ( pSttNd = pNd->FindFooterStartNode() ))
+ {
+ // Then find this StartNode in the PageDescs
+ sal_uInt16 nId;
+ UseOnPage eAskUse;
+ if( SwHeaderStartNode == pSttNd->GetStartNodeType())
+ {
+ nId = RES_HEADER;
+ eAskUse = UseOnPage::HeaderShare;
+ }
+ else
+ {
+ nId = RES_FOOTER;
+ eAskUse = UseOnPage::FooterShare;
+ }
+
+ for( size_t n = rDoc.GetPageDescCnt(); n && !pPgDesc; )
+ {
+ const SwPageDesc& rPgDsc = rDoc.GetPageDesc( --n );
+ const SwFrameFormat* pFormat = &rPgDsc.GetMaster();
+ int nStt = 0, nLast = 1;
+ if( !( eAskUse & rPgDsc.ReadUseOn() )) ++nLast;
+
+ for( ; nStt < nLast; ++nStt, pFormat = &rPgDsc.GetLeft() )
+ {
+ const SwFrameFormat * pHdFtFormat = nId == RES_HEADER
+ ? static_cast<SwFormatHeader const &>(
+ pFormat->GetFormatAttr(nId)).GetHeaderFormat()
+ : static_cast<SwFormatFooter const &>(
+ pFormat->GetFormatAttr(nId)).GetFooterFormat();
+ if( pHdFtFormat )
+ {
+ const SwFormatContent& rContent = pHdFtFormat->GetContent();
+ if( rContent.GetContentIdx() &&
+ &rContent.GetContentIdx()->GetNode() ==
+ static_cast<SwNode const *>(pSttNd) )
+ {
+ pPgDesc = &rPgDsc;
+ break;
+ }
+ }
+ }
+ }
+
+ if( !pPgDesc )
+ pPgDesc = &rDoc.GetPageDesc( 0 );
+ pNd = nullptr;
+ }
+ else if( nullptr != ( pSttNd = pNd->FindFootnoteStartNode() ))
+ {
+ // the Anchor can only be in the Body text
+ const SwTextFootnote* pTextFootnote;
+ const SwFootnoteIdxs& rFootnoteArr = rDoc.GetFootnoteIdxs();
+ for( size_t n = 0; n < rFootnoteArr.size(); ++n )
+ if( nullptr != ( pTextFootnote = rFootnoteArr[ n ])->GetStartNode() &&
+ static_cast<SwNode const *>(pSttNd) ==
+ &pTextFootnote->GetStartNode()->GetNode() )
+ {
+ pNd = &pTextFootnote->GetTextNode();
+ break;
+ }
+ }
+ else
+ {
+ // Can only be a page-bound Fly (or something newer).
+ // we can only return the standard here
+ OSL_ENSURE( pNd->FindFlyStartNode(),
+ "Where is this Node?" );
+
+ pPgDesc = &rDoc.GetPageDesc( 0 );
+ pNd = nullptr;
+ }
+ }
+ }
+
+ if( pNd )
+ {
+ SwFindNearestNode aInfo( *pNd );
+ // Over all Nodes of all PageDescs
+ for (const SfxPoolItem* pItem : rDoc.GetAttrPool().GetItemSurrogates(RES_PAGEDESC))
+ {
+ auto pPageDescItem = dynamic_cast<const SwFormatPageDesc*>(pItem);
+ if( pPageDescItem && pPageDescItem->GetDefinedIn() )
+ {
+ const sw::BroadcastingModify* pMod = pPageDescItem->GetDefinedIn();
+ if( auto pContentNode = dynamic_cast<const SwContentNode*>( pMod) )
+ aInfo.CheckNode( *pContentNode );
+ else if( auto pFormat = dynamic_cast<const SwFormat*>( pMod) )
+ pFormat->GetInfo( aInfo );
+ }
+ }
+
+ pNd = aInfo.GetFoundNode();
+ if( nullptr != pNd )
+ {
+ if( pNd->IsContentNode() )
+ pPgDesc = pNd->GetContentNode()->GetAttr( RES_PAGEDESC ).GetPageDesc();
+ else if( pNd->IsTableNode() )
+ pPgDesc = pNd->GetTableNode()->GetTable().
+ GetFrameFormat()->GetPageDesc().GetPageDesc();
+ else if( pNd->IsSectionNode() )
+ pPgDesc = pNd->GetSectionNode()->GetSection().
+ GetFormat()->GetPageDesc().GetPageDesc();
+ if ( pPgDescNdIdx )
+ {
+ *pPgDescNdIdx = pNd->GetIndex();
+ }
+ }
+ if( !pPgDesc )
+ pPgDesc = &rDoc.GetPageDesc( 0 );
+ }
+ }
+ return pPgDesc;
+}
+
+/// If the node is located in a Fly, we return it formatted accordingly
+SwFrameFormat* SwNode::GetFlyFormat() const
+{
+ SwFrameFormat* pRet = nullptr;
+ const SwNode* pSttNd = FindFlyStartNode();
+ if( pSttNd )
+ {
+ if( IsContentNode() )
+ {
+ SwContentFrame* pFrame = SwIterator<SwContentFrame, SwContentNode, sw::IteratorMode::UnwrapMulti>(*static_cast<const SwContentNode*>(this)).First();
+ if( pFrame )
+ pRet = pFrame->FindFlyFrame()->GetFormat();
+ }
+ if( !pRet )
+ {
+ // The hard way through the Doc is our last way out
+ const SwFrameFormats& rFrameFormatTable = *GetDoc().GetSpzFrameFormats();
+ for( size_t n = 0; n < rFrameFormatTable.size(); ++n )
+ {
+ SwFrameFormat* pFormat = rFrameFormatTable[n];
+ // Only Writer fly frames can contain Writer nodes.
+ if (pFormat->Which() != RES_FLYFRMFMT)
+ continue;
+ const SwFormatContent& rContent = pFormat->GetContent();
+ if( rContent.GetContentIdx() &&
+ &rContent.GetContentIdx()->GetNode() == pSttNd )
+ {
+ pRet = pFormat;
+ break;
+ }
+ }
+ }
+ }
+ return pRet;
+}
+
+SwTableBox* SwNode::GetTableBox() const
+{
+ SwTableBox* pBox = nullptr;
+ const SwNode* pSttNd = FindTableBoxStartNode();
+ if( pSttNd )
+ pBox = const_cast<SwTableBox*>(pSttNd->FindTableNode()->GetTable().GetTableBox(
+ pSttNd->GetIndex() ));
+ return pBox;
+}
+
+SwStartNode* SwNode::FindSttNodeByType( SwStartNodeType eTyp )
+{
+ SwStartNode* pTmp = IsStartNode() ? static_cast<SwStartNode*>(this) : m_pStartOfSection;
+
+ while( eTyp != pTmp->GetStartNodeType() && pTmp->GetIndex() )
+ pTmp = pTmp->m_pStartOfSection;
+ return eTyp == pTmp->GetStartNodeType() ? pTmp : nullptr;
+}
+
+const SwTextNode* SwNode::FindOutlineNodeOfLevel(sal_uInt8 const nLvl,
+ SwRootFrame const*const pLayout) const
+{
+ const SwTextNode* pRet = nullptr;
+ const SwOutlineNodes& rONds = GetNodes().GetOutLineNds();
+ if( MAXLEVEL > nLvl && !rONds.empty() )
+ {
+ SwOutlineNodes::size_type nPos;
+ SwNode* pNd = const_cast<SwNode*>(this);
+ bool bCheckFirst = false;
+ if( !rONds.Seek_Entry( pNd, &nPos ))
+ {
+ if (nPos == 0)
+ bCheckFirst = true;
+ }
+ else
+ {
+ ++nPos;
+ }
+
+ if( bCheckFirst )
+ {
+ // The first OutlineNode comes after the one asking.
+ // Test if both are on the same page.
+ // If not it's invalid.
+ for (nPos = 0; nPos < rONds.size(); ++nPos)
+ {
+ pRet = rONds[nPos]->GetTextNode();
+ if (!pLayout || sw::IsParaPropsNode(*pLayout, *pRet))
+ {
+ break;
+ }
+ }
+ if (nPos == rONds.size())
+ {
+ return nullptr;
+ }
+
+ const SwContentNode* pCNd = GetContentNode();
+
+ Point aPt( 0, 0 );
+ std::pair<Point, bool> const tmp(aPt, false);
+ const SwFrame* pFrame = pRet->getLayoutFrame(pRet->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(), nullptr, &tmp),
+ * pMyFrame = pCNd ? pCNd->getLayoutFrame(pCNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(), nullptr, &tmp) : nullptr;
+ const SwPageFrame* pPgFrame = pFrame ? pFrame->FindPageFrame() : nullptr;
+ if( pPgFrame && pMyFrame &&
+ pPgFrame->getFrameArea().Top() > pMyFrame->getFrameArea().Top() )
+ {
+ // The one asking precedes the Page, thus its invalid
+ pRet = nullptr;
+ }
+ }
+ else
+ {
+ for ( ; 0 < nPos; --nPos)
+ {
+ SwTextNode const*const pNode = rONds[nPos - 1]->GetTextNode();
+ if ((nPos == 1 /*as before*/ || pNode->GetAttrOutlineLevel() - 1 <= nLvl)
+ && (!pLayout || sw::IsParaPropsNode(*pLayout, *pNode)))
+ {
+ pRet = pNode;
+ break;
+ }
+ }
+ }
+ }
+ return pRet;
+}
+
+static bool IsValidNextPrevNd( const SwNode& rNd )
+{
+ return SwNodeType::Table == rNd.GetNodeType() ||
+ ( SwNodeType::ContentMask & rNd.GetNodeType() ) ||
+ ( SwNodeType::End == rNd.GetNodeType() && rNd.StartOfSectionNode() &&
+ SwNodeType::Table == rNd.StartOfSectionNode()->GetNodeType() );
+}
+
+sal_uInt8 SwNode::HasPrevNextLayNode() const
+{
+ // assumption: <this> node is a node inside the document nodes array section.
+
+ sal_uInt8 nRet = 0;
+ if( IsValidNextPrevNd( *this ))
+ {
+ SwNodeIndex aIdx( *this, -1 );
+ // #i77805# - skip section start and end nodes
+ while ( aIdx.GetNode().IsSectionNode() ||
+ ( aIdx.GetNode().IsEndNode() &&
+ aIdx.GetNode().StartOfSectionNode()->IsSectionNode() ) )
+ {
+ --aIdx;
+ }
+ if( IsValidNextPrevNd( aIdx.GetNode() ))
+ nRet |= ND_HAS_PREV_LAYNODE;
+ // #i77805# - skip section start and end nodes
+ aIdx.Assign(*this, +1);
+ while ( aIdx.GetNode().IsSectionNode() ||
+ ( aIdx.GetNode().IsEndNode() &&
+ aIdx.GetNode().StartOfSectionNode()->IsSectionNode() ) )
+ {
+ ++aIdx;
+ }
+ if( IsValidNextPrevNd( aIdx.GetNode() ))
+ nRet |= ND_HAS_NEXT_LAYNODE;
+ }
+ return nRet;
+}
+
+void SwNode::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ const char* pName = "???";
+ switch (GetNodeType())
+ {
+ case SwNodeType::End:
+ pName = "end";
+ break;
+ case SwNodeType::Start:
+ case SwNodeType::Text:
+ case SwNodeType::Ole:
+ abort(); // overridden
+ case SwNodeType::Table:
+ pName = "table";
+ break;
+ case SwNodeType::Grf:
+ pName = "grf";
+ break;
+ default: break;
+ }
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST(pName));
+
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("type"), BAD_CAST(OString::number(static_cast<sal_uInt8>(GetNodeType())).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("index"), BAD_CAST(OString::number(sal_Int32(GetIndex())).getStr()));
+
+ switch (GetNodeType())
+ {
+ case SwNodeType::Grf:
+ {
+ auto pNoTextNode = static_cast<const SwNoTextNode*>(this);
+ const tools::PolyPolygon* pContour = pNoTextNode->HasContour();
+ if (pContour)
+ {
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("pContour"));
+ for (sal_uInt16 i = 0; i < pContour->Count(); ++i)
+ {
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("polygon"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("index"),
+ BAD_CAST(OString::number(i).getStr()));
+ const tools::Polygon& rPolygon = pContour->GetObject(i);
+ for (sal_uInt16 j = 0; j < rPolygon.GetSize(); ++j)
+ {
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("point"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("index"),
+ BAD_CAST(OString::number(j).getStr()));
+ const Point& rPoint = rPolygon.GetPoint(j);
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("x"),
+ BAD_CAST(OString::number(rPoint.X()).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("y"),
+ BAD_CAST(OString::number(rPoint.Y()).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+ }
+ (void)xmlTextWriterEndElement(pWriter);
+ }
+ (void)xmlTextWriterEndElement(pWriter);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ (void)xmlTextWriterEndElement(pWriter);
+ if (GetNodeType() == SwNodeType::End)
+ (void)xmlTextWriterEndElement(pWriter); // end start node
+}
+
+SwStartNode::SwStartNode( const SwNodeIndex &rWhere, const SwNodeType nNdType,
+ SwStartNodeType eSttNd )
+ : SwNode( rWhere, nNdType ), m_eStartNodeType( eSttNd )
+{
+ if( !rWhere.GetIndex() )
+ {
+ SwNodes& rNodes = const_cast<SwNodes&> (rWhere.GetNodes());
+ rNodes.InsertNode( this, rWhere );
+ m_pStartOfSection = this;
+ }
+ // Just do this temporarily until the EndNode is inserted
+ m_pEndOfSection = reinterpret_cast<SwEndNode*>(this);
+}
+
+SwStartNode::SwStartNode( SwNodes& rNodes, SwNodeOffset nPos )
+ : SwNode( rNodes, nPos, SwNodeType::Start ), m_eStartNodeType( SwNormalStartNode )
+{
+ if( !nPos )
+ {
+ rNodes.InsertNode( this, nPos );
+ m_pStartOfSection = this;
+ }
+ // Just do this temporarily until the EndNode is inserted
+ m_pEndOfSection = reinterpret_cast<SwEndNode*>(this);
+}
+
+void SwStartNode::CheckSectionCondColl() const
+{
+//FEATURE::CONDCOLL
+ SwNodeIndex aIdx( *this );
+ SwNodeOffset nEndIdx = EndOfSectionIndex();
+ const SwNodes& rNds = GetNodes();
+ SwContentNode* pCNd;
+ while( nullptr != ( pCNd = rNds.GoNext( &aIdx )) && pCNd->GetIndex() < nEndIdx )
+ pCNd->ChkCondColl();
+//FEATURE::CONDCOLL
+}
+
+void SwStartNode::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ const char* pName = "???";
+ switch (GetNodeType())
+ {
+ case SwNodeType::Table:
+ pName = "table";
+ break;
+ default:
+ switch(GetStartNodeType())
+ {
+ case SwNormalStartNode:
+ pName = "start";
+ break;
+ case SwTableBoxStartNode:
+ pName = "tablebox";
+ break;
+ case SwFlyStartNode:
+ pName = "fly";
+ break;
+ case SwFootnoteStartNode:
+ pName = "footnote";
+ break;
+ case SwHeaderStartNode:
+ pName = "header";
+ break;
+ case SwFooterStartNode:
+ pName = "footer";
+ break;
+ }
+ break;
+ }
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST(pName));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("type"), BAD_CAST(OString::number(static_cast<sal_uInt8>(GetNodeType())).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("index"), BAD_CAST(OString::number(sal_Int32(GetIndex())).getStr()));
+
+ if (GetStartNodeType() == SwTableBoxStartNode)
+ {
+ if (SwTableBox* pBox = GetTableBox())
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("rowspan"), BAD_CAST(OString::number(pBox->getRowSpan()).getStr()));
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("attrset"));
+ if (SwTableBox* pBox = GetTableBox())
+ pBox->GetFrameFormat()->GetAttrSet().dumpAsXml(pWriter);
+ (void)xmlTextWriterEndElement(pWriter);
+ }
+
+ // (void)xmlTextWriterEndElement(pWriter); - it is a start node, so don't end, will make xml better nested
+}
+
+
+/** Insert a node into the array
+ *
+ * The StartOfSection pointer is set to the given node.
+ *
+ * The EndOfSection pointer of the corresponding start node is set to this node.
+ *
+ * @param rWhere position where the node should be inserted
+ * @param rSttNd the start note of the section
+ */
+
+SwEndNode::SwEndNode( const SwNodeIndex &rWhere, SwStartNode& rSttNd )
+ : SwNode( rWhere, SwNodeType::End )
+{
+ m_pStartOfSection = &rSttNd;
+ m_pStartOfSection->m_pEndOfSection = this;
+}
+
+SwEndNode::SwEndNode( SwNodes& rNds, SwNodeOffset nPos, SwStartNode& rSttNd )
+ : SwNode( rNds, nPos, SwNodeType::End )
+{
+ m_pStartOfSection = &rSttNd;
+ m_pStartOfSection->m_pEndOfSection = this;
+}
+
+SwContentNode::SwContentNode( const SwNodeIndex &rWhere, const SwNodeType nNdType,
+ SwFormatColl *pColl )
+ : SwNode( rWhere, nNdType )
+ , m_aCondCollListener( *this )
+ , m_pCondColl( nullptr )
+ , mbSetModifyAtAttr( false )
+{
+ if(pColl)
+ pColl->Add(this);
+}
+
+SwContentNode::~SwContentNode()
+{
+ // The base class SwClient of SwFrame excludes itself from the dependency list!
+ // Thus, we need to delete all Frames in the dependency list.
+ if (!IsTextNode()) // see ~SwTextNode
+ {
+ DelFrames(nullptr);
+ }
+
+ m_aCondCollListener.EndListeningAll();
+ m_pCondColl = nullptr;
+
+ if ( mpAttrSet && mbSetModifyAtAttr )
+ const_cast<SwAttrSet*>(static_cast<const SwAttrSet*>(mpAttrSet.get()))->SetModifyAtAttr( nullptr );
+ InvalidateInSwCache(RES_OBJECTDYING);
+}
+void SwContentNode::UpdateAttr(const SwUpdateAttr& rUpdate)
+{
+ if (GetNodes().IsDocNodes()
+ && IsTextNode()
+ && RES_ATTRSET_CHG == rUpdate.getWhichAttr())
+ static_cast<SwTextNode*>(this)->SetCalcHiddenCharFlags();
+ CallSwClientNotify(sw::LegacyModifyHint(&rUpdate, &rUpdate));
+}
+
+void SwContentNode::SwClientNotify( const SwModify&, const SfxHint& rHint)
+{
+ if (rHint.GetId() == SfxHintId::SwLegacyModify)
+ {
+ auto pLegacyHint = static_cast<const sw::LegacyModifyHint*>(&rHint);
+ const sal_uInt16 nWhich = pLegacyHint->GetWhich();
+ InvalidateInSwCache(nWhich);
+
+ bool bSetParent = false;
+ bool bCalcHidden = false;
+ SwFormatColl* pFormatColl = nullptr;
+ switch(nWhich)
+ {
+ case RES_OBJECTDYING:
+ {
+ SwFormat* pFormat = pLegacyHint->m_pNew
+ ? static_cast<SwFormat*>(static_cast<const SwPtrMsgPoolItem*>(pLegacyHint->m_pNew)->pObject)
+ : nullptr;
+ // Do not mangle pointers if it is the upper-most format!
+ if(pFormat && GetRegisteredIn() == pFormat)
+ {
+ if(pFormat->GetRegisteredIn())
+ {
+ // If Parent, register anew in the new Parent
+ pFormat->GetRegisteredIn()->Add(this);
+ pFormatColl = GetFormatColl();
+ }
+ else
+ EndListeningAll();
+ bSetParent = true;
+ }
+ }
+ break;
+
+ case RES_FMT_CHG:
+ // If the Format parent was switched, register the Attrset at the new one
+ // Skip own Modify!
+ if(GetpSwAttrSet()
+ && pLegacyHint->m_pNew
+ && static_cast<const SwFormatChg*>(pLegacyHint->m_pNew)->pChangedFormat == GetRegisteredIn())
+ {
+ pFormatColl = GetFormatColl();
+ bSetParent = true;
+ }
+ break;
+
+ case RES_ATTRSET_CHG:
+ if (GetNodes().IsDocNodes()
+ && IsTextNode()
+ && pLegacyHint->m_pOld
+ && SfxItemState::SET == pLegacyHint->m_pOld->StaticWhichCast(RES_ATTRSET_CHG).GetChgSet()->GetItemState(RES_CHRATR_HIDDEN, false))
+ bCalcHidden = true;
+ break;
+
+ case RES_UPDATE_ATTR:
+ // RES_UPDATE_ATTR _should_ always contain a SwUpdateAttr hint in old and new.
+ // However, faking one with just a basic SfxPoolItem setting a WhichId has been observed.
+ // This makes the crude "WhichId" type divert from the true type, which is bad.
+ // Thus we are asserting here, but falling back to an proper
+ // hint instead. so that we at least will not spread such poison further.
+#ifdef DBG_UTIL
+ if(pLegacyHint->m_pNew != pLegacyHint->m_pOld)
+ {
+ auto pBT = sal::backtrace_get(20);
+ SAL_WARN("sw.core", "UpdateAttr not matching! " << sal::backtrace_to_string(pBT.get()));
+ }
+#endif
+ assert(pLegacyHint->m_pNew == pLegacyHint->m_pOld);
+ assert(dynamic_cast<const SwUpdateAttr*>(pLegacyHint->m_pNew));
+ const SwUpdateAttr aFallbackHint(0,0,0);
+ const SwUpdateAttr& rUpdateAttr = pLegacyHint->m_pNew ? *static_cast<const SwUpdateAttr*>(pLegacyHint->m_pNew) : aFallbackHint;
+ UpdateAttr(rUpdateAttr);
+ return;
+ }
+ if(bSetParent && GetpSwAttrSet())
+ AttrSetHandleHelper::SetParent(mpAttrSet, *this, pFormatColl, pFormatColl);
+ if(bCalcHidden)
+ static_cast<SwTextNode*>(this)->SetCalcHiddenCharFlags();
+ CallSwClientNotify(rHint);
+ }
+ else if (auto pModifyChangedHint = dynamic_cast<const sw::ModifyChangedHint*>(&rHint))
+ {
+ m_pCondColl = const_cast<SwFormatColl*>(static_cast<const SwFormatColl*>(pModifyChangedHint->m_pNew));
+ }
+ else if(auto pCondCollCondChgHint = dynamic_cast<const sw::CondCollCondChg*>(&rHint))
+ {
+ ChkCondColl(&pCondCollCondChgHint->m_rColl);
+ }
+}
+
+bool SwContentNode::InvalidateNumRule()
+{
+ SwNumRule* pRule = nullptr;
+ const SfxPoolItem* pItem;
+ if( GetNodes().IsDocNodes() &&
+ nullptr != ( pItem = GetNoCondAttr( RES_PARATR_NUMRULE, true )) &&
+ !static_cast<const SwNumRuleItem*>(pItem)->GetValue().isEmpty() &&
+ nullptr != (pRule = GetDoc().FindNumRulePtr(
+ static_cast<const SwNumRuleItem*>(pItem)->GetValue() ) ) )
+ {
+ pRule->SetInvalidRule( true );
+ }
+ return nullptr != pRule;
+}
+
+SwContentFrame *SwContentNode::getLayoutFrame( const SwRootFrame* _pRoot,
+ const SwPosition *const pPos,
+ std::pair<Point, bool> const*const pViewPosAndCalcFrame) const
+{
+ return static_cast<SwContentFrame*>( ::GetFrameOfModify( _pRoot, *this, FRM_CNTNT,
+ pPos, pViewPosAndCalcFrame));
+}
+
+SwRect SwContentNode::FindLayoutRect( const bool bPrtArea, const Point* pPoint ) const
+{
+ SwRect aRet;
+ std::pair<Point, bool> tmp;
+ if (pPoint)
+ {
+ tmp.first = *pPoint;
+ tmp.second = false;
+ }
+ SwContentFrame* pFrame = static_cast<SwContentFrame*>( ::GetFrameOfModify( nullptr, *this,
+ FRM_CNTNT, nullptr, pPoint ? &tmp : nullptr) );
+ if( pFrame )
+ aRet = bPrtArea ? pFrame->getFramePrintArea() : pFrame->getFrameArea();
+ return aRet;
+}
+
+SwRect SwContentNode::FindPageFrameRect() const
+{
+ SwRect aRet;
+ SwFrame* pFrame = ::GetFrameOfModify( nullptr, *this, FRM_CNTNT );
+ if( pFrame && nullptr != ( pFrame = pFrame->FindPageFrame() ))
+ aRet = pFrame->getFrameArea();
+ return aRet;
+}
+
+sal_Int32 SwContentNode::Len() const { return 0; }
+
+SwFormatColl *SwContentNode::ChgFormatColl( SwFormatColl *pNewColl )
+{
+ OSL_ENSURE( pNewColl, "Collectionpointer is 0." );
+ SwFormatColl *pOldColl = GetFormatColl();
+
+ if( pNewColl != pOldColl )
+ {
+ pNewColl->Add( this );
+
+ // Set the Parent of out AutoAttributes to the new Collection
+ if( GetpSwAttrSet() )
+ AttrSetHandleHelper::SetParent( mpAttrSet, *this, pNewColl, pNewColl );
+
+ SetCondFormatColl( nullptr );
+
+ if( !IsModifyLocked() )
+ {
+ assert(dynamic_cast<SwTextFormatColl*>(pNewColl));
+ ChkCondColl(static_cast<SwTextFormatColl*>(pNewColl));
+ SwFormatChg aTmp1( pOldColl );
+ SwFormatChg aTmp2( pNewColl );
+ CallSwClientNotify( sw::LegacyModifyHint(&aTmp1, &aTmp2) );
+ }
+ }
+ InvalidateInSwCache(RES_ATTRSET_CHG);
+ return pOldColl;
+}
+
+bool SwContentNode::GoNext(SwIndex * pIdx, sal_uInt16 nMode ) const
+{
+ bool bRet = true;
+ if( pIdx->GetIndex() < Len() )
+ {
+ if( !IsTextNode() )
+ ++(*pIdx);
+ else
+ {
+ const SwTextNode& rTNd = *GetTextNode();
+ sal_Int32 nPos = pIdx->GetIndex();
+ assert(g_pBreakIt && g_pBreakIt->GetBreakIter().is());
+ sal_Int32 nDone = 0;
+ sal_uInt16 nItrMode = ( CRSR_SKIP_CELLS & nMode ) ?
+ CharacterIteratorMode::SKIPCELL :
+ CharacterIteratorMode::SKIPCONTROLCHARACTER;
+ nPos = g_pBreakIt->GetBreakIter()->nextCharacters( rTNd.GetText(), nPos,
+ g_pBreakIt->GetLocale( rTNd.GetLang( nPos ) ),
+ nItrMode, 1, nDone );
+
+ // Check if nPos is inside hidden text range:
+ if ( CRSR_SKIP_HIDDEN & nMode )
+ {
+ sal_Int32 nHiddenStart;
+ sal_Int32 nHiddenEnd;
+ SwScriptInfo::GetBoundsOfHiddenRange( rTNd, nPos, nHiddenStart, nHiddenEnd );
+ if ( nHiddenStart != COMPLETE_STRING && nHiddenStart != nPos )
+ nPos = nHiddenEnd;
+ }
+
+ if( 1 == nDone )
+ *pIdx = nPos;
+ else
+ bRet = false;
+ }
+ }
+ else
+ bRet = false;
+ return bRet;
+}
+
+bool SwContentNode::GoPrevious(SwIndex * pIdx, sal_uInt16 nMode ) const
+{
+ bool bRet = true;
+ if( pIdx->GetIndex() > 0 )
+ {
+ if( !IsTextNode() )
+ --(*pIdx);
+ else
+ {
+ const SwTextNode& rTNd = *GetTextNode();
+ sal_Int32 nPos = pIdx->GetIndex();
+ assert(g_pBreakIt && g_pBreakIt->GetBreakIter().is());
+ sal_Int32 nDone = 0;
+ sal_uInt16 nItrMode = ( CRSR_SKIP_CELLS & nMode ) ?
+ CharacterIteratorMode::SKIPCELL :
+ CharacterIteratorMode::SKIPCONTROLCHARACTER;
+ nPos = g_pBreakIt->GetBreakIter()->previousCharacters( rTNd.GetText(), nPos,
+ g_pBreakIt->GetLocale( rTNd.GetLang( nPos ) ),
+ nItrMode, 1, nDone );
+
+ // Check if nPos is inside hidden text range:
+ if ( CRSR_SKIP_HIDDEN & nMode )
+ {
+ sal_Int32 nHiddenStart;
+ sal_Int32 nHiddenEnd;
+ SwScriptInfo::GetBoundsOfHiddenRange( rTNd, nPos, nHiddenStart, nHiddenEnd );
+ if ( nHiddenStart != COMPLETE_STRING )
+ nPos = nHiddenStart;
+ }
+
+ if( 1 == nDone )
+ *pIdx = nPos;
+ else
+ bRet = false;
+ }
+ }
+ else
+ bRet = false;
+ return bRet;
+}
+
+/**
+ * Creates all Views for the Doc for this Node.
+ * The created ContentFrames are attached to the corresponding Layout.
+ */
+void SwContentNode::MakeFramesForAdjacentContentNode(SwContentNode& rNode)
+{
+ OSL_ENSURE( &rNode != this,
+ "No ContentNode or CopyNode and new Node identical." );
+
+ if( !HasWriterListeners() || &rNode == this ) // Do we actually have Frames?
+ return;
+
+ SwFrame *pFrame;
+ SwLayoutFrame *pUpper;
+ // Create Frames for Nodes which come after the Table?
+ OSL_ENSURE( FindTableNode() == rNode.FindTableNode(), "Table confusion" );
+
+ SwNode2Layout aNode2Layout( *this, rNode.GetIndex() );
+
+ while( nullptr != (pUpper = aNode2Layout.UpperFrame( pFrame, rNode )) )
+ {
+ if (pUpper->getRootFrame()->HasMergedParas()
+ && !rNode.IsCreateFrameWhenHidingRedlines())
+ {
+ continue;
+ }
+ SwFrame *pNew = rNode.MakeFrame( pUpper );
+ pNew->Paste( pUpper, pFrame );
+ // #i27138#
+ // notify accessibility paragraphs objects about changed
+ // CONTENT_FLOWS_FROM/_TO relation.
+ // Relation CONTENT_FLOWS_FROM for next paragraph will change
+ // and relation CONTENT_FLOWS_TO for previous paragraph will change.
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ if ( pNew->IsTextFrame() )
+ {
+ SwViewShell* pViewShell( pNew->getRootFrame()->GetCurrShell() );
+ if ( pViewShell && pViewShell->GetLayout() &&
+ pViewShell->GetLayout()->IsAnyShellAccessible() )
+ {
+ auto pNext = pNew->FindNextCnt( true );
+ auto pPrev = pNew->FindPrevCnt();
+ pViewShell->InvalidateAccessibleParaFlowRelation(
+ pNext ? pNext->DynCastTextFrame() : nullptr,
+ pPrev ? pPrev->DynCastTextFrame() : nullptr );
+ }
+ }
+#endif
+ }
+}
+
+/**
+ * Deletes all Views from the Doc for this Node.
+ * The ContentFrames are removed from the corresponding Layout.
+ */
+void SwContentNode::DelFrames(SwRootFrame const*const pLayout)
+{
+ if( !HasWriterListeners() )
+ return;
+
+ SwIterator<SwContentFrame, SwContentNode, sw::IteratorMode::UnwrapMulti> aIter(*this);
+ for( SwContentFrame* pFrame = aIter.First(); pFrame; pFrame = aIter.Next() )
+ {
+ if (pLayout && pLayout != pFrame->getRootFrame())
+ {
+ continue; // skip it
+ }
+ if (pFrame->IsTextFrame())
+ {
+ if (sw::MergedPara * pMerged =
+ static_cast<SwTextFrame *>(pFrame)->GetMergedPara())
+ {
+ if (this != pMerged->pFirstNode)
+ {
+ // SwNodes::RemoveNode iterates *backwards* - so
+ // ensure there are no more extents pointing to this
+ // node as SwFrame::InvalidatePage() will access them.
+ // Note: cannot send via SwClientNotify from dtor
+ // because that would access deleted wrong-lists
+ sw::UpdateMergedParaForDelete(*pMerged, true,
+ *static_cast<SwTextNode*>(this), 0, Len());
+ if (this == pMerged->pParaPropsNode)
+ {
+ // otherwise pointer should have been updated to a different node
+ assert(this == pMerged->pLastNode);
+ assert(pMerged->extents.empty());
+ for (SwNodeOffset i = pMerged->pLastNode->GetIndex() - 1;;
+ --i)
+ {
+ assert(pMerged->pFirstNode->GetIndex() <= i);
+ SwNode *const pNode(GetNodes()[i]);
+ if (pNode->IsTextNode()
+ && pNode->GetRedlineMergeFlag() != Merge::Hidden)
+ {
+ pMerged->pParaPropsNode = pNode->GetTextNode();
+ break;
+ }
+ else if (pMerged->pFirstNode->GetIndex() == i)
+ { // this can only happen when called from CheckParaRedlineMerge()
+ // and the pMerged will be deleted anyway
+ pMerged->pParaPropsNode = pMerged->pFirstNode;
+ break;
+ }
+ }
+ assert(pMerged->listener.IsListeningTo(pMerged->pParaPropsNode));
+ }
+ assert(GetIndex() <= pMerged->pLastNode->GetIndex());
+ if (this == pMerged->pLastNode)
+ {
+ // tdf#130680 find the previous node that is a
+ // listener of pMerged; see CheckParaRedlineMerge()
+ for (SwNodeOffset i = GetIndex() - 1;
+ this == pMerged->pLastNode; --i)
+ {
+ SwNode *const pNode = GetNodes()[i];
+ if (pNode->IsTextNode())
+ {
+ pMerged->pLastNode = pNode->GetTextNode();
+ }
+ else if (SwEndNode const*const pEnd = pNode->GetEndNode())
+ {
+ SwStartNode const*const pStart(pEnd->StartOfSectionNode());
+ i = pStart->GetIndex(); // skip table or section
+ }
+ }
+ assert(pMerged->pFirstNode->GetIndex() <= pMerged->pLastNode->GetIndex());
+ assert(pMerged->listener.IsListeningTo(pMerged->pLastNode));
+ }
+ // avoid re-parenting mess (ModifyChangedHint)
+ pMerged->listener.EndListening(this);
+ continue; // don't delete
+ }
+ }
+ // #i27138#
+ // notify accessibility paragraphs objects about changed
+ // CONTENT_FLOWS_FROM/_TO relation.
+ // Relation CONTENT_FLOWS_FROM for current next paragraph will change
+ // and relation CONTENT_FLOWS_TO for current previous paragraph will change.
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ SwViewShell* pViewShell( pFrame->getRootFrame()->GetCurrShell() );
+ if ( pViewShell && pViewShell->GetLayout() &&
+ pViewShell->GetLayout()->IsAnyShellAccessible() )
+ {
+ auto pNext = pFrame->FindNextCnt( true );
+ auto pPrev = pFrame->FindPrevCnt();
+ pViewShell->InvalidateAccessibleParaFlowRelation(
+ pNext ? pNext->DynCastTextFrame() : nullptr,
+ pPrev ? pPrev->DynCastTextFrame() : nullptr );
+ }
+#endif
+ }
+
+ if( pFrame->IsFollow() )
+ {
+ SwContentFrame* pMaster = pFrame->FindMaster();
+ pMaster->SetFollow( pFrame->GetFollow() );
+ }
+ pFrame->SetFollow( nullptr );//So it doesn't get funny ideas.
+ //Otherwise it could be possible that a follow
+ //gets destroyed before its master. Following
+ //the now invalid pointer will then lead to an
+ //illegal memory access. The chain can be
+ //crushed here because we'll destroy all of it
+ //anyway.
+
+ if( pFrame->GetUpper() && pFrame->IsInFootnote() && !pFrame->GetIndNext() &&
+ !pFrame->GetIndPrev() )
+ {
+ SwFootnoteFrame *pFootnote = pFrame->FindFootnoteFrame();
+ OSL_ENSURE( pFootnote, "You promised a FootnoteFrame?" );
+ SwContentFrame* pCFrame;
+ if( !pFootnote->GetFollow() && !pFootnote->GetMaster() &&
+ nullptr != ( pCFrame = pFootnote->GetRefFromAttr()) && pCFrame->IsFollow() )
+ {
+ OSL_ENSURE( pCFrame->IsTextFrame(), "NoTextFrame has Footnote?" );
+ pCFrame->FindMaster()->Prepare( PrepareHint::FootnoteInvalidationGone );
+ }
+ }
+ pFrame->Cut();
+ SwFrame::DestroyFrame(pFrame);
+ }
+}
+
+SwContentNode *SwContentNode::JoinNext()
+{
+ return this;
+}
+
+/// Get info from Modify
+bool SwContentNode::GetInfo( SfxPoolItem& rInfo ) const
+{
+ switch( rInfo.Which() )
+ {
+ case RES_AUTOFMT_DOCNODE:
+ if( &GetNodes() == static_cast<SwAutoFormatGetDocNode&>(rInfo).pNodes )
+ {
+ return false;
+ }
+ break;
+
+ case RES_FINDNEARESTNODE:
+ if( GetAttr( RES_PAGEDESC ).GetPageDesc() )
+ static_cast<SwFindNearestNode&>(rInfo).CheckNode( *this );
+ return true;
+
+ case RES_CONTENT_VISIBLE:
+ {
+ static_cast<SwPtrMsgPoolItem&>(rInfo).pObject =
+ SwIterator<SwFrame, SwContentNode, sw::IteratorMode::UnwrapMulti>(*this).First();
+ }
+ return false;
+ }
+
+ return sw::BroadcastingModify::GetInfo( rInfo );
+}
+
+/// @param rAttr the attribute to set
+bool SwContentNode::SetAttr(const SfxPoolItem& rAttr )
+{
+ if( !GetpSwAttrSet() ) // Have the Nodes created by the corresponding AttrSets
+ NewAttrSet( GetDoc().GetAttrPool() );
+
+ OSL_ENSURE( GetpSwAttrSet(), "Why did't we create an AttrSet?");
+
+ InvalidateInSwCache(RES_ATTRSET_CHG);
+
+ bool bRet = false;
+ // If Modify is locked, we do not send any Modifys
+ if( IsModifyLocked() ||
+ ( !HasWriterListeners() && RES_PARATR_NUMRULE != rAttr.Which() ))
+ {
+ bRet = nullptr != AttrSetHandleHelper::Put( mpAttrSet, *this, rAttr );
+ }
+ else
+ {
+ SwAttrSet aOld( *GetpSwAttrSet()->GetPool(), GetpSwAttrSet()->GetRanges() ),
+ aNew( *GetpSwAttrSet()->GetPool(), GetpSwAttrSet()->GetRanges() );
+ bRet = AttrSetHandleHelper::Put_BC( mpAttrSet, *this, rAttr, &aOld, &aNew );
+ if( bRet )
+ sw::ClientNotifyAttrChg(*this, *GetpSwAttrSet(), aOld, aNew);
+ }
+ return bRet;
+}
+
+bool SwContentNode::SetAttr( const SfxItemSet& rSet )
+{
+ InvalidateInSwCache(RES_ATTRSET_CHG);
+
+ if( const SwFormatAutoFormat* pFnd = rSet.GetItemIfSet( RES_AUTO_STYLE, false ) )
+ {
+ OSL_ENSURE( rSet.Count() == 1, "SetAutoStyle mixed with other attributes?!" );
+
+ // If there already is an attribute set (usually containing a numbering
+ // item), we have to merge the attribute of the new set into the old set:
+ bool bSetParent = true;
+ if ( GetpSwAttrSet() )
+ {
+ bSetParent = false;
+ AttrSetHandleHelper::Put( mpAttrSet, *this, *pFnd->GetStyleHandle() );
+ }
+ else
+ {
+ mpAttrSet = pFnd->GetStyleHandle();
+ }
+
+ if ( bSetParent )
+ {
+ // If the content node has a conditional style, we have to set the
+ // string item containing the correct conditional style name (the
+ // style name property has already been set during the import!)
+ // In case we do not have a conditional style, we make use of the
+ // fact that nobody else uses the attribute set behind the handle.
+ // FME 2007-07-10 #i78124# If autostyle does not have a parent,
+ // the string is empty.
+ const SfxStringItem* pNameItem = nullptr;
+ if ( nullptr != GetCondFormatColl() ||
+ !(pNameItem = mpAttrSet->GetItemIfSet( RES_FRMATR_STYLE_NAME, false )) ||
+ pNameItem->GetValue().isEmpty() )
+ AttrSetHandleHelper::SetParent( mpAttrSet, *this, &GetAnyFormatColl(), GetFormatColl() );
+ else
+ const_cast<SfxItemSet*>(mpAttrSet.get())->SetParent( &GetFormatColl()->GetAttrSet() );
+ }
+
+ return true;
+ }
+
+ if( !GetpSwAttrSet() ) // Have the AttrsSets created by the corresponding Nodes
+ NewAttrSet( GetDoc().GetAttrPool() );
+
+ bool bRet = false;
+ // If Modify is locked, do not send any Modifys
+ if ( IsModifyLocked() ||
+ ( !HasWriterListeners() &&
+ SfxItemState::SET != rSet.GetItemState( RES_PARATR_NUMRULE, false ) ) )
+ {
+ // Some special treatment for Attributes
+ bRet = AttrSetHandleHelper::Put( mpAttrSet, *this, rSet );
+ }
+ else
+ {
+ SwAttrSet aOld( *GetpSwAttrSet()->GetPool(), GetpSwAttrSet()->GetRanges() ),
+ aNew( *GetpSwAttrSet()->GetPool(), GetpSwAttrSet()->GetRanges() );
+ bRet = AttrSetHandleHelper::Put_BC( mpAttrSet, *this, rSet, &aOld, &aNew );
+ if( bRet )
+ sw::ClientNotifyAttrChg(*this, *GetpSwAttrSet(), aOld, aNew);
+ }
+ return bRet;
+}
+
+// With nWhich it takes the Hint from the Delta array
+bool SwContentNode::ResetAttr( sal_uInt16 nWhich1, sal_uInt16 nWhich2 )
+{
+ if( !GetpSwAttrSet() )
+ return false;
+
+ InvalidateInSwCache(RES_ATTRSET_CHG);
+
+ // If Modify is locked, do not send out any Modifys
+ if( IsModifyLocked() )
+ {
+ sal_uInt16 nDel = 0;
+ if ( !nWhich2 || nWhich2 < nWhich1 )
+ {
+ nDel = ClearItemsFromAttrSet( { nWhich1 } );
+ }
+ else
+ nDel = AttrSetHandleHelper::ClearItem_BC( mpAttrSet, *this, nWhich1, nWhich2, nullptr, nullptr );
+
+ if( !GetpSwAttrSet()->Count() ) // Empty? Delete
+ mpAttrSet.reset();
+ return 0 != nDel;
+ }
+
+ // No valid area defined?
+ if( !nWhich2 || nWhich2 < nWhich1 )
+ nWhich2 = nWhich1; // Then set only this Item to 1st Id
+
+ SwAttrSet aOld( *GetpSwAttrSet()->GetPool(), GetpSwAttrSet()->GetRanges() ),
+ aNew( *GetpSwAttrSet()->GetPool(), GetpSwAttrSet()->GetRanges() );
+ bool bRet = 0 != AttrSetHandleHelper::ClearItem_BC( mpAttrSet, *this, nWhich1, nWhich2, &aOld, &aNew );
+
+ if( bRet )
+ {
+ sw::ClientNotifyAttrChg(*this, *GetpSwAttrSet(), aOld, aNew);
+
+ if( !GetpSwAttrSet()->Count() ) // Empty?, delete it
+ mpAttrSet.reset();
+ }
+ return bRet;
+}
+
+bool SwContentNode::ResetAttr( const std::vector<sal_uInt16>& rWhichArr )
+{
+ if( !GetpSwAttrSet() )
+ return false;
+
+ InvalidateInSwCache(RES_ATTRSET_CHG);
+ // If Modify is locked, do not send out any Modifys
+ sal_uInt16 nDel = 0;
+ if( IsModifyLocked() )
+ {
+ nDel = ClearItemsFromAttrSet( rWhichArr );
+ }
+ else
+ {
+ SwAttrSet aOld( *GetpSwAttrSet()->GetPool(), GetpSwAttrSet()->GetRanges() ),
+ aNew( *GetpSwAttrSet()->GetPool(), GetpSwAttrSet()->GetRanges() );
+
+ for ( const auto& rWhich : rWhichArr )
+ if( AttrSetHandleHelper::ClearItem_BC( mpAttrSet, *this, rWhich, &aOld, &aNew ))
+ ++nDel;
+
+ if( nDel )
+ sw::ClientNotifyAttrChg(*this, *GetpSwAttrSet(), aOld, aNew);
+ }
+ if( !GetpSwAttrSet()->Count() ) // Empty?, delete it
+ mpAttrSet.reset();
+ return 0 != nDel ;
+}
+
+sal_uInt16 SwContentNode::ResetAllAttr()
+{
+ if( !GetpSwAttrSet() )
+ return 0;
+ InvalidateInSwCache(RES_ATTRSET_CHG);
+
+ // If Modify is locked, do not send out any Modifys
+ if( IsModifyLocked() )
+ {
+ sal_uInt16 nDel = ClearItemsFromAttrSet( { 0 } );
+ if( !GetpSwAttrSet()->Count() ) // Empty? Delete
+ mpAttrSet.reset();
+ return nDel;
+ }
+
+ SwAttrSet aOld( *GetpSwAttrSet()->GetPool(), GetpSwAttrSet()->GetRanges() ),
+ aNew( *GetpSwAttrSet()->GetPool(), GetpSwAttrSet()->GetRanges() );
+ bool bRet = 0 != AttrSetHandleHelper::ClearItem_BC( mpAttrSet, *this, 0, &aOld, &aNew );
+
+ if( bRet )
+ {
+ sw::ClientNotifyAttrChg(*this, *GetpSwAttrSet(), aOld, aNew);
+ if( !GetpSwAttrSet()->Count() ) // Empty? Delete
+ mpAttrSet.reset();
+ }
+ return aNew.Count();
+}
+
+bool SwContentNode::GetAttr( SfxItemSet& rSet ) const
+{
+ if( rSet.Count() )
+ rSet.ClearItem();
+
+ const SwAttrSet& rAttrSet = GetSwAttrSet();
+ return rSet.Set( rAttrSet );
+}
+
+sal_uInt16 SwContentNode::ClearItemsFromAttrSet( const std::vector<sal_uInt16>& rWhichIds )
+{
+ sal_uInt16 nRet = 0;
+ if ( rWhichIds.empty() )
+ return nRet;
+
+ OSL_ENSURE( GetpSwAttrSet(), "no item set" );
+ SwAttrSet aNewAttrSet( *GetpSwAttrSet() );
+ for ( const auto& rWhichId : rWhichIds )
+ {
+ nRet = nRet + aNewAttrSet.ClearItem( rWhichId );
+ }
+ if ( nRet )
+ AttrSetHandleHelper::GetNewAutoStyle( mpAttrSet, *this, aNewAttrSet );
+
+ return nRet;
+}
+
+const SfxPoolItem* SwContentNode::GetNoCondAttr( sal_uInt16 nWhich,
+ bool bInParents ) const
+{
+ const SfxPoolItem* pFnd = nullptr;
+ if( m_pCondColl && m_pCondColl->GetRegisteredIn() )
+ {
+ if( !GetpSwAttrSet() || ( SfxItemState::SET != GetpSwAttrSet()->GetItemState(
+ nWhich, false, &pFnd ) && bInParents ))
+ {
+ (void)static_cast<const SwFormat*>(GetRegisteredIn())->GetItemState( nWhich, bInParents, &pFnd );
+ }
+ }
+ // undo change of issue #i51029#
+ // Note: <GetSwAttrSet()> returns <mpAttrSet>, if set, otherwise it returns
+ // the attribute set of the paragraph style, which is valid for the
+ // content node - see file <node.hxx>
+ else
+ {
+ GetSwAttrSet().GetItemState( nWhich, bInParents, &pFnd );
+ }
+ return pFnd;
+}
+
+static bool lcl_CheckMaxLength(SwNode const& rPrev, SwNode const& rNext)
+{
+ if (rPrev.GetNodeType() != rNext.GetNodeType())
+ {
+ return false;
+ }
+ if (!rPrev.IsTextNode())
+ {
+ return true;
+ }
+
+ // Check if a node can contain the other (order is not significant)
+ return rPrev.GetTextNode()->GetSpaceLeft() > rNext.GetTextNode()->Len();
+}
+
+/// Can we join two Nodes?
+/// We can return the 2nd position in pIdx.
+bool SwContentNode::CanJoinNext( SwNodeIndex* pIdx ) const
+{
+ const SwNodes& rNds = GetNodes();
+ SwNodeIndex aIdx( *this, 1 );
+
+ const SwNode* pNd = this;
+ while( aIdx < rNds.Count()-1 &&
+ (( pNd = &aIdx.GetNode())->IsSectionNode() ||
+ ( pNd->IsEndNode() && pNd->StartOfSectionNode()->IsSectionNode() )))
+ ++aIdx;
+
+ if (rNds.Count()-1 == aIdx.GetIndex())
+ return false;
+ if (!lcl_CheckMaxLength(*this, *pNd))
+ {
+ return false;
+ }
+ if( pIdx )
+ *pIdx = aIdx;
+ return true;
+}
+
+/// Can we join two Nodes?
+/// We can return the 2nd position in pIdx.
+bool SwContentNode::CanJoinPrev( SwNodeIndex* pIdx ) const
+{
+ SwNodeIndex aIdx( *this, -1 );
+
+ const SwNode* pNd = this;
+ while( aIdx.GetIndex() &&
+ (( pNd = &aIdx.GetNode())->IsSectionNode() ||
+ ( pNd->IsEndNode() && pNd->StartOfSectionNode()->IsSectionNode() )))
+ --aIdx;
+
+ if (SwNodeOffset(0) == aIdx.GetIndex())
+ return false;
+ if (!lcl_CheckMaxLength(*pNd, *this))
+ {
+ return false;
+ }
+ if( pIdx )
+ *pIdx = aIdx;
+ return true;
+}
+
+void SwContentNode::SetCondFormatColl(SwFormatColl* pColl)
+{
+ if( !((!pColl && m_pCondColl) || ( pColl && !m_pCondColl ) ||
+ ( pColl && pColl != m_pCondColl->GetRegisteredIn() )) )
+ return;
+
+ SwFormatColl* pOldColl = GetCondFormatColl();
+ m_aCondCollListener.EndListeningAll();
+ if(pColl)
+ m_aCondCollListener.StartListening(pColl);
+ m_pCondColl = pColl;
+ if(GetpSwAttrSet())
+ AttrSetHandleHelper::SetParent(mpAttrSet, *this, &GetAnyFormatColl(), GetFormatColl());
+
+ if(!IsModifyLocked())
+ {
+ SwFormatChg aTmp1(pOldColl ? pOldColl : GetFormatColl());
+ SwFormatChg aTmp2(pColl ? pColl : GetFormatColl());
+ CallSwClientNotify(sw::LegacyModifyHint(&aTmp1, &aTmp2));
+ }
+ InvalidateInSwCache(RES_ATTRSET_CHG);
+}
+
+bool SwContentNode::IsAnyCondition( SwCollCondition& rTmp ) const
+{
+ const SwNodes& rNds = GetNodes();
+ {
+ Master_CollCondition nCond = Master_CollCondition::NONE;
+ const SwStartNode* pSttNd = StartOfSectionNode();
+ while( pSttNd )
+ {
+ switch( pSttNd->GetNodeType() )
+ {
+ case SwNodeType::Table: nCond = Master_CollCondition::PARA_IN_TABLEBODY; break;
+ case SwNodeType::Section: nCond = Master_CollCondition::PARA_IN_SECTION; break;
+
+ default:
+ switch( pSttNd->GetStartNodeType() )
+ {
+ case SwTableBoxStartNode:
+ {
+ nCond = Master_CollCondition::PARA_IN_TABLEBODY;
+ const SwTableNode* pTableNd = pSttNd->FindTableNode();
+ const SwTableBox* pBox;
+ if( pTableNd && nullptr != ( pBox = pTableNd->GetTable().
+ GetTableBox(pSttNd->GetIndex()) ) &&
+ pBox->IsInHeadline( &pTableNd->GetTable() ) )
+ nCond = Master_CollCondition::PARA_IN_TABLEHEAD;
+ }
+ break;
+ case SwFlyStartNode: nCond = Master_CollCondition::PARA_IN_FRAME; break;
+ case SwFootnoteStartNode:
+ {
+ nCond = Master_CollCondition::PARA_IN_FOOTNOTE;
+ const SwFootnoteIdxs& rFootnoteArr = rNds.GetDoc().GetFootnoteIdxs();
+ const SwTextFootnote* pTextFootnote;
+ const SwNode* pSrchNd = pSttNd;
+
+ for( size_t n = 0; n < rFootnoteArr.size(); ++n )
+ if( nullptr != ( pTextFootnote = rFootnoteArr[ n ])->GetStartNode() &&
+ pSrchNd == &pTextFootnote->GetStartNode()->GetNode() )
+ {
+ if( pTextFootnote->GetFootnote().IsEndNote() )
+ nCond = Master_CollCondition::PARA_IN_ENDNOTE;
+ break;
+ }
+ }
+ break;
+ case SwHeaderStartNode: nCond = Master_CollCondition::PARA_IN_HEADER; break;
+ case SwFooterStartNode: nCond = Master_CollCondition::PARA_IN_FOOTER; break;
+ case SwNormalStartNode: break;
+ }
+ }
+
+ if( nCond != Master_CollCondition::NONE )
+ {
+ rTmp.SetCondition( nCond, 0 );
+ return true;
+ }
+ pSttNd = pSttNd->GetIndex()
+ ? pSttNd->StartOfSectionNode()
+ : nullptr;
+ }
+ }
+
+ {
+ SwOutlineNodes::size_type nPos;
+ const SwOutlineNodes& rOutlNds = rNds.GetOutLineNds();
+ if( !rOutlNds.empty() )
+ {
+ if( !rOutlNds.Seek_Entry( const_cast<SwContentNode*>(this), &nPos ) && nPos )
+ --nPos;
+ if( nPos < rOutlNds.size() &&
+ rOutlNds[ nPos ]->GetIndex() < GetIndex() )
+ {
+ SwTextNode* pOutlNd = rOutlNds[ nPos ]->GetTextNode();
+
+ if( pOutlNd->IsOutline())
+ {
+ rTmp.SetCondition( Master_CollCondition::PARA_IN_OUTLINE, pOutlNd->GetAttrOutlineLevel() - 1 );
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
+void SwContentNode::ChkCondColl(const SwTextFormatColl* pColl)
+{
+ if(pColl != GetRegisteredIn())
+ {
+ SAL_WARN("sw.core", "Wrong cond collection, skipping check of Cond Colls.");
+ return;
+ }
+ if(&GetNodes() != &GetDoc().GetNodes())
+ {
+ SAL_WARN("sw.core", "Nodes amiss, skipping check of Cond Colls.");
+ return;
+ }
+ // Check, just to be sure
+ if( RES_CONDTXTFMTCOLL != GetFormatColl()->Which() )
+ return;
+
+ SwCollCondition aTmp( nullptr, Master_CollCondition::NONE, 0 );
+ const SwCollCondition* pCColl;
+
+ bool bDone = false;
+
+ if( IsAnyCondition( aTmp ))
+ {
+ pCColl = static_cast<SwConditionTextFormatColl*>(GetFormatColl())
+ ->HasCondition( aTmp );
+
+ if (pCColl)
+ {
+ SetCondFormatColl( pCColl->GetTextFormatColl() );
+ bDone = true;
+ }
+ }
+
+ if (bDone)
+ return;
+
+ if( IsTextNode() && static_cast<SwTextNode*>(this)->GetNumRule())
+ {
+ // Is at which Level in a list?
+ aTmp.SetCondition( Master_CollCondition::PARA_IN_LIST,
+ static_cast<SwTextNode*>(this)->GetActualListLevel() );
+ pCColl = static_cast<SwConditionTextFormatColl*>(GetFormatColl())->
+ HasCondition( aTmp );
+ }
+ else
+ pCColl = nullptr;
+
+ if( pCColl )
+ SetCondFormatColl( pCColl->GetTextFormatColl() );
+ else if( m_pCondColl )
+ SetCondFormatColl( nullptr );
+}
+
+// #i42921#
+SvxFrameDirection SwContentNode::GetTextDirection( const SwPosition& rPos,
+ const Point* pPt ) const
+{
+ SvxFrameDirection nRet = SvxFrameDirection::Unknown;
+
+ Point aPt;
+ if( pPt )
+ aPt = *pPt;
+
+ // #i72024# - No format of the frame, because this can cause recursive layout actions
+ std::pair<Point, bool> const tmp(aPt, false);
+ SwFrame* pFrame = getLayoutFrame(GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(), &rPos, &tmp);
+
+ if ( pFrame )
+ {
+ if ( pFrame->IsVertical() )
+ {
+ if (pFrame->IsVertLRBT())
+ nRet = SvxFrameDirection::Vertical_LR_BT;
+ else if (pFrame->IsRightToLeft())
+ nRet = SvxFrameDirection::Vertical_LR_TB;
+ else
+ nRet = SvxFrameDirection::Vertical_RL_TB;
+ }
+ else
+ {
+ if ( pFrame->IsRightToLeft() )
+ nRet = SvxFrameDirection::Horizontal_RL_TB;
+ else
+ nRet = SvxFrameDirection::Horizontal_LR_TB;
+ }
+ }
+
+ return nRet;
+}
+
+std::unique_ptr<SwOLENodes> SwContentNode::CreateOLENodesArray( const SwFormatColl& rColl, bool bOnlyWithInvalidSize )
+{
+ std::unique_ptr<SwOLENodes> pNodes;
+ SwIterator<SwContentNode,SwFormatColl> aIter( rColl );
+ for( SwContentNode* pNd = aIter.First(); pNd; pNd = aIter.Next() )
+ {
+ SwOLENode *pONd = pNd->GetOLENode();
+ if ( pONd && (!bOnlyWithInvalidSize || pONd->IsOLESizeInvalid()) )
+ {
+ if ( !pNodes )
+ pNodes.reset(new SwOLENodes);
+ pNodes->push_back( pONd );
+ }
+ }
+
+ return pNodes;
+}
+
+drawinglayer::attribute::SdrAllFillAttributesHelperPtr SwContentNode::getSdrAllFillAttributesHelper() const
+{
+ return drawinglayer::attribute::SdrAllFillAttributesHelperPtr();
+}
+
+/*
+ * Document Interface Access
+ */
+const IDocumentSettingAccess* SwNode::getIDocumentSettingAccess() const { return &GetDoc().GetDocumentSettingManager(); }
+const IDocumentDeviceAccess& SwNode::getIDocumentDeviceAccess() const { return GetDoc().getIDocumentDeviceAccess(); }
+const IDocumentRedlineAccess& SwNode::getIDocumentRedlineAccess() const { return GetDoc().getIDocumentRedlineAccess(); }
+const IDocumentStylePoolAccess& SwNode::getIDocumentStylePoolAccess() const { return GetDoc().getIDocumentStylePoolAccess(); }
+const IDocumentDrawModelAccess& SwNode::getIDocumentDrawModelAccess() const { return GetDoc().getIDocumentDrawModelAccess(); }
+const IDocumentLayoutAccess& SwNode::getIDocumentLayoutAccess() const { return GetDoc().getIDocumentLayoutAccess(); }
+IDocumentLayoutAccess& SwNode::getIDocumentLayoutAccess() { return GetDoc().getIDocumentLayoutAccess(); }
+const IDocumentLinksAdministration& SwNode::getIDocumentLinksAdministration() const { return GetDoc().getIDocumentLinksAdministration(); }
+IDocumentLinksAdministration& SwNode::getIDocumentLinksAdministration() { return GetDoc().getIDocumentLinksAdministration(); }
+const IDocumentFieldsAccess& SwNode::getIDocumentFieldsAccess() const { return GetDoc().getIDocumentFieldsAccess(); }
+IDocumentFieldsAccess& SwNode::getIDocumentFieldsAccess() { return GetDoc().getIDocumentFieldsAccess(); }
+IDocumentContentOperations& SwNode::getIDocumentContentOperations() { return GetDoc().getIDocumentContentOperations(); }
+IDocumentListItems& SwNode::getIDocumentListItems() { return GetDoc().getIDocumentListItems(); } // #i83479#
+
+const IDocumentMarkAccess* SwNode::getIDocumentMarkAccess() const { return GetDoc().getIDocumentMarkAccess(); }
+IStyleAccess& SwNode::getIDocumentStyleAccess() { return GetDoc().GetIStyleAccess(); }
+
+bool SwNode::IsInRedlines() const
+{
+ const SwDoc& rDoc = GetDoc();
+
+ return rDoc.getIDocumentRedlineAccess().IsInRedlines(*this);
+}
+
+void SwNode::AddAnchoredFly(SwFrameFormat *const pFlyFormat)
+{
+ assert(pFlyFormat);
+ assert(&pFlyFormat->GetAnchor(false).GetContentAnchor()->nNode.GetNode() == this);
+ // check node type, cf. SwFormatAnchor::SetAnchor()
+ assert(IsTextNode() || IsStartNode() || IsTableNode());
+ m_aAnchoredFlys.push_back(pFlyFormat);
+}
+
+void SwNode::RemoveAnchoredFly(SwFrameFormat *const pFlyFormat)
+{
+ assert(pFlyFormat);
+ // cannot assert this in Remove because it is called when new anchor is already set
+// assert(&pFlyFormat->GetAnchor(false).GetContentAnchor()->nNode.GetNode() == this);
+ assert(IsTextNode() || IsStartNode() || IsTableNode());
+ auto it(std::find(m_aAnchoredFlys.begin(), m_aAnchoredFlys.end(), pFlyFormat));
+ assert(it != m_aAnchoredFlys.end());
+ m_aAnchoredFlys.erase(it);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/docnode/node2lay.cxx b/sw/source/core/docnode/node2lay.cxx
new file mode 100644
index 000000000..5cc15c310
--- /dev/null
+++ b/sw/source/core/docnode/node2lay.cxx
@@ -0,0 +1,517 @@
+/* -*- 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 <calbck.hxx>
+#include <node.hxx>
+#include <ndindex.hxx>
+#include <pam.hxx>
+#include <swtable.hxx>
+#include <ftnfrm.hxx>
+#include <sectfrm.hxx>
+#include <cntfrm.hxx>
+#include <tabfrm.hxx>
+#include <frmtool.hxx>
+#include <section.hxx>
+#include <node2lay.hxx>
+#include <osl/diagnose.h>
+
+/**
+ * The SwNode2LayImpl class does the actual work, the SwNode2Layout class is
+ * just the public interface.
+ */
+class SwNode2LayImpl
+{
+ std::unique_ptr<SwIterator<SwFrame, sw::BroadcastingModify, sw::IteratorMode::UnwrapMulti>> mpIter;
+ sw::BroadcastingModify* mpMod;
+ std::vector<SwFrame*> mvUpperFrames; // To collect the Upper
+ SwNodeOffset mnIndex; // The Index of the to-be-inserted Nodes
+ bool mbMaster : 1; // true => only Master, false => only Frames without Follow
+ bool mbInit : 1; // Did we already call First() at SwClient?
+
+ SwNode2LayImpl(const SwNode2LayImpl&) = delete;
+ SwNode2LayImpl& operator=(const SwNode2LayImpl&) = delete;
+
+public:
+ SwNode2LayImpl( const SwNode& rNode, SwNodeOffset nIdx, bool bSearch );
+ SwFrame* NextFrame(); // Returns the next "useful" Frame
+ SwLayoutFrame* UpperFrame( SwFrame* &rpFrame, const SwNode &rNode );
+ void SaveUpperFrames(); // Saves (and locks if needed) the pUpper
+ // Inserts a Frame under every pUpper of the array
+ void RestoreUpperFrames( SwNodes& rNds, SwNodeOffset nStt, SwNodeOffset nEnd );
+
+ SwFrame* GetFrame( const Point* pDocPos ) const;
+};
+
+static SwNode* GoNextWithFrame(const SwNodes& rNodes, SwNodeIndex *pIdx, SwFlowFrame const**const ppFrame)
+{
+ if( pIdx->GetIndex() >= rNodes.Count() - 1 )
+ return nullptr;
+
+ SwNodeIndex aTmp(*pIdx, +1);
+ SwNode* pNd = nullptr;
+ while( aTmp < rNodes.Count()-1 )
+ {
+ pNd = &aTmp.GetNode();
+ SwFrame const* pFound(nullptr);
+ if ( pNd->IsContentNode() )
+ // sw_redlinehide: assume that it's OK to find a node with the same
+ // frame as the caller's one
+ pFound = SwIterator<SwFrame, SwContentNode, sw::IteratorMode::UnwrapMulti>(*static_cast<SwContentNode*>(pNd)).First();
+ else if ( pNd->IsTableNode() )
+ pFound = SwIterator<SwFrame,SwFormat>(*static_cast<SwTableNode*>(pNd)->GetTable().GetFrameFormat()).First() ;
+ else if( pNd->IsEndNode() && !pNd->StartOfSectionNode()->IsSectionNode() )
+ {
+ pNd = nullptr;
+ break;
+ }
+ if (pFound != nullptr)
+ {
+ if (ppFrame)
+ {
+ *ppFrame = SwFlowFrame::CastFlowFrame(pFound);
+ assert(*ppFrame);
+ }
+ break;
+ }
+ ++aTmp;
+ }
+
+ if( aTmp == rNodes.Count()-SwNodeOffset(1) )
+ pNd = nullptr;
+ else if( pNd )
+ (*pIdx) = aTmp;
+ return pNd;
+}
+
+static SwNode* GoPreviousWithFrame(SwNodeIndex *pIdx, SwFlowFrame const**const ppFrame)
+{
+ if( !pIdx->GetIndex() )
+ return nullptr;
+
+ SwNodeIndex aTmp( *pIdx, -1 );
+ SwNode* pNd(nullptr);
+ while( aTmp.GetIndex() )
+ {
+ pNd = &aTmp.GetNode();
+ SwFrame const* pFound(nullptr);
+ if ( pNd->IsContentNode() )
+ // sw_redlinehide: assume that it's OK to find a node with the same
+ // frame as the caller's one
+ pFound = SwIterator<SwFrame, SwContentNode, sw::IteratorMode::UnwrapMulti>(*static_cast<SwContentNode*>(pNd)).First();
+ else if ( pNd->IsTableNode() )
+ pFound = SwIterator<SwFrame,SwFormat>(*static_cast<SwTableNode*>(pNd)->GetTable().GetFrameFormat()).First();
+ else if( pNd->IsStartNode() && !pNd->IsSectionNode() )
+ {
+ pNd = nullptr;
+ break;
+ }
+ if (pFound != nullptr)
+ {
+ if (ppFrame)
+ {
+ *ppFrame = SwFlowFrame::CastFlowFrame(pFound);
+ assert(*ppFrame);
+ }
+ break;
+ }
+ --aTmp;
+ }
+
+ if( !aTmp.GetIndex() )
+ pNd = nullptr;
+ else if( pNd )
+ (*pIdx) = aTmp;
+ return pNd;
+}
+
+namespace sw {
+
+SwFrame const* FindNeighbourFrameForNode(SwNode const& rNode)
+{
+ SwNodeIndex idx(rNode);
+ SwFlowFrame const* pFlow(nullptr);
+ if (SwNode *const pNode = GoPreviousWithFrame(&idx, &pFlow))
+ {
+ if (::CheckNodesRange(rNode, idx, true))
+ {
+ while (pFlow->HasFollow())
+ { // try to get the one on the current page
+ pFlow = pFlow->GetFollow();
+ }
+ return &pFlow->GetFrame();
+ }
+ }
+ idx = rNode;
+ if (SwNode *const pNode = GoNextWithFrame(idx.GetNodes(), &idx, &pFlow))
+ {
+ if (::CheckNodesRange(rNode, idx, true))
+ {
+ while (pFlow->IsFollow())
+ { // try to get the one on the current page
+ pFlow = pFlow->GetPrecede();
+ }
+ return &pFlow->GetFrame();
+ }
+ }
+ return nullptr;
+}
+
+}
+
+/**
+ * The main purpose of this ctor is to find the right sw::BroadcastingModify to iterate over.
+ *
+ * @param bSearch true: find the next Content or TableNode which contains
+ * Frames (to collect the pUpper).
+ * Else we assume that rNode points already to such a
+ * Content or TableNode.
+ * We insert before or after it.
+ */
+SwNode2LayImpl::SwNode2LayImpl( const SwNode& rNode, SwNodeOffset nIdx, bool bSearch )
+ : mnIndex( nIdx ), mbInit( false )
+{
+ const SwNode* pNd;
+ if( bSearch || rNode.IsSectionNode() )
+ {
+ // Find the next Content/TableNode that contains a Frame, so that we can add
+ // ourselves before/after it
+ if( !bSearch && rNode.GetIndex() < mnIndex )
+ {
+ SwNodeIndex aTmp( *rNode.EndOfSectionNode(), +1 );
+ pNd = GoPreviousWithFrame(&aTmp, nullptr);
+ if( pNd && rNode.GetIndex() > pNd->GetIndex() )
+ pNd = nullptr; // Do not go over the limits
+ mbMaster = false;
+ }
+ else
+ {
+ SwNodeIndex aTmp( rNode, -1 );
+ pNd = GoNextWithFrame(rNode.GetNodes(), &aTmp, nullptr);
+ mbMaster = true;
+ if( !bSearch && pNd && rNode.EndOfSectionIndex() < pNd->GetIndex() )
+ pNd = nullptr; // Do not go over the limits
+ }
+ }
+ else
+ {
+ pNd = &rNode;
+ mbMaster = mnIndex < rNode.GetIndex();
+ }
+ if( pNd )
+ {
+ if( pNd->IsContentNode() )
+ mpMod = const_cast<sw::BroadcastingModify*>(static_cast<sw::BroadcastingModify const *>(pNd->GetContentNode()));
+ else
+ {
+ assert(pNd->IsTableNode());
+ mpMod = pNd->GetTableNode()->GetTable().GetFrameFormat();
+ }
+ mpIter.reset(new SwIterator<SwFrame, sw::BroadcastingModify, sw::IteratorMode::UnwrapMulti>(*mpMod));
+ }
+ else
+ {
+ mpIter = nullptr;
+ mpMod = nullptr;
+ }
+}
+
+/**
+ * Returns the next "useful" Frame.
+ *
+ * When calling this method for the first time, a First is triggered at the
+ * actual Iterator. The result is check for suitability: Follows are not
+ * accepted, a Master is accepted when collecting the pUpper and when
+ * inserting before it.
+ * When inserting after it, we find and return the last Follow starting
+ * from the Master.
+ *
+ * If the Frame is located in a SectionFrame, we check to see whether the
+ * SectionFrame is the suitable return value (instead of the Frame itself).
+ * This is the case if the to-be-inserted Node is outside of the Section.
+ */
+SwFrame* SwNode2LayImpl::NextFrame()
+{
+ SwFrame* pRet;
+ if( !mpIter )
+ return nullptr;
+ if( !mbInit )
+ {
+ pRet = mpIter->First();
+ mbInit = true;
+ }
+ else
+ pRet = mpIter->Next();
+ while( pRet )
+ {
+ SwFlowFrame* pFlow = SwFlowFrame::CastFlowFrame( pRet );
+ assert(pFlow);
+ // Follows are pretty volatile, thus we ignore them.
+ // Even if we insert after the Frame, we start from the Master
+ // and iterate through it until the last Follow
+ if( !pFlow->IsFollow() )
+ {
+ if( !mbMaster )
+ {
+ while( pFlow->HasFollow() )
+ pFlow = pFlow->GetFollow();
+ pRet = &(pFlow->GetFrame());
+ }
+ if( pRet->IsInSct() )
+ {
+ SwSectionFrame* pSct = pRet->FindSctFrame();
+ // ATTENTION: If we are in a Footnote, from a Layout point of view
+ // it could be located in a Section with columns, although it
+ // should be outside of it when looking at the Nodes.
+ // Thus, when dealing with Footnotes, we need to check whether the
+ // SectionFrame is also located within the Footnote and not outside of it.
+ if( !pRet->IsInFootnote() || pSct->IsInFootnote() )
+ {
+ assert(pSct && pSct->GetSection());
+ SwSectionNode* pNd = pSct->GetSection()->GetFormat()->GetSectionNode();
+ assert(pNd);
+ // If the result Frame is located within a Section Frame
+ // whose Section does not contain the Node, we return with
+ // the SectionFrame, else we return with the Content/TabFrame
+ if( mbMaster )
+ {
+ if( pNd->GetIndex() >= mnIndex )
+ pRet = pSct;
+ }
+ else if( pNd->EndOfSectionIndex() < mnIndex )
+ pRet = pSct;
+ }
+ }
+ return pRet;
+ }
+ pRet = mpIter->Next();
+ }
+ return nullptr;
+}
+
+void SwNode2LayImpl::SaveUpperFrames()
+{
+ SwFrame* pFrame;
+ while( nullptr != (pFrame = NextFrame()) )
+ {
+ SwFrame* pPrv = pFrame->GetPrev();
+ pFrame = pFrame->GetUpper();
+ if( pFrame )
+ {
+ if( pFrame->IsFootnoteFrame() )
+ static_cast<SwFootnoteFrame*>(pFrame)->ColLock();
+ else if( pFrame->IsInSct() )
+ pFrame->FindSctFrame()->ColLock();
+ if( pPrv && pPrv->IsSctFrame() )
+ static_cast<SwSectionFrame*>(pPrv)->LockJoin();
+ mvUpperFrames.push_back( pPrv );
+ mvUpperFrames.push_back( pFrame );
+ }
+ }
+ mpIter.reset();
+ mpMod = nullptr;
+}
+
+SwLayoutFrame* SwNode2LayImpl::UpperFrame( SwFrame* &rpFrame, const SwNode &rNode )
+{
+ rpFrame = NextFrame();
+ if( !rpFrame )
+ return nullptr;
+ SwLayoutFrame* pUpper = rpFrame->GetUpper();
+ if( rpFrame->IsSctFrame() )
+ {
+ const SwNode* pNode = rNode.StartOfSectionNode();
+ if( pNode->IsSectionNode() )
+ {
+ SwFrame* pFrame = mbMaster ? rpFrame->FindPrev() : rpFrame->FindNext();
+ if( pFrame && pFrame->IsSctFrame() )
+ {
+ // pFrame could be a "dummy"-section
+ if( static_cast<SwSectionFrame*>(pFrame)->GetSection() &&
+ (&static_cast<const SwSectionNode*>(pNode)->GetSection() ==
+ static_cast<SwSectionFrame*>(pFrame)->GetSection()) )
+ {
+ // #i22922# - consider columned sections
+ // 'Go down' the section frame as long as the layout frame
+ // is found, which would contain content.
+ while ( pFrame->IsLayoutFrame() &&
+ static_cast<SwLayoutFrame*>(pFrame)->Lower() &&
+ !static_cast<SwLayoutFrame*>(pFrame)->Lower()->IsFlowFrame() &&
+ static_cast<SwLayoutFrame*>(pFrame)->Lower()->IsLayoutFrame() )
+ {
+ pFrame = static_cast<SwLayoutFrame*>(pFrame)->Lower();
+ }
+ assert(pFrame->IsLayoutFrame());
+ rpFrame = mbMaster ? nullptr
+ : static_cast<SwLayoutFrame*>(pFrame)->Lower();
+ assert((!rpFrame || rpFrame->IsFlowFrame()) &&
+ "<SwNode2LayImpl::UpperFrame(..)> - expected sibling isn't a flow frame." );
+ return static_cast<SwLayoutFrame*>(pFrame);
+ }
+
+ pUpper = new SwSectionFrame(const_cast<SwSectionNode*>(static_cast<const SwSectionNode*>(pNode))->GetSection(), rpFrame);
+ pUpper->Paste( rpFrame->GetUpper(),
+ mbMaster ? rpFrame : rpFrame->GetNext() );
+ static_cast<SwSectionFrame*>(pUpper)->Init();
+ rpFrame = nullptr;
+ // 'Go down' the section frame as long as the layout frame
+ // is found, which would contain content.
+ while ( pUpper->Lower() &&
+ !pUpper->Lower()->IsFlowFrame() &&
+ pUpper->Lower()->IsLayoutFrame() )
+ {
+ pUpper = static_cast<SwLayoutFrame*>(pUpper->Lower());
+ }
+ return pUpper;
+ }
+ }
+ }
+ if( !mbMaster )
+ rpFrame = rpFrame->GetNext();
+ return pUpper;
+}
+
+void SwNode2LayImpl::RestoreUpperFrames( SwNodes& rNds, SwNodeOffset nStt, SwNodeOffset nEnd )
+{
+ SwNode* pNd;
+ SwDoc& rDoc = rNds.GetDoc();
+ bool bFirst = true;
+ for( ; nStt < nEnd; ++nStt )
+ {
+ SwFrame* pNew = nullptr;
+ SwFrame* pNxt;
+ SwLayoutFrame* pUp;
+ if( (pNd = rNds[nStt])->IsContentNode() )
+ for( std::vector<SwFrame*>::size_type n = 0; n < mvUpperFrames.size(); )
+ {
+ pNxt = mvUpperFrames[n++];
+ if( bFirst && pNxt && pNxt->IsSctFrame() )
+ static_cast<SwSectionFrame*>(pNxt)->UnlockJoin();
+ pUp = static_cast<SwLayoutFrame*>(mvUpperFrames[n++]);
+ if( pNxt )
+ pNxt = pNxt->GetNext();
+ else
+ pNxt = pUp->Lower();
+ pNew = static_cast<SwContentNode*>(pNd)->MakeFrame( pUp );
+ pNew->Paste( pUp, pNxt );
+ mvUpperFrames[n-2] = pNew;
+ }
+ else if( pNd->IsTableNode() )
+ for( std::vector<SwFrame*>::size_type x = 0; x < mvUpperFrames.size(); )
+ {
+ pNxt = mvUpperFrames[x++];
+ if( bFirst && pNxt && pNxt->IsSctFrame() )
+ static_cast<SwSectionFrame*>(pNxt)->UnlockJoin();
+ pUp = static_cast<SwLayoutFrame*>(mvUpperFrames[x++]);
+ if( pNxt )
+ pNxt = pNxt->GetNext();
+ else
+ pNxt = pUp->Lower();
+ pNew = static_cast<SwTableNode*>(pNd)->MakeFrame( pUp );
+ assert(pNew->IsTabFrame());
+ pNew->Paste( pUp, pNxt );
+ static_cast<SwTabFrame*>(pNew)->RegistFlys();
+ mvUpperFrames[x-2] = pNew;
+ }
+ else if( pNd->IsSectionNode() )
+ {
+ nStt = pNd->EndOfSectionIndex();
+ for( std::vector<SwFrame*>::size_type x = 0; x < mvUpperFrames.size(); )
+ {
+ pNxt = mvUpperFrames[x++];
+ if( bFirst && pNxt && pNxt->IsSctFrame() )
+ static_cast<SwSectionFrame*>(pNxt)->UnlockJoin();
+ pUp = static_cast<SwLayoutFrame*>(mvUpperFrames[x++]);
+ OSL_ENSURE( pUp->GetUpper() || pUp->IsFlyFrame(), "Lost Upper" );
+ ::InsertCnt_( pUp, &rDoc, pNd->GetIndex(), false, nStt+1, pNxt );
+ pNxt = pUp->GetLastLower();
+ mvUpperFrames[x-2] = pNxt;
+ }
+ }
+ bFirst = false;
+ }
+ for( std::vector<SwFrame*>::size_type x = 0; x < mvUpperFrames.size(); ++x )
+ {
+ SwFrame* pTmp = mvUpperFrames[++x];
+ if( pTmp->IsFootnoteFrame() )
+ static_cast<SwFootnoteFrame*>(pTmp)->ColUnlock();
+ else if ( pTmp->IsInSct() )
+ {
+ SwSectionFrame* pSctFrame = pTmp->FindSctFrame();
+ pSctFrame->ColUnlock();
+ // #i18103# - invalidate size of section in order to
+ // assure, that the section is formatted, unless it was 'Collocked'
+ // from its 'collection' until its 'restoration'.
+ pSctFrame->InvalidateSize_();
+ }
+ }
+}
+
+SwFrame* SwNode2LayImpl::GetFrame( const Point* pDocPos ) const
+{
+ // test if change of member pIter -> pMod broke anything
+ std::pair<Point, bool> tmp;
+ if (pDocPos)
+ {
+ tmp.first = *pDocPos;
+ tmp.second = false;
+ }
+ return mpMod ? ::GetFrameOfModify(nullptr, *mpMod, FRM_ALL, nullptr, pDocPos ? &tmp : nullptr) : nullptr;
+}
+
+SwNode2Layout::SwNode2Layout( const SwNode& rNd, SwNodeOffset nIdx )
+ : m_pImpl( new SwNode2LayImpl( rNd, nIdx, false ) )
+{
+}
+
+SwNode2LayoutSaveUpperFrames::SwNode2LayoutSaveUpperFrames(const SwNode& rNd)
+ : m_pImpl( new SwNode2LayImpl( rNd, rNd.GetIndex(), true ) )
+{
+ m_pImpl->SaveUpperFrames();
+}
+
+void SwNode2LayoutSaveUpperFrames::RestoreUpperFrames(
+ SwNodes& rNds, SwNodeOffset const nStt, SwNodeOffset const nEnd)
+{
+ m_pImpl->RestoreUpperFrames( rNds, nStt, nEnd );
+}
+
+SwFrame* SwNode2Layout::NextFrame()
+{
+ return m_pImpl->NextFrame();
+}
+
+SwLayoutFrame* SwNode2Layout::UpperFrame( SwFrame* &rpFrame, const SwNode &rNode )
+{
+ return m_pImpl->UpperFrame( rpFrame, rNode );
+}
+
+SwNode2Layout::~SwNode2Layout()
+{
+}
+
+SwNode2LayoutSaveUpperFrames::~SwNode2LayoutSaveUpperFrames()
+{
+}
+
+SwFrame* SwNode2Layout::GetFrame( const Point* pDocPos ) const
+{
+ return m_pImpl->GetFrame( pDocPos );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/docnode/nodes.cxx b/sw/source/core/docnode/nodes.cxx
new file mode 100644
index 000000000..85f01cea7
--- /dev/null
+++ b/sw/source/core/docnode/nodes.cxx
@@ -0,0 +1,2392 @@
+/* -*- 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 <stdlib.h>
+
+#include <libxml/xmlwriter.h>
+#include <osl/diagnose.h>
+
+#include <node.hxx>
+#include <doc.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <IDocumentFieldsAccess.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <pam.hxx>
+#include <txtfld.hxx>
+#include <fmtfld.hxx>
+#include <numrule.hxx>
+#include <ndtxt.hxx>
+#include <ndnotxt.hxx>
+#include <swtable.hxx>
+#include <section.hxx>
+#include <ddefld.hxx>
+#include <swddetbl.hxx>
+#include <txtatr.hxx>
+#include <tox.hxx>
+#include <fmtrfmrk.hxx>
+#include <fmtftn.hxx>
+
+#include <docsh.hxx>
+#include <rootfrm.hxx>
+#include <txtfrm.hxx>
+
+typedef std::vector<SwStartNode*> SwStartNodePointers;
+
+// function to determine the highest level in the given range
+static sal_uInt16 HighestLevel( SwNodes & rNodes, const SwNodeRange & rRange );
+
+/** Constructor
+ *
+ * creates the base sections (PostIts, Inserts, AutoText, RedLines, Content)
+ *
+ * @param rDocument TODO: provide documentation
+ */
+SwNodes::SwNodes( SwDoc& rDocument )
+ : m_vIndices(nullptr), m_rMyDoc( rDocument )
+{
+ m_bInNodesDel = m_bInDelUpdOutline = false;
+
+ SwNodeOffset nPos(0);
+ SwStartNode* pSttNd = new SwStartNode( *this, nPos++ );
+ m_pEndOfPostIts = new SwEndNode( *this, nPos++, *pSttNd );
+
+ SwStartNode* pTmp = new SwStartNode( *this, nPos++ );
+ m_pEndOfInserts = new SwEndNode( *this, nPos++, *pTmp );
+
+ pTmp = new SwStartNode( *this, nPos++ );
+ pTmp->m_pStartOfSection = pSttNd;
+ m_pEndOfAutotext = new SwEndNode( *this, nPos++, *pTmp );
+
+ pTmp = new SwStartNode( *this, nPos++ );
+ pTmp->m_pStartOfSection = pSttNd;
+ m_pEndOfRedlines = new SwEndNode( *this, nPos++, *pTmp );
+
+ pTmp = new SwStartNode( *this, nPos++ );
+ pTmp->m_pStartOfSection = pSttNd;
+ m_pEndOfContent.reset(new SwEndNode( *this, nPos++, *pTmp ));
+
+ m_pOutlineNodes.reset(new SwOutlineNodes);
+}
+
+/** Destructor
+ *
+ * Deletes all nodes whose pointer are in a dynamic array. This should be no
+ * problem as nodes cannot be created outside this array and, thus, cannot be
+ * part of multiple arrays.
+ */
+SwNodes::~SwNodes()
+{
+ m_pOutlineNodes.reset();
+
+ {
+ SwNodeIndex aNdIdx( *this );
+ while( true )
+ {
+ SwNode *pNode = &aNdIdx.GetNode();
+ if( pNode == m_pEndOfContent.get() )
+ break;
+
+ ++aNdIdx;
+ delete pNode;
+ }
+ }
+
+ // here, all SwNodeIndices must be unregistered
+ m_pEndOfContent.reset();
+}
+
+static bool IsInsertOutline(SwNodes const& rNodes, SwNodeOffset const nIndex)
+{
+ if (!rNodes.IsDocNodes())
+ {
+ return false;
+ }
+ return nIndex < rNodes.GetEndOfRedlines().StartOfSectionNode()->GetIndex()
+ || rNodes.GetEndOfRedlines().GetIndex() < nIndex;
+}
+
+void SwNodes::ChgNode( SwNodeIndex const & rDelPos, SwNodeOffset nSz,
+ SwNodeIndex& rInsPos, bool bNewFrames )
+{
+ // no need for frames in the UndoArea
+ SwNodes& rNds = rInsPos.GetNodes();
+ const SwNode* pPrevInsNd = rNds[ rInsPos.GetIndex() -SwNodeOffset(1) ];
+
+ // declare all fields as invalid, updating will happen
+ // in the idle-handler of the doc
+ if( GetDoc().getIDocumentFieldsAccess().SetFieldsDirty( true, &rDelPos.GetNode(), nSz ) &&
+ &rNds.GetDoc() != &GetDoc() )
+ rNds.GetDoc().getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, SwNodeOffset(0) );
+
+ // NEVER include nodes from the RedLineArea
+ SwNodeOffset nNd = rInsPos.GetIndex();
+ bool const bInsOutlineIdx = IsInsertOutline(rNds, nNd);
+
+ if( &rNds == this ) // if in the same node array -> move
+ {
+ // Move order: from front to back, so that new entries are added at
+ // first position, thus, deletion position stays the same
+ const SwNodeOffset nDiff(rDelPos.GetIndex() < rInsPos.GetIndex() ? 0 : 1);
+
+ for( SwNodeOffset n = rDelPos.GetIndex(); nSz; n += nDiff, --nSz )
+ {
+ SwNodeIndex aDelIdx( *this, n );
+ SwNode& rNd = aDelIdx.GetNode();
+
+ // #i57920# - correction of refactoring done by cws swnumtree:
+ // - <SwTextNode::SetLevel( NO_NUMBERING ) is deprecated and
+ // set <IsCounted> state of the text node to <false>, which
+ // isn't correct here.
+ if ( rNd.IsTextNode() )
+ {
+ SwTextNode* pTextNode = rNd.GetTextNode();
+
+ pTextNode->RemoveFromList();
+
+ if (pTextNode->IsOutline())
+ {
+ SwNode* pSrch = &rNd;
+ m_pOutlineNodes->erase( pSrch );
+ }
+ }
+
+ BigPtrArray::Move( sal_Int32(aDelIdx.GetIndex()), sal_Int32(rInsPos.GetIndex()) );
+
+ if( rNd.IsTextNode() )
+ {
+ SwTextNode& rTextNd = static_cast<SwTextNode&>(rNd);
+
+ rTextNd.AddToList();
+
+ if (bInsOutlineIdx && rTextNd.IsOutline())
+ {
+ SwNode* pSrch = &rNd;
+ m_pOutlineNodes->insert( pSrch );
+ }
+ rTextNd.InvalidateNumRule();
+
+//FEATURE::CONDCOLL
+ if( RES_CONDTXTFMTCOLL == rTextNd.GetTextColl()->Which() )
+ rTextNd.ChkCondColl();
+//FEATURE::CONDCOLL
+ }
+ else if( rNd.IsContentNode() )
+ static_cast<SwContentNode&>(rNd).InvalidateNumRule();
+ }
+ }
+ else
+ {
+ bool bSavePersData(GetDoc().GetIDocumentUndoRedo().IsUndoNodes(rNds));
+ bool bRestPersData(GetDoc().GetIDocumentUndoRedo().IsUndoNodes(*this));
+ SwDoc* pDestDoc = &rNds.GetDoc() != &GetDoc() ? &rNds.GetDoc() : nullptr;
+ OSL_ENSURE(!pDestDoc, "SwNodes::ChgNode(): "
+ "the code to handle text fields here looks broken\n"
+ "if the target is in a different document.");
+ if( !bRestPersData && !bSavePersData && pDestDoc )
+ bSavePersData = bRestPersData = true;
+
+ OUString sNumRule;
+ for( SwNodeOffset n(0); n < nSz; n++ )
+ {
+ SwNode* pNd = &rDelPos.GetNode();
+
+ // NoTextNode keep their persistent data
+ if( pNd->IsNoTextNode() )
+ {
+ if( bSavePersData )
+ static_cast<SwNoTextNode*>(pNd)->SavePersistentData();
+ }
+ else if( pNd->IsTextNode() )
+ {
+ SwTextNode* pTextNd = static_cast<SwTextNode*>(pNd);
+
+ // remove outline index from old nodes array
+ if (pTextNd->IsOutline())
+ {
+ m_pOutlineNodes->erase( pNd );
+ }
+
+ // copy rules if needed
+ if( pDestDoc )
+ {
+ const SwNumRule* pNumRule = pTextNd->GetNumRule();
+ if( pNumRule && sNumRule != pNumRule->GetName() )
+ {
+ sNumRule = pNumRule->GetName();
+ SwNumRule* pDestRule = pDestDoc->FindNumRulePtr( sNumRule );
+ if( pDestRule )
+ pDestRule->SetInvalidRule( true );
+ else
+ pDestDoc->MakeNumRule( sNumRule, pNumRule );
+ }
+ }
+ else
+ {
+ // if movement into the UndoNodes-array, update numbering
+ if (sw::HasNumberingWhichNeedsLayoutUpdate(*pTextNd))
+ {
+ pTextNd->InvalidateNumRule();
+ }
+ }
+
+ pTextNd->RemoveFromList();
+ }
+
+ RemoveNode( rDelPos.GetIndex(), SwNodeOffset(1), false ); // move indices
+ SwContentNode * pCNd = pNd->GetContentNode();
+ rNds.InsertNode( pNd, rInsPos );
+
+ if( pCNd )
+ {
+ SwTextNode* pTextNd = pCNd->GetTextNode();
+ if( pTextNd )
+ {
+ SwpHints * const pHts = pTextNd->GetpSwpHints();
+ // OutlineNodes set the new nodes in the array
+ if (bInsOutlineIdx && pTextNd->IsOutline())
+ {
+ rNds.m_pOutlineNodes->insert( pTextNd );
+ }
+
+ pTextNd->AddToList();
+
+ // special treatment for fields
+ if( pHts && pHts->Count() )
+ {
+ bool const bToUndo = !pDestDoc &&
+ GetDoc().GetIDocumentUndoRedo().IsUndoNodes(rNds);
+ for( size_t i = pHts->Count(); i; )
+ {
+ SwTextAttr * const pAttr = pHts->Get( --i );
+ switch ( pAttr->Which() )
+ {
+ case RES_TXTATR_FIELD:
+ case RES_TXTATR_ANNOTATION:
+ case RES_TXTATR_INPUTFIELD:
+ {
+ SwTextField* pTextField = static_txtattr_cast<SwTextField*>(pAttr);
+ rNds.GetDoc().getIDocumentFieldsAccess().InsDelFieldInFieldLst( !bToUndo, *pTextField );
+
+ const SwFieldType* pTyp = pTextField->GetFormatField().GetField()->GetTyp();
+ if ( SwFieldIds::Postit == pTyp->Which() )
+ {
+ rNds.GetDoc().GetDocShell()->Broadcast(
+ SwFormatFieldHint(
+ &pTextField->GetFormatField(),
+ ( pTextField->GetFormatField().IsFieldInDoc()
+ ? SwFormatFieldHintWhich::INSERTED
+ : SwFormatFieldHintWhich::REMOVED ) ) );
+ }
+ else if( SwFieldIds::Dde == pTyp->Which() )
+ {
+ if( bToUndo )
+ const_cast<SwDDEFieldType*>(static_cast<const SwDDEFieldType*>(pTyp))->DecRefCnt();
+ else
+ const_cast<SwDDEFieldType*>(static_cast<const SwDDEFieldType*>(pTyp))->IncRefCnt();
+ }
+ static_cast<SwFormatField&>(pAttr->GetAttr())
+ .InvalidateField();
+ }
+ break;
+
+ case RES_TXTATR_FTN:
+ static_cast<SwFormatFootnote&>(pAttr->GetAttr())
+ .InvalidateFootnote();
+ break;
+
+ case RES_TXTATR_TOXMARK:
+ static_cast<SwTOXMark&>(pAttr->GetAttr())
+ .InvalidateTOXMark();
+ break;
+
+ case RES_TXTATR_REFMARK:
+ static_cast<SwFormatRefMark&>(pAttr->GetAttr())
+ .InvalidateRefMark();
+ break;
+
+ case RES_TXTATR_META:
+ case RES_TXTATR_METAFIELD:
+ {
+ SwTextMeta *const pTextMeta(
+ static_txtattr_cast<SwTextMeta*>(pAttr));
+ // force removal of UNO object
+ pTextMeta->ChgTextNode(nullptr);
+ pTextMeta->ChgTextNode(pTextNd);
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+ //FEATURE::CONDCOLL
+ if( RES_CONDTXTFMTCOLL == pTextNd->GetTextColl()->Which() )
+ pTextNd->ChkCondColl();
+ //FEATURE::CONDCOLL
+ }
+ else
+ {
+ // Moved into different Docs? Persist data again!
+ if( pCNd->IsNoTextNode() && bRestPersData )
+ static_cast<SwNoTextNode*>(pCNd)->RestorePersistentData();
+ }
+ }
+ }
+ }
+
+ // declare all fields as invalid, updating will happen
+ // in the idle-handler of the doc
+ GetDoc().getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, SwNodeOffset(0) );
+ if( &rNds.GetDoc() != &GetDoc() )
+ rNds.GetDoc().getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, SwNodeOffset(0) );
+
+ if( bNewFrames )
+ bNewFrames = &GetDoc().GetNodes() == &rNds &&
+ GetDoc().getIDocumentLayoutAccess().GetCurrentViewShell();
+
+ if( !bNewFrames )
+ return;
+
+ // get the frames:
+ SwNodeIndex aIdx( *pPrevInsNd, 1 );
+ SwNodeIndex aFrameNdIdx( aIdx );
+ SwNode* pFrameNd = rNds.FindPrvNxtFrameNode( aFrameNdIdx,
+ rNds[ rInsPos.GetIndex() - 1 ] );
+
+ if( !pFrameNd )
+ return;
+
+ while( aIdx != rInsPos )
+ {
+ SwContentNode* pCNd = aIdx.GetNode().GetContentNode();
+ if( pCNd )
+ {
+ if( pFrameNd->IsTableNode() )
+ static_cast<SwTableNode*>(pFrameNd)->MakeFramesForAdjacentContentNode(aIdx);
+ else if( pFrameNd->IsSectionNode() )
+ static_cast<SwSectionNode*>(pFrameNd)->MakeFramesForAdjacentContentNode(aIdx);
+ else
+ static_cast<SwContentNode*>(pFrameNd)->MakeFramesForAdjacentContentNode(*pCNd);
+ pFrameNd = pCNd;
+ }
+ ++aIdx;
+ }
+}
+
+// TODO: provide documentation
+/** move the node pointer
+ *
+ * Move the node pointer from "(inclusive) start position to (exclusive) end
+ * position" to target position.
+ * If the target is in front of the first or in the area between first and
+ * last element to move, nothing happens.
+ * If the area to move is empty or the end position is before the start
+ * position, nothing happens.
+ *
+ * @param aRange range to move (excluding end node)
+ * @param rNodes
+ * @param aIndex
+ * @param bNewFrames
+ * @return
+ */
+bool SwNodes::MoveNodes( const SwNodeRange& aRange, SwNodes & rNodes,
+ const SwNodeIndex& aIndex, bool bNewFrames )
+{
+ SwNode * pCurrentNode;
+ if( aIndex == SwNodeOffset(0) ||
+ ( (pCurrentNode = &aIndex.GetNode())->GetStartNode() &&
+ !pCurrentNode->StartOfSectionIndex() ))
+ return false;
+
+ SwNodeRange aRg( aRange );
+
+ // skip "simple" start or end nodes
+ while( SwNodeType::Start == (pCurrentNode = &aRg.aStart.GetNode())->GetNodeType()
+ || ( pCurrentNode->IsEndNode() &&
+ !pCurrentNode->m_pStartOfSection->IsSectionNode() ) )
+ ++aRg.aStart;
+ --aRg.aStart;
+
+ // if aEnd-1 points to no ContentNode, search previous one
+ --aRg.aEnd;
+ while( ( (( pCurrentNode = &aRg.aEnd.GetNode())->GetStartNode() &&
+ !pCurrentNode->IsSectionNode() ) ||
+ ( pCurrentNode->IsEndNode() &&
+ SwNodeType::Start == pCurrentNode->m_pStartOfSection->GetNodeType()) ) &&
+ aRg.aEnd > aRg.aStart )
+ --aRg.aEnd;
+
+ // if in same array, check insertion position
+ if( aRg.aStart >= aRg.aEnd )
+ return false;
+
+ if( this == &rNodes )
+ {
+ if( ( aIndex.GetIndex()-SwNodeOffset(1) >= aRg.aStart.GetIndex() &&
+ aIndex.GetIndex()-SwNodeOffset(1) < aRg.aEnd.GetIndex()) ||
+ ( aIndex.GetIndex()-SwNodeOffset(1) == aRg.aEnd.GetIndex() ) )
+ return false;
+ }
+
+ SwNodeOffset nInsPos(0); // counter for tmp array
+
+ // array as a stack, storing all StartOfSelections
+ SwStartNodePointers aSttNdStack;
+ SwStartNodePointers::size_type nLevel = 0; // level counter
+
+ // set start index
+ SwNodeIndex aIdx( aIndex );
+
+ SwStartNode* pStartNode = aIdx.GetNode().m_pStartOfSection;
+ aSttNdStack.insert( aSttNdStack.begin(), pStartNode );
+
+ SwNodeRange aOrigInsPos( aIdx, SwNodeOffset(-1), aIdx ); // original insertion position
+
+ // call DelFrames/MakeFrames for the upmost SectionNode
+ int nSectNdCnt = 0;
+ bool bSaveNewFrames = bNewFrames;
+
+ // continue until everything has been moved
+ while( aRg.aStart < aRg.aEnd )
+ {
+ pCurrentNode = &aRg.aEnd.GetNode();
+ switch( pCurrentNode->GetNodeType() )
+ {
+ case SwNodeType::End:
+ {
+ if( nInsPos ) // move everything until here
+ {
+ // delete and copy. CAUTION: all indices after
+ // "aRg.aEnd+1" will be moved as well!
+ SwNodeIndex aSwIndex( aRg.aEnd, 1 );
+ ChgNode( aSwIndex, nInsPos, aIdx, bNewFrames );
+ aIdx -= nInsPos;
+ nInsPos = SwNodeOffset(0);
+ }
+
+ SwStartNode* pSttNd = pCurrentNode->m_pStartOfSection;
+ if( pSttNd->IsTableNode() )
+ {
+ SwTableNode* pTableNd = static_cast<SwTableNode*>(pSttNd);
+
+ // move the whole table/range
+ nInsPos = (aRg.aEnd.GetIndex() -
+ pSttNd->GetIndex() )+1;
+ aRg.aEnd -= nInsPos;
+
+ // NEVER include nodes from the RedLineArea
+ SwNodeOffset nNd = aIdx.GetIndex();
+ bool const bInsOutlineIdx = IsInsertOutline(rNodes, nNd);
+
+ if( bNewFrames )
+ // delete all frames
+ pTableNd->DelFrames(nullptr);
+ if( &rNodes == this ) // move into self?
+ {
+ // move all Start/End/ContentNodes
+ // ContentNodes: delete also the frames!
+ pTableNd->m_pStartOfSection = aIdx.GetNode().m_pStartOfSection;
+ for( SwNodeOffset n(0); n < nInsPos; ++n )
+ {
+ SwNodeIndex aMvIdx( aRg.aEnd, 1 );
+ SwContentNode* pCNd = nullptr;
+ SwNode* pTmpNd = &aMvIdx.GetNode();
+ if( pTmpNd->IsContentNode() )
+ {
+ pCNd = static_cast<SwContentNode*>(pTmpNd);
+ if( pTmpNd->IsTextNode() )
+ static_cast<SwTextNode*>(pTmpNd)->RemoveFromList();
+
+ // remove outline index from old nodes array
+ if (pCNd->IsTextNode() && pCNd->GetTextNode()->IsOutline())
+ {
+ m_pOutlineNodes->erase( pCNd );
+ }
+ else
+ pCNd = nullptr;
+ }
+
+ BigPtrArray::Move( sal_Int32(aMvIdx.GetIndex()), sal_Int32(aIdx.GetIndex()) );
+
+ if( bInsOutlineIdx && pCNd )
+ m_pOutlineNodes->insert( pCNd );
+ if( pTmpNd->IsTextNode() )
+ static_cast<SwTextNode*>(pTmpNd)->AddToList();
+ }
+ }
+ else
+ {
+ // get StartNode
+ // Even aIdx points to a startnode, we need the startnode
+ // of the environment of aIdx (#i80941)
+ SwStartNode* pSttNode = aIdx.GetNode().m_pStartOfSection;
+
+ // get all boxes with content because their indices
+ // pointing to the StartNodes need to be reset
+ // (copying the array and deleting all found ones eases
+ // searching)
+ SwNodeIndex aMvIdx( aRg.aEnd, 1 );
+ for( SwNodeOffset n(0); n < nInsPos; ++n )
+ {
+ SwNode* pNd = &aMvIdx.GetNode();
+
+ const bool bOutlNd = pNd->IsTextNode() && pNd->GetTextNode()->IsOutline();
+ // delete outline indices from old node array
+ if( bOutlNd )
+ m_pOutlineNodes->erase( pNd );
+
+ RemoveNode( aMvIdx.GetIndex(), SwNodeOffset(1), false );
+ pNd->m_pStartOfSection = pSttNode;
+ rNodes.InsertNode( pNd, aIdx );
+
+ // set correct indices in Start/EndNodes
+ if( bInsOutlineIdx && bOutlNd )
+ // and put them into the new node array
+ rNodes.m_pOutlineNodes->insert( pNd );
+ else if( pNd->IsStartNode() )
+ pSttNode = static_cast<SwStartNode*>(pNd);
+ else if( pNd->IsEndNode() )
+ {
+ pSttNode->m_pEndOfSection = static_cast<SwEndNode*>(pNd);
+ if( pSttNode->IsSectionNode() )
+ static_cast<SwSectionNode*>(pSttNode)->NodesArrChgd();
+ pSttNode = pSttNode->m_pStartOfSection;
+ }
+ }
+
+ if( auto pDDETable = dynamic_cast<SwDDETable*>(&pTableNd->GetTable()) )
+ {
+ SwDDEFieldType* pTyp = pDDETable->GetDDEFieldType();
+ if( pTyp )
+ {
+ if( rNodes.IsDocNodes() )
+ pTyp->IncRefCnt();
+ else
+ pTyp->DecRefCnt();
+ }
+ }
+
+ if (GetDoc().GetIDocumentUndoRedo().IsUndoNodes(rNodes))
+ {
+ SwFrameFormat* pTableFormat = pTableNd->GetTable().GetFrameFormat();
+ pTableFormat->GetNotifier().Broadcast(SfxHint(SfxHintId::Dying));
+ }
+ }
+ if( bNewFrames )
+ {
+ SwNodeIndex aTmp( aIdx );
+ pTableNd->MakeOwnFrames(&aTmp);
+ }
+ aIdx -= nInsPos;
+ nInsPos = SwNodeOffset(0);
+ }
+ else if( pSttNd->GetIndex() < aRg.aStart.GetIndex() )
+ {
+ // SectionNode: not the whole section will be moved, thus,
+ // move only the ContentNodes
+ // StartNode: create a new section at the given position
+ do { // middle check loop
+ if( !pSttNd->IsSectionNode() )
+ {
+ // create StartNode and EndNode at InsertPos
+ SwStartNode* pTmp = new SwStartNode( aIdx,
+ SwNodeType::Start,
+/*?? NodeType ??*/ SwNormalStartNode );
+
+ nLevel++; // put the index to StartNode on the stack
+ aSttNdStack.insert( aSttNdStack.begin() + nLevel, pTmp );
+
+ // create EndNode
+ new SwEndNode( aIdx, *pTmp );
+ }
+ else if (GetDoc().GetIDocumentUndoRedo().IsUndoNodes(
+ rNodes))
+ {
+ // use placeholder in UndoNodes array
+ new SwPlaceholderNode(aIdx);
+ }
+ else
+ {
+ // JP 18.5.2001 (Bug 70454) creating new section?
+ --aRg.aEnd;
+ break;
+
+ }
+
+ --aRg.aEnd;
+ --aIdx;
+ } while( false );
+ }
+ else
+ {
+ // move StartNode and EndNode in total
+
+ // if Start is exactly the Start of the area,
+ // then the Node needs to be re-visited
+ if( &aRg.aStart.GetNode() == pSttNd )
+ --aRg.aStart;
+
+ SwSectionNode* pSctNd = pSttNd->GetSectionNode();
+ if( bNewFrames && pSctNd )
+ { // tdf#135056 skip over code in DelFrames() that moves
+ // SwNodeIndex around because in case of nested
+ // sections, m_pStartOfSection will point between
+ // undo nodes-array and doc nodes-array
+ pSctNd->DelFrames(nullptr, true);
+ }
+
+ RemoveNode( aRg.aEnd.GetIndex(), SwNodeOffset(1), false ); // delete EndNode
+ SwNodeOffset nSttPos = pSttNd->GetIndex();
+
+ // this StartNode will be removed later
+ SwStartNode* pTmpSttNd = new SwStartNode( *this, nSttPos+1 );
+ pTmpSttNd->m_pStartOfSection = pSttNd->m_pStartOfSection;
+
+ RemoveNode( nSttPos, SwNodeOffset(1), false ); // delete SttNode
+
+ pSttNd->m_pStartOfSection = aIdx.GetNode().m_pStartOfSection;
+ rNodes.InsertNode( pSttNd, aIdx );
+ rNodes.InsertNode( pCurrentNode, aIdx );
+ --aIdx;
+ pSttNd->m_pEndOfSection = static_cast<SwEndNode*>(pCurrentNode);
+
+ --aRg.aEnd;
+
+ nLevel++; // put the index pointing to the StartNode onto the stack
+ aSttNdStack.insert( aSttNdStack.begin() + nLevel, pSttNd );
+
+ // reset remaining indices if SectionNode
+ if( pSctNd )
+ {
+ pSctNd->NodesArrChgd();
+ ++nSectNdCnt;
+ // tdf#132326 do not let frames survive in undo nodes
+ if (!GetDoc().GetIDocumentUndoRedo().IsUndoNodes(rNodes))
+ {
+ bNewFrames = false;
+ }
+ }
+ }
+ }
+ break;
+
+ case SwNodeType::Section:
+ if( !nLevel &&
+ GetDoc().GetIDocumentUndoRedo().IsUndoNodes(rNodes))
+ {
+ // here, a SectionDummyNode needs to be inserted at the current position
+ if( nInsPos ) // move everything until here
+ {
+ // delete and copy. CAUTION: all indices after
+ // "aRg.aEnd+1" will be moved as well!
+ SwNodeIndex aSwIndex( aRg.aEnd, 1 );
+ ChgNode( aSwIndex, nInsPos, aIdx, bNewFrames );
+ aIdx -= nInsPos;
+ nInsPos = SwNodeOffset(0);
+ }
+ new SwPlaceholderNode(aIdx);
+ --aRg.aEnd;
+ --aIdx;
+ break;
+ }
+ [[fallthrough]];
+ case SwNodeType::Table:
+ case SwNodeType::Start:
+ {
+ // empty section -> nothing to do
+ // and only if it's a top level section
+ if( !nInsPos && !nLevel )
+ {
+ --aRg.aEnd;
+ break;
+ }
+
+ if( !nLevel ) // level is decreasing
+ {
+ // create decrease
+ SwNodeIndex aTmpSIdx( aOrigInsPos.aStart, 1 );
+ SwStartNode* pTmpStt = new SwStartNode( aTmpSIdx,
+ SwNodeType::Start,
+ static_cast<SwStartNode*>(pCurrentNode)->GetStartNodeType() );
+
+ --aTmpSIdx;
+
+ SwNodeIndex aTmpEIdx( aOrigInsPos.aEnd );
+ new SwEndNode( aTmpEIdx, *pTmpStt );
+ --aTmpEIdx;
+ ++aTmpSIdx;
+
+ // set correct StartOfSection
+ ++aRg.aEnd;
+ {
+ SwNodeIndex aCntIdx( aRg.aEnd );
+ for( SwNodeOffset n(0); n < nInsPos; n++, ++aCntIdx)
+ aCntIdx.GetNode().m_pStartOfSection = pTmpStt;
+ }
+
+ // also set correct StartNode for all decreased nodes
+ while( aTmpSIdx < aTmpEIdx )
+ if( nullptr != (( pCurrentNode = &aTmpEIdx.GetNode())->GetEndNode()) )
+ aTmpEIdx = pCurrentNode->StartOfSectionIndex();
+ else
+ {
+ pCurrentNode->m_pStartOfSection = pTmpStt;
+ --aTmpEIdx;
+ }
+
+ --aIdx; // after the inserted StartNode
+ --aRg.aEnd; // before StartNode
+ // copy array. CAUTION: all indices after
+ // "aRg.aEnd+1" will be moved as well!
+ SwNodeIndex aSwIndex( aRg.aEnd, 1 );
+ ChgNode( aSwIndex, nInsPos, aIdx, bNewFrames );
+ aIdx -= nInsPos+1;
+ nInsPos = SwNodeOffset(0);
+ }
+ else // all nodes between StartNode and EndNode were moved
+ {
+ OSL_ENSURE( pCurrentNode == aSttNdStack[nLevel] ||
+ ( pCurrentNode->IsStartNode() &&
+ aSttNdStack[nLevel]->IsSectionNode()),
+ "wrong StartNode" );
+
+ SwNodeIndex aSwIndex( aRg.aEnd, 1 );
+ ChgNode( aSwIndex, nInsPos, aIdx, bNewFrames );
+ aIdx -= nInsPos+1; // before inserted StartNode
+ nInsPos = SwNodeOffset(0);
+
+ // remove pointer from node array
+ RemoveNode( aRg.aEnd.GetIndex(), SwNodeOffset(1), true );
+ --aRg.aEnd;
+
+ SwSectionNode* pSectNd = aSttNdStack[ nLevel ]->GetSectionNode();
+ if( pSectNd && !--nSectNdCnt )
+ {
+ SwNodeIndex aTmp( *pSectNd );
+ pSectNd->MakeOwnFrames(&aTmp);
+ bNewFrames = bSaveNewFrames;
+ }
+ aSttNdStack.erase( aSttNdStack.begin() + nLevel ); // remove from stack
+ nLevel--;
+ }
+
+ // delete all resulting empty start/end node pairs
+ SwNode* pTmpNode = (*this)[ aRg.aEnd.GetIndex()+1 ]->GetEndNode();
+ if( pTmpNode && SwNodeType::Start == (pCurrentNode = &aRg.aEnd.GetNode())
+ ->GetNodeType() && pCurrentNode->StartOfSectionIndex() &&
+ pTmpNode->StartOfSectionNode() == pCurrentNode )
+ {
+ DelNodes( aRg.aEnd, SwNodeOffset(2) );
+ --aRg.aEnd;
+ }
+ }
+ break;
+
+ case SwNodeType::Text:
+ //Add special function to text node.
+ {
+ if( bNewFrames && pCurrentNode->GetContentNode() )
+ static_cast<SwContentNode*>(pCurrentNode)->DelFrames(nullptr);
+ pCurrentNode->m_pStartOfSection = aSttNdStack[ nLevel ];
+ nInsPos++;
+ --aRg.aEnd;
+ }
+ break;
+ case SwNodeType::Grf:
+ case SwNodeType::Ole:
+ {
+ if( bNewFrames && pCurrentNode->GetContentNode() )
+ static_cast<SwContentNode*>(pCurrentNode)->DelFrames(nullptr);
+
+ pCurrentNode->m_pStartOfSection = aSttNdStack[ nLevel ];
+ nInsPos++;
+ --aRg.aEnd;
+ }
+ break;
+
+ case SwNodeType::PlaceHolder:
+ if (GetDoc().GetIDocumentUndoRedo().IsUndoNodes(*this))
+ {
+ if( &rNodes == this ) // inside UndoNodesArray
+ {
+ // move everything
+ pCurrentNode->m_pStartOfSection = aSttNdStack[ nLevel ];
+ nInsPos++;
+ }
+ else // move into "normal" node array
+ {
+ // than a SectionNode (start/end) is needed at the current
+ // InsPos; if so skip it, otherwise ignore current node
+ if( nInsPos ) // move everything until here
+ {
+ // delete and copy. CAUTION: all indices after
+ // "aRg.aEnd+1" will be moved as well!
+ SwNodeIndex aSwIndex( aRg.aEnd, 1 );
+ ChgNode( aSwIndex, nInsPos, aIdx, bNewFrames );
+ aIdx -= nInsPos;
+ nInsPos = SwNodeOffset(0);
+ }
+ SwNode* pTmpNd = &aIdx.GetNode();
+ if( pTmpNd->IsSectionNode() ||
+ pTmpNd->StartOfSectionNode()->IsSectionNode() )
+ --aIdx; // skip
+ }
+ }
+ else {
+ assert(!"How can this node be in the node array?");
+ }
+ --aRg.aEnd;
+ break;
+
+ default:
+ assert(!"Unknown node type");
+ break;
+ }
+ }
+
+ if( nInsPos ) // copy remaining rest
+ {
+ // rest should be ok
+ SwNodeIndex aSwIndex( aRg.aEnd, 1 );
+ ChgNode( aSwIndex, nInsPos, aIdx, bNewFrames );
+ }
+ ++aRg.aEnd; // again, exclusive end
+
+ // delete all resulting empty start/end node pairs
+ if( ( pCurrentNode = &aRg.aStart.GetNode())->GetStartNode() &&
+ pCurrentNode->StartOfSectionIndex() &&
+ aRg.aEnd.GetNode().GetEndNode() )
+ DelNodes( aRg.aStart, SwNodeOffset(2) );
+
+ // initialize numbering update
+ ++aOrigInsPos.aStart;
+ // Moved in same node array? Then call update top down!
+ if( this == &rNodes &&
+ aRg.aEnd.GetIndex() >= aOrigInsPos.aStart.GetIndex() )
+ {
+ UpdateOutlineIdx( aOrigInsPos.aStart.GetNode() );
+ UpdateOutlineIdx( aRg.aEnd.GetNode() );
+ }
+ else
+ {
+ UpdateOutlineIdx( aRg.aEnd.GetNode() );
+ rNodes.UpdateOutlineIdx( aOrigInsPos.aStart.GetNode() );
+ }
+
+ return true;
+}
+
+/** create a start/end section pair
+ *
+ * Other nodes might be in between.
+ *
+ * After this method call, the start node of pRange will be pointing to the
+ * first node after the start section node and the end node will be the index
+ * of the end section node. If this method is called multiple times with the
+ * same input, multiple sections containing the previous ones will be created
+ * (no content nodes between start or end node).
+ *
+ * @note Start and end node of the range must be on the same level but MUST
+ * NOT be on the top level.
+ *
+ * @param [IN,OUT] pRange the range (excl. end)
+ * @param eSttNdTyp type of the start node
+ */
+void SwNodes::SectionDown(SwNodeRange *pRange, SwStartNodeType eSttNdTyp )
+{
+ if( pRange->aStart >= pRange->aEnd ||
+ pRange->aEnd >= Count() ||
+ !::CheckNodesRange(pRange->aStart, pRange->aEnd, false))
+ {
+ return;
+ }
+
+ // If the beginning of a range is before or at a start node position, so
+ // delete it, otherwise empty S/E or E/S nodes would be created.
+ // For other nodes, create a new start node.
+ SwNode * pCurrentNode = &pRange->aStart.GetNode();
+ SwNodeIndex aTmpIdx( *pCurrentNode->StartOfSectionNode() );
+
+ if( pCurrentNode->GetEndNode() )
+ DelNodes( pRange->aStart ); // prevent empty section
+ else
+ {
+ // insert a new StartNode
+ SwNode* pSttNd = new SwStartNode( pRange->aStart, SwNodeType::Start, eSttNdTyp );
+ pRange->aStart = *pSttNd;
+ aTmpIdx = pRange->aStart;
+ }
+
+ // If the end of a range is before or at a StartNode, so delete it,
+ // otherwise empty S/E or E/S nodes would be created.
+ // For other nodes, insert a new end node.
+ --pRange->aEnd;
+ if( pRange->aEnd.GetNode().GetStartNode() )
+ DelNodes( pRange->aEnd );
+ else
+ {
+ ++pRange->aEnd;
+ // insert a new EndNode
+ new SwEndNode( pRange->aEnd, *pRange->aStart.GetNode().GetStartNode() );
+ }
+ --pRange->aEnd;
+
+ SectionUpDown( aTmpIdx, pRange->aEnd );
+}
+
+/** increase level of the given range
+ *
+ * The range contained in pRange will be lifted to the next higher level.
+ * This is done by adding an end node at pRange.start and a start node at
+ * pRange.end. Furthermore all indices for this range will be updated.
+ *
+ * After this method call, the start node of pRange will be pointing to the
+ * first node inside the lifted range and the end node will be pointing to the
+ * last position inside the lifted range.
+ *
+ * @param [IN,OUT] pRange the range of nodes where the level should be increased
+ */
+void SwNodes::SectionUp(SwNodeRange *pRange)
+{
+ if( pRange->aStart >= pRange->aEnd ||
+ pRange->aEnd >= Count() ||
+ !::CheckNodesRange(pRange->aStart, pRange->aEnd, false) ||
+ ( HighestLevel( *this, *pRange ) <= 1 ))
+ {
+ return;
+ }
+
+ // If the beginning of a range is before or at a start node position, so
+ // delete it, otherwise empty S/E or E/S nodes would be created.
+ // For other nodes, create a new start node.
+ SwNode * pCurrentNode = &pRange->aStart.GetNode();
+ SwNodeIndex aIdx( *pCurrentNode->StartOfSectionNode() );
+ if( pCurrentNode->IsStartNode() ) // is StartNode itself
+ {
+ SwEndNode* pEndNd = pRange->aEnd.GetNode().GetEndNode();
+ if (pEndNd && pCurrentNode == pEndNd->m_pStartOfSection)
+ {
+ // there was a pairwise reset, adjust only those in the range
+ SwStartNode* pTmpSttNd = pCurrentNode->m_pStartOfSection;
+ RemoveNode( pRange->aStart.GetIndex(), SwNodeOffset(1), true );
+ RemoveNode( pRange->aEnd.GetIndex(), SwNodeOffset(1), true );
+
+ SwNodeIndex aTmpIdx( pRange->aStart );
+ while( aTmpIdx < pRange->aEnd )
+ {
+ pCurrentNode = &aTmpIdx.GetNode();
+ pCurrentNode->m_pStartOfSection = pTmpSttNd;
+ if( pCurrentNode->IsStartNode() )
+ aTmpIdx = pCurrentNode->EndOfSectionIndex() + 1;
+ else
+ ++aTmpIdx;
+ }
+ return ;
+ }
+ DelNodes( pRange->aStart );
+ }
+ else if( aIdx == pRange->aStart.GetIndex()-1 ) // before StartNode
+ DelNodes( aIdx );
+ else
+ new SwEndNode( pRange->aStart, *aIdx.GetNode().GetStartNode() );
+
+ // If the end of a range is before or at a StartNode, so delete it,
+ // otherwise empty S/E or E/S nodes would be created.
+ // For other nodes, insert a new end node.
+ SwNodeIndex aTmpIdx( pRange->aEnd );
+ if( pRange->aEnd.GetNode().IsEndNode() )
+ DelNodes( pRange->aEnd );
+ else
+ {
+ new SwStartNode( pRange->aEnd );
+/*?? which NodeType ??*/
+ aTmpIdx = *pRange->aEnd.GetNode().EndOfSectionNode();
+ --pRange->aEnd;
+ }
+
+ SectionUpDown( aIdx, aTmpIdx );
+}
+
+/** correct indices after movement
+ *
+ * Update all indices after movement so that the levels are consistent again.
+ *
+ * @param aStart index of the start node
+ * @param aEnd index of the end point
+ *
+ * @see SwNodes::SectionUp
+ * @see SwNodes::SectionDown
+ */
+void SwNodes::SectionUpDown( const SwNodeIndex & aStart, const SwNodeIndex & aEnd )
+{
+ SwNodeIndex aTmpIdx( aStart, +1 );
+ // array forms a stack, holding all StartOfSelections
+ SwStartNodePointers aSttNdStack;
+ SwStartNode* pTmp = aStart.GetNode().GetStartNode();
+ aSttNdStack.push_back( pTmp );
+
+ // loop until the first start node that needs to be change was found
+ // (the indices are updated from the end node backwards to the start)
+ for( ;; ++aTmpIdx )
+ {
+ SwNode * pCurrentNode = &aTmpIdx.GetNode();
+ pCurrentNode->m_pStartOfSection = aSttNdStack[ aSttNdStack.size()-1 ];
+
+ if( pCurrentNode->GetStartNode() )
+ {
+ pTmp = static_cast<SwStartNode*>(pCurrentNode);
+ aSttNdStack.push_back( pTmp );
+ }
+ else if( pCurrentNode->GetEndNode() )
+ {
+ SwStartNode* pSttNd = aSttNdStack[ aSttNdStack.size() - 1 ];
+ pSttNd->m_pEndOfSection = static_cast<SwEndNode*>(pCurrentNode);
+ aSttNdStack.pop_back();
+ if( !aSttNdStack.empty() )
+ continue; // still enough EndNodes on the stack
+
+ else if( aTmpIdx < aEnd ) // too many StartNodes
+ // if the end is not reached, yet, get the start of the section above
+ {
+ aSttNdStack.insert( aSttNdStack.begin(), pSttNd->m_pStartOfSection );
+ }
+ else // finished, as soon as out of the range
+ break;
+ }
+ }
+}
+
+/** delete nodes
+ *
+ * This is a specific implementation of a delete function for a variable array.
+ * It is necessary as there might be inconsistencies after deleting start or
+ * end nodes. This method can clean those up.
+ *
+ * @param rIndex position to delete at (unchanged afterwards)
+ * @param nNodes number of nodes to delete (default: 1)
+ */
+void SwNodes::Delete(const SwNodeIndex &rIndex, SwNodeOffset nNodes)
+{
+ int nLevel = 0; // level counter
+ SwNode * pCurrentNode;
+
+ SwNodeOffset nCnt = Count() - rIndex.GetIndex() - 1;
+ if( nCnt > nNodes ) nCnt = nNodes;
+
+ if( nCnt == SwNodeOffset(0) ) // no count -> return
+ return;
+
+ SwNodeRange aRg( rIndex, SwNodeOffset(0), rIndex, nCnt-1 );
+ // check if [rIndex..rIndex + nCnt] is larger than the range
+ if( ( !aRg.aStart.GetNode().StartOfSectionIndex() &&
+ !aRg.aStart.GetIndex() ) ||
+ !::CheckNodesRange(aRg.aStart, aRg.aEnd, false))
+ {
+ return;
+ }
+
+ // if aEnd is not on a ContentNode, search the previous one
+ while( ( pCurrentNode = &aRg.aEnd.GetNode())->GetStartNode() ||
+ ( pCurrentNode->GetEndNode() &&
+ !pCurrentNode->m_pStartOfSection->IsTableNode() ))
+ --aRg.aEnd;
+
+ nCnt = SwNodeOffset(0);
+//TODO: check/improve comment
+ // increase start so that we are able to use "<" (using "<=" might cause
+ // problems if aEnd == aStart and aEnd is deleted, so aEnd <= aStart)
+ --aRg.aStart;
+
+ bool bSaveInNodesDel = m_bInNodesDel;
+ m_bInNodesDel = true;
+ bool bUpdateOutline = false;
+
+ // loop until everything is deleted
+ while( aRg.aStart < aRg.aEnd )
+ {
+ pCurrentNode = &aRg.aEnd.GetNode();
+
+ if( pCurrentNode->GetEndNode() )
+ {
+ // delete the whole section?
+ if( pCurrentNode->StartOfSectionIndex() > aRg.aStart.GetIndex() )
+ {
+ SwTableNode* pTableNd = pCurrentNode->m_pStartOfSection->GetTableNode();
+ if( pTableNd )
+ pTableNd->DelFrames();
+
+ SwNode *pNd, *pChkNd = pCurrentNode->m_pStartOfSection;
+ SwOutlineNodes::size_type nIdxPos;
+ do {
+ pNd = &aRg.aEnd.GetNode();
+
+ if( pNd->IsTextNode() )
+ {
+ SwTextNode *const pTextNode(pNd->GetTextNode());
+ if (pTextNode->IsOutline() &&
+ m_pOutlineNodes->Seek_Entry( pNd, &nIdxPos ))
+ {
+ // remove outline indices
+ m_pOutlineNodes->erase_at(nIdxPos);
+ bUpdateOutline = true;
+ }
+ pTextNode->InvalidateNumRule();
+ }
+ else if( pNd->IsEndNode() &&
+ pNd->m_pStartOfSection->IsTableNode() )
+ static_cast<SwTableNode*>(pNd->m_pStartOfSection)->DelFrames();
+
+ --aRg.aEnd;
+ nCnt++;
+
+ } while( pNd != pChkNd );
+ }
+ else
+ {
+ RemoveNode( aRg.aEnd.GetIndex()+1, nCnt, true ); // delete
+ nCnt = SwNodeOffset(0);
+ --aRg.aEnd; // before the EndNode
+ nLevel++;
+ }
+ }
+ else if( pCurrentNode->GetStartNode() ) // found StartNode
+ {
+ if( nLevel == 0 ) // decrease one level
+ {
+ if( nCnt )
+ {
+ // now delete array
+ ++aRg.aEnd;
+ RemoveNode( aRg.aEnd.GetIndex(), nCnt, true );
+ nCnt = SwNodeOffset(0);
+ }
+ }
+ else // remove all nodes between start and end node (incl. both)
+ {
+ RemoveNode( aRg.aEnd.GetIndex(), nCnt + 2, true ); // delete array
+ nCnt = SwNodeOffset(0);
+ nLevel--;
+ }
+
+ // after deletion, aEnd might point to an EndNode...
+ // delete all empty start/end node pairs
+ SwNode* pTmpNode = aRg.aEnd.GetNode().GetEndNode();
+ --aRg.aEnd;
+ while( pTmpNode &&
+ ( pCurrentNode = &aRg.aEnd.GetNode())->GetStartNode() &&
+ pCurrentNode->StartOfSectionIndex() )
+ {
+ // remove end and start node
+ DelNodes( aRg.aEnd, SwNodeOffset(2) );
+ pTmpNode = aRg.aEnd.GetNode().GetEndNode();
+ --aRg.aEnd;
+ }
+ }
+ else // "normal" node, so insert into TmpArray
+ {
+ SwTextNode* pTextNd = pCurrentNode->GetTextNode();
+ if( pTextNd )
+ {
+ if( pTextNd->IsOutline())
+ {
+ // delete outline indices
+ m_pOutlineNodes->erase( pTextNd );
+ bUpdateOutline = true;
+ }
+ if (sw::HasNumberingWhichNeedsLayoutUpdate(*pTextNd))
+ {
+ pTextNd->InvalidateNumRule();
+ }
+ }
+ else if( pCurrentNode->IsContentNode() )
+ static_cast<SwContentNode*>(pCurrentNode)->InvalidateNumRule();
+
+ --aRg.aEnd;
+ nCnt++;
+ }
+ }
+
+ ++aRg.aEnd;
+ if( nCnt != SwNodeOffset(0) )
+ RemoveNode( aRg.aEnd.GetIndex(), nCnt, true ); // delete the rest
+
+ // delete all empty start/end node pairs
+ while( aRg.aEnd.GetNode().GetEndNode() &&
+ ( pCurrentNode = &aRg.aStart.GetNode())->GetStartNode() &&
+ pCurrentNode->StartOfSectionIndex() )
+ // but none of the holy 5. (???)
+ {
+ DelNodes( aRg.aStart, SwNodeOffset(2) ); // delete start and end node
+ --aRg.aStart;
+ }
+
+ m_bInNodesDel = bSaveInNodesDel;
+
+ if( !m_bInNodesDel )
+ {
+ // update numbering
+ if( bUpdateOutline || m_bInDelUpdOutline )
+ {
+ UpdateOutlineIdx( aRg.aEnd.GetNode() );
+ m_bInDelUpdOutline = false;
+ }
+
+ }
+ else
+ {
+ if( bUpdateOutline )
+ m_bInDelUpdOutline = true;
+ }
+}
+
+/** get section level at the given position
+ *
+ * @note The first node in an array should always be a start node.
+ * Because of this, there is a special treatment here based on the
+ * assumption that this is true in this context as well.
+ *
+ * @param rIdx position of the node
+ * @return section level at the given position
+ */
+sal_uInt16 SwNodes::GetSectionLevel(const SwNodeIndex &rIdx)
+{
+ // special treatment for 1st Node
+ if(rIdx == SwNodeOffset(0)) return 1;
+ // no recursion! This calls a SwNode::GetSectionLevel (missing "s")
+ return rIdx.GetNode().GetSectionLevel();
+}
+
+void SwNodes::GoStartOfSection(SwNodeIndex *pIdx)
+{
+ // after the next start node
+ SwNodeIndex aTmp( *pIdx->GetNode().StartOfSectionNode(), +1 );
+
+ // If index points to no ContentNode, then go to one.
+ // If there is no further available, do not change the index' position!
+ while( !aTmp.GetNode().IsContentNode() )
+ { // go from this StartNode (can only be one) to its end
+ if( *pIdx <= aTmp )
+ return; // ERROR: already after the section
+ aTmp = aTmp.GetNode().EndOfSectionIndex()+1;
+ if( *pIdx <= aTmp )
+ return; // ERROR: already after the section
+ }
+ (*pIdx) = aTmp; // is on a ContentNode
+}
+
+void SwNodes::GoEndOfSection(SwNodeIndex *pIdx)
+{
+ if( !pIdx->GetNode().IsEndNode() )
+ (*pIdx) = *pIdx->GetNode().EndOfSectionNode();
+}
+
+SwContentNode* SwNodes::GoNext(SwNodeIndex *pIdx) const
+{
+ if( pIdx->GetIndex() >= Count() - 1 )
+ return nullptr;
+
+ SwNodeIndex aTmp(*pIdx, +1);
+ SwNode* pNd = nullptr;
+ while( aTmp < Count()-1 && !( pNd = &aTmp.GetNode())->IsContentNode() )
+ ++aTmp;
+
+ if( aTmp == Count()-1 )
+ pNd = nullptr;
+ else
+ (*pIdx) = aTmp;
+ return static_cast<SwContentNode*>(pNd);
+}
+
+SwContentNode* SwNodes::GoPrevious(SwNodeIndex *pIdx)
+{
+ if( !pIdx->GetIndex() )
+ return nullptr;
+
+ SwNodeIndex aTmp( *pIdx, -1 );
+ SwNode* pNd = nullptr;
+ while( aTmp.GetIndex() && !( pNd = &aTmp.GetNode())->IsContentNode() )
+ --aTmp;
+
+ if( !aTmp.GetIndex() )
+ pNd = nullptr;
+ else
+ (*pIdx) = aTmp;
+ return static_cast<SwContentNode*>(pNd);
+}
+
+/** Delete a number of nodes
+ *
+ * @param rStart starting position in this nodes array
+ * @param nCnt number of nodes to delete
+ */
+void SwNodes::DelNodes( const SwNodeIndex & rStart, SwNodeOffset nCnt )
+{
+ SwNodeOffset nSttIdx = rStart.GetIndex();
+
+ if( !nSttIdx && nCnt == GetEndOfContent().GetIndex()+1 )
+ {
+ // The whole nodes array will be destroyed, you're in the Doc's DTOR!
+ // The initial start/end nodes should be only destroyed in the SwNodes' DTOR!
+ SwNode* aEndNdArr[] = { m_pEndOfContent.get(),
+ m_pEndOfPostIts, m_pEndOfInserts,
+ m_pEndOfAutotext, m_pEndOfRedlines,
+ nullptr
+ };
+
+ SwNode** ppEndNdArr = aEndNdArr;
+ while( *ppEndNdArr )
+ {
+ nSttIdx = (*ppEndNdArr)->StartOfSectionIndex() + 1;
+ SwNodeOffset nEndIdx = (*ppEndNdArr)->GetIndex();
+
+ if( nSttIdx != nEndIdx )
+ RemoveNode( nSttIdx, nEndIdx - nSttIdx, true );
+
+ ++ppEndNdArr;
+ }
+ }
+ else
+ {
+ int bUpdateNum = 0;
+ for( SwNodeOffset n = nSttIdx, nEnd = nSttIdx + nCnt; n < nEnd; ++n )
+ {
+ SwNode* pNd = (*this)[ n ];
+
+ if (pNd->IsTextNode() && pNd->GetTextNode()->IsOutline())
+ {
+ // remove the outline indices
+ if (m_pOutlineNodes->erase(pNd))
+ bUpdateNum = 1;
+ }
+ if( pNd->IsContentNode() )
+ {
+ static_cast<SwContentNode*>(pNd)->InvalidateNumRule();
+ static_cast<SwContentNode*>(pNd)->DelFrames(nullptr);
+ }
+ }
+ RemoveNode( nSttIdx, nCnt, true );
+
+ // update numbering
+ if( bUpdateNum )
+ UpdateOutlineIdx( rStart.GetNode() );
+ }
+}
+
+namespace {
+
+struct HighLevel
+{
+ sal_uInt16 nLevel, nTop;
+ explicit HighLevel( sal_uInt16 nLv ) : nLevel( nLv ), nTop( nLv ) {}
+};
+
+}
+
+static bool lcl_HighestLevel( SwNode* pNode, void * pPara )
+{
+ HighLevel * pHL = static_cast<HighLevel*>(pPara);
+ if( pNode->GetStartNode() )
+ pHL->nLevel++;
+ else if( pNode->GetEndNode() )
+ pHL->nLevel--;
+ if( pHL->nTop > pHL->nLevel )
+ pHL->nTop = pHL->nLevel;
+ return true;
+
+}
+
+/** Calculate the highest level in a range
+ *
+ * @param rNodes the nodes array
+ * @param rRange the range to inspect
+ * @return the highest level
+ */
+sal_uInt16 HighestLevel( SwNodes & rNodes, const SwNodeRange & rRange )
+{
+ HighLevel aPara( SwNodes::GetSectionLevel( rRange.aStart ));
+ rNodes.ForEach( rRange.aStart, rRange.aEnd, lcl_HighestLevel, &aPara );
+ return aPara.nTop;
+
+}
+
+/** move a range
+ *
+ * @param rPam the range to move
+ * @param rPos to destination position in the given nodes array
+ * @param rNodes the node array to move the range into
+ */
+void SwNodes::MoveRange( SwPaM & rPam, SwPosition & rPos, SwNodes& rNodes )
+{
+ SwPosition * const pStt = rPam.Start();
+ SwPosition * const pEnd = rPam.End();
+
+ if( !rPam.HasMark() || *pStt >= *pEnd )
+ return;
+
+ if( this == &rNodes && *pStt <= rPos && rPos < *pEnd )
+ return;
+
+ SwNodeIndex aEndIdx( pEnd->nNode );
+ SwNodeIndex aSttIdx( pStt->nNode );
+ SwTextNode *const pSrcNd = aSttIdx.GetNode().GetTextNode();
+ SwTextNode * pDestNd = rPos.nNode.GetNode().GetTextNode();
+ bool bSplitDestNd = true;
+ bool bCopyCollFormat = pDestNd && pDestNd->GetText().isEmpty();
+
+ if( pSrcNd )
+ {
+ // if the first node is a TextNode, then there must
+ // be also a TextNode in the NodesArray to store the content
+ if( !pDestNd )
+ {
+ pDestNd = rNodes.MakeTextNode( rPos.nNode, pSrcNd->GetTextColl() );
+ --rPos.nNode;
+ rPos.nContent.Assign( pDestNd, 0 );
+ bCopyCollFormat = true;
+ }
+ bSplitDestNd = pDestNd->Len() > rPos.nContent.GetIndex() ||
+ pEnd->nNode.GetNode().IsTextNode();
+
+ // move the content into the new node
+ bool bOneNd = pStt->nNode == pEnd->nNode;
+ const sal_Int32 nLen =
+ ( bOneNd ? std::min(pEnd->nContent.GetIndex(), pSrcNd->Len()) : pSrcNd->Len() )
+ - pStt->nContent.GetIndex();
+
+ if( !pEnd->nNode.GetNode().IsContentNode() )
+ {
+ bOneNd = true;
+ SwNodeOffset nSttNdIdx = pStt->nNode.GetIndex() + 1;
+ const SwNodeOffset nEndNdIdx = pEnd->nNode.GetIndex();
+ for( ; nSttNdIdx < nEndNdIdx; ++nSttNdIdx )
+ {
+ if( (*this)[ nSttNdIdx ]->IsContentNode() )
+ {
+ bOneNd = false;
+ break;
+ }
+ }
+ }
+
+ // templates must be copied/set after a split
+ if( !bOneNd && bSplitDestNd )
+ {
+ if( !rPos.nContent.GetIndex() )
+ {
+ bCopyCollFormat = true;
+ }
+ if( rNodes.IsDocNodes() )
+ {
+ SwDoc& rInsDoc = pDestNd->GetDoc();
+ ::sw::UndoGuard const ug(rInsDoc.GetIDocumentUndoRedo());
+ rInsDoc.getIDocumentContentOperations().SplitNode( rPos, false );
+ }
+ else
+ {
+ pDestNd->SplitContentNode(rPos, nullptr);
+ }
+
+ if( rPos.nNode == aEndIdx )
+ {
+ --aEndIdx;
+ }
+ bSplitDestNd = true;
+
+ pDestNd = rNodes[ rPos.nNode.GetIndex() - 1 ]->GetTextNode();
+ if( nLen )
+ {
+ pSrcNd->CutText( pDestNd, SwIndex( pDestNd, pDestNd->Len()),
+ pStt->nContent, nLen );
+ }
+ }
+ else if ( nLen )
+ {
+ pSrcNd->CutText( pDestNd, rPos.nContent, pStt->nContent, nLen );
+ }
+
+ if( bCopyCollFormat )
+ {
+ SwDoc& rInsDoc = pDestNd->GetDoc();
+ ::sw::UndoGuard const undoGuard(rInsDoc.GetIDocumentUndoRedo());
+ pSrcNd->CopyCollFormat( *pDestNd );
+ }
+
+ if( bOneNd )
+ {
+ // Correct the PaM, because it might have happened that the move
+ // went over the node borders (so the data might be in different nodes).
+ // Also, a selection is invalidated.
+ pEnd->nContent = pStt->nContent;
+ rPam.DeleteMark();
+ GetDoc().GetDocShell()->Broadcast( SwFormatFieldHint( nullptr,
+ rNodes.IsDocNodes() ? SwFormatFieldHintWhich::INSERTED : SwFormatFieldHintWhich::REMOVED ) );
+ return;
+ }
+
+ ++aSttIdx;
+ }
+ else if( pDestNd )
+ {
+ if( rPos.nContent.GetIndex() )
+ {
+ if( rPos.nContent.GetIndex() == pDestNd->Len() )
+ {
+ ++rPos.nNode;
+ }
+ else if( rPos.nContent.GetIndex() )
+ {
+ // if the EndNode is split than correct the EndIdx
+ const bool bCorrEnd = aEndIdx == rPos.nNode;
+
+ // if no text is attached to the TextNode, split it
+ if( rNodes.IsDocNodes() )
+ {
+ SwDoc& rInsDoc = pDestNd->GetDoc();
+ ::sw::UndoGuard const ug(rInsDoc.GetIDocumentUndoRedo());
+ rInsDoc.getIDocumentContentOperations().SplitNode( rPos, false );
+ }
+ else
+ {
+ pDestNd->SplitContentNode(rPos, nullptr);
+ }
+
+ if ( bCorrEnd )
+ {
+ --aEndIdx;
+ }
+ }
+ }
+ // at the end only an empty TextNode is left over
+ bSplitDestNd = true;
+ }
+
+ SwTextNode* const pEndSrcNd = aEndIdx.GetNode().GetTextNode();
+ if ( pEndSrcNd )
+ {
+ // at the end of this range a new TextNode will be created
+ if( !bSplitDestNd )
+ {
+ if( rPos.nNode < rNodes.GetEndOfContent().GetIndex() )
+ {
+ ++rPos.nNode;
+ }
+
+ pDestNd =
+ rNodes.MakeTextNode( rPos.nNode, pEndSrcNd->GetTextColl() );
+ --rPos.nNode;
+ rPos.nContent.Assign( pDestNd, 0 );
+ }
+ else
+ {
+ pDestNd = rPos.nNode.GetNode().GetTextNode();
+ }
+
+ if (pDestNd && pEnd->nContent.GetIndex())
+ {
+ // move the content into the new node
+ SwIndex aIdx( pEndSrcNd, 0 );
+ pEndSrcNd->CutText( pDestNd, rPos.nContent, aIdx,
+ pEnd->nContent.GetIndex());
+ }
+
+ if (pDestNd && bCopyCollFormat)
+ {
+ SwDoc& rInsDoc = pDestNd->GetDoc();
+ ::sw::UndoGuard const ug(rInsDoc.GetIDocumentUndoRedo());
+ pEndSrcNd->CopyCollFormat( *pDestNd );
+ }
+ }
+ else
+ {
+ if ( pSrcNd && aEndIdx.GetNode().IsContentNode() )
+ {
+ ++aEndIdx;
+ }
+ if( !bSplitDestNd )
+ {
+ ++rPos.nNode;
+ rPos.nContent.Assign( rPos.nNode.GetNode().GetContentNode(), 0 );
+ }
+ }
+
+ if( aEndIdx != aSttIdx )
+ {
+ // move the nodes into the NodesArray
+ const SwNodeOffset nSttDiff = aSttIdx.GetIndex() - pStt->nNode.GetIndex();
+ SwNodeRange aRg( aSttIdx, aEndIdx );
+ MoveNodes( aRg, rNodes, rPos.nNode );
+
+ // if in the same node array, all indices are now at new positions (so correct them)
+ if( &rNodes == this )
+ {
+ pStt->nNode = aRg.aEnd.GetIndex() - nSttDiff;
+ }
+ }
+
+ // if the StartNode was moved to whom the cursor pointed, so
+ // the content must be registered in the current content!
+ if ( &pStt->nNode.GetNode() == &GetEndOfContent() )
+ {
+ const bool bSuccess = GoPrevious( &pStt->nNode );
+ OSL_ENSURE( bSuccess, "Move() - no ContentNode here" );
+ }
+ pStt->nContent.Assign( pStt->nNode.GetNode().GetContentNode(),
+ pStt->nContent.GetIndex() );
+ // Correct the PaM, because it might have happened that the move
+ // went over the node borders (so the data might be in different nodes).
+ // Also, a selection is invalidated.
+ *pEnd = *pStt;
+ rPam.DeleteMark();
+ GetDoc().GetDocShell()->Broadcast( SwFormatFieldHint( nullptr,
+ rNodes.IsDocNodes() ? SwFormatFieldHintWhich::INSERTED : SwFormatFieldHintWhich::REMOVED ) );
+}
+
+///@see SwNodes::MoveNodes (TODO: seems to be C&P programming here)
+void SwNodes::CopyNodes( const SwNodeRange& rRange,
+ const SwNodeIndex& rIndex, bool bNewFrames, bool bTableInsDummyNode ) const
+{
+ SwDoc& rDoc = rIndex.GetNode().GetDoc();
+
+ SwNode * pCurrentNode;
+ if( rIndex == SwNodeOffset(0) ||
+ ( (pCurrentNode = &rIndex.GetNode())->GetStartNode() &&
+ !pCurrentNode->StartOfSectionIndex() ))
+ return;
+
+ SwNodeRange aRg( rRange );
+
+ // skip "simple" StartNodes or EndNodes
+ while( SwNodeType::Start == (pCurrentNode = & aRg.aStart.GetNode())->GetNodeType()
+ || ( pCurrentNode->IsEndNode() &&
+ !pCurrentNode->m_pStartOfSection->IsSectionNode() ) )
+ ++aRg.aStart;
+
+ const SwNode *aEndNode = &aRg.aEnd.GetNode();
+ SwNodeOffset nIsEndOfContent((aEndNode == &aEndNode->GetNodes().GetEndOfContent()) ? 1 : 0);
+
+ if (SwNodeOffset(0) == nIsEndOfContent)
+ {
+ // if aEnd-1 points to no ContentNode, search previous one
+ --aRg.aEnd;
+ // #i107142#: if aEnd is start node of a special section, do nothing.
+ // Otherwise this could lead to crash: going through all previous
+ // special section nodes and then one before the first.
+ if (aRg.aEnd.GetNode().StartOfSectionIndex() != SwNodeOffset(0))
+ {
+ while( ((pCurrentNode = & aRg.aEnd.GetNode())->GetStartNode() &&
+ !pCurrentNode->IsSectionNode() ) ||
+ ( pCurrentNode->IsEndNode() &&
+ SwNodeType::Start == pCurrentNode->m_pStartOfSection->GetNodeType()) )
+ {
+ --aRg.aEnd;
+ }
+ }
+ ++aRg.aEnd;
+ }
+
+ // is there anything left to copy?
+ if( aRg.aStart >= aRg.aEnd )
+ return;
+
+ // when inserting into the source range, nothing need to be done
+ OSL_ENSURE( &aRg.aStart.GetNodes() == this,
+ "aRg should use this node array" );
+ OSL_ENSURE( &aRg.aStart.GetNodes() == &aRg.aEnd.GetNodes(),
+ "Range across different nodes arrays? You deserve punishment!");
+ if( &rIndex.GetNodes() == &aRg.aStart.GetNodes() &&
+ rIndex.GetIndex() >= aRg.aStart.GetIndex() &&
+ rIndex.GetIndex() < aRg.aEnd.GetIndex() )
+ return;
+
+ SwNodeIndex aInsPos( rIndex );
+ SwNodeIndex aOrigInsPos( rIndex, -1 ); // original insertion position
+ int nLevel = 0; // level counter
+
+ for( SwNodeOffset nNodeCnt = aRg.aEnd.GetIndex() - aRg.aStart.GetIndex();
+ nNodeCnt > SwNodeOffset(0); --nNodeCnt )
+ {
+ pCurrentNode = &aRg.aStart.GetNode();
+ switch( pCurrentNode->GetNodeType() )
+ {
+ case SwNodeType::Table:
+ // Does it copy a table in(to) a footnote?
+ if( aInsPos < rDoc.GetNodes().GetEndOfInserts().GetIndex() &&
+ rDoc.GetNodes().GetEndOfInserts().StartOfSectionIndex()
+ < aInsPos.GetIndex() )
+ {
+ const SwNodeOffset nDistance =
+ pCurrentNode->EndOfSectionIndex() -
+ aRg.aStart.GetIndex();
+ if (nDistance < nNodeCnt)
+ nNodeCnt -= nDistance;
+ else
+ nNodeCnt = SwNodeOffset(1);
+
+ // insert a DummyNode for a TableNode
+ if( bTableInsDummyNode )
+ new SwPlaceholderNode(aInsPos);
+
+ // copy all of the table's nodes into the current cell
+ for( ++aRg.aStart; aRg.aStart.GetIndex() <
+ pCurrentNode->EndOfSectionIndex();
+ ++aRg.aStart )
+ {
+ // insert a DummyNode for the box-StartNode?
+ if( bTableInsDummyNode )
+ new SwPlaceholderNode(aInsPos);
+
+ SwStartNode* pSttNd = aRg.aStart.GetNode().GetStartNode();
+ CopyNodes( SwNodeRange( *pSttNd, SwNodeOffset(+ 1),
+ *pSttNd->EndOfSectionNode() ),
+ aInsPos, bNewFrames );
+
+ // insert a DummyNode for the box-EndNode?
+ if( bTableInsDummyNode )
+ new SwPlaceholderNode(aInsPos);
+ aRg.aStart = *pSttNd->EndOfSectionNode();
+ }
+ // insert a DummyNode for the table-EndNode
+ if( bTableInsDummyNode )
+ new SwPlaceholderNode(aInsPos);
+ aRg.aStart = *pCurrentNode->EndOfSectionNode();
+ }
+ else
+ {
+ SwNodeIndex nStt( aInsPos, -1 );
+ SwTableNode* pTableNd = static_cast<SwTableNode*>(pCurrentNode)->
+ MakeCopy( rDoc, aInsPos );
+ const SwNodeOffset nDistance = aInsPos.GetIndex() - nStt.GetIndex() - 2;
+ if (nDistance < nNodeCnt)
+ nNodeCnt -= nDistance;
+ else
+ nNodeCnt = SwNodeOffset(1) - nIsEndOfContent;
+
+ aRg.aStart = pCurrentNode->EndOfSectionIndex();
+
+ if( bNewFrames && pTableNd )
+ {
+ nStt = aInsPos;
+ pTableNd->MakeOwnFrames(&nStt);
+ }
+ }
+ break;
+
+ case SwNodeType::Section:
+ // If the end of the section is outside the copy range,
+ // the section node will skipped, not copied!
+ // If someone want to change this behaviour, he has to adjust the function
+ // lcl_NonCopyCount(..) in ndcopy.cxx which relies on it.
+ if( pCurrentNode->EndOfSectionIndex() < aRg.aEnd.GetIndex() )
+ {
+ // copy of the whole section, so create a new SectionNode
+ SwNodeIndex nStt( aInsPos, -1 );
+ SwSectionNode* pSectNd = static_cast<SwSectionNode*>(pCurrentNode)->
+ MakeCopy( rDoc, aInsPos );
+
+ const SwNodeOffset nDistance = aInsPos.GetIndex() - nStt.GetIndex() - 2;
+ if (nDistance < nNodeCnt)
+ nNodeCnt -= nDistance;
+ else
+ nNodeCnt = SwNodeOffset(1) - nIsEndOfContent;
+ aRg.aStart = pCurrentNode->EndOfSectionIndex();
+
+ if( bNewFrames && pSectNd &&
+ !pSectNd->GetSection().IsHidden() )
+ pSectNd->MakeOwnFrames(&nStt);
+ }
+ break;
+
+ case SwNodeType::Start:
+ {
+ SwStartNode* pTmp = new SwStartNode( aInsPos, SwNodeType::Start,
+ static_cast<SwStartNode*>(pCurrentNode)->GetStartNodeType() );
+ new SwEndNode( aInsPos, *pTmp );
+ --aInsPos;
+ nLevel++;
+ }
+ break;
+
+ case SwNodeType::End:
+ if( nLevel ) // complete section
+ {
+ --nLevel;
+ ++aInsPos; // EndNode already exists
+ }
+ else if( SwNodeOffset(1) == nNodeCnt && SwNodeOffset(1) == nIsEndOfContent )
+ // we have reached the EndOfContent node - nothing to do!
+ continue;
+ else if( !pCurrentNode->m_pStartOfSection->IsSectionNode() )
+ {
+ // create a section at the original InsertPosition
+ SwNodeRange aTmpRg( aOrigInsPos, SwNodeOffset(1), aInsPos );
+ rDoc.GetNodes().SectionDown( &aTmpRg,
+ pCurrentNode->m_pStartOfSection->GetStartNodeType() );
+ }
+ break;
+
+ case SwNodeType::Text:
+ case SwNodeType::Grf:
+ case SwNodeType::Ole:
+ {
+ static_cast<SwContentNode*>(pCurrentNode)->MakeCopy(
+ rDoc, aInsPos, bNewFrames);
+ }
+ break;
+
+ case SwNodeType::PlaceHolder:
+ if (GetDoc().GetIDocumentUndoRedo().IsUndoNodes(*this))
+ {
+ // than a SectionNode (start/end) is needed at the current
+ // InsPos; if so skip it, otherwise ignore current node
+ SwNode *const pTmpNd = & aInsPos.GetNode();
+ if( pTmpNd->IsSectionNode() ||
+ pTmpNd->StartOfSectionNode()->IsSectionNode() )
+ ++aInsPos; // skip
+ }
+ else {
+ assert(!"How can this node be in the node array?");
+ }
+ break;
+
+ default:
+ assert(false);
+ }
+ ++aRg.aStart;
+ }
+}
+
+void SwNodes::DelDummyNodes( const SwNodeRange& rRg )
+{
+ SwNodeIndex aIdx( rRg.aStart );
+ while( aIdx.GetIndex() < rRg.aEnd.GetIndex() )
+ {
+ if (SwNodeType::PlaceHolder == aIdx.GetNode().GetNodeType())
+ RemoveNode( aIdx.GetIndex(), SwNodeOffset(1), true );
+ else
+ ++aIdx;
+ }
+}
+
+SwStartNode* SwNodes::MakeEmptySection( const SwNodeIndex& rIdx,
+ SwStartNodeType eSttNdTyp )
+{
+ SwStartNode* pSttNd = new SwStartNode( rIdx, SwNodeType::Start, eSttNdTyp );
+ new SwEndNode( rIdx, *pSttNd );
+ return pSttNd;
+}
+
+SwStartNode* SwNodes::MakeTextSection( const SwNodeIndex & rWhere,
+ SwStartNodeType eSttNdTyp,
+ SwTextFormatColl *pColl )
+{
+ SwStartNode* pSttNd = new SwStartNode( rWhere, SwNodeType::Start, eSttNdTyp );
+ new SwEndNode( rWhere, *pSttNd );
+ MakeTextNode( SwNodeIndex( rWhere, - 1 ), pColl );
+ return pSttNd;
+}
+
+//TODO: provide better documentation
+/** go to next section that is not protected nor hidden
+ *
+ * @note if !bSkipHidden and !bSkipProtect, use GoNext/GoPrevious
+ *
+ * @param pIdx
+ * @param bSkipHidden
+ * @param bSkipProtect
+ * @return
+ * @see SwNodes::GoNext
+ * @see SwNodes::GoPrevious
+ * @see SwNodes::GoNextSection (TODO: seems to be C&P programming here)
+*/
+SwContentNode* SwNodes::GoNextSection( SwNodeIndex * pIdx,
+ bool bSkipHidden, bool bSkipProtect ) const
+{
+ bool bFirst = true;
+ SwNodeIndex aTmp( *pIdx );
+ const SwNode* pNd;
+ while( aTmp < Count() - 1 )
+ {
+ pNd = & aTmp.GetNode();
+ if (SwNodeType::Section == pNd->GetNodeType())
+ {
+ const SwSection& rSect = static_cast<const SwSectionNode*>(pNd)->GetSection();
+ if( (bSkipHidden && rSect.IsHiddenFlag()) ||
+ (bSkipProtect && rSect.IsProtectFlag()) )
+ // than skip the section
+ aTmp = *pNd->EndOfSectionNode();
+ }
+ else if( bFirst )
+ {
+ if( pNd->m_pStartOfSection->IsSectionNode() )
+ {
+ const SwSection& rSect = static_cast<SwSectionNode*>(pNd->
+ m_pStartOfSection)->GetSection();
+ if( (bSkipHidden && rSect.IsHiddenFlag()) ||
+ (bSkipProtect && rSect.IsProtectFlag()) )
+ // than skip the section
+ aTmp = *pNd->EndOfSectionNode();
+ }
+ }
+ else if( SwNodeType::ContentMask & pNd->GetNodeType() )
+ {
+ const SwSectionNode* pSectNd;
+ if( ( bSkipHidden || bSkipProtect ) &&
+ nullptr != (pSectNd = pNd->FindSectionNode() ) &&
+ ( ( bSkipHidden && pSectNd->GetSection().IsHiddenFlag() ) ||
+ ( bSkipProtect && pSectNd->GetSection().IsProtectFlag() )) )
+ {
+ aTmp = *pSectNd->EndOfSectionNode();
+ }
+ else
+ {
+ (*pIdx) = aTmp;
+ return const_cast<SwContentNode*>(static_cast<const SwContentNode*>(pNd));
+ }
+ }
+ ++aTmp;
+ bFirst = false;
+ }
+ return nullptr;
+}
+
+///@see SwNodes::GoNextSection (TODO: seems to be C&P programming here)
+SwContentNode* SwNodes::GoPrevSection( SwNodeIndex * pIdx,
+ bool bSkipHidden, bool bSkipProtect )
+{
+ bool bFirst = true;
+ SwNodeIndex aTmp( *pIdx );
+ const SwNode* pNd;
+ while( aTmp > SwNodeOffset(0) )
+ {
+ pNd = & aTmp.GetNode();
+ if (SwNodeType::End == pNd->GetNodeType())
+ {
+ if( pNd->m_pStartOfSection->IsSectionNode() )
+ {
+ const SwSection& rSect = static_cast<SwSectionNode*>(pNd->
+ m_pStartOfSection)->GetSection();
+ if( (bSkipHidden && rSect.IsHiddenFlag()) ||
+ (bSkipProtect && rSect.IsProtectFlag()) )
+ // than skip section
+ aTmp = *pNd->StartOfSectionNode();
+ }
+ bFirst = false;
+ }
+ else if( bFirst )
+ {
+ bFirst = false;
+ if( pNd->m_pStartOfSection->IsSectionNode() )
+ {
+ const SwSection& rSect = static_cast<SwSectionNode*>(pNd->
+ m_pStartOfSection)->GetSection();
+ if( (bSkipHidden && rSect.IsHiddenFlag()) ||
+ (bSkipProtect && rSect.IsProtectFlag()) )
+ // than skip section
+ aTmp = *pNd->StartOfSectionNode();
+ }
+ }
+ else if( SwNodeType::ContentMask & pNd->GetNodeType() )
+ {
+ const SwSectionNode* pSectNd;
+ if( ( bSkipHidden || bSkipProtect ) &&
+ nullptr != (pSectNd = pNd->FindSectionNode() ) &&
+ ( ( bSkipHidden && pSectNd->GetSection().IsHiddenFlag() ) ||
+ ( bSkipProtect && pSectNd->GetSection().IsProtectFlag() )) )
+ {
+ aTmp = *pSectNd;
+ }
+ else
+ {
+ (*pIdx) = aTmp;
+ return const_cast<SwContentNode*>(static_cast<const SwContentNode*>(pNd));
+ }
+ }
+ --aTmp;
+ }
+ return nullptr;
+}
+
+//TODO: The inventor of the "single responsibility principle" will be crying if you ever show this code to him!
+/** find the next/previous ContentNode or table node that should have layout
+ * frames that are siblings to the ones of the node at rFrameIdx.
+ *
+ * Search is started backward with the one before rFrameIdx and
+ * forward after pEnd.
+ *
+ * @param rFrameIdx in: node with frames to search in; out: found node
+ * @param pEnd last node after rFrameIdx that should be excluded from search
+ * @return result node; 0 if not found
+ */
+SwNode* SwNodes::FindPrvNxtFrameNode( SwNodeIndex& rFrameIdx,
+ SwNode const*const pEnd,
+ SwRootFrame const*const pLayout) const
+{
+ assert(pEnd != nullptr); // every caller currently
+
+ SwNode* pFrameNd = nullptr;
+
+ // no layout -> skip
+ if( GetDoc().getIDocumentLayoutAccess().GetCurrentViewShell() )
+ {
+ SwNode *const pSttNd = &rFrameIdx.GetNode();
+
+ // inside a hidden section?
+ SwSectionNode *const pSectNd = pSttNd->IsSectionNode()
+ ? pSttNd->StartOfSectionNode()->FindSectionNode()
+ : pSttNd->FindSectionNode();
+ if( !( pSectNd && pSectNd->GetSection().CalcHiddenFlag() ) )
+ {
+ // in a table in table situation we have to assure that we don't leave the
+ // outer table cell when the inner table is looking for a PrvNxt...
+ SwTableNode *const pTableNd = pSttNd->IsTableNode()
+ ? pSttNd->StartOfSectionNode()->FindTableNode()
+ : pSttNd->FindTableNode();
+ SwNodeIndex aIdx( rFrameIdx );
+
+ // search backward for a content or table node
+
+ --aIdx;
+ pFrameNd = &aIdx.GetNode();
+
+ do
+ {
+ if (pFrameNd->IsContentNode())
+ {
+ // TODO why does this not check for nested tables like forward direction
+ rFrameIdx = aIdx;
+ return pFrameNd;
+ }
+ else if (pFrameNd->IsEndNode() && pFrameNd->StartOfSectionNode()->IsTableNode())
+ {
+ if (pLayout == nullptr
+ || !pLayout->HasMergedParas()
+ || pFrameNd->StartOfSectionNode()->GetRedlineMergeFlag() != SwNode::Merge::Hidden)
+ {
+ pFrameNd = pFrameNd->StartOfSectionNode();
+ rFrameIdx = *pFrameNd;
+ return pFrameNd;
+ }
+ else
+ {
+ aIdx = *pFrameNd->StartOfSectionNode();
+ --aIdx;
+ pFrameNd = &aIdx.GetNode();
+ }
+ }
+ else
+ {
+ pFrameNd = GoPrevSection( &aIdx, true, false );
+ if ( nullptr != pFrameNd && !(
+ ::CheckNodesRange( aIdx, rFrameIdx, true ) &&
+ // Never out of the table at the start
+ pFrameNd->FindTableNode() == pTableNd &&
+ // Bug 37652: Never out of the table at the end
+ (!pFrameNd->FindTableNode() || pFrameNd->FindTableBoxStartNode()
+ == pSttNd->FindTableBoxStartNode() ) &&
+ (!pSectNd || pSttNd->IsSectionNode() ||
+ pSectNd->GetIndex() < pFrameNd->GetIndex())
+ ))
+ {
+ pFrameNd = nullptr; // no preceding content node, stop search
+ }
+ }
+ }
+ while (pFrameNd != nullptr);
+
+ // search forward for a content or table node
+
+ aIdx = pEnd->GetIndex() + 1;
+ pFrameNd = &aIdx.GetNode();
+
+ do
+ {
+ if (pFrameNd->IsContentNode())
+ {
+ // Undo when merging a table with one before, if there is also one after it.
+ // However, if the node is in a table, it needs to be returned if the
+ // SttNode is a section or a table!
+ SwTableNode *const pTableNode = pFrameNd->FindTableNode();
+ if (pSttNd->IsTableNode() &&
+ nullptr != pTableNode &&
+ // TABLE IN TABLE:
+ pTableNode != pSttNd->StartOfSectionNode()->FindTableNode())
+ {
+ pFrameNd = pTableNode;
+ rFrameIdx = *pFrameNd;
+ }
+ else
+ {
+ rFrameIdx = aIdx;
+ }
+ return pFrameNd;
+ }
+ else if (pFrameNd->IsTableNode())
+ {
+ if (pLayout == nullptr
+ || !pLayout->HasMergedParas()
+ || pFrameNd->GetRedlineMergeFlag() != SwNode::Merge::Hidden)
+ {
+ rFrameIdx = *pFrameNd;
+ return pFrameNd;
+ }
+ else
+ {
+ aIdx = *pFrameNd->EndOfSectionNode();
+ ++aIdx;
+ pFrameNd = &aIdx.GetNode();
+ }
+ }
+ else
+ {
+ pFrameNd = GoNextSection( &aIdx, true, false );
+ // NEVER leave the section when doing this!
+ if (pFrameNd
+ && !(::CheckNodesRange(aIdx, rFrameIdx, true)
+ && (pFrameNd->FindTableNode() == pTableNd &&
+ // NEVER go out of the table cell at the end
+ (!pFrameNd->FindTableNode() || pFrameNd->FindTableBoxStartNode()
+ == pSttNd->FindTableBoxStartNode()))
+ && (!pSectNd || pSttNd->IsSectionNode() ||
+ pSectNd->EndOfSectionIndex() > pFrameNd->GetIndex()))
+ )
+ {
+ pFrameNd = nullptr; // no following content node, stop search
+ }
+ }
+ }
+ while (pFrameNd != nullptr);
+
+ // probably this is dead code, because the GoNextSection()
+ // should have ended up in the first text node in the table and
+ // then checked it's in a table?
+ {
+ aIdx = pEnd->GetIndex() + 1;
+
+ pFrameNd = &aIdx.GetNode();
+ {
+ pFrameNd = nullptr;
+
+ // is there some sectionnodes before a tablenode?
+ while( aIdx.GetNode().IsSectionNode() )
+ {
+ const SwSection& rSect = aIdx.GetNode().
+ GetSectionNode()->GetSection();
+ if( rSect.IsHiddenFlag() )
+ aIdx = aIdx.GetNode().EndOfSectionIndex()+1;
+ else
+ ++aIdx;
+ }
+ if( aIdx.GetNode().IsTableNode() )
+ {
+ rFrameIdx = aIdx;
+ pFrameNd = &aIdx.GetNode();
+ assert(!"this isn't dead code?");
+ }
+ }
+ }
+ }
+ }
+ return pFrameNd;
+}
+
+void SwNodes::ForEach( SwNodeOffset nStart, SwNodeOffset nEnd,
+ FnForEach_SwNodes fn, void* pArgs )
+{
+ if( nEnd > SwNodeOffset(m_nSize) )
+ nEnd = SwNodeOffset(m_nSize);
+
+ if( nStart >= nEnd )
+ return;
+
+ sal_uInt16 cur = Index2Block( sal_Int32(nStart) );
+ BlockInfo** pp = m_ppInf.get() + cur;
+ BlockInfo* p = *pp;
+ sal_uInt16 nElem = sal_uInt16( sal_Int32(nStart) - p->nStart );
+ auto pElem = p->mvData.begin() + nElem;
+ nElem = p->nElem - nElem;
+ for(;;)
+ {
+ if( !(*fn)( static_cast<SwNode *>(*pElem++), pArgs ) || ++nStart >= nEnd )
+ break;
+
+ // next element
+ if( !--nElem )
+ {
+ // new block
+ p = *++pp;
+ pElem = p->mvData.begin();
+ nElem = p->nElem;
+ }
+ }
+}
+
+void SwNodes::ForEach( const SwNodeIndex& rStart, const SwNodeIndex& rEnd,
+ FnForEach_SwNodes fnForEach, void* pArgs )
+{
+ ForEach( rStart.GetIndex(), rEnd.GetIndex(), fnForEach, pArgs );
+}
+
+void SwNodes::RemoveNode( SwNodeOffset nDelPos, SwNodeOffset nSz, bool bDel )
+{
+#ifndef NDEBUG
+ SwNode *const pFirst((*this)[nDelPos]);
+#endif
+ for (SwNodeOffset nCnt(0); nCnt < nSz; nCnt++)
+ {
+ SwNode* pNode = (*this)[ nDelPos + nCnt ];
+ SwTextNode * pTextNd = pNode->GetTextNode();
+
+ if (pTextNd)
+ {
+ pTextNd->RemoveFromList();
+ // remove RndStdIds::FLY_AS_CHAR *before* adjusting SwNodeIndex
+ // so their anchor still points to correct node when deleted!
+ // NOTE: this will call RemoveNode() recursively!
+ // so adjust our indexes to account for removed nodes
+ SwNodeOffset const nPos = pTextNd->GetIndex();
+ SwpHints *const pHints(pTextNd->GetpSwpHints());
+ if (pHints)
+ {
+ std::vector<SwTextAttr*> flys;
+ for (size_t i = 0; i < pHints->Count(); ++i)
+ {
+ SwTextAttr *const pHint(pHints->Get(i));
+ if (RES_TXTATR_FLYCNT == pHint->Which())
+ {
+ flys.push_back(pHint);
+ }
+ }
+ for (SwTextAttr * pHint : flys)
+ {
+ pTextNd->DeleteAttribute(pHint);
+ } // pHints may be dead now
+ SwNodeOffset const nDiff = nPos - pTextNd->GetIndex();
+ if (nDiff)
+ {
+ nDelPos -= nDiff;
+ }
+ assert(pTextNd == (*this)[nDelPos + nCnt]);
+ assert(pFirst == (*this)[nDelPos]);
+ }
+ }
+ SwTableNode* pTableNode = pNode->GetTableNode();
+ if (pTableNode)
+ {
+ // The node that is deleted is a table node.
+ // Need to make sure that all the redlines that are
+ // related to this table are removed from the
+ // 'Extra Redlines' array
+ pTableNode->RemoveRedlines();
+ }
+ }
+
+ SwNodeOffset nEnd = nDelPos + nSz;
+ SwNode* pNew = (*this)[ nEnd ];
+
+ for (SwNodeIndex& rIndex : m_vIndices->GetRingContainer())
+ {
+ SwNodeOffset const nIdx = rIndex.GetIndex();
+ if (nDelPos <= nIdx && nIdx < nEnd)
+ rIndex = *pNew;
+ }
+
+ std::vector<BigPtrEntry> aTempEntries;
+ if( bDel )
+ {
+ SwNodeOffset nCnt = nSz;
+ BigPtrEntry *pDel = (*this)[ nDelPos+nCnt-1 ], *pPrev = (*this)[ nDelPos+nCnt-2 ];
+
+ // set temporary object
+ // JP 24.08.98: this should actually be removed because one could
+ // call Remove recursively, e.g. for character bound frames. However,
+ // since there happens way too much here, this temporary object was
+ // inserted that will be deleted in Remove again (see Bug 55406)
+ aTempEntries.resize(sal_Int32(nCnt));
+
+ while( nCnt-- )
+ {
+ delete pDel;
+ // coverity[use_after_free : FALSE] - pPrev will be reassigned if there will be another iteration to the loop
+ pDel = pPrev;
+ sal_uLong nPrevNdIdx = pPrev->GetPos();
+ BigPtrEntry* pTempEntry = &aTempEntries[sal_Int32(nCnt)];
+ BigPtrArray::Replace( nPrevNdIdx+1, pTempEntry );
+ if( nCnt )
+ pPrev = BigPtrArray::operator []( nPrevNdIdx - 1 );
+ // the accessed element can be a naked BigPtrEntry from
+ // aTempEntries, so the downcast to SwNode* in
+ // SwNodes::operator[] would be illegal (and unnecessary)
+ }
+ nDelPos = SwNodeOffset(pDel->GetPos() + 1);
+ }
+
+ BigPtrArray::Remove( sal_Int32(nDelPos), sal_Int32(nSz) );
+}
+
+void SwNodes::InsertNode( SwNode* pNode, const SwNodeIndex& rPos )
+{
+ BigPtrEntry* pIns = pNode;
+ BigPtrArray::Insert( pIns, sal_Int32(rPos.GetIndex()) );
+}
+
+void SwNodes::InsertNode( SwNode* pNode, SwNodeOffset nPos )
+{
+ BigPtrEntry* pIns = pNode;
+ BigPtrArray::Insert( pIns, sal_Int32(nPos) );
+}
+
+// ->#112139#
+SwNode * SwNodes::DocumentSectionStartNode(SwNode * pNode) const
+{
+ if (nullptr != pNode)
+ {
+ SwNodeIndex aIdx(*pNode);
+
+ if (aIdx <= (*this)[SwNodeOffset(0)]->EndOfSectionIndex())
+ pNode = (*this)[SwNodeOffset(0)];
+ else
+ {
+ while ((*this)[SwNodeOffset(0)] != pNode->StartOfSectionNode())
+ pNode = pNode->StartOfSectionNode();
+ }
+ }
+
+ return pNode;
+}
+
+SwNode * SwNodes::DocumentSectionEndNode(SwNode * pNode) const
+{
+ return DocumentSectionStartNode(pNode)->EndOfSectionNode();
+}
+
+bool SwNodes::IsDocNodes() const
+{
+ return this == &m_rMyDoc.GetNodes();
+}
+
+void SwNodes::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwNodes"));
+ for (SwNodeOffset i(0); i < Count(); ++i)
+ (*this)[i]->dumpAsXml(pWriter);
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/docnode/observablethread.cxx b/sw/source/core/docnode/observablethread.cxx
new file mode 100644
index 000000000..98eaabb29
--- /dev/null
+++ b/sw/source/core/docnode/observablethread.cxx
@@ -0,0 +1,63 @@
+/* -*- 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 <observablethread.hxx>
+#include <ifinishedthreadlistener.hxx>
+#include <memory>
+
+/* class for an observable thread
+
+ #i73788#
+*/
+ObservableThread::ObservableThread()
+ : mnThreadID( 0 )
+{
+}
+
+ObservableThread::~ObservableThread()
+{
+}
+
+void ObservableThread::SetListener( std::weak_ptr< IFinishedThreadListener > const & pThreadListener,
+ const oslInterlockedCount nThreadID )
+{
+ mpThreadListener = pThreadListener;
+ mnThreadID = nThreadID;
+}
+
+void SAL_CALL ObservableThread::run()
+{
+ acquire();
+
+ threadFunction();
+}
+
+void SAL_CALL ObservableThread::onTerminated()
+{
+ // notify observer
+ std::shared_ptr< IFinishedThreadListener > pThreadListener = mpThreadListener.lock();
+ if ( pThreadListener )
+ {
+ pThreadListener->NotifyAboutFinishedThread( mnThreadID );
+ }
+
+ release();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/docnode/pausethreadstarting.cxx b/sw/source/core/docnode/pausethreadstarting.cxx
new file mode 100644
index 000000000..953bd3a3f
--- /dev/null
+++ b/sw/source/core/docnode/pausethreadstarting.cxx
@@ -0,0 +1,48 @@
+/* -*- 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 <pausethreadstarting.hxx>
+#include <swthreadmanager.hxx>
+
+/* Helper class to pause starting of threads during existence of an instance
+ of this class
+
+ #i73788#
+*/
+
+SwPauseThreadStarting::SwPauseThreadStarting()
+ : mbPausedThreadStarting(false)
+{
+ if (SwThreadManager::ExistsThreadManager()
+ && !SwThreadManager::GetThreadManager().StartingOfThreadsSuspended())
+ {
+ SwThreadManager::GetThreadManager().SuspendStartingOfThreads();
+ mbPausedThreadStarting = true;
+ }
+}
+
+SwPauseThreadStarting::~SwPauseThreadStarting() COVERITY_NOEXCEPT_FALSE
+{
+ if (mbPausedThreadStarting)
+ {
+ SwThreadManager::GetThreadManager().ResumeStartingOfThreads();
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/docnode/retrievedinputstreamdata.cxx b/sw/source/core/docnode/retrievedinputstreamdata.cxx
new file mode 100644
index 000000000..311be07ca
--- /dev/null
+++ b/sw/source/core/docnode/retrievedinputstreamdata.cxx
@@ -0,0 +1,143 @@
+/* -*- 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 <retrievedinputstreamdata.hxx>
+#include <retrieveinputstreamconsumer.hxx>
+#include <vcl/svapp.hxx>
+
+// #i73788#
+
+SwRetrievedInputStreamDataManager::tDataKey SwRetrievedInputStreamDataManager::snNextKeyValue = 1;
+
+SwRetrievedInputStreamDataManager& SwRetrievedInputStreamDataManager::GetManager()
+{
+ static SwRetrievedInputStreamDataManager theSwRetrievedInputStreamDataManager;
+ return theSwRetrievedInputStreamDataManager;
+}
+
+SwRetrievedInputStreamDataManager::tDataKey SwRetrievedInputStreamDataManager::ReserveData(
+ std::weak_ptr< SwAsyncRetrieveInputStreamThreadConsumer > const & pThreadConsumer )
+{
+ std::unique_lock aGuard(maMutex);
+
+ // create empty data container for given thread Consumer
+ tDataKey nDataKey( snNextKeyValue );
+ tData aNewEntry( pThreadConsumer );
+ maInputStreamData[ nDataKey ] = aNewEntry;
+
+ // prepare next data key value
+ if ( snNextKeyValue < SAL_MAX_UINT64 )
+ {
+ ++snNextKeyValue;
+ }
+ else
+ {
+ snNextKeyValue = 1;
+ }
+
+ return nDataKey;
+}
+
+void SwRetrievedInputStreamDataManager::PushData(
+ const tDataKey nDataKey,
+ css::uno::Reference<css::io::XInputStream> const & xInputStream,
+ const bool bIsStreamReadOnly )
+{
+ std::unique_lock aGuard(maMutex);
+
+ std::map< tDataKey, tData >::iterator aIter = maInputStreamData.find( nDataKey );
+
+ if ( aIter == maInputStreamData.end() )
+ return;
+
+ // Fill data container.
+ (*aIter).second.mxInputStream = xInputStream;
+ (*aIter).second.mbIsStreamReadOnly = bIsStreamReadOnly;
+
+ // post user event to process the retrieved input stream data
+ if ( GetpApp() )
+ {
+
+ tDataKey* pDataKey = new tDataKey;
+ *pDataKey = nDataKey;
+ Application::PostUserEvent( LINK( this, SwRetrievedInputStreamDataManager, LinkedInputStreamReady ), pDataKey );
+ }
+ else
+ {
+ // no application available -> discard data
+ maInputStreamData.erase( aIter );
+ }
+}
+
+bool SwRetrievedInputStreamDataManager::PopData( const tDataKey nDataKey,
+ tData& rData )
+{
+ std::unique_lock aGuard(maMutex);
+
+ bool bDataProvided( false );
+
+ std::map< tDataKey, tData >::iterator aIter = maInputStreamData.find( nDataKey );
+
+ if ( aIter != maInputStreamData.end() )
+ {
+ rData.mpThreadConsumer = (*aIter).second.mpThreadConsumer;
+ rData.mxInputStream = (*aIter).second.mxInputStream;
+ rData.mbIsStreamReadOnly = (*aIter).second.mbIsStreamReadOnly;
+
+ maInputStreamData.erase( aIter );
+
+ bDataProvided = true;
+ }
+
+ return bDataProvided;
+}
+
+/** callback function, which is triggered by input stream data manager on
+ filling of the data container to provide retrieved input stream to the
+ thread Consumer using <Application::PostUserEvent(..)>
+
+ #i73788#
+ Note: This method has to be run in the main thread.
+*/
+IMPL_STATIC_LINK( SwRetrievedInputStreamDataManager, LinkedInputStreamReady,
+ void*, p, void )
+{
+ SwRetrievedInputStreamDataManager::tDataKey* pDataKey = static_cast<SwRetrievedInputStreamDataManager::tDataKey*>(p);
+ if ( !pDataKey )
+ {
+ return;
+ }
+
+ SwRetrievedInputStreamDataManager& rDataManager =
+ SwRetrievedInputStreamDataManager::GetManager();
+ SwRetrievedInputStreamDataManager::tData aInputStreamData;
+ if ( rDataManager.PopData( *pDataKey, aInputStreamData ) )
+ {
+ std::shared_ptr< SwAsyncRetrieveInputStreamThreadConsumer > pThreadConsumer =
+ aInputStreamData.mpThreadConsumer.lock();
+ if ( pThreadConsumer )
+ {
+ pThreadConsumer->ApplyInputStream( aInputStreamData.mxInputStream,
+ aInputStreamData.mbIsStreamReadOnly );
+ }
+ }
+ delete pDataKey;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/docnode/retrieveinputstream.cxx b/sw/source/core/docnode/retrieveinputstream.cxx
new file mode 100644
index 000000000..8054e341b
--- /dev/null
+++ b/sw/source/core/docnode/retrieveinputstream.cxx
@@ -0,0 +1,83 @@
+/* -*- 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 <retrieveinputstream.hxx>
+
+#include <comphelper/propertyvalue.hxx>
+#include <unotools/mediadescriptor.hxx>
+#include <com/sun/star/io/XStream.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+
+/* class for a thread to retrieve an input stream given by a URL
+
+ #i73788#
+*/
+::rtl::Reference< ObservableThread > SwAsyncRetrieveInputStreamThread::createThread(
+ const SwRetrievedInputStreamDataManager::tDataKey nDataKey,
+ const OUString& rLinkedURL, const OUString& rReferer )
+{
+ SwAsyncRetrieveInputStreamThread* pNewThread =
+ new SwAsyncRetrieveInputStreamThread( nDataKey, rLinkedURL, rReferer );
+ return pNewThread;
+}
+
+SwAsyncRetrieveInputStreamThread::SwAsyncRetrieveInputStreamThread(
+ const SwRetrievedInputStreamDataManager::tDataKey nDataKey,
+ const OUString& rLinkedURL,
+ const OUString& rReferer )
+ : mnDataKey( nDataKey ),
+ mrLinkedURL( rLinkedURL ),
+ mrReferer( rReferer )
+{
+}
+
+SwAsyncRetrieveInputStreamThread::~SwAsyncRetrieveInputStreamThread()
+{
+}
+
+void SwAsyncRetrieveInputStreamThread::threadFunction()
+{
+ osl_setThreadName("SwAsyncRetrieveInputStreamThread");
+
+ css::uno::Sequence < css::beans::PropertyValue > xProps{
+ comphelper::makePropertyValue("URL", mrLinkedURL),
+ comphelper::makePropertyValue("Referer", mrReferer)
+ };
+ utl::MediaDescriptor aMedium( xProps );
+
+ aMedium.addInputStream();
+
+ css::uno::Reference<css::io::XInputStream> xInputStream;
+ aMedium[utl::MediaDescriptor::PROP_INPUTSTREAM] >>= xInputStream;
+ if ( !xInputStream.is() )
+ {
+ css::uno::Reference<css::io::XStream> xStream;
+ aMedium[utl::MediaDescriptor::PROP_STREAM] >>= xStream;
+ if ( xStream.is() )
+ {
+ xInputStream = xStream->getInputStream();
+ }
+ }
+
+ SwRetrievedInputStreamDataManager::GetManager().PushData( mnDataKey,
+ xInputStream,
+ aMedium.isStreamReadOnly() );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/docnode/retrieveinputstreamconsumer.cxx b/sw/source/core/docnode/retrieveinputstreamconsumer.cxx
new file mode 100644
index 000000000..d89a05361
--- /dev/null
+++ b/sw/source/core/docnode/retrieveinputstreamconsumer.cxx
@@ -0,0 +1,63 @@
+/* -*- 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 <retrieveinputstreamconsumer.hxx>
+#include <ndgrf.hxx>
+#include <retrieveinputstream.hxx>
+#include <swthreadmanager.hxx>
+
+/* class to provide creation of a thread to retrieve an input stream given by
+ a URL and to consume the retrieved input stream.
+
+ #i73788#
+*/
+SwAsyncRetrieveInputStreamThreadConsumer::SwAsyncRetrieveInputStreamThreadConsumer(
+ SwGrfNode& rGrfNode )
+ : mrGrfNode( rGrfNode ),
+ mnThreadID( 0 )
+{
+}
+
+SwAsyncRetrieveInputStreamThreadConsumer::~SwAsyncRetrieveInputStreamThreadConsumer() COVERITY_NOEXCEPT_FALSE
+{
+ SwThreadManager::GetThreadManager().RemoveThread( mnThreadID );
+}
+
+void SwAsyncRetrieveInputStreamThreadConsumer::CreateThread( const OUString& rURL, const OUString& rReferer )
+{
+ // Get new data container for input stream data
+ SwRetrievedInputStreamDataManager::tDataKey nDataKey =
+ SwRetrievedInputStreamDataManager::GetManager().ReserveData(
+ mrGrfNode.GetThreadConsumer() );
+
+ rtl::Reference< ObservableThread > pNewThread =
+ SwAsyncRetrieveInputStreamThread::createThread( nDataKey, rURL, rReferer );
+
+ // Add thread to thread manager and pass ownership of thread to thread manager.
+ mnThreadID = SwThreadManager::GetThreadManager().AddThread( pNewThread );
+}
+
+void SwAsyncRetrieveInputStreamThreadConsumer::ApplyInputStream(
+ css::uno::Reference<css::io::XInputStream> const & xInputStream,
+ const bool bIsStreamReadOnly )
+{
+ mrGrfNode.ApplyInputStream( xInputStream, bIsStreamReadOnly );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/docnode/section.cxx b/sw/source/core/docnode/section.cxx
new file mode 100644
index 000000000..c35a614f8
--- /dev/null
+++ b/sw/source/core/docnode/section.cxx
@@ -0,0 +1,1512 @@
+/* -*- 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/xmlstring.h>
+#include <libxml/xmlwriter.h>
+#include <stdlib.h>
+#include <hintids.hxx>
+#include <osl/diagnose.h>
+#include <sot/exchange.hxx>
+#include <svl/stritem.hxx>
+#include <sfx2/docfile.hxx>
+#include <editeng/protitem.hxx>
+#include <sfx2/linkmgr.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <docary.hxx>
+#include <fmtcntnt.hxx>
+#include <fmtpdsc.hxx>
+#include <doc.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <DocumentLinksAdministrationManager.hxx>
+#include <DocumentContentOperationsManager.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <IDocumentFieldsAccess.hxx>
+#include <IDocumentStylePoolAccess.hxx>
+#include <IDocumentState.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <node.hxx>
+#include <pam.hxx>
+#include <frmatr.hxx>
+#include <frmtool.hxx>
+#include <editsh.hxx>
+#include <hints.hxx>
+#include <docsh.hxx>
+#include <ndtxt.hxx>
+#include <section.hxx>
+#include <swserv.hxx>
+#include <shellio.hxx>
+#include <poolfmt.hxx>
+#include <swbaslnk.hxx>
+#include <mvsave.hxx>
+#include <ftnidx.hxx>
+#include <doctxm.hxx>
+#include <fmteiro.hxx>
+#include <unosection.hxx>
+#include <calbck.hxx>
+#include <fmtclds.hxx>
+#include <algorithm>
+#include "ndsect.hxx"
+
+using namespace ::com::sun::star;
+
+namespace {
+ class SwIntrnlSectRefLink : public SwBaseLink
+ {
+ SwSectionFormat& m_rSectFormat;
+
+ public:
+ SwIntrnlSectRefLink(SwSectionFormat& rFormat, SfxLinkUpdateMode nUpdateType)
+ : SwBaseLink(nUpdateType, SotClipboardFormatId::RTF)
+ , m_rSectFormat(rFormat)
+ {}
+
+ virtual void Closed() override;
+ virtual ::sfx2::SvBaseLink::UpdateResult DataChanged(
+ const OUString& rMimeType, const css::uno::Any & rValue ) override;
+
+ virtual const SwNode* GetAnchor() const override;
+ virtual bool IsInRange( SwNodeOffset nSttNd, SwNodeOffset nEndNd ) const override;
+
+ SwSectionNode* GetSectNode()
+ {
+ const SwNode* pSectNd( GetAnchor() );
+ return const_cast<SwSectionNode*>( dynamic_cast<const SwSectionNode*>( pSectNd ) );
+ }
+ };
+}
+
+SwSectionData::SwSectionData(SectionType const eType, OUString const& rName)
+ : m_eType(eType)
+ , m_sSectionName(rName)
+ , m_bHiddenFlag(false)
+ , m_bProtectFlag(false)
+ , m_bEditInReadonlyFlag(false) // edit in readonly sections
+ , m_bHidden(false)
+ , m_bCondHiddenFlag(true)
+ , m_bConnectFlag(true)
+{
+}
+
+// this must have the same semantics as operator=()
+SwSectionData::SwSectionData(SwSection const& rSection)
+ : m_eType(rSection.GetType())
+ , m_sSectionName(rSection.GetSectionName())
+ , m_sCondition(rSection.GetCondition())
+ , m_sLinkFileName(rSection.GetLinkFileName())
+ , m_sLinkFilePassword(rSection.GetLinkFilePassword())
+ , m_Password(rSection.GetPassword())
+ , m_bHiddenFlag(rSection.IsHiddenFlag())
+ , m_bProtectFlag(rSection.IsProtect())
+ // edit in readonly sections
+ , m_bEditInReadonlyFlag(rSection.IsEditInReadonly())
+ , m_bHidden(rSection.IsHidden())
+ , m_bCondHiddenFlag(true)
+ , m_bConnectFlag(rSection.IsConnectFlag())
+{
+}
+
+// this must have the same semantics as operator=()
+SwSectionData::SwSectionData(SwSectionData const& rOther)
+ : m_eType(rOther.m_eType)
+ , m_sSectionName(rOther.m_sSectionName)
+ , m_sCondition(rOther.m_sCondition)
+ , m_sLinkFileName(rOther.m_sLinkFileName)
+ , m_sLinkFilePassword(rOther.m_sLinkFilePassword)
+ , m_Password(rOther.m_Password)
+ , m_bHiddenFlag(rOther.m_bHiddenFlag)
+ , m_bProtectFlag(rOther.m_bProtectFlag)
+ // edit in readonly sections
+ , m_bEditInReadonlyFlag(rOther.m_bEditInReadonlyFlag)
+ , m_bHidden(rOther.m_bHidden)
+ , m_bCondHiddenFlag(true)
+ , m_bConnectFlag(rOther.m_bConnectFlag)
+{
+}
+
+// the semantics here are weird for reasons of backward compatibility
+SwSectionData & SwSectionData::operator= (SwSectionData const& rOther)
+{
+ m_eType = rOther.m_eType;
+ m_sSectionName = rOther.m_sSectionName;
+ m_sCondition = rOther.m_sCondition;
+ m_sLinkFileName = rOther.m_sLinkFileName;
+ m_sLinkFilePassword = rOther.m_sLinkFilePassword;
+ m_bConnectFlag = rOther.m_bConnectFlag;
+ m_Password = rOther.m_Password;
+
+ m_bEditInReadonlyFlag = rOther.m_bEditInReadonlyFlag;
+ m_bProtectFlag = rOther.m_bProtectFlag;
+
+ m_bHidden = rOther.m_bHidden;
+ // FIXME: old code did not assign m_bHiddenFlag ?
+ // FIXME: why should m_bCondHiddenFlag always default to true?
+ m_bCondHiddenFlag = true;
+
+ return *this;
+}
+
+// the semantics here are weird for reasons of backward compatibility
+bool SwSectionData::operator==(SwSectionData const& rOther) const
+{
+ return (m_eType == rOther.m_eType)
+ && (m_sSectionName == rOther.m_sSectionName)
+ && (m_sCondition == rOther.m_sCondition)
+ && (m_bHidden == rOther.m_bHidden)
+ && (m_bProtectFlag == rOther.m_bProtectFlag)
+ && (m_bEditInReadonlyFlag == rOther.m_bEditInReadonlyFlag)
+ && (m_sLinkFileName == rOther.m_sLinkFileName)
+ && (m_sLinkFilePassword == rOther.m_sLinkFilePassword)
+ && (m_Password == rOther.m_Password);
+ // FIXME: old code ignored m_bCondHiddenFlag m_bHiddenFlag m_bConnectFlag
+}
+
+SwSection::SwSection(
+ SectionType const eType, OUString const& rName, SwSectionFormat & rFormat)
+ : SwClient(& rFormat)
+ , m_Data(eType, rName)
+{
+ StartListening(rFormat.GetNotifier());
+
+ SwSection *const pParentSect = GetParent();
+ if( pParentSect )
+ {
+ // edit in readonly sections
+ m_Data.SetEditInReadonlyFlag( pParentSect->IsEditInReadonlyFlag() );
+ }
+
+ m_Data.SetProtectFlag( rFormat.GetProtect().IsContentProtected() );
+
+ if (!m_Data.IsEditInReadonlyFlag()) // edit in readonly sections
+ {
+ m_Data.SetEditInReadonlyFlag( rFormat.GetEditInReadonly().GetValue() );
+ }
+}
+
+SwSection::~SwSection()
+{
+ SwSectionFormat* pFormat = GetFormat();
+ if( !pFormat )
+ return;
+
+ SwDoc* pDoc = pFormat->GetDoc();
+ if( pDoc->IsInDtor() )
+ {
+ // We reattach our Format to the default FrameFormat
+ // to not get any dependencies
+ if( pFormat->DerivedFrom() != pDoc->GetDfltFrameFormat() )
+ pFormat->RegisterToFormat( *pDoc->GetDfltFrameFormat() );
+ }
+ else
+ {
+ pFormat->Remove( this ); // remove
+ SvtListener::EndListeningAll();
+
+ if (SectionType::Content != m_Data.GetType())
+ {
+ pDoc->getIDocumentLinksAdministration().GetLinkManager().Remove( m_RefLink.get() );
+ }
+
+ if (m_RefObj.is())
+ {
+ pDoc->getIDocumentLinksAdministration().GetLinkManager().RemoveServer( m_RefObj.get() );
+ }
+
+ // If the Section is the last Client in the Format we can delete it
+ pFormat->RemoveAllUnos();
+ if( !pFormat->HasWriterListeners() )
+ {
+ // Do not add to the Undo. This should've happened earlier.
+ ::sw::UndoGuard const undoGuard(pDoc->GetIDocumentUndoRedo());
+ pDoc->DelSectionFormat( pFormat );
+ }
+ }
+ if (m_RefObj.is())
+ {
+ m_RefObj->Closed();
+ }
+}
+
+void SwSection::SetSectionData(SwSectionData const& rData)
+{
+ bool const bOldHidden( m_Data.IsHidden() );
+ m_Data = rData;
+ // The next two may actually overwrite the m_Data.m_bProtect or EditInReadonly Flag
+ // in Modify, which should result in same flag value as the old code!
+ SetProtect(m_Data.IsProtectFlag());
+ SetEditInReadonly(m_Data.IsEditInReadonlyFlag());
+ if (bOldHidden != m_Data.IsHidden()) // check if changed...
+ {
+ ImplSetHiddenFlag(m_Data.IsHidden(), m_Data.IsCondHidden());
+ }
+}
+
+bool SwSection::DataEquals(SwSectionData const& rCmp) const
+{
+ // note that the old code compared the flags of the parameter with the
+ // format attributes of this; the following mess should do the same...
+ (void) GetLinkFileName(); // updates m_sLinkFileName
+ bool const bProtect(m_Data.IsProtectFlag());
+ bool const bEditInReadonly(m_Data.IsEditInReadonlyFlag());
+ m_Data.SetProtectFlag(IsProtect());
+ m_Data.SetEditInReadonlyFlag(IsEditInReadonly());
+ bool const bResult( m_Data == rCmp );
+ m_Data.SetProtectFlag(bProtect);
+ m_Data.SetEditInReadonlyFlag(bEditInReadonly);
+ return bResult;
+}
+
+void SwSection::ImplSetHiddenFlag(bool const bTmpHidden, bool const bCondition)
+{
+ SwSectionFormat* pFormat = GetFormat();
+ OSL_ENSURE(pFormat, "ImplSetHiddenFlag: no format?");
+ if( !pFormat )
+ return;
+
+ const bool bHide = bTmpHidden && bCondition;
+
+ if (bHide) // should be hidden
+ {
+ if (!m_Data.IsHiddenFlag()) // is not hidden
+ {
+ // Is the Parent hidden?
+ // This should be shown by the bHiddenFlag.
+
+ // Tell all Children that they are hidden
+ const SwMsgPoolItem aMsgItem( RES_SECTION_HIDDEN );
+ pFormat->CallSwClientNotify(sw::LegacyModifyHint(&aMsgItem, &aMsgItem));
+
+ // Delete all Frames
+ pFormat->DelFrames();
+ }
+ }
+ else if (m_Data.IsHiddenFlag()) // show Nodes again
+ {
+ // Show all Frames (Child Sections are accounted for by MakeFrames)
+ // Only if the Parent Section is not restricting us!
+ SwSection* pParentSect = pFormat->GetParentSection();
+ if( !pParentSect || !pParentSect->IsHiddenFlag() )
+ {
+ // Tell all Children that the Parent is not hidden anymore
+ const SwMsgPoolItem aMsgItem( RES_SECTION_NOT_HIDDEN );
+ pFormat->CallSwClientNotify(sw::LegacyModifyHint(&aMsgItem, &aMsgItem));
+
+ pFormat->MakeFrames();
+ }
+ }
+}
+
+bool SwSection::CalcHiddenFlag() const
+{
+ const SwSection* pSect = this;
+ do {
+ if( pSect->IsHidden() && pSect->IsCondHidden() )
+ return true;
+ } while( nullptr != ( pSect = pSect->GetParent()) );
+
+ return false;
+}
+
+bool SwSection::IsProtect() const
+{
+ SwSectionFormat const *const pFormat( GetFormat() );
+ OSL_ENSURE(pFormat, "SwSection::IsProtect: no format?");
+ return pFormat
+ ? pFormat->GetProtect().IsContentProtected()
+ : IsProtectFlag();
+}
+
+// edit in readonly sections
+bool SwSection::IsEditInReadonly() const
+{
+ SwSectionFormat const *const pFormat( GetFormat() );
+ OSL_ENSURE(pFormat, "SwSection::IsEditInReadonly: no format?");
+ return pFormat
+ ? pFormat->GetEditInReadonly().GetValue()
+ : IsEditInReadonlyFlag();
+}
+
+void SwSection::SetHidden(bool const bFlag)
+{
+ if (!m_Data.IsHidden() == !bFlag)
+ return;
+
+ m_Data.SetHidden(bFlag);
+ ImplSetHiddenFlag(bFlag, m_Data.IsCondHidden());
+}
+
+void SwSection::SetProtect(bool const bFlag)
+{
+ SwSectionFormat *const pFormat( GetFormat() );
+ OSL_ENSURE(pFormat, "SwSection::SetProtect: no format?");
+ if (pFormat)
+ {
+ SvxProtectItem aItem( RES_PROTECT );
+ aItem.SetContentProtect( bFlag );
+ pFormat->SetFormatAttr( aItem );
+ // note: this will call m_Data.SetProtectFlag via Modify!
+ }
+ else
+ {
+ m_Data.SetProtectFlag(bFlag);
+ }
+}
+
+// edit in readonly sections
+void SwSection::SetEditInReadonly(bool const bFlag)
+{
+ SwSectionFormat *const pFormat( GetFormat() );
+ OSL_ENSURE(pFormat, "SwSection::SetEditInReadonly: no format?");
+ if (pFormat)
+ {
+ SwFormatEditInReadonly aItem;
+ aItem.SetValue( bFlag );
+ pFormat->SetFormatAttr( aItem );
+ // note: this will call m_Data.SetEditInReadonlyFlag via Modify!
+ }
+ else
+ {
+ m_Data.SetEditInReadonlyFlag(bFlag);
+ }
+}
+
+void SwSection::SwClientNotify(const SwModify&, const SfxHint& rHint)
+{
+ Notify(rHint);
+}
+
+void SwSection::Notify(SfxHint const& rHint)
+{
+ if (rHint.GetId() != SfxHintId::SwLegacyModify)
+ return;
+ auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
+ auto pOld = pLegacy->m_pOld;
+ auto pNew = pLegacy->m_pNew;
+ bool bUpdateFootnote = false;
+ switch(pLegacy->GetWhich())
+ {
+ case RES_ATTRSET_CHG:
+ if (pNew && pOld)
+ {
+ SfxItemSet* pNewSet = const_cast<SwAttrSetChg*>(static_cast<const SwAttrSetChg*>(pNew))->GetChgSet();
+ SfxItemSet* pOldSet = const_cast<SwAttrSetChg*>(static_cast<const SwAttrSetChg*>(pOld))->GetChgSet();
+
+ if( const SvxProtectItem* pItem = pNewSet->GetItemIfSet(
+ RES_PROTECT, false ) )
+ {
+ m_Data.SetProtectFlag( pItem->IsContentProtected() );
+ pNewSet->ClearItem( RES_PROTECT );
+ pOldSet->ClearItem( RES_PROTECT );
+ }
+
+ // --> edit in readonly sections
+ if( const SwFormatEditInReadonly* pItem = pNewSet->GetItemIfSet(
+ RES_EDIT_IN_READONLY, false ) )
+ {
+ m_Data.SetEditInReadonlyFlag(pItem->GetValue());
+ pNewSet->ClearItem( RES_EDIT_IN_READONLY );
+ pOldSet->ClearItem( RES_EDIT_IN_READONLY );
+ }
+
+ if( SfxItemState::SET == pNewSet->GetItemState(
+ RES_FTN_AT_TXTEND, false ) ||
+ SfxItemState::SET == pNewSet->GetItemState(
+ RES_END_AT_TXTEND, false ))
+ {
+ bUpdateFootnote = true;
+ }
+
+ if( !pNewSet->Count() )
+ return;
+ }
+ break;
+
+ case RES_PROTECT:
+ if( pNew )
+ {
+ bool bNewFlag =
+ static_cast<const SvxProtectItem*>(pNew)->IsContentProtected();
+ // this used to inherit the flag from the parent, but then there is
+ // no way to turn it off in an inner section
+ m_Data.SetProtectFlag( bNewFlag );
+ }
+ return;
+ // edit in readonly sections
+ case RES_EDIT_IN_READONLY:
+ if( pNew )
+ {
+ const bool bNewFlag =
+ static_cast<const SwFormatEditInReadonly*>(pNew)->GetValue();
+ m_Data.SetEditInReadonlyFlag( bNewFlag );
+ }
+ return;
+
+ case RES_SECTION_HIDDEN:
+ m_Data.SetHiddenFlag(true);
+ return;
+
+ case RES_SECTION_NOT_HIDDEN:
+ m_Data.SetHiddenFlag( m_Data.IsHidden() && m_Data.IsCondHidden() );
+ return;
+
+ case RES_COL:
+ // Is handled by the Layout, if appropriate
+ break;
+
+ case RES_FTN_AT_TXTEND:
+ if( pNew && pOld )
+ {
+ bUpdateFootnote = true;
+ }
+ break;
+
+ case RES_END_AT_TXTEND:
+ if( pNew && pOld )
+ {
+ bUpdateFootnote = true;
+ }
+ break;
+
+ default:
+ CheckRegistration( pOld );
+ break;
+ }
+
+ if( bUpdateFootnote )
+ {
+ SwSectionNode* pSectNd = GetFormat()->GetSectionNode();
+ if( pSectNd )
+ pSectNd->GetDoc().GetFootnoteIdxs().UpdateFootnote(SwNodeIndex( *pSectNd ));
+ }
+}
+
+void SwSection::SetRefObject( SwServerObject* pObj )
+{
+ m_RefObj = pObj;
+}
+
+void SwSection::SetCondHidden(bool const bFlag)
+{
+ if (!m_Data.IsCondHidden() == !bFlag)
+ return;
+
+ m_Data.SetCondHidden(bFlag);
+ ImplSetHiddenFlag(m_Data.IsHidden(), bFlag);
+}
+
+// Set/remove the linked FileName
+OUString const & SwSection::GetLinkFileName() const
+{
+ if (m_RefLink.is())
+ {
+ OUString sTmp;
+ switch (m_Data.GetType())
+ {
+ case SectionType::DdeLink:
+ sTmp = m_RefLink->GetLinkSourceName();
+ break;
+
+ case SectionType::FileLink:
+ {
+ OUString sRange;
+ OUString sFilter;
+ if (m_RefLink->GetLinkManager() &&
+ sfx2::LinkManager::GetDisplayNames(
+ m_RefLink.get(), nullptr, &sTmp, &sRange, &sFilter ))
+ {
+ sTmp += OUStringChar(sfx2::cTokenSeparator) + sFilter
+ + OUStringChar(sfx2::cTokenSeparator) + sRange;
+ }
+ else if( GetFormat() && !GetFormat()->GetSectionNode() )
+ {
+ // If the Section is in the UndoNodesArray, the LinkManager
+ // does not contain the Link, thus it cannot be queried for it.
+ // Thus return the current Name.
+ return m_Data.GetLinkFileName();
+ }
+ }
+ break;
+ default: break;
+ }
+ m_Data.SetLinkFileName(sTmp);
+ }
+ return m_Data.GetLinkFileName();
+}
+
+void SwSection::SetLinkFileName(const OUString& rNew)
+{
+ if (m_RefLink.is())
+ {
+ m_RefLink->SetLinkSourceName( rNew );
+ }
+ m_Data.SetLinkFileName(rNew);
+}
+
+// If it was a Linked Section, we need to make all Child Links visible
+void SwSection::MakeChildLinksVisible( const SwSectionNode& rSectNd )
+{
+ const SwNode* pNd;
+ const ::sfx2::SvBaseLinks& rLnks = rSectNd.GetDoc().getIDocumentLinksAdministration().GetLinkManager().GetLinks();
+ for( auto n = rLnks.size(); n; )
+ {
+ sfx2::SvBaseLink& rBLnk = *rLnks[--n];
+ if (!rBLnk.IsVisible() && dynamic_cast<const SwBaseLink*>(&rBLnk) != nullptr
+ && nullptr != (pNd = static_cast<SwBaseLink&>(rBLnk).GetAnchor()))
+ {
+ pNd = pNd->StartOfSectionNode(); // If it's a SectionNode
+ const SwSectionNode* pParent;
+ while( nullptr != ( pParent = pNd->FindSectionNode() ) &&
+ ( SectionType::Content == pParent->GetSection().GetType()
+ || pNd == &rSectNd ))
+ pNd = pParent->StartOfSectionNode();
+
+ // It's within a normal Section, so show again
+ if( !pParent )
+ rBLnk.SetVisible(true);
+ }
+ }
+}
+
+const SwTOXBase* SwSection::GetTOXBase() const
+{
+ const SwTOXBase* pRet = nullptr;
+ if( SectionType::ToxContent == GetType() )
+ pRet = dynamic_cast<const SwTOXBaseSection*>(this);
+ return pRet;
+}
+
+SwSectionFormat::SwSectionFormat( SwFrameFormat* pDrvdFrame, SwDoc *pDoc )
+ : SwFrameFormat( pDoc->GetAttrPool(), OUString(), pDrvdFrame )
+{
+ LockModify();
+ SetFormatAttr( *GetDfltAttr( RES_COL ) );
+ UnlockModify();
+}
+
+SwSectionFormat::~SwSectionFormat()
+{
+ if( GetDoc()->IsInDtor() )
+ return;
+
+ SwSectionNode* pSectNd;
+ const SwNodeIndex* pIdx = GetContent( false ).GetContentIdx();
+ if( pIdx && &GetDoc()->GetNodes() == &pIdx->GetNodes() &&
+ nullptr != (pSectNd = pIdx->GetNode().GetSectionNode() ))
+ {
+ SwSection& rSect = pSectNd->GetSection();
+ // If it was a linked Section, we need to make all Child Links
+ // visible again
+ if( rSect.IsConnected() )
+ SwSection::MakeChildLinksVisible( *pSectNd );
+
+ // Check whether we need to be visible, before deleting the Nodes
+ if( rSect.IsHiddenFlag() )
+ {
+ SwSection* pParentSect = rSect.GetParent();
+ if( !pParentSect || !pParentSect->IsHiddenFlag() )
+ {
+ // Make Nodes visible again
+ rSect.SetHidden(false);
+ }
+ }
+ // mba: test iteration; objects are removed while iterating
+ // use hint which allows to specify, if the content shall be saved or not
+ CallSwClientNotify( SwSectionFrameMoveAndDeleteHint( true ) );
+
+ // Raise the Section up
+ SwNodeRange aRg( *pSectNd, SwNodeOffset(0), *pSectNd->EndOfSectionNode() );
+ GetDoc()->GetNodes().SectionUp( &aRg );
+ }
+ LockModify();
+ ResetFormatAttr( RES_CNTNT );
+ UnlockModify();
+}
+
+SwSection * SwSectionFormat::GetSection() const
+{
+ return SwIterator<SwSection,SwSectionFormat>( *this ).First();
+}
+
+// Do not destroy all Frames in aDepend (Frames are recognized with a dynamic_cast).
+void SwSectionFormat::DelFrames()
+{
+ SwSectionNode* pSectNd;
+ const SwNodeIndex* pIdx = GetContent(false).GetContentIdx();
+ if( pIdx && &GetDoc()->GetNodes() == &pIdx->GetNodes() &&
+ nullptr != (pSectNd = pIdx->GetNode().GetSectionNode() ))
+ {
+ // First delete the <SwSectionFrame> of the <SwSectionFormat> instance
+ // mba: test iteration as objects are removed in iteration
+ // use hint which allows to specify, if the content shall be saved or not
+ CallSwClientNotify( SwSectionFrameMoveAndDeleteHint( false ) );
+
+ // Then delete frames of the nested <SwSectionFormat> instances
+ SwIterator<SwSectionFormat,SwSectionFormat> aIter( *this );
+ SwSectionFormat *pLast = aIter.First();
+ while ( pLast )
+ {
+ pLast->DelFrames();
+ pLast = aIter.Next();
+ }
+
+ SwNodeOffset nEnd = pSectNd->EndOfSectionIndex();
+ SwNodeOffset nStart = pSectNd->GetIndex()+1;
+ sw_DeleteFootnote( pSectNd, nStart, nEnd );
+ }
+ if( !pIdx )
+ return;
+
+ // Send Hint for PageDesc. Actually the Layout contained in the
+ // Paste of the Frame itself would need to do this. But that leads
+ // to subsequent errors, which we'd need to solve at run-time.
+ SwNodeIndex aNextNd( *pIdx );
+ SwContentNode* pCNd = GetDoc()->GetNodes().GoNextSection( &aNextNd, true, false );
+ if( pCNd )
+ {
+ const SfxPoolItem& rItem = pCNd->GetSwAttrSet().Get(RES_PAGEDESC);
+ pCNd->CallSwClientNotify(sw::LegacyModifyHint(&rItem, &rItem));
+ }
+}
+
+// Create the Views
+void SwSectionFormat::MakeFrames()
+{
+ SwSectionNode* pSectNd;
+ const SwNodeIndex* pIdx = GetContent(false).GetContentIdx();
+
+ if( pIdx && &GetDoc()->GetNodes() == &pIdx->GetNodes() &&
+ nullptr != (pSectNd = pIdx->GetNode().GetSectionNode() ))
+ {
+ SwNodeIndex aIdx( *pIdx );
+ pSectNd->MakeOwnFrames( &aIdx );
+ }
+}
+
+void SwSectionFormat::SwClientNotify(const SwModify& rMod, const SfxHint& rHint)
+{
+ if (rHint.GetId() != SfxHintId::SwLegacyModify)
+ return;
+ auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
+ sal_uInt16 nWhich = pLegacy->GetWhich();
+ auto pOld = pLegacy->m_pOld;
+ auto pNew = pLegacy->m_pNew;
+ switch( nWhich )
+ {
+ case RES_ATTRSET_CHG:
+ if (HasWriterListeners() && pOld && pNew)
+ {
+ SfxItemSet* pNewSet = const_cast<SwAttrSetChg*>(static_cast<const SwAttrSetChg*>(pNew))->GetChgSet();
+ SfxItemSet* pOldSet = const_cast<SwAttrSetChg*>(static_cast<const SwAttrSetChg*>(pOld))->GetChgSet();
+ const SfxPoolItem *pItem;
+ if( SfxItemState::SET == pNewSet->GetItemState(
+ RES_PROTECT, false, &pItem ))
+ {
+ GetNotifier().Broadcast(sw::LegacyModifyHint(pItem, pItem));
+ pNewSet->ClearItem( RES_PROTECT );
+ pOldSet->ClearItem( RES_PROTECT );
+ }
+
+ // --> edit in readonly sections
+ if( SfxItemState::SET == pNewSet->GetItemState(
+ RES_EDIT_IN_READONLY, false, &pItem ) )
+ {
+ GetNotifier().Broadcast(sw::LegacyModifyHint(pItem, pItem));
+ pNewSet->ClearItem( RES_EDIT_IN_READONLY );
+ pOldSet->ClearItem( RES_EDIT_IN_READONLY );
+ }
+
+ if( SfxItemState::SET == pNewSet->GetItemState(
+ RES_FTN_AT_TXTEND, false, &pItem ))
+ {
+ GetNotifier().Broadcast(sw::LegacyModifyHint(pItem, pItem));
+ pNewSet->ClearItem( RES_FTN_AT_TXTEND );
+ pOldSet->ClearItem( RES_FTN_AT_TXTEND );
+ }
+ if( SfxItemState::SET == pNewSet->GetItemState(
+ RES_END_AT_TXTEND, false, &pItem ))
+ {
+ GetNotifier().Broadcast(sw::LegacyModifyHint(pItem, pItem));
+ pNewSet->ClearItem( RES_END_AT_TXTEND );
+ pOldSet->ClearItem( RES_END_AT_TXTEND );
+ }
+ if( !static_cast<const SwAttrSetChg*>(pOld)->GetChgSet()->Count() )
+ return;
+ }
+ break;
+
+ case RES_SECTION_HIDDEN:
+ case RES_SECTION_NOT_HIDDEN:
+ {
+ auto pSect = GetSection();
+ if(!pSect || (RES_SECTION_HIDDEN == nWhich) == pSect->IsHiddenFlag()) // already at target state, skipping.
+ return;
+ }
+ [[fallthrough]];
+ case RES_FTN_AT_TXTEND:
+ case RES_END_AT_TXTEND:
+ GetNotifier().Broadcast(sw::LegacyModifyHint(pOld, pNew));
+ return;
+ case RES_PROTECT:
+ case RES_EDIT_IN_READONLY: // edit in readonly sections
+ // Pass through these Messages until the End of the tree!
+ GetNotifier().Broadcast(sw::LegacyModifyHint(pOld, pNew));
+ return; // That's it!
+
+ case RES_OBJECTDYING:
+ if( !GetDoc()->IsInDtor() && pOld &&
+ static_cast<const SwPtrMsgPoolItem *>(pOld)->pObject == static_cast<void*>(GetRegisteredIn()) )
+ {
+ // My Parents will be destroyed, so get the Parent's Parent
+ // and update
+ SwFrameFormat::SwClientNotify(rMod, rHint);
+ UpdateParent();
+ return;
+ }
+ break;
+
+ case RES_FMT_CHG:
+ if( !GetDoc()->IsInDtor() &&
+ static_cast<const SwFormatChg*>(pNew)->pChangedFormat == static_cast<void*>(GetRegisteredIn()) &&
+ dynamic_cast<const SwSectionFormat*>(static_cast<const SwFormatChg*>(pNew)->pChangedFormat) != nullptr )
+ {
+ // My Parent will be changed, thus I need to update
+ SwFrameFormat::SwClientNotify(rMod, rHint);
+ UpdateParent();
+ return;
+ }
+ break;
+ }
+ SwFrameFormat::SwClientNotify(rMod, rHint);
+
+ if (pOld && (RES_REMOVE_UNO_OBJECT == pOld->Which()))
+ { // invalidate cached uno object
+ SetXTextSection(uno::Reference<text::XTextSection>(nullptr));
+ }
+}
+
+// Get info from the Format
+bool SwSectionFormat::GetInfo( SfxPoolItem& rInfo ) const
+{
+ switch( rInfo.Which() )
+ {
+ case RES_FINDNEARESTNODE:
+ if( GetFormatAttr( RES_PAGEDESC ).GetPageDesc() )
+ {
+ const SwSectionNode* pNd = GetSectionNode();
+ if( pNd )
+ static_cast<SwFindNearestNode&>(rInfo).CheckNode( *pNd );
+ }
+ return true;
+
+ case RES_CONTENT_VISIBLE:
+ {
+ SwFrame* pFrame = SwIterator<SwFrame,SwFormat>(*this).First();
+ // if the current section has no own frame search for the children
+ if(!pFrame)
+ {
+ SwIterator<SwSectionFormat,SwSectionFormat> aFormatIter(*this);
+ SwSectionFormat* pChild = aFormatIter.First();
+ while(pChild && !pFrame)
+ {
+ pFrame = SwIterator<SwFrame,SwFormat>(*pChild).First();
+ pChild = aFormatIter.Next();
+ }
+ }
+ static_cast<SwPtrMsgPoolItem&>(rInfo).pObject = pFrame;
+ }
+ return false;
+ }
+ return sw::BroadcastingModify::GetInfo( rInfo );
+}
+
+static bool lcl_SectionCmpPos( const SwSection *pFirst, const SwSection *pSecond)
+{
+ const SwSectionFormat* pFSectFormat = pFirst->GetFormat();
+ const SwSectionFormat* pSSectFormat = pSecond->GetFormat();
+ OSL_ENSURE( pFSectFormat && pSSectFormat &&
+ pFSectFormat->GetContent(false).GetContentIdx() &&
+ pSSectFormat->GetContent(false).GetContentIdx(),
+ "Invalid sections" );
+ return pFSectFormat->GetContent(false).GetContentIdx()->GetIndex() <
+ pSSectFormat->GetContent(false).GetContentIdx()->GetIndex();
+}
+
+// get all Sections that have been derived from this one
+void SwSectionFormat::GetChildSections( SwSections& rArr,
+ SectionSort eSort,
+ bool bAllSections ) const
+{
+ rArr.clear();
+
+ if( !HasWriterListeners() )
+ return;
+
+ SwIterator<SwSectionFormat,SwSectionFormat> aIter(*this);
+ const SwNodeIndex* pIdx;
+ for( SwSectionFormat* pLast = aIter.First(); pLast; pLast = aIter.Next() )
+ if( bAllSections ||
+ ( nullptr != ( pIdx = pLast->GetContent(false).
+ GetContentIdx()) && &pIdx->GetNodes() == &GetDoc()->GetNodes() ))
+ {
+ SwSection* pDummy = pLast->GetSection();
+ rArr.push_back( pDummy );
+ }
+
+ // Do we need any sorting?
+ if( 1 < rArr.size() )
+ switch( eSort )
+ {
+ case SectionSort::Pos:
+ std::sort( rArr.begin(), rArr.end(), lcl_SectionCmpPos );
+ break;
+ case SectionSort::Not: break;
+ }
+}
+
+// See whether the Section is within the Nodes or the UndoNodes array
+bool SwSectionFormat::IsInNodesArr() const
+{
+ const SwNodeIndex* pIdx = GetContent(false).GetContentIdx();
+ return pIdx && &pIdx->GetNodes() == &GetDoc()->GetNodes();
+}
+
+// Parent was changed
+void SwSectionFormat::UpdateParent()
+{
+ if(!HasWriterListeners())
+ return;
+
+ const SwSection* pSection = GetSection();
+ const SvxProtectItem* pProtect = &GetProtect();
+ // edit in readonly sections
+ const SwFormatEditInReadonly* pEditInReadonly = &GetEditInReadonly();
+ bool bIsHidden = pSection->IsHidden();
+ if(GetRegisteredIn())
+ {
+ const SwSection* pPS = GetParentSection();
+ pProtect = &pPS->GetFormat()->GetProtect();
+ pEditInReadonly = &pPS->GetFormat()->GetEditInReadonly();
+ bIsHidden = pPS->IsHiddenFlag();
+ }
+ if(!pProtect->IsContentProtected() != !pSection->IsProtectFlag())
+ CallSwClientNotify(sw::LegacyModifyHint(pProtect, pProtect));
+
+ // edit in readonly sections
+ if(!pEditInReadonly->GetValue() != !pSection->IsEditInReadonlyFlag())
+ CallSwClientNotify(sw::LegacyModifyHint(pEditInReadonly, pEditInReadonly));
+
+ if(bIsHidden == pSection->IsHiddenFlag())
+ {
+ SwMsgPoolItem aMsgItem(o3tl::narrowing<sal_uInt16>(bIsHidden
+ ? RES_SECTION_HIDDEN
+ : RES_SECTION_NOT_HIDDEN));
+ CallSwClientNotify(sw::LegacyModifyHint(&aMsgItem, &aMsgItem));
+ }
+}
+
+SwSectionNode* SwSectionFormat::GetSectionNode()
+{
+ const SwNodeIndex* pIdx = GetContent(false).GetContentIdx();
+ if( pIdx && ( &pIdx->GetNodes() == &GetDoc()->GetNodes() ))
+ return pIdx->GetNode().GetSectionNode();
+ return nullptr;
+}
+
+// Is this Section valid for the GlobalDocument?
+const SwSection* SwSectionFormat::GetGlobalDocSection() const
+{
+ const SwSectionNode* pNd = GetSectionNode();
+ if( pNd &&
+ ( SectionType::FileLink == pNd->GetSection().GetType() ||
+ SectionType::ToxContent == pNd->GetSection().GetType() ) &&
+ pNd->GetIndex() > pNd->GetNodes().GetEndOfExtras().GetIndex() &&
+ !pNd->StartOfSectionNode()->IsSectionNode() &&
+ !pNd->StartOfSectionNode()->FindSectionNode() )
+ return &pNd->GetSection();
+ return nullptr;
+}
+
+// sw::Metadatable
+::sfx2::IXmlIdRegistry& SwSectionFormat::GetRegistry()
+{
+ return GetDoc()->GetXmlIdRegistry();
+}
+
+bool SwSectionFormat::IsInClipboard() const
+{
+ return GetDoc()->IsClipBoard();
+}
+
+bool SwSectionFormat::IsInUndo() const
+{
+ return !IsInNodesArr();
+}
+
+bool SwSectionFormat::IsInContent() const
+{
+ SwNodeIndex const*const pIdx = GetContent(false).GetContentIdx();
+ OSL_ENSURE(pIdx, "SwSectionFormat::IsInContent: no index?");
+ return pIdx == nullptr || !GetDoc()->IsInHeaderFooter(*pIdx);
+}
+
+// n.b.: if the section format represents an index, then there is both a
+// SwXDocumentIndex and a SwXTextSection instance for this single core object.
+// these two can both implement XMetadatable and forward to the same core
+// section format. but here only one UNO object can be returned,
+// so always return the text section.
+uno::Reference< rdf::XMetadatable >
+SwSectionFormat::MakeUnoObject()
+{
+ uno::Reference<rdf::XMetadatable> xMeta;
+ SwSection *const pSection( GetSection() );
+ if (pSection)
+ {
+ xMeta.set( SwXTextSection::CreateXTextSection(this,
+ SectionType::ToxHeader == pSection->GetType()),
+ uno::UNO_QUERY );
+ }
+ return xMeta;
+}
+
+bool SwSectionFormat::supportsFullDrawingLayerFillAttributeSet() const
+{
+ return false;
+}
+
+void SwSectionFormat::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwSectionFormat"));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("name"), BAD_CAST(GetName().toUtf8().getStr()));
+ GetAttrSet().dumpAsXml(pWriter);
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+void SwSectionFormats::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwSectionFormats"));
+ for (size_t i = 0; i < size(); ++i)
+ GetFormat(i)->dumpAsXml(pWriter);
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+// Method to break section links inside a linked section
+static void lcl_BreakSectionLinksInSect( const SwSectionNode& rSectNd )
+{
+ if ( !rSectNd.GetSection().IsConnected() )
+ {
+ OSL_FAIL( "method <lcl_RemoveSectionLinksInSect(..)> - no Link at Section of SectionNode" );
+ return;
+ }
+ const ::sfx2::SvBaseLink* pOwnLink( &(rSectNd.GetSection().GetBaseLink() ) );
+ const ::sfx2::SvBaseLinks& rLnks = rSectNd.GetDoc().getIDocumentLinksAdministration().GetLinkManager().GetLinks();
+ for ( auto n = rLnks.size(); n > 0; )
+ {
+ SwIntrnlSectRefLink* pSectLnk = dynamic_cast<SwIntrnlSectRefLink*>(&(*rLnks[ --n ]));
+ if ( pSectLnk && pSectLnk != pOwnLink &&
+ pSectLnk->IsInRange( rSectNd.GetIndex(), rSectNd.EndOfSectionIndex() ) )
+ {
+ // break the link of the corresponding section.
+ // the link is also removed from the link manager
+ SwSectionNode* pSectNode = pSectLnk->GetSectNode();
+ assert(pSectNode);
+ pSectNode->GetSection().BreakLink();
+
+ // for robustness, because link is removed from the link manager
+ if ( n > rLnks.size() )
+ {
+ n = rLnks.size();
+ }
+ }
+ }
+}
+
+static void lcl_UpdateLinksInSect( const SwBaseLink& rUpdLnk, SwSectionNode& rSectNd )
+{
+ SwDoc& rDoc = rSectNd.GetDoc();
+ SwDocShell* pDShell = rDoc.GetDocShell();
+ if( !pDShell || !pDShell->GetMedium() )
+ return ;
+
+ const OUString sName( pDShell->GetMedium()->GetName() );
+ const OUString sMimeType( SotExchange::GetFormatMimeType( SotClipboardFormatId::SIMPLE_FILE ));
+ uno::Any aValue;
+ aValue <<= sName; // Arbitrary name
+
+ const ::sfx2::SvBaseLinks& rLnks = rDoc.getIDocumentLinksAdministration().GetLinkManager().GetLinks();
+ for( auto n = rLnks.size(); n; )
+ {
+ ::sfx2::SvBaseLink* pLnk = &(*rLnks[ --n ]);
+ if( pLnk == &rUpdLnk )
+ continue;
+ if( sfx2::SvBaseLinkObjectType::ClientFile != pLnk->GetObjType() )
+ continue;
+ SwBaseLink* pBLink = dynamic_cast<SwBaseLink*>( pLnk );
+ if( pBLink && pBLink->IsInRange( rSectNd.GetIndex(),
+ rSectNd.EndOfSectionIndex() ) )
+ {
+ // It's in the Section, so update. But only if it's not in the same File!
+ OUString sFName;
+ sfx2::LinkManager::GetDisplayNames( pBLink, nullptr, &sFName );
+ if( sFName != sName )
+ {
+ pBLink->DataChanged( sMimeType, aValue );
+
+ // If needed find the Link pointer to avoid skipping one or calling one twice
+ if( n >= rLnks.size() && 0 != ( n = rLnks.size() ))
+ --n;
+
+ if( n && pLnk != &(*rLnks[ n ]) )
+ {
+ // Find - it can only precede it!
+ while( n )
+ if( pLnk == &(*rLnks[ --n ] ) )
+ break;
+ }
+ }
+ }
+ }
+}
+
+::sfx2::SvBaseLink::UpdateResult SwIntrnlSectRefLink::DataChanged(
+ const OUString& rMimeType, const uno::Any & rValue )
+{
+ SwSectionNode* pSectNd = m_rSectFormat.GetSectionNode();
+ SwDoc* pDoc = m_rSectFormat.GetDoc();
+
+ SotClipboardFormatId nDataFormat = SotExchange::GetFormatIdFromMimeType( rMimeType );
+
+ if( !pSectNd || !pDoc || pDoc->IsInDtor() || ChkNoDataFlag() ||
+ sfx2::LinkManager::RegisterStatusInfoId() == nDataFormat )
+ {
+ // Should we be in the Undo already?
+ return SUCCESS;
+ }
+
+ // #i38810# - Due to possible existing signatures, the
+ // document has to be modified after updating a link.
+ pDoc->getIDocumentState().SetModified();
+ // set additional flag that links have been updated, in order to check this
+ // during load.
+ pDoc->getIDocumentLinksAdministration().SetLinksUpdated( true );
+
+ // Always switch off Undo
+ bool const bWasUndo = pDoc->GetIDocumentUndoRedo().DoesUndo();
+ pDoc->GetIDocumentUndoRedo().DoUndo(false);
+ bool bWasVisibleLinks = pDoc->getIDocumentLinksAdministration().IsVisibleLinks();
+ pDoc->getIDocumentLinksAdministration().SetVisibleLinks( false );
+
+ SwPaM* pPam;
+ SwViewShell* pVSh = pDoc->getIDocumentLayoutAccess().GetCurrentViewShell();
+ SwEditShell* pESh = pDoc->GetEditShell();
+ pDoc->getIDocumentFieldsAccess().LockExpFields();
+ {
+ // Insert an empty TextNode at the Section's start
+ SwNodeIndex aIdx( *pSectNd, +1 );
+ SwNodeIndex aEndIdx( *pSectNd->EndOfSectionNode() );
+ SwTextNode* pNewNd = pDoc->GetNodes().MakeTextNode( aIdx,
+ pDoc->getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_TEXT ) );
+
+ if( pESh )
+ pESh->StartAllAction();
+ else if( pVSh )
+ pVSh->StartAction();
+
+ SwPosition aPos( aIdx, SwIndex( pNewNd, 0 ));
+ --aPos.nNode;
+ SwDoc::CorrAbs( aIdx, aEndIdx, aPos, true );
+
+ pPam = new SwPaM( aPos );
+
+ // Delete everything succeeding it
+ --aIdx;
+ DelFlyInRange( aIdx, aEndIdx );
+ DelBookmarks(aIdx, aEndIdx);
+ ++aIdx;
+
+ pDoc->GetNodes().Delete( aIdx, aEndIdx.GetIndex() - aIdx.GetIndex() );
+ }
+
+ SwSection& rSection = pSectNd->GetSection();
+ rSection.SetConnectFlag(false);
+
+ Reader* pRead = nullptr;
+ switch( nDataFormat )
+ {
+ case SotClipboardFormatId::STRING:
+ pRead = ReadAscii;
+ break;
+
+ case SotClipboardFormatId::RICHTEXT:
+ case SotClipboardFormatId::RTF:
+ pRead = SwReaderWriter::GetRtfReader();
+ break;
+
+ case SotClipboardFormatId::SIMPLE_FILE:
+ if ( rValue.hasValue() )
+ {
+ OUString sFileName;
+ if ( !(rValue >>= sFileName) )
+ break;
+ OUString sFilter;
+ OUString sRange;
+ sfx2::LinkManager::GetDisplayNames( this, nullptr, &sFileName,
+ &sRange, &sFilter );
+
+ RedlineFlags eOldRedlineFlags = RedlineFlags::NONE;
+ SfxObjectShellRef xDocSh;
+ SfxObjectShellLock xLockRef;
+ int nRet;
+ if( sFileName.isEmpty() )
+ {
+ xDocSh = pDoc->GetDocShell();
+ nRet = 1;
+ }
+ else
+ {
+ nRet = SwFindDocShell( xDocSh, xLockRef, sFileName,
+ rSection.GetLinkFilePassword(),
+ sFilter, 0, pDoc->GetDocShell() );
+ if( nRet )
+ {
+ SwDoc* pSrcDoc = static_cast<SwDocShell*>( xDocSh.get() )->GetDoc();
+ eOldRedlineFlags = pSrcDoc->getIDocumentRedlineAccess().GetRedlineFlags();
+ pSrcDoc->getIDocumentRedlineAccess().SetRedlineFlags( RedlineFlags::ShowInsert );
+ }
+ }
+
+ if( nRet )
+ {
+ rSection.SetConnectFlag();
+
+ SwNodeIndex aSave( pPam->GetPoint()->nNode, -1 );
+ std::unique_ptr<SwNodeRange> pCpyRg;
+
+ if( xDocSh->GetMedium() &&
+ rSection.GetLinkFilePassword().isEmpty() )
+ {
+ if( const SfxStringItem* pItem = xDocSh->GetMedium()->GetItemSet()->
+ GetItemIfSet( SID_PASSWORD, false ) )
+ rSection.SetLinkFilePassword( pItem->GetValue() );
+ }
+
+ SwDoc* pSrcDoc = static_cast<SwDocShell*>( xDocSh.get() )->GetDoc();
+
+ if( !sRange.isEmpty() )
+ {
+ // Catch recursion
+ bool bRecursion = false;
+ if( pSrcDoc == pDoc )
+ {
+ tools::SvRef<SwServerObject> refObj( static_cast<SwServerObject*>(
+ pDoc->getIDocumentLinksAdministration().CreateLinkSource( sRange )));
+ if( refObj.is() )
+ {
+ bRecursion = refObj->IsLinkInServer( this ) ||
+ ChkNoDataFlag();
+ }
+ }
+
+ SwNodeIndex& rInsPos = pPam->GetPoint()->nNode;
+
+ SwPaM* pCpyPam = nullptr;
+ if( !bRecursion &&
+ pSrcDoc->GetDocumentLinksAdministrationManager().SelectServerObj( sRange, pCpyPam, pCpyRg )
+ && pCpyPam )
+ {
+ if( pSrcDoc != pDoc ||
+ pCpyPam->Start()->nNode > rInsPos ||
+ rInsPos >= pCpyPam->End()->nNode )
+ {
+ pSrcDoc->getIDocumentContentOperations().CopyRange(*pCpyPam, *pPam->GetPoint(), SwCopyFlags::CheckPosInFly);
+ }
+ delete pCpyPam;
+ }
+ if( pCpyRg && pSrcDoc == pDoc &&
+ pCpyRg->aStart < rInsPos && rInsPos < pCpyRg->aEnd )
+ {
+ pCpyRg.reset();
+ }
+ }
+ else if( pSrcDoc != pDoc )
+ pCpyRg.reset(new SwNodeRange( pSrcDoc->GetNodes().GetEndOfExtras(), SwNodeOffset(2),
+ pSrcDoc->GetNodes().GetEndOfContent() ));
+
+ // #i81653#
+ // Update links of extern linked document or extern linked
+ // document section, if section is protected.
+ if ( pSrcDoc != pDoc &&
+ rSection.IsProtectFlag() )
+ {
+ pSrcDoc->getIDocumentLinksAdministration().GetLinkManager().UpdateAllLinks( false, false, nullptr );
+ }
+
+ if( pCpyRg )
+ {
+ SwNodeIndex& rInsPos = pPam->GetPoint()->nNode;
+ bool bCreateFrame = rInsPos.GetIndex() <=
+ pDoc->GetNodes().GetEndOfExtras().GetIndex() ||
+ rInsPos.GetNode().FindTableNode();
+
+ SwTableNumFormatMerge aTNFM( *pSrcDoc, *pDoc );
+
+ pSrcDoc->GetDocumentContentOperationsManager().CopyWithFlyInFly(*pCpyRg, rInsPos, nullptr, bCreateFrame);
+ ++aSave;
+
+ if( !bCreateFrame )
+ ::MakeFrames( pDoc, aSave, rInsPos );
+
+ // Delete last Node, only if it was copied successfully
+ // (the Section contains more than one Node)
+ if( SwNodeOffset(2) < pSectNd->EndOfSectionIndex() - pSectNd->GetIndex() )
+ {
+ aSave = rInsPos;
+ pPam->Move( fnMoveBackward, GoInNode );
+ pPam->SetMark(); // Rewire both SwPositions
+
+ pDoc->CorrAbs( aSave, *pPam->GetPoint(), 0, true );
+ pDoc->GetNodes().Delete( aSave );
+ }
+ pCpyRg.reset();
+ }
+
+ lcl_BreakSectionLinksInSect( *pSectNd );
+
+ // Update all Links in this Section
+ lcl_UpdateLinksInSect( *this, *pSectNd );
+ }
+ if( xDocSh.is() )
+ {
+ if( 2 == nRet )
+ xDocSh->DoClose();
+ else if( static_cast<SwDocShell*>( xDocSh.get() )->GetDoc() )
+ static_cast<SwDocShell*>( xDocSh.get() )->GetDoc()->getIDocumentRedlineAccess().SetRedlineFlags(
+ eOldRedlineFlags );
+ }
+ }
+ break;
+ default: break;
+ }
+
+ // Only create DDE if Shell is available!
+ uno::Sequence< sal_Int8 > aSeq;
+ if( pRead && rValue.hasValue() && ( rValue >>= aSeq ) )
+ {
+ if( pESh )
+ {
+ pESh->Push();
+ SwPaM* pCursor = pESh->GetCursor();
+ *pCursor->GetPoint() = *pPam->GetPoint();
+ delete pPam;
+ pPam = pCursor;
+ }
+
+ SvMemoryStream aStrm( const_cast<sal_Int8 *>(aSeq.getConstArray()), aSeq.getLength(),
+ StreamMode::READ );
+ aStrm.Seek( 0 );
+
+ // TODO/MBA: it's impossible to set a BaseURL here!
+ SwReader aTmpReader( aStrm, OUString(), pDoc->GetDocShell()->GetMedium()->GetBaseURL(), *pPam );
+
+ if( ! aTmpReader.Read( *pRead ).IsError() )
+ {
+ rSection.SetConnectFlag();
+ }
+
+ if( pESh )
+ {
+ pESh->Pop(SwCursorShell::PopMode::DeleteCurrent);
+ pPam = nullptr; // pam was deleted earlier
+ }
+ }
+
+ // remove all undo actions and turn undo on again
+ pDoc->GetIDocumentUndoRedo().DelAllUndoObj();
+ pDoc->GetIDocumentUndoRedo().DoUndo(bWasUndo);
+ pDoc->getIDocumentLinksAdministration().SetVisibleLinks( bWasVisibleLinks );
+
+ pDoc->getIDocumentFieldsAccess().UnlockExpFields();
+ if( !pDoc->getIDocumentFieldsAccess().IsExpFieldsLocked() )
+ pDoc->getIDocumentFieldsAccess().UpdateExpFields(nullptr, true);
+
+ if( pESh )
+ pESh->EndAllAction();
+ else if( pVSh )
+ pVSh->EndAction();
+ delete pPam; // Was created at the start
+
+ return SUCCESS;
+}
+
+void SwIntrnlSectRefLink::Closed()
+{
+ SwDoc* pDoc = m_rSectFormat.GetDoc();
+ if( pDoc && !pDoc->IsInDtor() )
+ {
+ // Advise says goodbye: mark the Section as not protected
+ // and change the Flag
+ const SwSectionFormats& rFormats = pDoc->GetSections();
+ for( auto n = rFormats.size(); n; )
+ if (rFormats[--n] == &m_rSectFormat)
+ {
+ SwViewShell* pSh = pDoc->getIDocumentLayoutAccess().GetCurrentViewShell();
+ SwEditShell* pESh = pDoc->GetEditShell();
+
+ if( pESh )
+ pESh->StartAllAction();
+ else
+ pSh->StartAction();
+
+ SwSectionData aSectionData(*m_rSectFormat.GetSection());
+ aSectionData.SetType( SectionType::Content );
+ aSectionData.SetLinkFileName( OUString() );
+ aSectionData.SetProtectFlag( false );
+ // edit in readonly sections
+ aSectionData.SetEditInReadonlyFlag( false );
+
+ aSectionData.SetConnectFlag( false );
+
+ pDoc->UpdateSection( n, aSectionData );
+
+ // Make all Links within the Section visible again
+ SwSectionNode* pSectNd = m_rSectFormat.GetSectionNode();
+ if( pSectNd )
+ SwSection::MakeChildLinksVisible( *pSectNd );
+
+ if( pESh )
+ pESh->EndAllAction();
+ else
+ pSh->EndAction();
+ break;
+ }
+ }
+ SvBaseLink::Closed();
+}
+
+void SwSection::CreateLink( LinkCreateType eCreateType )
+{
+ SwSectionFormat* pFormat = GetFormat();
+ OSL_ENSURE(pFormat, "SwSection::CreateLink: no format?");
+ if (!pFormat || (SectionType::Content == m_Data.GetType()))
+ return ;
+
+ SfxLinkUpdateMode nUpdateType = SfxLinkUpdateMode::ALWAYS;
+
+ if (!m_RefLink.is())
+ {
+ // create BaseLink
+ m_RefLink = new SwIntrnlSectRefLink( *pFormat, nUpdateType );
+ }
+ else
+ {
+ pFormat->GetDoc()->getIDocumentLinksAdministration().GetLinkManager().Remove( m_RefLink.get() );
+ }
+
+ SwIntrnlSectRefLink *const pLnk =
+ static_cast<SwIntrnlSectRefLink*>( m_RefLink.get() );
+
+ const OUString sCmd(m_Data.GetLinkFileName());
+ pLnk->SetUpdateMode( nUpdateType );
+ pLnk->SetVisible( pFormat->GetDoc()->getIDocumentLinksAdministration().IsVisibleLinks() );
+
+ switch (m_Data.GetType())
+ {
+ case SectionType::DdeLink:
+ pLnk->SetLinkSourceName( sCmd );
+ pFormat->GetDoc()->getIDocumentLinksAdministration().GetLinkManager().InsertDDELink( pLnk );
+ break;
+ case SectionType::FileLink:
+ {
+ pLnk->SetContentType( SotClipboardFormatId::SIMPLE_FILE );
+ sal_Int32 nIndex = 0;
+ const OUString sFile(sCmd.getToken( 0, sfx2::cTokenSeparator, nIndex ));
+ const OUString sFltr(sCmd.getToken( 0, sfx2::cTokenSeparator, nIndex ));
+ const OUString sRange(sCmd.getToken( 0, sfx2::cTokenSeparator, nIndex ));
+ pFormat->GetDoc()->getIDocumentLinksAdministration().GetLinkManager().InsertFileLink( *pLnk,
+ static_cast<sfx2::SvBaseLinkObjectType>(m_Data.GetType()),
+ sFile,
+ ( !sFltr.isEmpty() ? &sFltr : nullptr ),
+ ( !sRange.isEmpty() ? &sRange : nullptr ) );
+ }
+ break;
+ default:
+ OSL_ENSURE( false, "What kind of Link is this?" );
+ }
+
+ switch( eCreateType )
+ {
+ case LinkCreateType::Connect: // Connect Link right away
+ pLnk->Connect();
+ break;
+
+ case LinkCreateType::Update: // Connect Link and update
+ pLnk->Update();
+ break;
+ case LinkCreateType::NONE: break;
+ }
+}
+
+void SwSection::BreakLink()
+{
+ const SectionType eCurrentType( GetType() );
+ if ( eCurrentType == SectionType::Content ||
+ eCurrentType == SectionType::ToxHeader ||
+ eCurrentType == SectionType::ToxContent )
+ {
+ // nothing to do
+ return;
+ }
+
+ // Release link, if it exists
+ if (m_RefLink.is())
+ {
+ SwSectionFormat *const pFormat( GetFormat() );
+ OSL_ENSURE(pFormat, "SwSection::BreakLink: no format?");
+ if (pFormat)
+ {
+ pFormat->GetDoc()->getIDocumentLinksAdministration().GetLinkManager().Remove( m_RefLink.get() );
+ }
+ m_RefLink.clear();
+ }
+ // change type
+ SetType( SectionType::Content );
+ // reset linked file data
+ SetLinkFileName( OUString() );
+ SetLinkFilePassword( OUString() );
+}
+
+void SwSection::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwSection"));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("registered-in"), "%p",
+ GetRegisteredIn());
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+const SwNode* SwIntrnlSectRefLink::GetAnchor() const { return m_rSectFormat.GetSectionNode(); }
+
+bool SwIntrnlSectRefLink::IsInRange( SwNodeOffset nSttNd, SwNodeOffset nEndNd ) const
+{
+ SwStartNode* pSttNd = m_rSectFormat.GetSectionNode();
+ return pSttNd &&
+ nSttNd < pSttNd->GetIndex() &&
+ pSttNd->EndOfSectionIndex() < nEndNd;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/docnode/swbaslnk.cxx b/sw/source/core/docnode/swbaslnk.cxx
new file mode 100644
index 000000000..5a5967c87
--- /dev/null
+++ b/sw/source/core/docnode/swbaslnk.cxx
@@ -0,0 +1,326 @@
+/* -*- 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 <vcl/svapp.hxx>
+
+#include <osl/diagnose.h>
+#include <sfx2/lnkbase.hxx>
+#include <editeng/boxitem.hxx>
+#include <sfx2/linkmgr.hxx>
+#include <sfx2/event.hxx>
+#include <sot/exchange.hxx>
+#include <fmtfsize.hxx>
+#include <fmtanchr.hxx>
+#include <frmatr.hxx>
+#include <frmfmt.hxx>
+#include <doc.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <IDocumentLinksAdministration.hxx>
+#include <pam.hxx>
+#include <swtable.hxx>
+#include <swevent.hxx>
+#include <swbaslnk.hxx>
+#include <swserv.hxx>
+#include <viewsh.hxx>
+#include <ndgrf.hxx>
+#include <htmltbl.hxx>
+#include <dialoghelp.hxx>
+#include <memory>
+
+using namespace com::sun::star;
+
+static bool SetGrfFlySize( const Size& rGrfSz, SwGrfNode* pGrfNd, const Size &rOrigGrfSize );
+
+
+::sfx2::SvBaseLink::UpdateResult SwBaseLink::DataChanged(
+ const OUString& rMimeType, const uno::Any & rValue )
+{
+ if( !m_pContentNode )
+ {
+ OSL_ENSURE(false, "DataChanged without ContentNode" );
+ return ERROR_GENERAL;
+ }
+
+ SwDoc& rDoc = m_pContentNode->GetDoc();
+ if( rDoc.IsInDtor() || ChkNoDataFlag() )
+ {
+ return SUCCESS;
+ }
+
+ SotClipboardFormatId nFormat = SotExchange::GetFormatIdFromMimeType( rMimeType );
+
+ if( m_pContentNode->IsNoTextNode() &&
+ nFormat == sfx2::LinkManager::RegisterStatusInfoId() )
+ {
+ // Only a status change - serve Events?
+ OUString sState;
+
+ if( rValue.hasValue() && ( rValue >>= sState ))
+ {
+ SvMacroItemId nEvent = SvMacroItemId::NONE;
+ switch( sState.toInt32() )
+ {
+ case sfx2::LinkManager::STATE_LOAD_OK: nEvent = SvMacroItemId::OnImageLoadDone; break;
+ case sfx2::LinkManager::STATE_LOAD_ERROR: nEvent = SvMacroItemId::OnImageLoadError; break;
+ case sfx2::LinkManager::STATE_LOAD_ABORT: nEvent = SvMacroItemId::OnImageLoadCancel; break;
+ }
+
+ SwFrameFormat* pFormat;
+ if( nEvent != SvMacroItemId::NONE && nullptr != ( pFormat = m_pContentNode->GetFlyFormat() ))
+ {
+ SwCallMouseEvent aCallEvent;
+ aCallEvent.Set( EVENT_OBJECT_IMAGE, pFormat );
+ rDoc.CallEvent( nEvent, aCallEvent );
+ }
+ }
+ return SUCCESS; // That's it!
+ }
+
+ bool bUpdate = false;
+ bool bFrameInPaint = false;
+ Size aGrfSz, aOldSz;
+
+ SwGrfNode* pSwGrfNode = nullptr;
+
+ if (m_pContentNode->IsGrfNode())
+ {
+ pSwGrfNode = m_pContentNode->GetGrfNode();
+ assert(pSwGrfNode && "Error, pSwGrfNode expected when node answers IsGrfNode() with true (!)");
+ aOldSz = pSwGrfNode->GetTwipSize();
+ const GraphicObject& rGrfObj = pSwGrfNode->GetGrfObj();
+
+ bFrameInPaint = pSwGrfNode->IsFrameInPaint();
+
+ Graphic aGrf;
+
+ // tdf#124698 if any auth dialog is needed, find what the parent window should be
+ weld::Window* pDlgParent = GetFrameWeld(&rDoc);
+
+ if (rDoc.getIDocumentLinksAdministration().GetLinkManager().GetGraphicFromAny(rMimeType, rValue, aGrf, pDlgParent) &&
+ ( GraphicType::Default != aGrf.GetType() ||
+ GraphicType::Default != rGrfObj.GetType() ) )
+ {
+ aGrfSz = ::GetGraphicSizeTwip( aGrf, nullptr );
+
+ pSwGrfNode->SetGraphic(aGrf);
+ bUpdate = true;
+
+ // Always use the correct graphic size
+ if( aGrfSz.Height() && aGrfSz.Width() &&
+ aOldSz.Height() && aOldSz.Width() &&
+ aGrfSz != aOldSz )
+ {
+ pSwGrfNode->SetTwipSize(aGrfSz);
+ aOldSz = aGrfSz;
+ }
+ }
+ }
+ else if( m_pContentNode->IsOLENode() )
+ bUpdate = true;
+
+ if ( !bUpdate || bFrameInPaint )
+ return SUCCESS;
+
+ if(pSwGrfNode && !SetGrfFlySize(aGrfSz, pSwGrfNode, aOldSz))
+ pSwGrfNode->TriggerGraphicArrived();
+
+ return SUCCESS;
+}
+
+static bool SetGrfFlySize( const Size& rGrfSz, SwGrfNode* pGrfNd, const Size& rOrigGrfSize )
+{
+ bool bRet = false;
+ SwViewShell *pSh = pGrfNd->GetDoc().getIDocumentLayoutAccess().GetCurrentViewShell();
+ std::unique_ptr<CurrShell> pCurr;
+ if ( pGrfNd->GetDoc().GetEditShell() )
+ pCurr.reset(new CurrShell( pSh ));
+
+ Size aSz = rOrigGrfSize;
+ if ( !(aSz.Width() && aSz.Height()) &&
+ rGrfSz.Width() && rGrfSz.Height() )
+ {
+ SwFrameFormat* pFormat = nullptr;
+ if (pGrfNd->IsChgTwipSize())
+ pFormat = pGrfNd->GetFlyFormat();
+ if (nullptr != pFormat)
+ {
+ Size aCalcSz( aSz );
+ if ( !aSz.Height() && aSz.Width() )
+ // Calculate the right height
+ aCalcSz.setHeight( rGrfSz.Height() *
+ aSz.Width() / rGrfSz.Width() );
+ else if ( !aSz.Width() && aSz.Height() )
+ // Calculate the right width
+ aCalcSz.setWidth( rGrfSz.Width() *
+ aSz.Height() / rGrfSz.Height() );
+ else
+ // Take over height and width
+ aCalcSz = rGrfSz;
+
+ const SvxBoxItem &rBox = pFormat->GetBox();
+ aCalcSz.AdjustWidth(rBox.CalcLineSpace(SvxBoxItemLine::LEFT) +
+ rBox.CalcLineSpace(SvxBoxItemLine::RIGHT) );
+ aCalcSz.AdjustHeight(rBox.CalcLineSpace(SvxBoxItemLine::TOP) +
+ rBox.CalcLineSpace(SvxBoxItemLine::BOTTOM) );
+ const SwFormatFrameSize& rOldAttr = pFormat->GetFrameSize();
+ if( rOldAttr.GetSize() != aCalcSz )
+ {
+ SwFormatFrameSize aAttr( rOldAttr );
+ aAttr.SetSize( aCalcSz );
+ pFormat->SetFormatAttr( aAttr );
+ bRet = true;
+ }
+
+ if( !aSz.Width() )
+ {
+ // If the graphic is anchored in a table, we need to recalculate
+ // the table rows
+ const SwDoc& rDoc = pGrfNd->GetDoc();
+ const SwPosition* pAPos = pFormat->GetAnchor().GetContentAnchor();
+ SwTableNode *pTableNd;
+ if (pAPos && nullptr != (pTableNd = pAPos->nNode.GetNode().FindTableNode()))
+ {
+ const bool bLastGrf = !pTableNd->GetTable().DecGrfsThatResize();
+ SwHTMLTableLayout *pLayout =
+ pTableNd->GetTable().GetHTMLTableLayout();
+ if( pLayout )
+ {
+ const sal_uInt16 nBrowseWidth =
+ pLayout->GetBrowseWidthByTable( rDoc );
+ if ( nBrowseWidth )
+ {
+ pLayout->Resize( nBrowseWidth, true, true,
+ bLastGrf ? HTMLTABLE_RESIZE_NOW
+ : 500 );
+ }
+ }
+ }
+ }
+ }
+
+ // SetTwipSize rescales an ImageMap if needed for which
+ // it requires the Frame Format
+ pGrfNd->SetTwipSize( rGrfSz );
+ }
+
+ return bRet;
+}
+
+bool SwBaseLink::SwapIn( bool bWaitForData, bool bNativFormat )
+{
+ if( !GetObj() && ( bNativFormat || ( !IsSynchron() && bWaitForData ) ))
+ {
+ AddNextRef();
+ GetRealObject_();
+ ReleaseRef();
+ }
+
+ bool bRes = false;
+
+ if( GetObj() )
+ {
+ OUString aMimeType( SotExchange::GetFormatMimeType( GetContentType() ));
+ uno::Any aValue;
+ (void)GetObj()->GetData( aValue, aMimeType, !IsSynchron() && bWaitForData );
+
+ if( bWaitForData && !GetObj() )
+ {
+ OSL_ENSURE( false, "The SvxFileObject was deleted in a GetData!" );
+ }
+ else
+ {
+ bRes = aValue.hasValue();
+ if ( bRes )
+ {
+ DataChanged( aMimeType, aValue );
+ }
+ }
+ }
+ else if( !IsSynchron() && bWaitForData )
+ {
+ SetSynchron( true );
+ bRes = Update();
+ SetSynchron( false );
+ }
+ else
+ bRes = Update();
+
+ return bRes;
+}
+
+void SwBaseLink::Closed()
+{
+ if( m_pContentNode && !m_pContentNode->GetDoc().IsInDtor() )
+ {
+ // Delete the connection
+ if( m_pContentNode->IsGrfNode() )
+ static_cast<SwGrfNode*>(m_pContentNode)->ReleaseLink();
+ }
+ SvBaseLink::Closed();
+}
+
+const SwNode* SwBaseLink::GetAnchor() const
+{
+ if (m_pContentNode)
+ {
+ SwFrameFormat *const pFormat = m_pContentNode->GetFlyFormat();
+ if (pFormat)
+ {
+ const SwFormatAnchor& rAnchor = pFormat->GetAnchor();
+ SwPosition const*const pAPos = rAnchor.GetContentAnchor();
+ if (pAPos &&
+ ((RndStdIds::FLY_AS_CHAR == rAnchor.GetAnchorId()) ||
+ (RndStdIds::FLY_AT_CHAR == rAnchor.GetAnchorId()) ||
+ (RndStdIds::FLY_AT_FLY == rAnchor.GetAnchorId()) ||
+ (RndStdIds::FLY_AT_PARA == rAnchor.GetAnchorId())))
+ {
+ return &pAPos->nNode.GetNode();
+ }
+ return nullptr;
+ }
+ }
+
+ OSL_ENSURE( false, "GetAnchor is not shadowed" );
+ return nullptr;
+}
+
+bool SwBaseLink::IsRecursion( const SwBaseLink* pChkLnk ) const
+{
+ tools::SvRef<SwServerObject> aRef( static_cast<SwServerObject*>(GetObj()) );
+ if( aRef.is() )
+ {
+ // As it's a ServerObject, we query all contained Links
+ // if we are contained in them. Else we have a recursion.
+ return aRef->IsLinkInServer( pChkLnk );
+ }
+ return false;
+}
+
+bool SwBaseLink::IsInRange( SwNodeOffset, SwNodeOffset ) const
+{
+ // Not Graphic or OLE Links
+ // Fields or Sections have their own derivation!
+ return false;
+}
+
+SwBaseLink::~SwBaseLink()
+{
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/docnode/swthreadjoiner.cxx b/sw/source/core/docnode/swthreadjoiner.cxx
new file mode 100644
index 000000000..6ff198d7d
--- /dev/null
+++ b/sw/source/core/docnode/swthreadjoiner.cxx
@@ -0,0 +1,49 @@
+/* -*- 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 <swthreadjoiner.hxx>
+#include <com/sun/star/util/JobManager.hpp>
+#include <comphelper/processfactory.hxx>
+#include <mutex>
+
+// Testing
+
+using namespace ::com::sun::star;
+
+namespace
+{
+uno::Reference<util::XJobManager> pThreadJoiner;
+}
+
+uno::Reference<util::XJobManager>& SwThreadJoiner::GetThreadJoiner()
+{
+ static std::mutex theJoinerMutex;
+ std::unique_lock aGuard(theJoinerMutex);
+
+ if (!pThreadJoiner.is())
+ {
+ pThreadJoiner = util::JobManager::create(comphelper::getProcessComponentContext());
+ }
+
+ return pThreadJoiner;
+}
+
+void SwThreadJoiner::ReleaseThreadJoiner() { pThreadJoiner.clear(); }
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/docnode/swthreadmanager.cxx b/sw/source/core/docnode/swthreadmanager.cxx
new file mode 100644
index 000000000..3c81ff570
--- /dev/null
+++ b/sw/source/core/docnode/swthreadmanager.cxx
@@ -0,0 +1,78 @@
+/* -*- 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 <swthreadmanager.hxx>
+#include <swthreadjoiner.hxx>
+#include <observablethread.hxx>
+#include "threadmanager.hxx"
+
+/** class to manage threads in Writer - it conforms the singleton pattern
+
+ #i73788#
+*/
+bool SwThreadManager::sbThreadManagerInstantiated = false;
+
+SwThreadManager::SwThreadManager()
+ : mpThreadManagerImpl( new ThreadManager( SwThreadJoiner::GetThreadJoiner() ) )
+{
+ mpThreadManagerImpl->Init();
+ sbThreadManagerInstantiated = true;
+}
+
+SwThreadManager::~SwThreadManager()
+{
+}
+
+SwThreadManager& SwThreadManager::GetThreadManager()
+{
+ static SwThreadManager gThreadManager;
+ return gThreadManager;
+}
+
+bool SwThreadManager::ExistsThreadManager()
+{
+ return sbThreadManagerInstantiated;
+}
+
+oslInterlockedCount SwThreadManager::AddThread( const rtl::Reference< ObservableThread >& rThread )
+{
+ return mpThreadManagerImpl->AddThread( rThread );
+}
+
+void SwThreadManager::RemoveThread( const oslInterlockedCount nThreadID )
+{
+ mpThreadManagerImpl->RemoveThread( nThreadID );
+}
+
+void SwThreadManager::SuspendStartingOfThreads()
+{
+ mpThreadManagerImpl->SuspendStartingOfThreads();
+}
+
+void SwThreadManager::ResumeStartingOfThreads()
+{
+ mpThreadManagerImpl->ResumeStartingOfThreads();
+}
+
+bool SwThreadManager::StartingOfThreadsSuspended()
+{
+ return mpThreadManagerImpl->StartingOfThreadsSuspended();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/docnode/threadlistener.cxx b/sw/source/core/docnode/threadlistener.cxx
new file mode 100644
index 000000000..cba118e46
--- /dev/null
+++ b/sw/source/core/docnode/threadlistener.cxx
@@ -0,0 +1,47 @@
+/* -*- 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 <threadlistener.hxx>
+#include "threadmanager.hxx"
+
+/** helper class to observe threads
+
+ #i73788#
+*/
+ThreadListener::ThreadListener( ThreadManager& rThreadListenerOwner )
+ : mrThreadListenerOwner( rThreadListenerOwner )
+{
+}
+
+ThreadListener::~ThreadListener()
+{
+}
+
+void ThreadListener::ListenToThread( const oslInterlockedCount nThreadID,
+ ObservableThread& rThread )
+{
+ rThread.SetListener( mrThreadListenerOwner.GetThreadListenerWeakRef(),
+ nThreadID );
+}
+
+void ThreadListener::NotifyAboutFinishedThread( const oslInterlockedCount nThreadID )
+{
+ mrThreadListenerOwner.NotifyAboutFinishedThread( nThreadID );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/docnode/threadmanager.cxx b/sw/source/core/docnode/threadmanager.cxx
new file mode 100644
index 000000000..20d71e746
--- /dev/null
+++ b/sw/source/core/docnode/threadmanager.cxx
@@ -0,0 +1,250 @@
+/* -*- 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 "cancellablejob.hxx"
+#include "threadmanager.hxx"
+#include <threadlistener.hxx>
+
+#include <osl/diagnose.h>
+
+#include <algorithm>
+
+#include <com/sun/star/util/XJobManager.hpp>
+
+using namespace ::com::sun::star;
+
+/** class to manage threads
+
+ #i73788#
+*/
+const std::deque< ThreadManager::tThreadData >::size_type ThreadManager::snStartedSize = 10;
+
+ThreadManager::ThreadManager( uno::Reference< util::XJobManager > const & rThreadJoiner )
+ : mrThreadJoiner( rThreadJoiner ),
+ mnThreadIDCounter( 0 ),
+ maStartNewThreadIdle("SW ThreadManager StartNewThreadIdle"),
+ mbStartingOfThreadsSuspended( false )
+{
+}
+
+void ThreadManager::Init()
+{
+ mpThreadListener = std::make_shared<ThreadListener>( *this );
+
+ maStartNewThreadIdle.SetPriority( TaskPriority::LOWEST );
+ maStartNewThreadIdle.SetInvokeHandler( LINK( this, ThreadManager, TryToStartNewThread ) );
+}
+
+ThreadManager::~ThreadManager()
+{
+ maWaitingForStartThreads.clear();
+ maStartedThreads.clear();
+}
+
+std::weak_ptr< IFinishedThreadListener > ThreadManager::GetThreadListenerWeakRef() const
+{
+ return mpThreadListener;
+}
+
+void ThreadManager::NotifyAboutFinishedThread( const oslInterlockedCount nThreadID )
+{
+ RemoveThread( nThreadID, true );
+}
+
+oslInterlockedCount ThreadManager::AddThread(
+ const rtl::Reference< ObservableThread >& rThread )
+
+{
+ std::unique_lock aGuard(maMutex);
+
+ // create new thread
+ tThreadData aThreadData;
+ oslInterlockedCount nNewThreadID( osl_atomic_increment( &mnThreadIDCounter ) );
+ {
+ aThreadData.nThreadID = nNewThreadID;
+
+ aThreadData.pThread = rThread;
+ aThreadData.aJob = new CancellableJob( aThreadData.pThread );
+
+ aThreadData.pThread->setPriority( osl_Thread_PriorityBelowNormal );
+ mpThreadListener->ListenToThread( aThreadData.nThreadID,
+ *(aThreadData.pThread) );
+ }
+
+ // add thread to manager
+ if ( maStartedThreads.size() < snStartedSize &&
+ !mbStartingOfThreadsSuspended )
+ {
+ // Try to start thread
+ if ( !StartThread( aThreadData ) )
+ {
+ // No success on starting thread
+ // If no more started threads exist, but still threads are waiting,
+ // setup Timer to start thread from waiting ones
+ if ( maStartedThreads.empty() && !maWaitingForStartThreads.empty() )
+ {
+ maStartNewThreadIdle.Start();
+ }
+ }
+ }
+ else
+ {
+ // Thread will be started later
+ maWaitingForStartThreads.push_back( aThreadData );
+ }
+
+ return nNewThreadID;
+}
+
+void ThreadManager::RemoveThread( const oslInterlockedCount nThreadID,
+ const bool bThreadFinished )
+{
+ // --> SAFE ----
+ std::unique_lock aGuard(maMutex);
+
+ std::deque< tThreadData >::iterator aIter =
+ std::find_if( maStartedThreads.begin(), maStartedThreads.end(),
+ ThreadPred( nThreadID ) );
+
+ if ( aIter != maStartedThreads.end() )
+ {
+ tThreadData aTmpThreadData( *aIter );
+
+ maStartedThreads.erase( aIter );
+
+ if ( bThreadFinished )
+ {
+ // release thread as job from thread joiner instance
+ css::uno::Reference< css::util::XJobManager > rThreadJoiner( mrThreadJoiner );
+ if ( rThreadJoiner.is() )
+ {
+ rThreadJoiner->releaseJob( aTmpThreadData.aJob );
+ }
+ else
+ {
+ OSL_FAIL( "<ThreadManager::RemoveThread(..)> - ThreadJoiner already gone!" );
+ }
+ }
+
+ // Try to start thread from waiting ones
+ aGuard.unlock();
+ TryToStartNewThread( nullptr );
+ }
+ else
+ {
+ aIter = std::find_if( maWaitingForStartThreads.begin(),
+ maWaitingForStartThreads.end(), ThreadPred( nThreadID ) );
+
+ if ( aIter != maWaitingForStartThreads.end() )
+ {
+ maWaitingForStartThreads.erase( aIter );
+ }
+ }
+ // <-- SAFE ----
+}
+
+bool ThreadManager::StartWaitingThread()
+{
+ if ( !maWaitingForStartThreads.empty() )
+ {
+ tThreadData aThreadData( maWaitingForStartThreads.front() );
+ maWaitingForStartThreads.pop_front();
+ return StartThread( aThreadData );
+ }
+ else
+ {
+ return false;
+ }
+}
+
+bool ThreadManager::StartThread( const tThreadData& rThreadData )
+{
+ bool bThreadStarted( false );
+
+ if ( rThreadData.pThread->create() )
+ {
+ // start of thread successful.
+ bThreadStarted = true;
+
+ maStartedThreads.push_back( rThreadData );
+
+ // register thread as job at thread joiner instance
+ css::uno::Reference< css::util::XJobManager > rThreadJoiner( mrThreadJoiner );
+ if ( rThreadJoiner.is() )
+ {
+ rThreadJoiner->registerJob( rThreadData.aJob );
+ }
+ else
+ {
+ OSL_FAIL( "<ThreadManager::StartThread(..)> - ThreadJoiner already gone!" );
+ }
+ }
+ else
+ {
+ // thread couldn't be started.
+ maWaitingForStartThreads.push_front( rThreadData );
+ }
+
+ return bThreadStarted;
+}
+
+IMPL_LINK_NOARG(ThreadManager, TryToStartNewThread, Timer *, void)
+{
+ std::unique_lock aGuard(maMutex);
+
+ if ( mbStartingOfThreadsSuspended )
+ return;
+
+ // Try to start thread from waiting ones
+ if ( !StartWaitingThread() )
+ {
+ // No success on starting thread
+ // If no more started threads exist, but still threads are waiting,
+ // setup Timer to start thread from waiting ones
+ if ( maStartedThreads.empty() && !maWaitingForStartThreads.empty() )
+ {
+ maStartNewThreadIdle.Start();
+ }
+ }
+}
+
+void ThreadManager::ResumeStartingOfThreads()
+{
+ std::unique_lock aGuard(maMutex);
+
+ mbStartingOfThreadsSuspended = false;
+
+ while ( maStartedThreads.size() < snStartedSize &&
+ !maWaitingForStartThreads.empty() )
+ {
+ if ( !StartWaitingThread() )
+ {
+ // No success on starting thread
+ // If no more started threads exist, but still threads are waiting,
+ // setup Timer to start thread from waiting ones
+ if ( maStartedThreads.empty() && !maWaitingForStartThreads.empty() )
+ {
+ maStartNewThreadIdle.Start();
+ break;
+ }
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/docnode/threadmanager.hxx b/sw/source/core/docnode/threadmanager.hxx
new file mode 100644
index 000000000..ace0bc031
--- /dev/null
+++ b/sw/source/core/docnode/threadmanager.hxx
@@ -0,0 +1,147 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <vcl/idle.hxx>
+#include <mutex>
+#include <osl/interlck.h>
+#include <rtl/ref.hxx>
+
+#include <deque>
+#include <cppuhelper/weakref.hxx>
+#include <observablethread.hxx>
+
+#include <memory>
+
+namespace com::sun::star::util { class XCancellable; }
+namespace com::sun::star::util { class XJobManager; }
+
+class IFinishedThreadListener;
+class ThreadListener;
+class Timer;
+
+/** class to manage threads
+
+ OD 2007-01-29 #i73788#
+ An instance of this class takes care of the starting of threads.
+ It assures that not more than <mnStartedSize> threads
+ are started.
+*/
+class ThreadManager final
+{
+ public:
+
+ explicit ThreadManager( css::uno::Reference< css::util::XJobManager > const & rThreadJoiner );
+ ~ThreadManager();
+
+ std::weak_ptr< IFinishedThreadListener > GetThreadListenerWeakRef() const;
+ void NotifyAboutFinishedThread( const oslInterlockedCount nThreadID );
+
+ /** initialization
+
+ IMPORTANT NOTE: Needs to be called directly after construction
+ */
+ void Init();
+
+ /** add thread to the thread manager and taking ownership for the thread
+
+ @return unique ID for added thread
+ */
+ oslInterlockedCount AddThread(
+ const ::rtl::Reference< ObservableThread >& rThread );
+
+ void RemoveThread( const oslInterlockedCount nThreadID,
+ const bool bThreadFinished = false );
+
+ DECL_LINK( TryToStartNewThread, Timer*, void );
+
+ /** suspend the starting of threads
+
+ Suspending the starting of further threads is sensible during the
+ destruction of a Writer document.
+ */
+ void SuspendStartingOfThreads()
+ {
+ std::unique_lock aGuard(maMutex);
+
+ mbStartingOfThreadsSuspended = true;
+ }
+
+ /** continues the starting of threads after it has been suspended
+ */
+ void ResumeStartingOfThreads();
+
+ bool StartingOfThreadsSuspended()
+ {
+ std::unique_lock aGuard(maMutex);
+
+ return mbStartingOfThreadsSuspended;
+ }
+
+ struct tThreadData
+ {
+ oslInterlockedCount nThreadID;
+ ::rtl::Reference< ObservableThread > pThread;
+ css::uno::Reference< css::util::XCancellable > aJob;
+
+ tThreadData()
+ : nThreadID( 0 ),
+ aJob()
+ {}
+ };
+
+ private:
+
+ static const std::deque< tThreadData >::size_type snStartedSize;
+
+ std::mutex maMutex;
+
+ css::uno::WeakReference< css::util::XJobManager > mrThreadJoiner;
+
+ std::shared_ptr< ThreadListener > mpThreadListener;
+
+ oslInterlockedCount mnThreadIDCounter;
+
+ std::deque< tThreadData > maWaitingForStartThreads;
+ std::deque< tThreadData > maStartedThreads;
+
+ Idle maStartNewThreadIdle;
+
+ bool mbStartingOfThreadsSuspended;
+
+ struct ThreadPred
+ {
+ oslInterlockedCount mnThreadID;
+ explicit ThreadPred( oslInterlockedCount nThreadID )
+ : mnThreadID( nThreadID )
+ {}
+
+ bool operator() ( const tThreadData& rThreadData ) const
+ {
+ return rThreadData.nThreadID == mnThreadID;
+ }
+ };
+
+ bool StartWaitingThread();
+
+ bool StartThread( const tThreadData& aThreadData );
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */