sw/inc/cmdid.h                      |    1 
 sw/qa/uibase/shells/shells.cxx      |   44 +++++++++++++++++++++++++++++
 sw/sdi/_textsh.sdi                  |    5 +++
 sw/sdi/swriter.sdi                  |   14 +++++++++
 sw/source/uibase/shells/textfld.cxx |   54 ++++++++++++++++++++++++++++++++++++
 5 files changed, 118 insertions(+)

New commits:
commit 66f8a0a5029004e17fb9f42051ec3784f5e0914e
Author:     Miklos Vajna <[email protected]>
AuthorDate: Tue Jan 10 15:40:34 2023 +0100
Commit:     Miklos Vajna <[email protected]>
CommitDate: Thu Jan 12 14:51:51 2023 +0000

    sw: add a new .uno:DeleteTextFormFields UNO command
    
    Users sometimes want to "unlink" their citations, which means deleting
    the fieldmarks (if fieldmarks are used to represent citations, e.g.
    with Zotero), which means keeping the field result as-is, but removing
    the field metadata and the actual field start/seprator/end characters.
    
    Do this similar to .uno:TextFormFields, which can do an update of such
    fieldmarks, i.e. add the ability to filter for a certain type and field
    command prefix. This is meant to allow removal af all fieldmark that
    belongs to one one feature, e.g. Zotero.
    
    This is similar to 7765b442e13048f857fd7ee49ced1731caee297e (sw: add a
    new .uno:TextFormFields UNO command, 2022-11-28), but this is about
    deleting (the field commands, not the result), while that was about
    updating.
    
    The same for bookmarks & refmarks are not yet supported.
    
    (cherry picked from commit c68d06dfa1498f862923eaddf3e5d247650a53d5)
    
    Change-Id: I02548b030b1822f7b36d3bc5ff9553d728f065c2
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/145347
    Tested-by: Jenkins CollaboraOffice <[email protected]>
    Reviewed-by: Justin Luth <[email protected]>
    Reviewed-by: Miklos Vajna <[email protected]>

diff --git a/sw/inc/cmdid.h b/sw/inc/cmdid.h
index 1d42d0113e41..666e41e3efe4 100644
--- a/sw/inc/cmdid.h
+++ b/sw/inc/cmdid.h
@@ -313,6 +313,7 @@
 #define FN_TABLE_PASTE_COL_BEFORE   (FN_INSERT2 + 32)  /* paste table as new 
table columns */
 #define FN_UPDATE_BOOKMARKS (FN_INSERT2 + 34)
 #define FN_UPDATE_SECTIONS (FN_INSERT2 + 35)
+#define FN_DELETE_TEXT_FORMFIELDS (FN_INSERT2 + 36)
 
 // 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 78a4f339c41b..2b949d26335d 100644
--- a/sw/qa/uibase/shells/shells.cxx
+++ b/sw/qa/uibase/shells/shells.cxx
@@ -648,6 +648,50 @@ CPPUNIT_TEST_FIXTURE(SwUibaseShellsTest, 
testUpdateSections)
     CPPUNIT_ASSERT_EQUAL(OUString("new content"), aActualResult);
 }
 
+CPPUNIT_TEST_FIXTURE(SwUibaseShellsTest, testDeleteFieldmarks)
+{
+    // Given a document with 2 fieldmarks:
+    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("result 1"))),
+        };
+        dispatchCommand(mxComponent, ".uno:TextFormField", aArgs);
+    }
+    {
+        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 2"))),
+            comphelper::makePropertyValue("FieldResult", 
uno::Any(OUString("result 2"))),
+        };
+        dispatchCommand(mxComponent, ".uno:TextFormField", aArgs);
+    }
+
+    // When deleting those fieldmarks:
+    uno::Sequence<css::beans::PropertyValue> aArgs
+        = { comphelper::makePropertyValue("FieldType", 
uno::Any(OUString(ODF_UNHANDLED))),
+            comphelper::makePropertyValue("FieldCommandPrefix",
+                                          uno::Any(OUString("ADDIN 
ZOTERO_ITEM"))) };
+    dispatchCommand(mxComponent, ".uno:DeleteTextFormFields", aArgs);
+
+    // Then make sure that the document doesn't contain fields anymore:
+    // Without the accompanying fix in place, this test would have failed with:
+    // - Expected: 0
+    // - Actual  : 2
+    // i.e. the fieldmarks were not deleted.
+    CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(0),
+                         pDoc->getIDocumentMarkAccess()->getAllMarksCount());
+    SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
+    pWrtShell->SttEndDoc(/*bStt=*/true);
+    SwCursor* pCursor = pWrtShell->GetCursor();
+    OUString aActual = pCursor->Start()->GetNode().GetTextNode()->GetText();
+    CPPUNIT_ASSERT_EQUAL(OUString("result 1result 2"), aActual);
+}
+
 CPPUNIT_PLUGIN_IMPLEMENT();
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/sdi/_textsh.sdi b/sw/sdi/_textsh.sdi
index a9b9eb998f8a..3b7cf4af83de 100644
--- a/sw/sdi/_textsh.sdi
+++ b/sw/sdi/_textsh.sdi
@@ -1803,6 +1803,11 @@ interface BaseText
         StateMethod = StateField ;
     ]
 
+    FN_DELETE_TEXT_FORMFIELDS
+    [
+        ExecMethod = ExecField ;
+    ]
+
     FN_PROTECT_FIELDS
     [
         ExecMethod = Execute ;
diff --git a/sw/sdi/swriter.sdi b/sw/sdi/swriter.sdi
index ddcd53517aa0..b978c73dd409 100644
--- a/sw/sdi/swriter.sdi
+++ b/sw/sdi/swriter.sdi
@@ -8301,6 +8301,20 @@ SfxVoidItem TextFormFields FN_UPDATE_TEXT_FORMFIELDS
     GroupId = SfxGroupId::Controls;
 ]
 
+SfxVoidItem DeleteTextFormFields FN_DELETE_TEXT_FORMFIELDS
+(SfxStringItem FieldType FN_PARAM_1, SfxStringItem FieldCommandPrefix 
FN_PARAM_2)
+[
+    AutoUpdate = TRUE,
+    FastCall = FALSE,
+    ReadOnlyDoc = FALSE,
+    Toggle = FALSE,
+    Container = FALSE,
+    RecordAbsolute = FALSE,
+    RecordPerSet;
+
+    GroupId = SfxGroupId::Controls;
+]
+
 SfxVoidItem UpdateTextFormField FN_UPDATE_TEXT_FORMFIELD
 (SfxStringItem FieldType FN_PARAM_1, SfxStringItem FieldCommandPrefix 
FN_PARAM_2, SfxUnoAnyItem Field FN_PARAM_3)
 [
diff --git a/sw/source/uibase/shells/textfld.cxx 
b/sw/source/uibase/shells/textfld.cxx
index 8d23221eaa53..c464ae31f528 100644
--- a/sw/source/uibase/shells/textfld.cxx
+++ b/sw/source/uibase/shells/textfld.cxx
@@ -924,6 +924,60 @@ FIELD_INSERT:
         
rSh.GetDoc()->GetIDocumentUndoRedo().EndUndo(SwUndoId::INSERT_FORM_FIELD, 
nullptr);
     }
     break;
+    case FN_DELETE_TEXT_FORMFIELDS:
+    {
+        // This deletes all fieldmarks that match the provided field type & 
field command prefix.
+        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();
+        }
+        
rSh.GetDoc()->GetIDocumentUndoRedo().StartUndo(SwUndoId::INSERT_FORM_FIELD, 
nullptr);
+        rSh.StartAction();
+
+        IDocumentMarkAccess* pMarkAccess = 
rSh.GetDoc()->getIDocumentMarkAccess();
+        std::vector<sw::mark::IMark*> aRemovals;
+        for (auto it = pMarkAccess->getFieldmarksBegin(); it != 
pMarkAccess->getFieldmarksEnd(); ++it)
+        {
+            auto pFieldmark = dynamic_cast<sw::mark::IFieldmark*>(*it);
+            assert(pFieldmark);
+            if (pFieldmark->GetFieldname() != aFieldType)
+            {
+                continue;
+            }
+
+            auto itParam = pFieldmark->GetParameters()->find(ODF_CODE_PARAM);
+            if (itParam == pFieldmark->GetParameters()->end())
+            {
+                continue;
+            }
+
+            OUString aCommand;
+            itParam->second >>= aCommand;
+            if (!aCommand.startsWith(aFieldCommandPrefix))
+            {
+                continue;
+            }
+
+            aRemovals.push_back(pFieldmark);
+        }
+
+        for (const auto& pMark : aRemovals)
+        {
+            pMarkAccess->deleteMark(pMark);
+        }
+
+        rSh.EndAction();
+        
rSh.GetDoc()->GetIDocumentUndoRedo().EndUndo(SwUndoId::INSERT_FORM_FIELD, 
nullptr);
+    }
+    break;
     case FN_PGNUMBER_WIZARD:
     {
         SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create();

Reply via email to