summaryrefslogtreecommitdiffstats
path: root/sw/source/core/unocore/unocrsrhelper.cxx
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 05:54:39 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 05:54:39 +0000
commit267c6f2ac71f92999e969232431ba04678e7437e (patch)
tree358c9467650e1d0a1d7227a21dac2e3d08b622b2 /sw/source/core/unocore/unocrsrhelper.cxx
parentInitial commit. (diff)
downloadlibreoffice-267c6f2ac71f92999e969232431ba04678e7437e.tar.xz
libreoffice-267c6f2ac71f92999e969232431ba04678e7437e.zip
Adding upstream version 4:24.2.0.upstream/4%24.2.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'sw/source/core/unocore/unocrsrhelper.cxx')
-rw-r--r--sw/source/core/unocore/unocrsrhelper.cxx1587
1 files changed, 1587 insertions, 0 deletions
diff --git a/sw/source/core/unocore/unocrsrhelper.cxx b/sw/source/core/unocore/unocrsrhelper.cxx
new file mode 100644
index 0000000000..70a724814c
--- /dev/null
+++ b/sw/source/core/unocore/unocrsrhelper.cxx
@@ -0,0 +1,1587 @@
+/* -*- 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 <unocrsrhelper.hxx>
+
+#include <map>
+#include <algorithm>
+#include <memory>
+
+#include <com/sun/star/beans/NamedValue.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/beans/PropertyState.hpp>
+#include <com/sun/star/embed/ElementModes.hpp>
+#include <com/sun/star/embed/XStorage.hpp>
+#include <com/sun/star/io/IOException.hpp>
+#include <com/sun/star/text/XTextSection.hpp>
+#include <com/sun/star/lang/XSingleServiceFactory.hpp>
+
+#include <svx/unoshape.hxx>
+
+#include <cmdid.h>
+#include <hintids.hxx>
+#include <unotextrange.hxx>
+#include <unodraw.hxx>
+#include <unofootnote.hxx>
+#include <unobookmark.hxx>
+#include <unomap.hxx>
+#include <unorefmark.hxx>
+#include <unoidx.hxx>
+#include <unofield.hxx>
+#include <unotbl.hxx>
+#include <unosett.hxx>
+#include <unoframe.hxx>
+#include <unocrsr.hxx>
+#include <doc.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <fmtftn.hxx>
+#include <charfmt.hxx>
+#include <pagedesc.hxx>
+#include <docstyle.hxx>
+#include <ndtxt.hxx>
+#include <docsh.hxx>
+#include <section.hxx>
+#include <shellio.hxx>
+#include <edimp.hxx>
+#include <swundo.hxx>
+#include <cntfrm.hxx>
+#include <pagefrm.hxx>
+#include <svl/eitem.hxx>
+#include <svl/lngmisc.hxx>
+#include <swtable.hxx>
+#include <tox.hxx>
+#include <doctxm.hxx>
+#include <fchrfmt.hxx>
+#include <editeng/editids.hrc>
+#include <editeng/flstitem.hxx>
+#include <editeng/prntitem.hxx>
+#include <vcl/metric.hxx>
+#include <svtools/ctrltool.hxx>
+#include <sfx2/docfilt.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/fcontnr.hxx>
+#include <svl/stritem.hxx>
+#include <SwStyleNameMapper.hxx>
+#include <redline.hxx>
+#include <numrule.hxx>
+#include <comphelper/storagehelper.hxx>
+#include <unotools/mediadescriptor.hxx>
+#include <comphelper/sequenceashashmap.hxx>
+#include <SwNodeNum.hxx>
+#include <fmtmeta.hxx>
+#include <txtfld.hxx>
+#include <unoparagraph.hxx>
+#include <poolfmt.hxx>
+#include <paratr.hxx>
+#include <sal/log.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::text;
+using namespace ::com::sun::star::table;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::lang;
+
+namespace SwUnoCursorHelper
+{
+
+static SwPaM* lcl_createPamCopy(const SwPaM& rPam)
+{
+ SwPaM *const pRet = new SwPaM(*rPam.GetPoint());
+ ::sw::DeepCopyPaM(rPam, *pRet);
+ return pRet;
+}
+
+void GetSelectableFromAny(uno::Reference<uno::XInterface> const& xIfc,
+ SwDoc & rTargetDoc,
+ SwPaM *& o_rpPaM, std::pair<OUString, FlyCntType> & o_rFrame,
+ OUString & o_rTableName, SwUnoTableCursor const*& o_rpTableCursor,
+ ::sw::mark::IMark const*& o_rpMark,
+ std::vector<SdrObject *> & o_rSdrObjects)
+{
+ uno::Reference<drawing::XShapes> const xShapes(xIfc, UNO_QUERY);
+ if (xShapes.is())
+ {
+ sal_Int32 nShapes(xShapes->getCount());
+ for (sal_Int32 i = 0; i < nShapes; ++i)
+ {
+ uno::Reference<lang::XUnoTunnel> xShape;
+ xShapes->getByIndex(i) >>= xShape;
+ if (xShape.is())
+ {
+ SvxShape *const pSvxShape(
+ comphelper::getFromUnoTunnel<SvxShape>(xShape));
+ if (pSvxShape)
+ {
+ SdrObject *const pSdrObject = pSvxShape->GetSdrObject();
+ if (pSdrObject)
+ { // hmm... needs view to verify it's in right doc...
+ o_rSdrObjects.push_back(pSdrObject);
+ }
+ }
+ }
+ }
+ return;
+ }
+
+ uno::Reference<lang::XUnoTunnel> const xTunnel(xIfc, UNO_QUERY);
+
+ SwXShape *const pShape(comphelper::getFromUnoTunnel<SwXShape>(xTunnel));
+ if (pShape)
+ {
+ if (SvxShape * pSvxShape = pShape->GetSvxShape())
+ {
+ SdrObject *const pSdrObject = pSvxShape->GetSdrObject();
+ if (pSdrObject)
+ { // hmm... needs view to verify it's in right doc...
+ o_rSdrObjects.push_back(pSdrObject);
+ }
+ }
+ return;
+ }
+
+ OTextCursorHelper *const pCursor(
+ dynamic_cast<OTextCursorHelper*>(xIfc.get()));
+ if (pCursor)
+ {
+ if (pCursor->GetDoc() == &rTargetDoc)
+ {
+ o_rpPaM = lcl_createPamCopy(*pCursor->GetPaM());
+ }
+ return;
+ }
+
+ SwXTextRanges* const pRanges = dynamic_cast<SwXTextRanges*>(xIfc.get());
+ if (pRanges)
+ {
+ SwUnoCursor const* pUnoCursor = pRanges->GetCursor();
+ if (pUnoCursor && &pUnoCursor->GetDoc() == &rTargetDoc)
+ {
+ o_rpPaM = lcl_createPamCopy(*pUnoCursor);
+ }
+ return;
+ }
+
+ // check these before Range to prevent misinterpretation of text frames
+ // and cells also implement XTextRange
+ SwXFrame *const pFrame = dynamic_cast<SwXFrame*>(xIfc.get());
+ if (pFrame)
+ {
+ const SwFrameFormat *const pFrameFormat(pFrame->GetFrameFormat());
+ if (pFrameFormat && pFrameFormat->GetDoc() == &rTargetDoc)
+ {
+ o_rFrame = std::make_pair(pFrameFormat->GetName(), pFrame->GetFlyCntType());
+ }
+ return;
+ }
+
+ SwXTextTable *const pTextTable = dynamic_cast<SwXTextTable*>(xIfc.get());
+ if (pTextTable)
+ {
+ SwFrameFormat *const pFrameFormat(pTextTable->GetFrameFormat());
+ if (pFrameFormat && pFrameFormat->GetDoc() == &rTargetDoc)
+ {
+ o_rTableName = pFrameFormat->GetName();
+ }
+ return;
+ }
+
+ SwXCell *const pCell = dynamic_cast<SwXCell*>(xIfc.get());
+ if (pCell)
+ {
+ SwFrameFormat *const pFrameFormat(pCell->GetFrameFormat());
+ if (pFrameFormat && pFrameFormat->GetDoc() == &rTargetDoc)
+ {
+ SwTableBox * pBox = pCell->GetTableBox();
+ SwTable *const pTable = SwTable::FindTable(pFrameFormat);
+ // ??? what's the benefit of setting pBox in this convoluted way?
+ pBox = pCell->FindBox(pTable, pBox);
+ if (pBox)
+ {
+ SwPaM aPam(*pBox->GetSttNd());
+ aPam.Move(fnMoveForward, GoInNode);
+ o_rpPaM = lcl_createPamCopy(aPam);
+ }
+ }
+ return;
+ }
+
+ uno::Reference<text::XTextRange> const xTextRange(xTunnel, UNO_QUERY);
+ if (xTextRange.is())
+ {
+ SwUnoInternalPaM aPam(rTargetDoc);
+ if (::sw::XTextRangeToSwPaM(aPam, xTextRange))
+ {
+ o_rpPaM = lcl_createPamCopy(aPam);
+ }
+ return;
+ }
+
+ SwXCellRange *const pCellRange = dynamic_cast<SwXCellRange*>(xIfc.get());
+ if (pCellRange)
+ {
+ SwUnoCursor const*const pUnoCursor(pCellRange->GetTableCursor());
+ if (pUnoCursor && &pUnoCursor->GetDoc() == &rTargetDoc)
+ {
+ // probably can't copy it to o_rpPaM for this since it's
+ // a SwTableCursor
+ o_rpTableCursor = dynamic_cast<SwUnoTableCursor const*>(pUnoCursor);
+ }
+ return;
+ }
+
+ ::sw::mark::IMark const*const pMark =
+ SwXBookmark::GetBookmarkInDoc(& rTargetDoc, xIfc);
+ if (pMark)
+ {
+ o_rpMark = pMark;
+ return;
+ }
+}
+
+uno::Reference<text::XTextContent>
+GetNestedTextContent(SwTextNode const & rTextNode, sal_Int32 const nIndex,
+ bool const bParent)
+{
+ // these should be unambiguous because of the dummy character
+ auto const eMode( bParent
+ ? ::sw::GetTextAttrMode::Parent : ::sw::GetTextAttrMode::Expand );
+ SwTextAttr *const pMetaTextAttr =
+ rTextNode.GetTextAttrAt(nIndex, RES_TXTATR_META, eMode);
+ SwTextAttr *const pMetaFieldTextAttr =
+ rTextNode.GetTextAttrAt(nIndex, RES_TXTATR_METAFIELD, eMode);
+ // which is innermost?
+ SwTextAttr *const pTextAttr = pMetaTextAttr
+ ? (pMetaFieldTextAttr
+ ? ((pMetaFieldTextAttr->GetStart() >
+ pMetaTextAttr->GetStart())
+ ? pMetaFieldTextAttr : pMetaTextAttr)
+ : pMetaTextAttr)
+ : pMetaFieldTextAttr;
+ uno::Reference<XTextContent> xRet;
+ if (pTextAttr)
+ {
+ ::sw::Meta *const pMeta(
+ static_cast<SwFormatMeta &>(pTextAttr->GetAttr()).GetMeta());
+ assert(pMeta);
+ xRet.set(pMeta->MakeUnoObject(), uno::UNO_QUERY);
+ }
+ return xRet;
+}
+
+static uno::Any GetParaListAutoFormat(SwTextNode const& rNode)
+{
+ SwFormatAutoFormat const*const pFormat(
+ rNode.GetSwAttrSet().GetItem<SwFormatAutoFormat>(RES_PARATR_LIST_AUTOFMT, false));
+ if (!pFormat)
+ {
+ return uno::Any();
+ }
+ const auto pSet(pFormat->GetStyleHandle());
+ if (!pSet)
+ return {};
+ SfxItemPropertySet const& rPropSet(*aSwMapProvider.GetPropertySet(PROPERTY_MAP_CHAR_AUTO_STYLE));
+ SfxItemPropertyMap const& rMap(rPropSet.getPropertyMap());
+ std::vector<beans::NamedValue> props;
+ // have to iterate the map, not the item set?
+ for (auto const pEntry : rMap.getPropertyEntries())
+ {
+ if (rPropSet.getPropertyState(*pEntry, *pSet) == PropertyState_DIRECT_VALUE)
+ {
+ Any value;
+ rPropSet.getPropertyValue(*pEntry, *pSet, value);
+ props.emplace_back(pEntry->aName, value);
+ }
+ }
+ return uno::Any(comphelper::containerToSequence(props));
+}
+
+// Read the special properties of the cursor
+bool getCursorPropertyValue(const SfxItemPropertyMapEntry& rEntry
+ , SwPaM& rPam
+ , Any *pAny
+ , PropertyState& eState
+ , const SwTextNode* pNode )
+{
+ PropertyState eNewState = PropertyState_DIRECT_VALUE;
+ bool bDone = true;
+ switch(rEntry.nWID)
+ {
+ case FN_UNO_PARA_CONT_PREV_SUBTREE:
+ if (pAny)
+ {
+ const SwTextNode * pTmpNode = pNode;
+
+ if (!pTmpNode)
+ pTmpNode = rPam.GetPointNode().GetTextNode();
+
+ bool bRet = false;
+
+ if ( pTmpNode &&
+ pTmpNode->GetNum() &&
+ pTmpNode->GetNum()->IsContinueingPreviousSubTree() )
+ {
+ bRet = true;
+ }
+
+ *pAny <<= bRet;
+ }
+ break;
+ case FN_UNO_PARA_NUM_STRING:
+ if (pAny)
+ {
+ const SwTextNode * pTmpNode = pNode;
+
+ if (!pTmpNode)
+ pTmpNode = rPam.GetPointNode().GetTextNode();
+
+ OUString sRet;
+ if ( pTmpNode && pTmpNode->GetNum() )
+ {
+ sRet = pTmpNode->GetNumString();
+ }
+
+ *pAny <<= sRet;
+ }
+ break;
+ case RES_PARATR_OUTLINELEVEL:
+ if (pAny)
+ {
+ const SwTextNode * pTmpNode = pNode;
+
+ if (!pTmpNode)
+ pTmpNode = rPam.GetPointNode().GetTextNode();
+
+ sal_Int16 nRet = -1;
+ if ( pTmpNode )
+ nRet = sal::static_int_cast< sal_Int16 >( pTmpNode->GetAttrOutlineLevel() );
+
+ *pAny <<= nRet;
+ }
+ break;
+ case FN_UNO_PARA_CONDITIONAL_STYLE_NAME:
+ case FN_UNO_PARA_STYLE :
+ {
+ SwFormatColl* pFormat = nullptr;
+ if(pNode)
+ pFormat = FN_UNO_PARA_CONDITIONAL_STYLE_NAME == rEntry.nWID
+ ? pNode->GetFormatColl() : &pNode->GetAnyFormatColl();
+ else
+ {
+ pFormat = SwUnoCursorHelper::GetCurTextFormatColl(rPam,
+ FN_UNO_PARA_CONDITIONAL_STYLE_NAME == rEntry.nWID);
+ }
+ if(pFormat)
+ {
+ if( pAny )
+ {
+ OUString sVal;
+ SwStyleNameMapper::FillProgName(pFormat->GetName(), sVal, SwGetPoolIdFromName::TxtColl );
+ *pAny <<= sVal;
+ }
+ }
+ else
+ eNewState = PropertyState_AMBIGUOUS_VALUE;
+ }
+ break;
+ case FN_UNO_PAGE_STYLE :
+ {
+ OUString sVal;
+ GetCurPageStyle(rPam, sVal);
+ if( pAny )
+ *pAny <<= sVal;
+ if(sVal.isEmpty())
+ eNewState = PropertyState_AMBIGUOUS_VALUE;
+ }
+ break;
+ case FN_UNO_NUM_START_VALUE :
+ if( pAny )
+ {
+ sal_Int16 nValue = IsNodeNumStart(rPam, eNewState);
+ *pAny <<= nValue;
+ }
+ break;
+ case FN_UNO_NUM_LEVEL :
+ case FN_UNO_IS_NUMBER :
+ // #i91601#
+ case FN_UNO_LIST_ID:
+ case FN_NUMBER_NEWSTART:
+ case FN_UNO_PARA_NUM_AUTO_FORMAT:
+ {
+ if (!pAny)
+ {
+ break;
+ }
+ // a multi selection is not considered
+ const SwTextNode* pTextNd = rPam.GetPointNode().GetTextNode();
+ if ( pTextNd && pTextNd->IsInList() )
+ {
+ switch (rEntry.nWID)
+ {
+ case FN_UNO_NUM_LEVEL:
+ {
+ *pAny <<= static_cast<sal_Int16>(pTextNd->GetActualListLevel());
+ break;
+ }
+ case FN_UNO_IS_NUMBER:
+ {
+ *pAny <<= pTextNd->IsCountedInList();
+ break;
+ }
+ // #i91601#
+ case FN_UNO_LIST_ID:
+ {
+ *pAny <<= pTextNd->GetListId();
+ break;
+ }
+ case FN_NUMBER_NEWSTART:
+ {
+ *pAny <<= pTextNd->IsListRestart();
+ break;
+ }
+ case FN_UNO_PARA_NUM_AUTO_FORMAT:
+ {
+ *pAny = GetParaListAutoFormat(*pTextNd);
+ break;
+ }
+ default:
+ assert(false);
+ }
+ }
+ else
+ {
+ eNewState = PropertyState_DEFAULT_VALUE;
+
+ // #i30838# set default values for default properties
+ switch (rEntry.nWID)
+ {
+ case FN_UNO_NUM_LEVEL:
+ {
+ *pAny <<= static_cast<sal_Int16>( 0 );
+ break;
+ }
+ case FN_UNO_IS_NUMBER:
+ {
+ *pAny <<= false;
+ break;
+ }
+ // #i91601#
+ case FN_UNO_LIST_ID:
+ {
+ *pAny <<= OUString();
+ break;
+ }
+ case FN_NUMBER_NEWSTART:
+ {
+ *pAny <<= false;
+ break;
+ }
+ case FN_UNO_PARA_NUM_AUTO_FORMAT:
+ {
+ break; // void
+ }
+ default:
+ assert(false);
+ }
+ }
+ //PROPERTY_MAYBEVOID!
+ }
+ break;
+ case FN_UNO_NUM_RULES :
+ if( pAny )
+ getNumberingProperty(rPam, eNewState, pAny);
+ else
+ {
+ if( !SwDoc::GetNumRuleAtPos( *rPam.GetPoint() ) )
+ eNewState = PropertyState_DEFAULT_VALUE;
+ }
+ break;
+ case FN_UNO_DOCUMENT_INDEX_MARK:
+ {
+ std::vector<SwTextAttr *> marks;
+ if (rPam.GetPointNode().IsTextNode())
+ {
+ marks = rPam.GetPointNode().GetTextNode()->GetTextAttrsAt(
+ rPam.GetPoint()->GetContentIndex(), RES_TXTATR_TOXMARK);
+ }
+ if (!marks.empty())
+ {
+ if( pAny )
+ { // hmm... can only return 1 here
+ SwTOXMark & rMark =
+ static_cast<SwTOXMark &>((*marks.begin())->GetAttr());
+ const uno::Reference< text::XDocumentIndexMark > xRef =
+ SwXDocumentIndexMark::CreateXDocumentIndexMark(
+ rPam.GetDoc(), &rMark);
+ (*pAny) <<= xRef;
+ }
+ }
+ else
+ //also here - indistinguishable
+ eNewState = PropertyState_DEFAULT_VALUE;
+ }
+ break;
+ case FN_UNO_DOCUMENT_INDEX:
+ {
+ SwTOXBase* pBase = SwDoc::GetCurTOX(
+ *rPam.Start() );
+ if( pBase )
+ {
+ if( pAny )
+ {
+ const uno::Reference< text::XDocumentIndex > xRef =
+ SwXDocumentIndex::CreateXDocumentIndex(rPam.GetDoc(),
+ static_cast<SwTOXBaseSection *>(pBase));
+ (*pAny) <<= xRef;
+ }
+ }
+ else
+ eNewState = PropertyState_DEFAULT_VALUE;
+ }
+ break;
+ case FN_UNO_TEXT_FIELD:
+ {
+ const SwPosition *pPos = rPam.Start();
+ const SwTextNode *pTextNd =
+ rPam.GetDoc().GetNodes()[pPos->GetNodeIndex()]->GetTextNode();
+ const SwTextAttr* pTextAttr = pTextNd
+ ? pTextNd->GetFieldTextAttrAt(pPos->GetContentIndex(), ::sw::GetTextAttrMode::Default)
+ : nullptr;
+ if ( pTextAttr != nullptr )
+ {
+ if( pAny )
+ {
+ uno::Reference<text::XTextField> const xField(
+ SwXTextField::CreateXTextField(&rPam.GetDoc(),
+ &pTextAttr->GetFormatField()));
+ *pAny <<= xField;
+ }
+ }
+ else
+ eNewState = PropertyState_DEFAULT_VALUE;
+ }
+ break;
+ case FN_UNO_TEXT_TABLE:
+ case FN_UNO_CELL:
+ {
+ SwStartNode* pSttNode = rPam.GetPointNode().StartOfSectionNode();
+ SwStartNodeType eType = pSttNode->GetStartNodeType();
+ if(SwTableBoxStartNode == eType)
+ {
+ if( pAny )
+ {
+ const SwTableNode* pTableNode = pSttNode->FindTableNode();
+ SwFrameFormat* pTableFormat = pTableNode->GetTable().GetFrameFormat();
+ //SwTable& rTable = static_cast<SwTableNode*>(pSttNode)->GetTable();
+ if(FN_UNO_TEXT_TABLE == rEntry.nWID)
+ {
+ uno::Reference< XTextTable > xTable = SwXTextTables::GetObject(*pTableFormat);
+ *pAny <<= xTable;
+ }
+ else
+ {
+ SwTableBox* pBox = pSttNode->GetTableBox();
+ uno::Reference< XCell > xCell = SwXCell::CreateXCell(pTableFormat, pBox);
+ *pAny <<= xCell;
+ }
+ }
+ }
+ else
+ eNewState = PropertyState_DEFAULT_VALUE;
+ }
+ break;
+ case FN_UNO_TEXT_FRAME:
+ {
+ SwStartNode* pSttNode = rPam.GetPointNode().StartOfSectionNode();
+ SwStartNodeType eType = pSttNode->GetStartNodeType();
+
+ SwFrameFormat* pFormat;
+ if(eType == SwFlyStartNode && nullptr != (pFormat = pSttNode->GetFlyFormat()))
+ {
+ // Create a wrapper only for text frames, not for graphic or OLE nodes.
+ if (pAny && !rPam.GetPointNode().IsNoTextNode())
+ {
+ uno::Reference<XTextFrame> const xFrame(
+ SwXTextFrame::CreateXTextFrame(*pFormat->GetDoc(), pFormat));
+ (*pAny) <<= xFrame;
+ }
+ }
+ else
+ eNewState = PropertyState_DEFAULT_VALUE;
+ }
+ break;
+ case FN_UNO_TEXT_SECTION:
+ {
+ SwSection* pSect = SwDoc::GetCurrSection(*rPam.GetPoint());
+ if(pSect)
+ {
+ if( pAny )
+ {
+ uno::Reference< XTextSection > xSect = SwXTextSections::GetObject( *pSect->GetFormat() );
+ *pAny <<= xSect;
+ }
+ }
+ else
+ eNewState = PropertyState_DEFAULT_VALUE;
+ }
+ break;
+ case FN_UNO_TEXT_PARAGRAPH:
+ {
+ SwTextNode* pTextNode = rPam.GetPoint()->GetNode().GetTextNode();
+ if (pTextNode)
+ {
+ if (pAny)
+ {
+ uno::Reference<text::XTextContent> xParagraph = SwXParagraph::CreateXParagraph(pTextNode->GetDoc(), pTextNode, nullptr);
+ *pAny <<= xParagraph;
+ }
+ }
+ else
+ eNewState = PropertyState_DEFAULT_VALUE;
+ }
+ break;
+ case FN_UNO_SORTED_TEXT_ID:
+ {
+ if( pAny )
+ {
+ sal_Int32 nIndex = -1;
+ SwTextNode* pTextNode = rPam.GetPoint()->GetNode().GetTextNode();
+ if ( pTextNode )
+ nIndex = pTextNode->GetIndex().get();
+ *pAny <<= nIndex;
+ }
+ }
+ break;
+ case FN_UNO_ENDNOTE:
+ case FN_UNO_FOOTNOTE:
+ {
+ SwTextAttr *const pTextAttr = rPam.GetPointNode().IsTextNode() ?
+ rPam.GetPointNode().GetTextNode()->GetTextAttrForCharAt(
+ rPam.GetPoint()->GetContentIndex(), RES_TXTATR_FTN) : nullptr;
+ if(pTextAttr)
+ {
+ const SwFormatFootnote& rFootnote = pTextAttr->GetFootnote();
+ if(rFootnote.IsEndNote() == (FN_UNO_ENDNOTE == rEntry.nWID))
+ {
+ if( pAny )
+ {
+ const uno::Reference< text::XFootnote > xFootnote =
+ SwXFootnote::CreateXFootnote(rPam.GetDoc(),
+ &const_cast<SwFormatFootnote&>(rFootnote));
+ *pAny <<= xFootnote;
+ }
+ }
+ else
+ eNewState = PropertyState_DEFAULT_VALUE;
+ }
+ else
+ eNewState = PropertyState_DEFAULT_VALUE;
+ }
+ break;
+ case FN_UNO_REFERENCE_MARK:
+ {
+ std::vector<SwTextAttr *> marks;
+ if (rPam.GetPointNode().IsTextNode())
+ {
+ marks = rPam.GetPointNode().GetTextNode()->GetTextAttrsAt(
+ rPam.GetPoint()->GetContentIndex(), RES_TXTATR_REFMARK);
+ }
+ if (!marks.empty())
+ {
+ if( pAny )
+ { // hmm... can only return 1 here
+ const SwFormatRefMark& rRef = (*marks.begin())->GetRefMark();
+ uno::Reference<XTextContent> const xRef =
+ SwXReferenceMark::CreateXReferenceMark(rPam.GetDoc(),
+ const_cast<SwFormatRefMark*>(&rRef));
+ *pAny <<= xRef;
+ }
+ }
+ else
+ eNewState = PropertyState_DEFAULT_VALUE;
+ }
+ break;
+ case FN_UNO_NESTED_TEXT_CONTENT:
+ {
+ uno::Reference<XTextContent> const xRet(rPam.GetPointNode().IsTextNode()
+ ? GetNestedTextContent(*rPam.GetPointNode().GetTextNode(),
+ rPam.GetPoint()->GetContentIndex(), false)
+ : nullptr);
+ if (xRet.is())
+ {
+ if (pAny)
+ {
+ (*pAny) <<= xRet;
+ }
+ }
+ else
+ {
+ eNewState = PropertyState_DEFAULT_VALUE;
+ }
+ }
+ break;
+ case FN_UNO_CHARFMT_SEQUENCE:
+ {
+
+ SwTextNode *const pTextNode = rPam.GetPointNode().GetTextNode();
+ if (&rPam.GetPointNode() == &rPam.GetMarkNode()
+ && pTextNode && pTextNode->GetpSwpHints())
+ {
+ sal_Int32 nPaMStart = rPam.Start()->GetContentIndex();
+ sal_Int32 nPaMEnd = rPam.End()->GetContentIndex();
+ Sequence< OUString> aCharStyles;
+ SwpHints* pHints = pTextNode->GetpSwpHints();
+ for( size_t nAttr = 0; nAttr < pHints->Count(); ++nAttr )
+ {
+ SwTextAttr* pAttr = pHints->Get( nAttr );
+ if(pAttr->Which() != RES_TXTATR_CHARFMT)
+ continue;
+ const sal_Int32 nAttrStart = pAttr->GetStart();
+ const sal_Int32 nAttrEnd = *pAttr->GetEnd();
+ //check if the attribute touches the selection
+ if( ( nAttrEnd > nPaMStart && nAttrStart < nPaMEnd ) ||
+ ( !nAttrStart && !nAttrEnd && !nPaMStart && !nPaMEnd ) )
+ {
+ //check for overlapping
+ if(nAttrStart > nPaMStart ||
+ nAttrEnd < nPaMEnd)
+ {
+ aCharStyles.realloc(0);
+ break;
+ }
+ else
+ {
+ //now the attribute should start before or at the selection
+ //and it should end at the end of the selection or behind
+ OSL_ENSURE(nAttrStart <= nPaMStart && nAttrEnd >=nPaMEnd,
+ "attribute overlaps or is outside");
+ //now the name of the style has to be added to the sequence
+ aCharStyles.realloc(aCharStyles.getLength() + 1);
+ OSL_ENSURE(pAttr->GetCharFormat().GetCharFormat(), "no character format set");
+ aCharStyles.getArray()[aCharStyles.getLength() - 1] =
+ SwStyleNameMapper::GetProgName(
+ pAttr->GetCharFormat().GetCharFormat()->GetName(), SwGetPoolIdFromName::ChrFmt);
+ }
+ }
+
+ }
+ eNewState =
+ aCharStyles.hasElements() ?
+ PropertyState_DIRECT_VALUE : PropertyState_DEFAULT_VALUE;
+ if(pAny)
+ (*pAny) <<= aCharStyles;
+ }
+ else
+ eNewState = PropertyState_DEFAULT_VALUE;
+ }
+ break;
+ case RES_TXTATR_CHARFMT:
+ // no break here!
+ default: bDone = false;
+ }
+ if( bDone )
+ eState = eNewState;
+ return bDone;
+};
+
+sal_Int16 IsNodeNumStart(SwPaM const & rPam, PropertyState& eState)
+{
+ const SwTextNode* pTextNd = rPam.GetPointNode().GetTextNode();
+ // correction: check, if restart value is set at the text node and use
+ // new method <SwTextNode::GetAttrListRestartValue()> to retrieve the value
+ if ( pTextNd && pTextNd->GetNumRule() && pTextNd->IsListRestart() &&
+ pTextNd->HasAttrListRestartValue() )
+ {
+ eState = PropertyState_DIRECT_VALUE;
+ sal_Int16 nTmp = sal::static_int_cast< sal_Int16 >(pTextNd->GetAttrListRestartValue());
+ return nTmp;
+ }
+ eState = PropertyState_DEFAULT_VALUE;
+ return -1;
+}
+
+void setNumberingProperty(const Any& rValue, SwPaM& rPam)
+{
+ uno::Reference<XIndexReplace> xIndexReplace;
+ if(rValue >>= xIndexReplace)
+ {
+ auto pSwNum = dynamic_cast<SwXNumberingRules*>(xIndexReplace.get());
+ if(pSwNum)
+ {
+ SwDoc& rDoc = rPam.GetDoc();
+ if(pSwNum->GetNumRule())
+ {
+ SwNumRule aRule(*pSwNum->GetNumRule());
+ const OUString* pNewCharStyles = pSwNum->GetNewCharStyleNames();
+ const OUString* pBulletFontNames = pSwNum->GetBulletFontNames();
+ for(sal_uInt16 i = 0; i < MAXLEVEL; i++)
+ {
+ SwNumFormat aFormat(aRule.Get( i ));
+ if (!pNewCharStyles[i].isEmpty() &&
+ !SwXNumberingRules::isInvalidStyle(pNewCharStyles[i]) &&
+ (!aFormat.GetCharFormat() || pNewCharStyles[i] != aFormat.GetCharFormat()->GetName()))
+ {
+ if (pNewCharStyles[i].isEmpty())
+ {
+ // FIXME
+ // Is something missing/wrong here?
+ // if condition is always false due to outer check!
+ aFormat.SetCharFormat(nullptr);
+ }
+ else
+ {
+
+ // get CharStyle and set the rule
+ const size_t nChCount = rDoc.GetCharFormats()->size();
+ SwCharFormat* pCharFormat = nullptr;
+ for(size_t nCharFormat = 0; nCharFormat < nChCount; ++nCharFormat)
+ {
+ SwCharFormat& rChFormat = *((*(rDoc.GetCharFormats()))[nCharFormat]);
+ if(rChFormat.GetName() == pNewCharStyles[i])
+ {
+ pCharFormat = &rChFormat;
+ break;
+ }
+ }
+
+ if(!pCharFormat)
+ {
+ SfxStyleSheetBasePool* pPool = rDoc.GetDocShell()->GetStyleSheetPool();
+ SfxStyleSheetBase* pBase;
+ pBase = pPool->Find(pNewCharStyles[i], SfxStyleFamily::Char);
+ // shall it really be created?
+ if(!pBase)
+ pBase = &pPool->Make(pNewCharStyles[i], SfxStyleFamily::Page);
+ pCharFormat = static_cast<SwDocStyleSheet*>(pBase)->GetCharFormat();
+ }
+ if(pCharFormat)
+ aFormat.SetCharFormat(pCharFormat);
+ }
+ }
+ //Now again for fonts
+ if(
+ !pBulletFontNames[i].isEmpty() &&
+ !SwXNumberingRules::isInvalidStyle(pBulletFontNames[i]) &&
+ (!aFormat.GetBulletFont() || aFormat.GetBulletFont()->GetFamilyName() != pBulletFontNames[i])
+ )
+ {
+ const SvxFontListItem* pFontListItem =
+ static_cast<const SvxFontListItem* >(rDoc.GetDocShell()
+ ->GetItem( SID_ATTR_CHAR_FONTLIST ));
+ const FontList* pList = pFontListItem->GetFontList();
+
+ vcl::Font aFont(pList->Get(
+ pBulletFontNames[i],WEIGHT_NORMAL, ITALIC_NONE));
+ aFormat.SetBulletFont(&aFont);
+ }
+ aRule.Set( i, aFormat );
+ }
+ UnoActionContext aAction(&rDoc);
+
+ if( rPam.GetNext() != &rPam ) // Multiple selection?
+ {
+ rDoc.GetIDocumentUndoRedo().StartUndo( SwUndoId::START, nullptr );
+ SwPamRanges aRangeArr( rPam );
+ SwPaM aPam( *rPam.GetPoint() );
+ for ( size_t n = 0; n < aRangeArr.Count(); ++n )
+ {
+ // no start of a new list
+ rDoc.SetNumRule( aRangeArr.SetPam( n, aPam ), aRule, false );
+ }
+ rDoc.GetIDocumentUndoRedo().EndUndo( SwUndoId::END, nullptr );
+ }
+ else
+ {
+ // no start of a new list
+ rDoc.SetNumRule( rPam, aRule, false );
+ }
+
+ }
+ else if(!pSwNum->GetCreatedNumRuleName().isEmpty())
+ {
+ UnoActionContext aAction( &rDoc );
+ SwNumRule* pRule = rDoc.FindNumRulePtr( pSwNum->GetCreatedNumRuleName() );
+ if ( !pRule )
+ throw RuntimeException();
+ // no start of a new list
+ rDoc.SetNumRule( rPam, *pRule, false );
+ }
+ else
+ {
+ // #i103817#
+ // outline numbering
+ UnoActionContext aAction(&rDoc);
+ SwNumRule* pRule = rDoc.GetOutlineNumRule();
+ if(!pRule)
+ throw RuntimeException();
+ rDoc.SetNumRule( rPam, *pRule, false );
+ }
+ }
+ }
+ else if ( rValue.getValueType() == cppu::UnoType<void>::get() )
+ {
+ rPam.GetDoc().DelNumRules(rPam);
+ }
+}
+
+void getNumberingProperty(SwPaM& rPam, PropertyState& eState, Any * pAny )
+{
+ const SwNumRule* pNumRule = SwDoc::GetNumRuleAtPos( *rPam.GetPoint() );
+ if(pNumRule)
+ {
+ uno::Reference< XIndexReplace > xNum = new SwXNumberingRules(*pNumRule);
+ if ( pAny )
+ *pAny <<= xNum;
+ eState = PropertyState_DIRECT_VALUE;
+ }
+ else
+ eState = PropertyState_DEFAULT_VALUE;
+}
+
+void GetCurPageStyle(SwPaM const & rPaM, OUString &rString)
+{
+ if (!rPaM.GetPointContentNode())
+ return; // TODO: is there an easy way to get it for tables/sections?
+ SwRootFrame* pLayout = rPaM.GetDoc().getIDocumentLayoutAccess().GetCurrentLayout();
+ // Consider the position inside the content node, since the node may span over multiple pages
+ // with different page styles.
+ SwContentFrame* pFrame = rPaM.GetPointContentNode()->getLayoutFrame(pLayout, rPaM.GetPoint());
+ if(pFrame)
+ {
+ const SwPageFrame* pPage = pFrame->FindPageFrame();
+ if(pPage)
+ {
+ SwStyleNameMapper::FillProgName(pPage->GetPageDesc()->GetName(),
+ rString, SwGetPoolIdFromName::PageDesc);
+ }
+ }
+}
+
+// reset special properties of the cursor
+void resetCursorPropertyValue(const SfxItemPropertyMapEntry& rEntry, SwPaM& rPam)
+{
+ SwDoc& rDoc = rPam.GetDoc();
+ switch(rEntry.nWID)
+ {
+ case FN_UNO_PARA_STYLE :
+ break;
+ case FN_UNO_PAGE_STYLE :
+ break;
+ case FN_UNO_NUM_START_VALUE :
+ {
+ UnoActionContext aAction(&rDoc);
+
+ if( rPam.GetNext() != &rPam ) // Multiple selection?
+ {
+ rDoc.GetIDocumentUndoRedo().StartUndo( SwUndoId::START, nullptr );
+ SwPamRanges aRangeArr( rPam );
+ SwPaM aPam( *rPam.GetPoint() );
+ for( size_t n = 0; n < aRangeArr.Count(); ++n )
+ rDoc.SetNodeNumStart( *aRangeArr.SetPam( n, aPam ).GetPoint(), 1 );
+ rDoc.GetIDocumentUndoRedo().EndUndo( SwUndoId::END, nullptr );
+ }
+ else
+ rDoc.SetNodeNumStart( *rPam.GetPoint(), 0 );
+ }
+
+ break;
+ case FN_UNO_NUM_LEVEL :
+ break;
+ case FN_UNO_NUM_RULES:
+ break;
+ case FN_UNO_CHARFMT_SEQUENCE:
+ {
+ rDoc.ResetAttrs(rPam, true, { RES_TXTATR_CHARFMT });
+ }
+ break;
+ }
+}
+
+void InsertFile(SwUnoCursor* pUnoCursor, const OUString& rURL,
+ const uno::Sequence< beans::PropertyValue >& rOptions)
+{
+ if (SwTextNode const*const pTextNode = pUnoCursor->GetPoint()->GetNode().GetTextNode())
+ {
+ // TODO: check meta field here too in case it ever grows a 2nd char
+ if (pTextNode->GetTextAttrAt(pUnoCursor->GetPoint()->GetContentIndex(),
+ RES_TXTATR_INPUTFIELD, ::sw::GetTextAttrMode::Parent))
+ {
+ throw uno::RuntimeException("cannot insert file inside input field");
+ }
+
+ if (pTextNode->GetTextAttrAt(pUnoCursor->GetPoint()->GetContentIndex(),
+ RES_TXTATR_CONTENTCONTROL, ::sw::GetTextAttrMode::Parent))
+ {
+ throw uno::RuntimeException("cannot insert file inside content controls");
+ }
+ }
+
+ std::unique_ptr<SfxMedium> pMed;
+ SwDoc& rDoc = pUnoCursor->GetDoc();
+ SwDocShell* pDocSh = rDoc.GetDocShell();
+ utl::MediaDescriptor aMediaDescriptor( rOptions );
+ OUString sFileName = rURL;
+ OUString sFilterName, sFilterOptions, sPassword, sBaseURL;
+ uno::Reference < io::XStream > xStream;
+ uno::Reference < io::XInputStream > xInputStream;
+
+ if( sFileName.isEmpty() )
+ aMediaDescriptor[utl::MediaDescriptor::PROP_URL] >>= sFileName;
+ if( sFileName.isEmpty() )
+ aMediaDescriptor[utl::MediaDescriptor::PROP_FILENAME] >>= sFileName;
+ aMediaDescriptor[utl::MediaDescriptor::PROP_INPUTSTREAM] >>= xInputStream;
+ aMediaDescriptor[utl::MediaDescriptor::PROP_STREAM] >>= xStream;
+ aMediaDescriptor[utl::MediaDescriptor::PROP_INPUTSTREAM] >>= xInputStream;
+ aMediaDescriptor[utl::MediaDescriptor::PROP_FILTERNAME] >>= sFilterName;
+ aMediaDescriptor[utl::MediaDescriptor::PROP_FILTEROPTIONS] >>= sFilterOptions;
+ aMediaDescriptor[utl::MediaDescriptor::PROP_PASSWORD] >>= sPassword;
+ aMediaDescriptor[utl::MediaDescriptor::PROP_DOCUMENTBASEURL ] >>= sBaseURL;
+ if ( !xInputStream.is() && xStream.is() )
+ xInputStream = xStream->getInputStream();
+
+ if(!pDocSh || (sFileName.isEmpty() && !xInputStream.is()))
+ return;
+
+ SfxObjectFactory& rFact = pDocSh->GetFactory();
+ std::shared_ptr<const SfxFilter> pFilter = rFact.GetFilterContainer()->GetFilter4FilterName( sFilterName );
+ uno::Reference < embed::XStorage > xReadStorage;
+ if( xInputStream.is() )
+ {
+ uno::Sequence< uno::Any > aArgs{ uno::Any(xInputStream),
+ uno::Any(embed::ElementModes::READ) };
+ try
+ {
+ xReadStorage.set( ::comphelper::OStorageHelper::GetStorageFactory()->createInstanceWithArguments( aArgs ),
+ uno::UNO_QUERY );
+ }
+ catch( const io::IOException&) {}
+ }
+ if ( !pFilter )
+ {
+ if( xInputStream.is() && !xReadStorage.is())
+ {
+ pMed.reset(new SfxMedium);
+ pMed->setStreamToLoadFrom(xInputStream, true );
+ }
+ else
+ pMed.reset(xReadStorage.is() ?
+ new SfxMedium(xReadStorage, sBaseURL ) :
+ new SfxMedium(sFileName, StreamMode::READ ));
+ if( !sBaseURL.isEmpty() )
+ pMed->GetItemSet().Put( SfxStringItem( SID_DOC_BASEURL, sBaseURL ) );
+
+ SfxFilterMatcher aMatcher( rFact.GetFilterContainer()->GetName() );
+ ErrCode nErr = aMatcher.GuessFilter(*pMed, pFilter, SfxFilterFlags::NONE);
+ if ( nErr || !pFilter)
+ return;
+ pMed->SetFilter( pFilter );
+ }
+ else
+ {
+ if( xInputStream.is() && !xReadStorage.is())
+ {
+ pMed.reset(new SfxMedium);
+ pMed->setStreamToLoadFrom(xInputStream, true );
+ pMed->SetFilter( pFilter );
+ }
+ else
+ {
+ if( xReadStorage.is() )
+ {
+ pMed.reset(new SfxMedium(xReadStorage, sBaseURL ));
+ pMed->SetFilter( pFilter );
+ }
+ else
+ pMed.reset(new SfxMedium(sFileName, StreamMode::READ, pFilter, nullptr));
+ }
+ if(!sFilterOptions.isEmpty())
+ pMed->GetItemSet().Put( SfxStringItem( SID_FILE_FILTEROPTIONS, sFilterOptions ) );
+ if(!sBaseURL.isEmpty())
+ pMed->GetItemSet().Put( SfxStringItem( SID_DOC_BASEURL, sBaseURL ) );
+ }
+
+ // this sourcecode is not responsible for the lifetime of the shell, SfxObjectShellLock should not be used
+ SfxObjectShellRef aRef( pDocSh );
+
+ pMed->Download(); // if necessary: start the download
+ if( !(aRef.is() && 1 < aRef->GetRefCount()) ) // Ref still valid?
+ return;
+
+ SwReaderPtr pRdr;
+ SfxItemSet& rSet = pMed->GetItemSet();
+ rSet.Put(SfxBoolItem(FN_API_CALL, true));
+ if(!sPassword.isEmpty())
+ rSet.Put(SfxStringItem(SID_PASSWORD, sPassword));
+ Reader *pRead = pDocSh->StartConvertFrom( *pMed, pRdr, nullptr, pUnoCursor);
+ if( !pRead )
+ return;
+
+ UnoActionContext aContext(&rDoc);
+
+ if(pUnoCursor->HasMark())
+ rDoc.getIDocumentContentOperations().DeleteAndJoin(*pUnoCursor);
+
+ SwNodeIndex aSave( pUnoCursor->GetPoint()->GetNode(), -1 );
+ sal_Int32 nContent = pUnoCursor->GetPoint()->GetContentIndex();
+
+ ErrCodeMsg nErrno = pRdr->Read( *pRead ); // and paste the document
+
+ if(!nErrno)
+ {
+ ++aSave;
+ pUnoCursor->SetMark();
+ pUnoCursor->GetMark()->Assign( aSave );
+
+ SwContentNode* pCntNode = aSave.GetNode().GetContentNode();
+ if( !pCntNode )
+ nContent = 0;
+ pUnoCursor->GetMark()->SetContent( nContent );
+ }
+}
+
+// insert text and scan for CR characters in order to insert
+// paragraph breaks at those positions by calling SplitNode
+bool DocInsertStringSplitCR(
+ SwDoc &rDoc,
+ const SwPaM &rNewCursor,
+ std::u16string_view rText,
+ const bool bForceExpandHints )
+{
+ bool bOK = true;
+
+ for (size_t i = 0; i < rText.size(); ++i)
+ {
+ sal_Unicode const ch(rText[i]);
+ if (linguistic::IsControlChar(ch)
+ && ch != '\r' && ch != '\n' && ch != '\t')
+ {
+ SAL_WARN("sw.uno", "DocInsertStringSplitCR: refusing to insert control character " << int(ch));
+ return false;
+ }
+ }
+
+ const SwInsertFlags nInsertFlags =
+ bForceExpandHints
+ ? ( SwInsertFlags::FORCEHINTEXPAND | SwInsertFlags::EMPTYEXPAND)
+ : SwInsertFlags::EMPTYEXPAND;
+
+ // grouping done in InsertString is intended for typing, not API calls
+ ::sw::GroupUndoGuard const undoGuard(rDoc.GetIDocumentUndoRedo());
+ SwTextNode* const pTextNd =
+ rNewCursor.GetPoint()->GetNode().GetTextNode();
+ if (!pTextNd)
+ {
+ SAL_INFO("sw.uno", "DocInsertStringSplitCR: need a text node");
+ return false;
+ }
+ OUString aText;
+ sal_Int32 nStartIdx = 0;
+ const sal_Int32 nMaxLength = COMPLETE_STRING - pTextNd->GetText().getLength();
+
+ size_t nIdx = rText.find( '\r', nStartIdx );
+ if( ( nIdx == std::u16string_view::npos && nMaxLength < sal_Int32(rText.size()) ) ||
+ ( nIdx != std::u16string_view::npos && nMaxLength < sal_Int32(nIdx) ) )
+ {
+ nIdx = nMaxLength;
+ }
+ while (nIdx != std::u16string_view::npos )
+ {
+ OSL_ENSURE( sal_Int32(nIdx) - nStartIdx >= 0, "index negative!" );
+ aText = rText.substr( nStartIdx, nIdx - nStartIdx );
+ if (!aText.isEmpty() &&
+ !rDoc.getIDocumentContentOperations().InsertString( rNewCursor, aText, nInsertFlags ))
+ {
+ OSL_FAIL( "Doc->Insert(Str) failed." );
+ bOK = false;
+ }
+ if (!rDoc.getIDocumentContentOperations().SplitNode( *rNewCursor.GetPoint(), false ) )
+ {
+ OSL_FAIL( "SplitNode failed" );
+ bOK = false;
+ }
+ nStartIdx = nIdx + 1;
+ nIdx = rText.find( '\r', nStartIdx );
+ }
+ aText = rText.substr( nStartIdx );
+ if (!aText.isEmpty() &&
+ !rDoc.getIDocumentContentOperations().InsertString( rNewCursor, aText, nInsertFlags ))
+ {
+ OSL_FAIL( "Doc->Insert(Str) failed." );
+ bOK = false;
+ }
+
+ return bOK;
+}
+
+void makeRedline( SwPaM const & rPaM,
+ std::u16string_view rRedlineType,
+ const uno::Sequence< beans::PropertyValue >& rRedlineProperties )
+{
+ IDocumentRedlineAccess& rRedlineAccess = rPaM.GetDoc().getIDocumentRedlineAccess();
+
+ RedlineType eType;
+ if ( rRedlineType == u"Insert" )
+ eType = RedlineType::Insert;
+ else if ( rRedlineType == u"Delete" )
+ eType = RedlineType::Delete;
+ else if ( rRedlineType == u"Format" )
+ eType = RedlineType::Format;
+ else if ( rRedlineType == u"TextTable" )
+ eType = RedlineType::Table;
+ else if ( rRedlineType == u"ParagraphFormat" )
+ eType = RedlineType::ParagraphFormat;
+ else
+ throw lang::IllegalArgumentException();
+
+ //todo: what about REDLINE_FMTCOLL?
+ std::size_t nAuthor = 0;
+ OUString sAuthor;
+ OUString sComment;
+ ::util::DateTime aStamp;
+ uno::Sequence< beans::PropertyValue > aRevertProperties;
+ sal_uInt32 nMovedID = 0;
+ bool bFoundComment = false;
+ bool bFoundStamp = false;
+ bool bFoundRevertProperties = false;
+ for (const css::beans::PropertyValue & rProp : rRedlineProperties )
+ {
+ if (rProp.Name == "RedlineAuthor")
+ {
+ if( rProp.Value >>= sAuthor )
+ nAuthor = rRedlineAccess.InsertRedlineAuthor(sAuthor);
+ }
+ else if (rProp.Name == "RedlineComment")
+ bFoundComment = rProp.Value >>= sComment;
+ else if (rProp.Name == "RedlineDateTime")
+ bFoundStamp = rProp.Value >>= aStamp;
+ else if (rProp.Name == "RedlineRevertProperties")
+ bFoundRevertProperties = rProp.Value >>= aRevertProperties;
+ else if (rProp.Name == "RedlineMoved")
+ rProp.Value >>= nMovedID;
+ }
+
+ SwRedlineData aRedlineData( eType, nAuthor );
+ if( bFoundComment )
+ aRedlineData.SetComment( sComment );
+ if( bFoundStamp )
+ aRedlineData.SetTimeStamp( DateTime( aStamp));
+
+ std::unique_ptr<SwRedlineExtraData_FormatColl> xRedlineExtraData;
+
+ // Read the 'Redline Revert Properties' from the parameters
+ // Check if the value exists
+ if ( bFoundRevertProperties )
+ {
+ int nMap = 0;
+ // Make sure that paragraph format gets its own map, otherwise e.g. fill attributes are not preserved.
+ if (eType == RedlineType::ParagraphFormat)
+ {
+ nMap = PROPERTY_MAP_PARAGRAPH;
+ if (!aRevertProperties.hasElements())
+ {
+ // to reject the paragraph style change, use standard style
+ xRedlineExtraData.reset(new SwRedlineExtraData_FormatColl( "", RES_POOLCOLL_STANDARD, nullptr ));
+ }
+ }
+ else
+ nMap = PROPERTY_MAP_TEXTPORTION_EXTENSIONS;
+ SfxItemPropertySet const& rPropSet = *aSwMapProvider.GetPropertySet(nMap);
+
+ // Check if there are any properties
+ if (aRevertProperties.hasElements())
+ {
+ SwDoc& rDoc = rPaM.GetDoc();
+
+ // Build set of attributes we want to fetch
+ WhichRangesContainer aWhichPairs;
+ std::vector<SfxItemPropertyMapEntry const*> aEntries;
+ std::vector<uno::Any> aValues;
+ aEntries.reserve(aRevertProperties.getLength());
+ sal_uInt16 nStyleId = USHRT_MAX;
+ sal_uInt16 nNumId = USHRT_MAX;
+ for (const auto& rRevertProperty : std::as_const(aRevertProperties))
+ {
+ const OUString &rPropertyName = rRevertProperty.Name;
+ SfxItemPropertyMapEntry const* pEntry = rPropSet.getPropertyMap().getByName(rPropertyName);
+
+ if (!pEntry)
+ {
+ // unknown property
+ break;
+ }
+ else if (pEntry->nFlags & beans::PropertyAttribute::READONLY)
+ {
+ break;
+ }
+ else if (rPropertyName == "NumberingRules")
+ {
+ aWhichPairs = aWhichPairs.MergeRange(RES_PARATR_NUMRULE, RES_PARATR_NUMRULE);
+ nNumId = aEntries.size();
+ }
+ else
+ {
+ aWhichPairs = aWhichPairs.MergeRange(pEntry->nWID, pEntry->nWID);
+ if (rPropertyName == "ParaStyleName")
+ nStyleId = aEntries.size();
+ }
+ aEntries.push_back(pEntry);
+ aValues.push_back(rRevertProperty.Value);
+ }
+
+ if (!aWhichPairs.empty())
+ {
+ sal_uInt16 nStylePoolId = USHRT_MAX;
+ OUString sParaStyleName, sUIStyle;
+ SfxItemSet aItemSet(rDoc.GetAttrPool(), std::move(aWhichPairs));
+
+ for (size_t i = 0; i < aEntries.size(); ++i)
+ {
+ const uno::Any &rValue = aValues[i];
+ if (i == nNumId)
+ {
+ uno::Reference<container::XNamed> xNumberingRules;
+ rValue >>= xNumberingRules;
+ if (xNumberingRules.is())
+ {
+ aItemSet.Put( SwNumRuleItem( xNumberingRules->getName() ));
+ // keep it during export
+ SwNumRule* pRule = rDoc.FindNumRulePtr(
+ xNumberingRules->getName());
+ if (pRule)
+ pRule->SetUsedByRedline(true);
+ }
+ }
+ else
+ {
+ SfxItemPropertyMapEntry const*const pEntry = aEntries[i];
+ rPropSet.setPropertyValue(*pEntry, rValue, aItemSet);
+ if (i == nStyleId)
+ rValue >>= sParaStyleName;
+ }
+ }
+
+ if (eType == RedlineType::ParagraphFormat && sParaStyleName.isEmpty())
+ nStylePoolId = RES_POOLCOLL_STANDARD;
+
+ // tdf#149747 Get UI style name from programmatic style name
+ SwStyleNameMapper::FillUIName(sParaStyleName, sUIStyle,
+ SwGetPoolIdFromName::TxtColl);
+ xRedlineExtraData.reset(new SwRedlineExtraData_FormatColl(
+ sUIStyle.isEmpty() ? sParaStyleName : sUIStyle, nStylePoolId, &aItemSet));
+ }
+ else if (eType == RedlineType::ParagraphFormat)
+ xRedlineExtraData.reset(new SwRedlineExtraData_FormatColl( "", RES_POOLCOLL_STANDARD, nullptr ));
+ }
+ }
+
+ SwRangeRedline* pRedline = new SwRangeRedline( aRedlineData, rPaM );
+
+ // set IsMoved bit of the redline to show and handle moved text
+ if ( nMovedID > 0 )
+ {
+ pRedline->SetMoved( nMovedID );
+ rRedlineAccess.GetRedlineTable().setMovedIDIfNeeded(nMovedID);
+ }
+
+ RedlineFlags nPrevMode = rRedlineAccess.GetRedlineFlags( );
+ // xRedlineExtraData is copied here
+ pRedline->SetExtraData( xRedlineExtraData.get() );
+
+ rRedlineAccess.SetRedlineFlags_intern(RedlineFlags::On);
+ auto const result(rRedlineAccess.AppendRedline(pRedline, false));
+ rRedlineAccess.SetRedlineFlags_intern( nPrevMode );
+ if (IDocumentRedlineAccess::AppendResult::IGNORED == result)
+ throw lang::IllegalArgumentException();
+}
+
+void makeTableRowRedline( SwTableLine& rTableLine,
+ std::u16string_view rRedlineType,
+ const uno::Sequence< beans::PropertyValue >& rRedlineProperties )
+{
+ SwDoc* pDoc = rTableLine.GetFrameFormat()->GetDoc();
+ IDocumentRedlineAccess* pRedlineAccess = &pDoc->getIDocumentRedlineAccess();
+
+ RedlineType eType;
+ if ( rRedlineType == u"TableRowInsert" )
+ {
+ eType = RedlineType::TableRowInsert;
+ }
+ else if ( rRedlineType == u"TableRowDelete" )
+ {
+ eType = RedlineType::TableRowDelete;
+ }
+ else
+ {
+ throw lang::IllegalArgumentException();
+ }
+
+ // set table row property "HasTextChangesOnly" to false
+ // to handle tracked deletion or insertion of the table row on the UI
+ const SvxPrintItem *pHasTextChangesOnlyProp =
+ rTableLine.GetFrameFormat()->GetAttrSet().GetItem<SvxPrintItem>(RES_PRINT);
+ if ( !pHasTextChangesOnlyProp || pHasTextChangesOnlyProp->GetValue() )
+ {
+ SvxPrintItem aSetTracking(RES_PRINT, false);
+ SwNodeIndex aInsPos( *(rTableLine.GetTabBoxes()[0]->GetSttNd()), 1 );
+ // as a workaround for the rows without text content,
+ // add a redline with invisible text CH_TXT_TRACKED_DUMMY_CHAR
+ if ( rTableLine.IsEmpty() )
+ {
+ SwPaM aPaM(aInsPos);
+ pDoc->getIDocumentContentOperations().InsertString( aPaM,
+ OUStringChar(CH_TXT_TRACKED_DUMMY_CHAR) );
+ aPaM.SetMark();
+ aPaM.GetMark()->SetContent(0);
+ makeRedline(aPaM, RedlineType::TableRowInsert == eType
+ ? u"Insert"
+ : u"Delete", rRedlineProperties);
+ }
+ SwCursor aCursor( SwPosition(aInsPos), nullptr );
+ pDoc->SetRowNotTracked( aCursor, aSetTracking );
+ }
+
+ comphelper::SequenceAsHashMap aPropMap( rRedlineProperties );
+ std::size_t nAuthor = 0;
+ OUString sAuthor;
+ if( aPropMap.getValue("RedlineAuthor") >>= sAuthor )
+ nAuthor = pRedlineAccess->InsertRedlineAuthor(sAuthor);
+
+ OUString sComment;
+ SwRedlineData aRedlineData( eType, nAuthor );
+ if( aPropMap.getValue("RedlineComment") >>= sComment )
+ aRedlineData.SetComment( sComment );
+
+ ::util::DateTime aStamp;
+ if( aPropMap.getValue("RedlineDateTime") >>= aStamp )
+ {
+ aRedlineData.SetTimeStamp(
+ DateTime( Date( aStamp.Day, aStamp.Month, aStamp.Year ), tools::Time( aStamp.Hours, aStamp.Minutes, aStamp.Seconds ) ) );
+ }
+
+ SwTableRowRedline* pRedline = new SwTableRowRedline( aRedlineData, rTableLine );
+ RedlineFlags nPrevMode = pRedlineAccess->GetRedlineFlags( );
+ pRedline->SetExtraData( nullptr );
+
+ pRedlineAccess->SetRedlineFlags_intern(RedlineFlags::On);
+ bool bRet = pRedlineAccess->AppendTableRowRedline( pRedline );
+ pRedlineAccess->SetRedlineFlags_intern( nPrevMode );
+ if( !bRet )
+ throw lang::IllegalArgumentException();
+}
+
+void makeTableCellRedline( SwTableBox& rTableBox,
+ std::u16string_view rRedlineType,
+ const uno::Sequence< beans::PropertyValue >& rRedlineProperties )
+{
+ SwDoc* pDoc = rTableBox.GetFrameFormat()->GetDoc();
+ IDocumentRedlineAccess* pRedlineAccess = &pDoc->getIDocumentRedlineAccess();
+
+ RedlineType eType;
+ if ( rRedlineType == u"TableCellInsert" )
+ {
+ eType = RedlineType::TableCellInsert;
+ }
+ else if ( rRedlineType == u"TableCellDelete" )
+ {
+ eType = RedlineType::TableCellDelete;
+ }
+ else
+ {
+ throw lang::IllegalArgumentException();
+ }
+
+ // set table row property "HasTextChangesOnly" to false
+ // to handle tracked deletion or insertion of the table row on the UI
+ const SvxPrintItem *pHasTextChangesOnlyProp =
+ rTableBox.GetFrameFormat()->GetAttrSet().GetItem<SvxPrintItem>(RES_PRINT);
+ if ( !pHasTextChangesOnlyProp || pHasTextChangesOnlyProp->GetValue() )
+ {
+ SvxPrintItem aSetTracking(RES_PRINT, false);
+ SwNodeIndex aInsPos( *rTableBox.GetSttNd(), 1 );
+ // as a workaround for the cells without text content,
+ // add a redline with invisible text CH_TXT_TRACKED_DUMMY_CHAR
+ if ( rTableBox.IsEmpty() )
+ {
+ SwPaM aPaM(aInsPos);
+ pDoc->getIDocumentContentOperations().InsertString( aPaM,
+ OUStringChar(CH_TXT_TRACKED_DUMMY_CHAR) );
+ aPaM.SetMark();
+ aPaM.GetMark()->SetContent(0);
+ makeRedline(aPaM, RedlineType::TableCellInsert == eType
+ ? u"Insert"
+ : u"Delete", rRedlineProperties);
+ }
+ SwCursor aCursor( SwPosition(aInsPos), nullptr );
+ pDoc->SetBoxAttr( aCursor, aSetTracking );
+ }
+
+ comphelper::SequenceAsHashMap aPropMap( rRedlineProperties );
+ std::size_t nAuthor = 0;
+ OUString sAuthor;
+ if( aPropMap.getValue("RedlineAuthor") >>= sAuthor )
+ nAuthor = pRedlineAccess->InsertRedlineAuthor(sAuthor);
+
+ OUString sComment;
+ SwRedlineData aRedlineData( eType, nAuthor );
+ if( aPropMap.getValue("RedlineComment") >>= sComment )
+ aRedlineData.SetComment( sComment );
+
+ ::util::DateTime aStamp;
+ if( aPropMap.getValue("RedlineDateTime") >>= aStamp )
+ {
+ aRedlineData.SetTimeStamp(
+ DateTime( Date( aStamp.Day, aStamp.Month, aStamp.Year ), tools::Time( aStamp.Hours, aStamp.Minutes, aStamp.Seconds ) ) );
+ }
+
+ SwTableCellRedline* pRedline = new SwTableCellRedline( aRedlineData, rTableBox );
+ RedlineFlags nPrevMode = pRedlineAccess->GetRedlineFlags( );
+ pRedline->SetExtraData( nullptr );
+
+ pRedlineAccess->SetRedlineFlags_intern(RedlineFlags::On);
+ bool bRet = pRedlineAccess->AppendTableCellRedline( pRedline );
+ pRedlineAccess->SetRedlineFlags_intern( nPrevMode );
+ if( !bRet )
+ throw lang::IllegalArgumentException();
+}
+
+void SwAnyMapHelper::SetValue( sal_uInt16 nWhichId, sal_uInt16 nMemberId, const uno::Any& rAny )
+{
+ sal_uInt32 nKey = (nWhichId << 16) + nMemberId;
+ m_Map[nKey] = rAny;
+}
+
+bool SwAnyMapHelper::FillValue( sal_uInt16 nWhichId, sal_uInt16 nMemberId, const uno::Any*& pAny )
+{
+ bool bRet = false;
+ sal_uInt32 nKey = (nWhichId << 16) + nMemberId;
+ auto aIt = m_Map.find( nKey );
+ if (aIt != m_Map.end())
+ {
+ pAny = & aIt->second;
+ bRet = true;
+ }
+ return bRet;
+}
+
+}//namespace SwUnoCursorHelper
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */