diff options
author | Miklos Vajna <vmiklos@collabora.com> | 2023-01-04 08:56:04 +0100 |
---|---|---|
committer | Miklos Vajna <vmiklos@collabora.com> | 2023-01-04 08:41:53 +0000 |
commit | babba472391d26aed68d7ac31c7a918c08e65256 (patch) | |
tree | 2f99dc80a00e67dcdbd5a643207463b5f706c1ea /sw | |
parent | 2eab74ab78b0b2a7a9c846251e2b79f595bc0878 (diff) |
sw, UpdateFields: add new TypeName, NamePrefix and Fields parameters
Currently the .uno:InsertField command allows inserting a refmark with a
provided name & content, but existing refmarks can't be updated
similarly. This is a problem in case Zotero citations are to be modeled
with refmarks.
Another trouble is that refmarks don't have dummy characters and have to
stay inside a single paragraph, so we need to be careful to replace the
content in a way that keeps the refmark alive, a naive delete + insert
will delete the refmark as well.
Fix the problem by extending the existing .uno:UpdateFields command with
3 new optional parameters, somewhat similar to what commit
724180ec495a696c79332653cb6fb52ecfbccc29 (sw: add a new
.uno:UpdateBookmarks UNO command, 2022-12-14) did.
As usual, the provided new text is meant to be HTML, which allows
formatted content.
Change-Id: Ib0951aa1a39e1b47bcf8b47bc9d65c89e0853e96
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/145033
Reviewed-by: Miklos Vajna <vmiklos@collabora.com>
Tested-by: Jenkins
Diffstat (limited to 'sw')
-rw-r--r-- | sw/qa/uibase/shells/shells.cxx | 55 | ||||
-rw-r--r-- | sw/sdi/swriter.sdi | 2 | ||||
-rw-r--r-- | sw/source/uibase/shells/basesh.cxx | 108 |
3 files changed, 164 insertions, 1 deletions
diff --git a/sw/qa/uibase/shells/shells.cxx b/sw/qa/uibase/shells/shells.cxx index 2f89720cf570..0348965b234c 100644 --- a/sw/qa/uibase/shells/shells.cxx +++ b/sw/qa/uibase/shells/shells.cxx @@ -532,6 +532,61 @@ CPPUNIT_TEST_FIXTURE(SwUibaseShellsTest, testInsertFieldmarkReadonly) CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), nActual); } +CPPUNIT_TEST_FIXTURE(SwUibaseShellsTest, testUpdateRefmarks) +{ + // 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: + SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell(); + pWrtShell->SttEndDoc(/*bStt=*/true); + std::vector<beans::PropertyValue> aArgsVec = comphelper::JsonToPropertyValues(R"json( +{ + "TypeName": { + "type": "string", + "value": "SetRef" + }, + "NamePrefix": { + "type": "string", + "value": "ZOTERO_ITEM CSL_CITATION" + }, + "Fields": { + "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:UpdateFields", aArgs); + + // Then make sure that the document text features the new content: + 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 doc 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/swriter.sdi b/sw/sdi/swriter.sdi index e14b52cba879..ebd54c064402 100644 --- a/sw/sdi/swriter.sdi +++ b/sw/sdi/swriter.sdi @@ -6514,7 +6514,7 @@ SfxVoidItem UpdateCurIndex FN_UPDATE_CUR_TOX ] SfxVoidItem UpdateFields FN_UPDATE_FIELDS -() +(SfxStringItem TypeName FN_PARAM_1, SfxStringItem NamePrefix FN_PARAM_2, SfxUnoAnyItem Fields FN_PARAM_3) [ AutoUpdate = FALSE, FastCall = TRUE, diff --git a/sw/source/uibase/shells/basesh.cxx b/sw/source/uibase/shells/basesh.cxx index 5e13fece9132..81eaec0f7c41 100644 --- a/sw/source/uibase/shells/basesh.cxx +++ b/sw/source/uibase/shells/basesh.cxx @@ -90,6 +90,7 @@ #include <svx/galleryitem.hxx> #include <sfx2/devtools/DevelopmentToolChildWindow.hxx> #include <com/sun/star/gallery/GalleryItemType.hpp> +#include <com/sun/star/beans/PropertyValues.hpp> #include <memory> #include <svx/unobrushitemhelper.hxx> @@ -98,12 +99,16 @@ #include <osl/diagnose.h> #include <svx/svxdlg.hxx> +#include <comphelper/sequenceashashmap.hxx> #include <shellres.hxx> #include <UndoTable.hxx> #include <ndtxt.hxx> #include <UndoManager.hxx> +#include <fmtrfmrk.hxx> +#include <txtrfmrk.hxx> +#include <translatehelper.hxx> FlyMode SwBaseShell::s_eFrameMode = FLY_DRAG_END; @@ -765,6 +770,102 @@ void SwBaseShell::StateUndo(SfxItemSet &rSet) } } +namespace +{ +/// Searches for the specified field type and field name prefix and update the matching fields to +/// have the provided new name and content. +bool UpdateFieldConents(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 false; + } + + const SfxStringItem* pNamePrefix = rReq.GetArg<SfxStringItem>(FN_PARAM_2); + if (!pNamePrefix) + { + return false; + } + const OUString& rNamePrefix = pNamePrefix->GetValue(); + + const SfxUnoAnyItem* pFields = rReq.GetArg<SfxUnoAnyItem>(FN_PARAM_3); + if (!pFields) + { + return false; + } + uno::Sequence<beans::PropertyValues> aFields; + pFields->GetValue() >>= aFields; + + SwDoc* pDoc = rWrtSh.GetDoc(); + pDoc->GetIDocumentUndoRedo().StartUndo(SwUndoId::INSBOOKMARK, nullptr); + rWrtSh.StartAction(); + for (sal_uInt16 nRefMark = 0; nRefMark < pDoc->GetRefMarks(); ++nRefMark) + { + auto pRefMark = const_cast<SwFormatRefMark*>(pDoc->GetRefMark(nRefMark)); + if (!pRefMark->GetRefName().startsWith(rNamePrefix)) + { + continue; + } + + if (aFields.getLength() <= nRefMark) + { + continue; + } + comphelper::SequenceAsHashMap aMap(aFields[nRefMark]); + auto aName = aMap["Name"].get<OUString>(); + pRefMark->GetRefName() = aName; + + OUString aContent = aMap["Content"].get<OUString>(); + auto pTextRefMark = const_cast<SwTextRefMark*>(pRefMark->GetTextRefMark()); + if (!pTextRefMark->End()) + { + continue; + } + + // 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); + bool bSuccess = rIDCO.InsertString(aMarkers, "XY"); + if (bSuccess) + { + 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); + } + } + + rWrtSh.EndAction(); + pDoc->GetIDocumentUndoRedo().EndUndo(SwUndoId::INSBOOKMARK, nullptr); + return true; +} +} + // Evaluate respectively dispatching the slot Id void SwBaseShell::Execute(SfxRequest &rReq) @@ -787,6 +888,13 @@ void SwBaseShell::Execute(SfxRequest &rReq) break; case FN_UPDATE_FIELDS: { + if (UpdateFieldConents(rReq, rSh)) + { + // Parameters indicated that the name / content of fields has to be updated to + // the provided values, don't do an actual fields update. + break; + } + rSh.UpdateDocStat(); rSh.EndAllTableBoxEdit(); rSh.SwViewShell::UpdateFields(true); |