sw/inc/cmdid.h | 1 sw/qa/uibase/shells/shells.cxx | 52 ++++++++++++++++++ sw/sdi/_basesh.sdi | 5 + sw/sdi/swriter.sdi | 14 ++++ sw/source/uibase/shells/basesh.cxx | 104 +++++++++++++++++++++++++++++++++++++ 5 files changed, 176 insertions(+)
New commits: commit 402ab3d145a1e8e123caabf4567aef7b6631fc3c Author: Miklos Vajna <vmik...@collabora.com> AuthorDate: Fri Jan 13 08:20:48 2023 +0100 Commit: Miklos Vajna <vmik...@collabora.com> CommitDate: Fri Jan 13 09:55:20 2023 +0000 sw: add a new .uno:UpdateField UNO command This is similar to commit ea208f6004770eb4b81d28e6930cd0c7bd5d8f12 (sw: add a new .uno:UpdateBookmark UNO command, 2023-01-11, but that was for the bookmark under cursor, and this is for fields (refmarks as a start). Change-Id: I3e547b668361898b7ed734ea325fdf1d74e5dbb2 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/145427 Reviewed-by: Miklos Vajna <vmik...@collabora.com> Tested-by: Jenkins diff --git a/sw/inc/cmdid.h b/sw/inc/cmdid.h index 3baa2f050ecc..96d935a3bd2e 100644 --- a/sw/inc/cmdid.h +++ b/sw/inc/cmdid.h @@ -326,6 +326,7 @@ class SwUINumRuleItem; #define FN_UPDATE_SECTIONS (FN_INSERT2 + 35) #define FN_DELETE_TEXT_FORMFIELDS (FN_INSERT2 + 36) #define FN_UPDATE_BOOKMARK (FN_INSERT2 + 37) +#define FN_UPDATE_FIELD (FN_INSERT2 + 38) // Region: Format #define FN_AUTOFORMAT_APPLY (FN_FORMAT + 1 ) /* apply autoformat options */ diff --git a/sw/qa/uibase/shells/shells.cxx b/sw/qa/uibase/shells/shells.cxx index 8a7d7fce4f87..22cd709378cb 100644 --- a/sw/qa/uibase/shells/shells.cxx +++ b/sw/qa/uibase/shells/shells.cxx @@ -809,6 +809,58 @@ CPPUNIT_TEST_FIXTURE(SwUibaseShellsTest, testUpdateBookmark) CPPUNIT_ASSERT(it != pDoc->getIDocumentMarkAccess()->getAllMarksEnd()); } +CPPUNIT_TEST_FIXTURE(SwUibaseShellsTest, testUpdateRefmark) +{ + // Given a document with a refmark: + createSwDoc(); + SwDoc* pDoc = getSwDoc(); + uno::Sequence<css::beans::PropertyValue> aArgs = { + comphelper::makePropertyValue("TypeName", uno::Any(OUString("SetRef"))), + comphelper::makePropertyValue( + "Name", uno::Any(OUString("ZOTERO_ITEM CSL_CITATION {} old refmark"))), + comphelper::makePropertyValue("Content", uno::Any(OUString("old content"))), + }; + dispatchCommand(mxComponent, ".uno:InsertField", aArgs); + + // When updating that refmark: + std::vector<beans::PropertyValue> aArgsVec = comphelper::JsonToPropertyValues(R"json( +{ + "TypeName": { + "type": "string", + "value": "SetRef" + }, + "NamePrefix": { + "type": "string", + "value": "ZOTERO_ITEM CSL_CITATION" + }, + "Field": { + "type": "[]com.sun.star.beans.PropertyValue", + "value": { + "Name": { + "type": "string", + "value": "ZOTERO_ITEM CSL_CITATION {} new refmark" + }, + "Content": { + "type": "string", + "value": "new content" + } + } + } +} +)json"); + aArgs = comphelper::containerToSequence(aArgsVec); + dispatchCommand(mxComponent, ".uno:UpdateField", aArgs); + + // Then make sure that the document text features the new content: + SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell(); + SwTextNode* pTextNode = pWrtShell->GetCursor()->GetPointNode().GetTextNode(); + // Without the accompanying fix in place, this test would have failed with: + // - Expected: new content + // - Actual : old content + // i.e. the content was not updated. + CPPUNIT_ASSERT_EQUAL(OUString("new content"), pTextNode->GetText()); +} + CPPUNIT_PLUGIN_IMPLEMENT(); /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/sdi/_basesh.sdi b/sw/sdi/_basesh.sdi index dd195496d1a8..5ac4cb001eb3 100644 --- a/sw/sdi/_basesh.sdi +++ b/sw/sdi/_basesh.sdi @@ -154,6 +154,11 @@ interface BaseTextSelection StateMethod = NoState ; ] + FN_UPDATE_FIELD // status(final|play) + [ + ExecMethod = Execute ; + ] + FN_UPDATE_CHARTS // status(final|play) [ ExecMethod = Execute ; diff --git a/sw/sdi/swriter.sdi b/sw/sdi/swriter.sdi index ebded3b24b72..0a0073ea5020 100644 --- a/sw/sdi/swriter.sdi +++ b/sw/sdi/swriter.sdi @@ -6576,6 +6576,20 @@ SfxVoidItem UpdateFields FN_UPDATE_FIELDS GroupId = SfxGroupId::Edit; ] +SfxVoidItem UpdateField FN_UPDATE_FIELD +(SfxStringItem TypeName FN_PARAM_1, SfxStringItem NamePrefix FN_PARAM_2, SfxUnoAnyItem Field FN_PARAM_3) +[ + AutoUpdate = FALSE, + FastCall = TRUE, + ReadOnlyDoc = FALSE, + Toggle = FALSE, + Container = FALSE, + RecordAbsolute = FALSE, + RecordPerSet; + + GroupId = SfxGroupId::Edit; +] + SfxVoidItem UpdateInputFields FN_UPDATE_INPUTFIELDS () [ diff --git a/sw/source/uibase/shells/basesh.cxx b/sw/source/uibase/shells/basesh.cxx index d6d4ac2ff8bb..2e7489fda6de 100644 --- a/sw/source/uibase/shells/basesh.cxx +++ b/sw/source/uibase/shells/basesh.cxx @@ -882,6 +882,105 @@ bool UpdateFieldContents(SfxRequest& rReq, SwWrtShell& rWrtSh) pDoc->GetIDocumentUndoRedo().EndUndo(SwUndoId::INSBOOKMARK, nullptr); return true; } + +/// Searches for the specified field type and field name prefix under cursor and update the matching +/// field to have the provided new name and content. +void UpdateFieldContent(SfxRequest& rReq, SwWrtShell& rWrtSh) +{ + const SfxStringItem* pTypeName = rReq.GetArg<SfxStringItem>(FN_PARAM_1); + if (!pTypeName || pTypeName->GetValue() != "SetRef") + { + // This is implemented so far only for reference marks. + return; + } + + const SfxStringItem* pNamePrefix = rReq.GetArg<SfxStringItem>(FN_PARAM_2); + if (!pNamePrefix) + { + return; + } + const OUString& rNamePrefix = pNamePrefix->GetValue(); + + const SfxUnoAnyItem* pField = rReq.GetArg<SfxUnoAnyItem>(FN_PARAM_3); + if (!pField) + { + return; + } + uno::Sequence<beans::PropertyValue> aField; + pField->GetValue() >>= aField; + + SwDoc* pDoc = rWrtSh.GetDoc(); + pDoc->GetIDocumentUndoRedo().StartUndo(SwUndoId::INSBOOKMARK, nullptr); + rWrtSh.StartAction(); + comphelper::ScopeGuard g( + [&rWrtSh] + { + rWrtSh.EndAction(); + rWrtSh.GetDoc()->GetIDocumentUndoRedo().EndUndo(SwUndoId::INSBOOKMARK, nullptr); + }); + + SwPosition& rCursor = *rWrtSh.GetCursor()->GetPoint(); + SwTextNode* pTextNode = rCursor.GetNode().GetTextNode(); + std::vector<SwTextAttr*> aAttrs + = pTextNode->GetTextAttrsAt(rCursor.GetContentIndex(), RES_TXTATR_REFMARK); + if (aAttrs.empty()) + { + return; + } + + auto& rRefmark = const_cast<SwFormatRefMark&>(aAttrs[0]->GetRefMark()); + if (!rRefmark.GetRefName().startsWith(rNamePrefix)) + { + return; + } + + comphelper::SequenceAsHashMap aMap(aField); + auto aName = aMap["Name"].get<OUString>(); + rRefmark.GetRefName() = aName; + + OUString aContent = aMap["Content"].get<OUString>(); + auto pTextRefMark = const_cast<SwTextRefMark*>(rRefmark.GetTextRefMark()); + if (!pTextRefMark->End()) + { + return; + } + + // Insert markers to remember where the paste positions are. + const SwTextNode& rTextNode = pTextRefMark->GetTextNode(); + SwPaM aMarkers(SwPosition(rTextNode, *pTextRefMark->End())); + IDocumentContentOperations& rIDCO = pDoc->getIDocumentContentOperations(); + pTextRefMark->SetDontExpand(false); + if (!rIDCO.InsertString(aMarkers, "XY")) + { + return; + } + + SwPaM aPasteEnd(SwPosition(rTextNode, *pTextRefMark->End())); + aPasteEnd.Move(fnMoveBackward, GoInContent); + + // Paste HTML content. + SwPaM* pCursorPos = rWrtSh.GetCursor(); + *pCursorPos = aPasteEnd; + SwTranslateHelper::PasteHTMLToPaM(rWrtSh, pCursorPos, aContent.toUtf8(), true); + + // Update the refmark to point to the new content. + sal_Int32 nOldStart = pTextRefMark->GetStart(); + sal_Int32 nNewStart = *pTextRefMark->End(); + // First grow it to include text till the end of the paste position. + pTextRefMark->SetEnd(aPasteEnd.GetPoint()->GetContentIndex()); + // Then shrink it to only start at the paste start: we know that the refmark was + // truncated to the paste start, as the refmark has to stay inside a single text node + pTextRefMark->SetStart(nNewStart); + rTextNode.GetSwpHints().SortIfNeedBe(); + SwPaM aEndMarker(*aPasteEnd.GetPoint()); + aEndMarker.SetMark(); + aEndMarker.GetMark()->AdjustContent(1); + SwPaM aStartMarker(SwPosition(rTextNode, nOldStart), SwPosition(rTextNode, nNewStart)); + + // Remove markers. The start marker includes the old content as well. + rIDCO.DeleteAndJoin(aStartMarker); + rIDCO.DeleteAndJoin(aEndMarker); +} } // Evaluate respectively dispatching the slot Id @@ -926,6 +1025,11 @@ void SwBaseShell::Execute(SfxRequest &rReq) } } break; + case FN_UPDATE_FIELD: + { + UpdateFieldContent(rReq, rSh); + } + break; case FN_UPDATE_CHARTS: { SwWait aWait( *m_rView.GetDocShell(), true );