sw/inc/cmdid.h | 1 sw/qa/uibase/shells/shells.cxx | 61 +++++++++++++++++++++++++++++++++ sw/sdi/_textsh.sdi | 6 +++ sw/sdi/swriter.sdi | 14 +++++++ sw/source/uibase/shells/textfld.cxx | 66 ++++++++++++++++++++++++++++++++++++ 5 files changed, 148 insertions(+)
New commits: commit 1293d212a148338eee4b62893e51ed0428f2fde5 Author: Miklos Vajna <vmik...@collabora.com> AuthorDate: Fri Jan 6 10:08:00 2023 +0100 Commit: Miklos Vajna <vmik...@collabora.com> CommitDate: Mon Jan 9 12:54:53 2023 +0000 sw: add a new .uno:UpdateTextFormField UNO command It is possible to update all fieldsmarks (of a certion type, of a certain field command prefix), but one can't update the fieldmark under the cursor, which is needed for Zotero citation clusters. To make this more complex, insertion inside an existing fieldmark is explicitly not wanted, see commit a178a2ac6df8dc63a7ab8d4a19b90ae8a17baca4 (sw UI: fix crash on inserting a fieldmark inside a fieldmark, 2023-01-02). Fix the problem by adding a new .uno:UpdateTextFormField UNO command that can update the (innermost) fieldmark under the current cursor. 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. (cherry picked from commit 337416dafb66ed8f930d2d69e83fae438fc85f3c) Conflicts: sw/source/uibase/shells/textfld.cxx Change-Id: I46fc4f701a20839945d765eb13aec7362ab83788 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/145144 Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com> Reviewed-by: Miklos Vajna <vmik...@collabora.com> diff --git a/sw/inc/cmdid.h b/sw/inc/cmdid.h index dfd22f400459..7ae4a76c3bff 100644 --- a/sw/inc/cmdid.h +++ b/sw/inc/cmdid.h @@ -305,6 +305,7 @@ #define FN_PROTECT_BOOKMARKS (FN_INSERT2 + 27) #define FN_UPDATE_TEXT_FORMFIELDS (FN_INSERT2 + 28) +#define FN_UPDATE_TEXT_FORMFIELD (FN_INSERT2 + 29) // clipboard table content #define FN_PASTE_NESTED_TABLE (FN_INSERT2 + 30) /* instead of the cell-by-cell copy between source and target tables */ diff --git a/sw/qa/uibase/shells/shells.cxx b/sw/qa/uibase/shells/shells.cxx index ced0157284ae..794932e8cf25 100644 --- a/sw/qa/uibase/shells/shells.cxx +++ b/sw/qa/uibase/shells/shells.cxx @@ -531,6 +531,67 @@ CPPUNIT_TEST_FIXTURE(SwUibaseShellsTest, testUpdateRefmarks) CPPUNIT_ASSERT_EQUAL(OUString("new content"), pTextNode->GetText()); } +CPPUNIT_TEST_FIXTURE(SwUibaseShellsTest, testUpdateFieldmark) +{ + // Given a document with a fieldmark: + SwDoc* pDoc = createSwDoc(); + uno::Sequence<css::beans::PropertyValue> aArgs = { + comphelper::makePropertyValue("FieldType", uno::Any(OUString(ODF_UNHANDLED))), + comphelper::makePropertyValue("FieldCommand", + uno::Any(OUString("ADDIN ZOTERO_ITEM old command 1"))), + comphelper::makePropertyValue("FieldResult", uno::Any(OUString("old result 1"))), + }; + dispatchCommand(mxComponent, ".uno:TextFormField", aArgs); + + // When updating that fieldmark to have new field command & result: + SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell(); + pWrtShell->SttEndDoc(/*bStt=*/false); + pWrtShell->Left(CRSR_SKIP_CHARS, /*bSelect=*/false, 1, /*bBasicCall=*/false); + std::vector<beans::PropertyValue> aArgsVec = comphelper::JsonToPropertyValues(R"json( +{ + "FieldType": { + "type": "string", + "value": "vnd.oasis.opendocument.field.UNHANDLED" + }, + "FieldCommandPrefix": { + "type": "string", + "value": "ADDIN ZOTERO_ITEM" + }, + "Field": { + "type": "[]com.sun.star.beans.PropertyValue", + "value": { + "FieldType": { + "type": "string", + "value": "vnd.oasis.opendocument.field.UNHANDLED" + }, + "FieldCommand": { + "type": "string", + "value": "ADDIN ZOTERO_ITEM new command 1" + }, + "FieldResult": { + "type": "string", + "value": "new result 1" + } + } + } +} +)json"); + aArgs = comphelper::containerToSequence(aArgsVec); + dispatchCommand(mxComponent, ".uno:UpdateTextFormField", aArgs); + + // Then make sure that the document text is updated accordingly: + SwCursor* pCursor = pWrtShell->GetCursor(); + OUString aActual = pCursor->Start()->GetNode().GetTextNode()->GetText(); + static sal_Unicode const aForbidden[] + = { CH_TXT_ATR_FIELDSTART, CH_TXT_ATR_FIELDSEP, CH_TXT_ATR_FIELDEND, 0 }; + aActual = comphelper::string::removeAny(aActual, aForbidden); + // Without the accompanying fix in place, this test would have failed with: + // - Expected: new result 1 + // - Actual : old result 1 + // i.e. the document text was not updated. + CPPUNIT_ASSERT_EQUAL(OUString("new result 1"), aActual); +} + CPPUNIT_PLUGIN_IMPLEMENT(); /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/sdi/_textsh.sdi b/sw/sdi/_textsh.sdi index fb7100fde476..39d2b33f7821 100644 --- a/sw/sdi/_textsh.sdi +++ b/sw/sdi/_textsh.sdi @@ -1791,6 +1791,12 @@ interface BaseText StateMethod = StateField ; ] + FN_UPDATE_TEXT_FORMFIELD + [ + ExecMethod = ExecField ; + StateMethod = StateField ; + ] + FN_PROTECT_FIELDS [ ExecMethod = Execute ; diff --git a/sw/sdi/swriter.sdi b/sw/sdi/swriter.sdi index 6deae2abb2c8..837ebdd3ef75 100644 --- a/sw/sdi/swriter.sdi +++ b/sw/sdi/swriter.sdi @@ -8287,6 +8287,20 @@ SfxVoidItem TextFormFields FN_UPDATE_TEXT_FORMFIELDS GroupId = SfxGroupId::Controls; ] +SfxVoidItem UpdateTextFormField FN_UPDATE_TEXT_FORMFIELD +(SfxStringItem FieldType FN_PARAM_1, SfxStringItem FieldCommandPrefix FN_PARAM_2, SfxUnoAnyItem Field FN_PARAM_3) +[ + AutoUpdate = TRUE, + FastCall = FALSE, + ReadOnlyDoc = FALSE, + Toggle = FALSE, + Container = FALSE, + RecordAbsolute = FALSE, + RecordPerSet; + + GroupId = SfxGroupId::Controls; +] + SfxVoidItem CheckBoxFormField FN_INSERT_CHECKBOX_FORMFIELD [ diff --git a/sw/source/uibase/shells/textfld.cxx b/sw/source/uibase/shells/textfld.cxx index 3d97a9fbcf28..93ee93458b5d 100644 --- a/sw/source/uibase/shells/textfld.cxx +++ b/sw/source/uibase/shells/textfld.cxx @@ -1001,6 +1001,72 @@ FIELD_INSERT: }); rReq.Done(); } + break; + case FN_UPDATE_TEXT_FORMFIELD: + { + OUString aFieldType; + const SfxStringItem* pFieldType = rReq.GetArg<SfxStringItem>(FN_PARAM_1); + if (pFieldType) + { + aFieldType = pFieldType->GetValue(); + } + OUString aFieldCommandPrefix; + const SfxStringItem* pFieldCommandPrefix = rReq.GetArg<SfxStringItem>(FN_PARAM_2); + if (pFieldCommandPrefix) + { + aFieldCommandPrefix = pFieldCommandPrefix->GetValue(); + } + uno::Sequence<beans::PropertyValue> aField; + const SfxUnoAnyItem* pFields = rReq.GetArg<SfxUnoAnyItem>(FN_PARAM_3); + if (pFields) + { + pFields->GetValue() >>= aField; + } + + IDocumentMarkAccess& rIDMA = *rSh.getIDocumentMarkAccess(); + SwPosition& rCursor = *rSh.GetCursor()->GetPoint(); + sw::mark::IFieldmark* pFieldmark = rIDMA.getFieldmarkFor(rCursor); + if (!pFieldmark) + { + break; + } + + if (pFieldmark->GetFieldname() != aFieldType) + { + break; + } + + auto itParam = pFieldmark->GetParameters()->find(ODF_CODE_PARAM); + if (itParam == pFieldmark->GetParameters()->end()) + { + break; + } + + OUString aCommand; + itParam->second >>= aCommand; + if (!aCommand.startsWith(aFieldCommandPrefix)) + { + break; + } + + rSh.GetDoc()->GetIDocumentUndoRedo().StartUndo(SwUndoId::INSERT_FORM_FIELD, nullptr); + rSh.StartAction(); + comphelper::SequenceAsHashMap aMap(aField); + itParam->second = aMap["FieldCommand"]; + SwPaM aPaM(pFieldmark->GetMarkPos(), pFieldmark->GetOtherMarkPos()); + aPaM.Normalize(); + // Skip field start & separator. + aPaM.GetPoint()->nContent += 2; + // Skip field end. + aPaM.GetMark()->nContent -= 1; + rSh.GetDoc()->getIDocumentContentOperations().DeleteAndJoin(aPaM); + OUString aFieldResult; + aMap["FieldResult"] >>= aFieldResult; + SwTranslateHelper::PasteHTMLToPaM(rSh, &aPaM, aFieldResult.toUtf8(), true); + + rSh.EndAction(); + rSh.GetDoc()->GetIDocumentUndoRedo().EndUndo(SwUndoId::INSERT_FORM_FIELD, nullptr); + } break; default: OSL_FAIL("wrong dispatcher");