sw/inc/cmdid.h                      |    1 
 sw/qa/uibase/shells/shells.cxx      |   62 +++++++++++++++++++++++++++++++++
 sw/sdi/_textsh.sdi                  |    6 +++
 sw/sdi/swriter.sdi                  |   14 +++++++
 sw/source/uibase/shells/textfld.cxx |   66 ++++++++++++++++++++++++++++++++++++
 5 files changed, 149 insertions(+)

New commits:
commit 337416dafb66ed8f930d2d69e83fae438fc85f3c
Author:     Miklos Vajna <vmik...@collabora.com>
AuthorDate: Fri Jan 6 10:08:00 2023 +0100
Commit:     Miklos Vajna <vmik...@collabora.com>
CommitDate: Fri Jan 6 11:32:29 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.
    
    Change-Id: I46fc4f701a20839945d765eb13aec7362ab83788
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/145135
    Reviewed-by: Miklos Vajna <vmik...@collabora.com>
    Tested-by: Jenkins

diff --git a/sw/inc/cmdid.h b/sw/inc/cmdid.h
index dbb3a259b422..6f51a0e5e855 100644
--- a/sw/inc/cmdid.h
+++ b/sw/inc/cmdid.h
@@ -314,6 +314,7 @@ class SwUINumRuleItem;
 #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 ae2c0bf2fb5c..a394221997cb 100644
--- a/sw/qa/uibase/shells/shells.cxx
+++ b/sw/qa/uibase/shells/shells.cxx
@@ -595,6 +595,68 @@ CPPUNIT_TEST_FIXTURE(SwUibaseShellsTest, 
testUpdateRefmarks)
     CPPUNIT_ASSERT_EQUAL(OUString("new content"), pTextNode->GetText());
 }
 
+CPPUNIT_TEST_FIXTURE(SwUibaseShellsTest, testUpdateFieldmark)
+{
+    // Given a document with a fieldmark:
+    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:
+    SwDoc* pDoc = getSwDoc();
+    SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
+    pWrtShell->SttEndDoc(/*bStt=*/false);
+    pWrtShell->Left(SwCursorSkipMode::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 de4f50393c07..6e77c6d15c21 100644
--- a/sw/sdi/_textsh.sdi
+++ b/sw/sdi/_textsh.sdi
@@ -1808,6 +1808,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 a819927a5576..15760fd72c02 100644
--- a/sw/sdi/swriter.sdi
+++ b/sw/sdi/swriter.sdi
@@ -8316,6 +8316,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 b5decbd8b13a..dffea8e924ee 100644
--- a/sw/source/uibase/shells/textfld.cxx
+++ b/sw/source/uibase/shells/textfld.cxx
@@ -1010,6 +1010,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()->AdjustContent(2);
+        // Skip field end.
+        aPaM.GetMark()->AdjustContent(-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");

Reply via email to