sw/inc/cmdid.h | 1 sw/qa/uibase/shells/shells.cxx | 51 +++++++++++++++++++++ sw/sdi/_textsh.sdi | 5 ++ sw/sdi/swriter.sdi | 14 +++++ sw/source/uibase/shells/textsh1.cxx | 86 ++++++++++++++++++++++++++++++++++++ 5 files changed, 157 insertions(+)
New commits: commit ea208f6004770eb4b81d28e6930cd0c7bd5d8f12 Author: Miklos Vajna <vmik...@collabora.com> AuthorDate: Wed Jan 11 15:56:31 2023 +0100 Commit: Miklos Vajna <vmik...@collabora.com> CommitDate: Wed Jan 11 22:54:33 2023 +0000 sw: add a new .uno:UpdateBookmark UNO command It is possible to update all bookmarks (having a certain name prefix) and their contet, but one can't update the bookmark under the cursor, which is needed for Zotero citation clusters. Fix the problem by adding a new .uno:UpdateBookmark UNO command that can update the (innermost) bookmark under the current cursor. This can be implemented on top of the recently added IDocumentMarkAccess::getBookmarkFor(). The UNO command is intentionally hidden from the customize dialog since it only makes sense to invoke it from a macro / API with parameters, not interactively. Change-Id: I3e750dfb637f50716be1155a94bc986131b84f20 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/145351 Reviewed-by: Miklos Vajna <vmik...@collabora.com> Tested-by: Jenkins diff --git a/sw/inc/cmdid.h b/sw/inc/cmdid.h index befe8e26d607..3baa2f050ecc 100644 --- a/sw/inc/cmdid.h +++ b/sw/inc/cmdid.h @@ -325,6 +325,7 @@ class SwUINumRuleItem; #define FN_UPDATE_BOOKMARKS (FN_INSERT2 + 34) #define FN_UPDATE_SECTIONS (FN_INSERT2 + 35) #define FN_DELETE_TEXT_FORMFIELDS (FN_INSERT2 + 36) +#define FN_UPDATE_BOOKMARK (FN_INSERT2 + 37) // 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 edfe255f4ad7..b8f466b63a8e 100644 --- a/sw/qa/uibase/shells/shells.cxx +++ b/sw/qa/uibase/shells/shells.cxx @@ -758,6 +758,57 @@ CPPUNIT_TEST_FIXTURE(SwUibaseShellsTest, testDeleteFieldmarks) CPPUNIT_ASSERT_EQUAL(OUString("result 1result 2"), aActual); } +CPPUNIT_TEST_FIXTURE(SwUibaseShellsTest, testUpdateBookmark) +{ + // Given a document with a bookmarks, covering "BC": + createSwDoc(); + SwDoc* pDoc = getSwDoc(); + SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell(); + pWrtShell->Insert("ABCD"); + pWrtShell->SttEndDoc(/*bStt=*/true); + pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/false, 1, /*bBasicCall=*/false); + pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/true, 2, /*bBasicCall=*/false); + pWrtShell->SetBookmark(vcl::KeyCode(), "ZOTERO_BREF_old"); + + // When updating the content of the bookmark under the cursor: + pWrtShell->SttEndDoc(/*bStt=*/true); + pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/false, 2, /*bBasicCall=*/false); + std::vector<beans::PropertyValue> aArgsVec = comphelper::JsonToPropertyValues(R"json( +{ + "BookmarkNamePrefix": { + "type": "string", + "value": "ZOTERO_BREF_" + }, + "Bookmark": { + "type": "[]com.sun.star.beans.PropertyValue", + "value": { + "Bookmark": { + "type": "string", + "value": "ZOTERO_BREF_new" + }, + "BookmarkText": { + "type": "string", + "value": "new result" + } + } + } +} +)json"); + uno::Sequence<beans::PropertyValue> aArgs = comphelper::containerToSequence(aArgsVec); + dispatchCommand(mxComponent, ".uno:UpdateBookmark", aArgs); + + // Then make sure that the only paragraph is updated correctly: + SwCursor* pCursor = pWrtShell->GetCursor(); + OUString aActual = pCursor->GetPointNode().GetTextNode()->GetText(); + // Without the accompanying fix in place, this test would have failed with: + // - Expected: Anew resultD + // - Actual : ABCD + // i.e. it was not possible to update just the bookmark under cursor. + CPPUNIT_ASSERT_EQUAL(OUString("Anew resultD"), aActual); + auto it = pDoc->getIDocumentMarkAccess()->findMark("ZOTERO_BREF_new"); + CPPUNIT_ASSERT(it != pDoc->getIDocumentMarkAccess()->getAllMarksEnd()); +} + CPPUNIT_PLUGIN_IMPLEMENT(); /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/sdi/_textsh.sdi b/sw/sdi/_textsh.sdi index 99f50c8e3b27..f08e0b21b675 100644 --- a/sw/sdi/_textsh.sdi +++ b/sw/sdi/_textsh.sdi @@ -152,6 +152,11 @@ interface BaseText StateMethod = GetState ; DisableFlags="SfxDisableFlags::SwOnProtectedCursor"; ] + FN_UPDATE_BOOKMARK + [ + ExecMethod = Execute ; + DisableFlags="SfxDisableFlags::SwOnProtectedCursor"; + ] FN_UPDATE_SECTIONS [ ExecMethod = Execute ; diff --git a/sw/sdi/swriter.sdi b/sw/sdi/swriter.sdi index 76939d94c03a..ebded3b24b72 100644 --- a/sw/sdi/swriter.sdi +++ b/sw/sdi/swriter.sdi @@ -2600,6 +2600,20 @@ SfxVoidItem UpdateBookmarks FN_UPDATE_BOOKMARKS GroupId = SfxGroupId::Insert; ] +SfxVoidItem UpdateBookmark FN_UPDATE_BOOKMARK +(SfxStringItem BookmarkNamePrefix FN_PARAM_1, SfxUnoAnyItem Bookmark FN_PARAM_2) +[ + AutoUpdate = FALSE, + FastCall = FALSE, + ReadOnlyDoc = FALSE, + Toggle = FALSE, + Container = FALSE, + RecordAbsolute = FALSE, + RecordPerSet; + + GroupId = SfxGroupId::Insert; +] + SfxVoidItem UpdateSections FN_UPDATE_SECTIONS (SfxStringItem SectionNamePrefix FN_PARAM_1, SfxUnoAnyItem Sections FN_PARAM_2) [ diff --git a/sw/source/uibase/shells/textsh1.cxx b/sw/source/uibase/shells/textsh1.cxx index 3a5a706c901f..c7fe4fc9c190 100644 --- a/sw/source/uibase/shells/textsh1.cxx +++ b/sw/source/uibase/shells/textsh1.cxx @@ -103,6 +103,7 @@ #include <bookmark.hxx> #include <linguistic/misc.hxx> #include <comphelper/sequenceashashmap.hxx> +#include <comphelper/scopeguard.hxx> #include <authfld.hxx> #include <config_wasm_strip.h> #if !ENABLE_WASM_STRIP_EXTRA @@ -535,6 +536,83 @@ void UpdateBookmarks(SfxRequest& rReq, SwWrtShell& rWrtSh) rWrtSh.EndAction(); rWrtSh.GetDoc()->GetIDocumentUndoRedo().EndUndo(SwUndoId::INSBOOKMARK, nullptr); } + +void UpdateBookmark(SfxRequest& rReq, SwWrtShell& rWrtSh) +{ + if (rWrtSh.getIDocumentSettingAccess().get(DocumentSettingId::PROTECT_BOOKMARKS)) + { + return; + } + + OUString aBookmarkNamePrefix; + const SfxStringItem* pBookmarkNamePrefix = rReq.GetArg<SfxStringItem>(FN_PARAM_1); + if (pBookmarkNamePrefix) + { + aBookmarkNamePrefix = pBookmarkNamePrefix->GetValue(); + } + + uno::Sequence<beans::PropertyValue> aBookmark; + const SfxUnoAnyItem* pBookmarks = rReq.GetArg<SfxUnoAnyItem>(FN_PARAM_2); + if (pBookmarks) + { + pBookmarks->GetValue() >>= aBookmark; + } + + rWrtSh.GetDoc()->GetIDocumentUndoRedo().StartUndo(SwUndoId::INSBOOKMARK, nullptr); + rWrtSh.StartAction(); + comphelper::ScopeGuard g( + [&rWrtSh] + { + rWrtSh.EndAction(); + rWrtSh.GetDoc()->GetIDocumentUndoRedo().EndUndo(SwUndoId::INSBOOKMARK, nullptr); + }); + + IDocumentMarkAccess& rIDMA = *rWrtSh.GetDoc()->getIDocumentMarkAccess(); + SwPosition& rCursor = *rWrtSh.GetCursor()->GetPoint(); + auto pBookmark = dynamic_cast<sw::mark::Bookmark*>(rIDMA.getBookmarkFor(rCursor)); + if (!pBookmark || !pBookmark->GetName().startsWith(aBookmarkNamePrefix)) + { + return; + } + + comphelper::SequenceAsHashMap aMap(aBookmark); + if (aMap["Bookmark"].get<OUString>() != pBookmark->GetName()) + { + rIDMA.renameMark(pBookmark, aMap["Bookmark"].get<OUString>()); + } + + OUString aBookmarkText = aMap["BookmarkText"].get<OUString>(); + + // Insert markers to remember where the paste positions are. + SwPaM aMarkers(pBookmark->GetMarkEnd()); + IDocumentContentOperations& rIDCO = rWrtSh.GetDoc()->getIDocumentContentOperations(); + if (!rIDCO.InsertString(aMarkers, "XY")) + { + return; + } + + SwPaM aPasteEnd(pBookmark->GetMarkEnd()); + aPasteEnd.Move(fnMoveForward, GoInContent); + + // Paste HTML content. + SwPaM* pCursorPos = rWrtSh.GetCursor(); + *pCursorPos = aPasteEnd; + SwTranslateHelper::PasteHTMLToPaM(rWrtSh, pCursorPos, aBookmarkText.toUtf8(), true); + + // Update the bookmark to point to the new content. + SwPaM aPasteStart(pBookmark->GetMarkEnd()); + aPasteStart.Move(fnMoveForward, GoInContent); + SwPaM aStartMarker(pBookmark->GetMarkStart(), *aPasteStart.GetPoint()); + SwPaM aEndMarker(*aPasteEnd.GetPoint(), *aPasteEnd.GetPoint()); + aEndMarker.GetMark()->AdjustContent(1); + pBookmark->SetMarkPos(*aPasteStart.GetPoint()); + pBookmark->SetOtherMarkPos(*aPasteEnd.GetPoint()); + + // Remove markers. the start marker includes the old content as well. + rIDCO.DeleteAndJoin(aStartMarker); + rIDCO.DeleteAndJoin(aEndMarker); + rIDMA.assureSortedMarkContainers(); +} } void SwTextShell::Execute(SfxRequest &rReq) @@ -916,9 +994,17 @@ void SwTextShell::Execute(SfxRequest &rReq) } case FN_UPDATE_BOOKMARKS: { + // This updates all bookmarks in the document that match the conditions specified in + // rReq. UpdateBookmarks(rReq, rWrtSh); break; } + case FN_UPDATE_BOOKMARK: + { + // This updates the bookmark under the cursor. + UpdateBookmark(rReq, rWrtSh); + break; + } case FN_DELETE_BOOKMARK: { if (pItem && !rWrtSh.getIDocumentSettingAccess().get(DocumentSettingId::PROTECT_BOOKMARKS))