sw content controls, checkbox: add insert UI

- extend SwWrtShell::InsertContentControl() to be able to create
  multiple content control types

- a new checkbox content control's content is always a non-checked
  checkbox

- expose this as a new .uno:InsertCheckboxContentControl uno command

- add this new command to the bottom of the form menu -- now that we
  have two types of content controls, have that in a sub-menu

Change-Id: I058659600b3face69b89262feb0979fff32521c4
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/133685
Reviewed-by: Miklos Vajna <vmiklos@collabora.com>
Tested-by: Jenkins
diff --git a/officecfg/registry/data/org/openoffice/Office/UI/WriterCommands.xcu b/officecfg/registry/data/org/openoffice/Office/UI/WriterCommands.xcu
index 8835dee..b368020 100644
--- a/officecfg/registry/data/org/openoffice/Office/UI/WriterCommands.xcu
+++ b/officecfg/registry/data/org/openoffice/Office/UI/WriterCommands.xcu
@@ -614,6 +614,11 @@
          <value>1</value>
        </prop>
      </node>
      <node oor:name=".uno:ContentControlsMenu" oor:op="replace">
        <prop oor:name="Label" oor:type="xs:string">
          <value xml:lang="en-US">Content Controls</value>
        </prop>
      </node>
      <node oor:name=".uno:InsertContentControl" oor:op="replace">
        <prop oor:name="Label" oor:type="xs:string">
          <value xml:lang="en-US">Insert Rich Text Content Control</value>
@@ -622,6 +627,14 @@
          <value>1</value>
        </prop>
      </node>
      <node oor:name=".uno:InsertCheckboxContentControl" oor:op="replace">
        <prop oor:name="Label" oor:type="xs:string">
          <value xml:lang="en-US">Insert Check Box Content Control</value>
        </prop>
        <prop oor:name="Properties" oor:type="xs:int">
          <value>1</value>
        </prop>
      </node>
      <node oor:name=".uno:InsertObjectDialog" oor:op="replace">
        <prop oor:name="Label" oor:type="xs:string">
          <value xml:lang="en-US">Insert Other Objects</value>
diff --git a/sw/inc/cmdid.h b/sw/inc/cmdid.h
index 29adc6a..355d607 100644
--- a/sw/inc/cmdid.h
+++ b/sw/inc/cmdid.h
@@ -218,6 +218,7 @@ class SwUINumRuleItem;
#define FN_INSERT_LINEBREAK     (FN_INSERT + 18)    /* Newline */
#define FN_INSERT_FIELD_DATA_ONLY (FN_INSERT + 19)  /* Field dialog for mail merge*/
#define FN_INSERT_CONTENT_CONTROL (FN_INSERT + 20)  /* Content control */
#define FN_INSERT_CHECKBOX_CONTENT_CONTROL (FN_INSERT + 21)  /* Checkbox content control */
#define FN_INSERT_OBJECT_DLG    (FN_INSERT + 22)    /* Object */
#define FN_INSERT_PAGEBREAK     (FN_INSERT + 23)    /* Page break*/
#define FN_POSTIT               (FN_INSERT + 29)    /* Insert/edit PostIt */
diff --git a/sw/inc/formatcontentcontrol.hxx b/sw/inc/formatcontentcontrol.hxx
index 368720a..8419446 100644
--- a/sw/inc/formatcontentcontrol.hxx
+++ b/sw/inc/formatcontentcontrol.hxx
@@ -32,6 +32,12 @@ class SwTextContentControl;
class SwTextNode;
class SwXContentControl;

enum class SwContentControlType
{
    RICH_TEXT,
    CHECKBOX,
};

/// SfxPoolItem subclass that wraps an SwContentControl.
class SAL_DLLPUBLIC_RTTI SwFormatContentControl final : public SfxPoolItem
{
diff --git a/sw/qa/uibase/wrtsh/wrtsh.cxx b/sw/qa/uibase/wrtsh/wrtsh.cxx
index b4d4cca0..5fddff1 100644
--- a/sw/qa/uibase/wrtsh/wrtsh.cxx
+++ b/sw/qa/uibase/wrtsh/wrtsh.cxx
@@ -136,7 +136,7 @@ CPPUNIT_TEST_FIXTURE(Test, testInsertContentControl)

    // When inserting a content control:
    SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
    pWrtShell->InsertContentControl();
    pWrtShell->InsertContentControl(SwContentControlType::RICH_TEXT);

    // Then make sure that the matching text attribute is added to the document model:
    SwTextNode* pTextNode = pWrtShell->GetCursor()->GetNode().GetTextNode();
@@ -144,6 +144,27 @@ CPPUNIT_TEST_FIXTURE(Test, testInsertContentControl)
    // InsertContentControl().
    CPPUNIT_ASSERT(pTextNode->GetTextAttrForCharAt(0, RES_TXTATR_CONTENTCONTROL));
}

CPPUNIT_TEST_FIXTURE(Test, testInsertCheckboxContentControl)
{
    // Given an empty document:
    SwDoc* pDoc = createSwDoc();

    // When inserting a content control:
    SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
    pWrtShell->InsertContentControl(SwContentControlType::CHECKBOX);

    // Then make sure that the matching text attribute is added to the document model:
    SwTextNode* pTextNode = pWrtShell->GetCursor()->GetNode().GetTextNode();
    SwTextAttr* pAttr = pTextNode->GetTextAttrForCharAt(0, RES_TXTATR_CONTENTCONTROL);
    auto pTextContentControl = static_txtattr_cast<SwTextContentControl*>(pAttr);
    auto& rFormatContentControl
        = static_cast<SwFormatContentControl&>(pTextContentControl->GetAttr());
    SwContentControl* pContentControl = rFormatContentControl.GetContentControl();
    // Without the accompanying fix in place, this test would have failed, the inserted content
    // control wasn't a checkbox one.
    CPPUNIT_ASSERT(pContentControl->GetCheckbox());
}
}

CPPUNIT_PLUGIN_IMPLEMENT();
diff --git a/sw/sdi/_textsh.sdi b/sw/sdi/_textsh.sdi
index 7610800..21b56b6 100644
--- a/sw/sdi/_textsh.sdi
+++ b/sw/sdi/_textsh.sdi
@@ -278,6 +278,12 @@ interface BaseText
        StateMethod = NoState ;
        DisableFlags="SfxDisableFlags::SwOnProtectedCursor";
    ]
    FN_INSERT_CHECKBOX_CONTENT_CONTROL  // status(final|play)
    [
        ExecMethod = ExecInsert ;
        StateMethod = NoState ;
        DisableFlags="SfxDisableFlags::SwOnProtectedCursor";
    ]
    FN_INSERT_COLUMN_BREAK // status(final|play)
    [
        ExecMethod = ExecInsert ;
diff --git a/sw/sdi/swriter.sdi b/sw/sdi/swriter.sdi
index 274122f..0b80825 100644
--- a/sw/sdi/swriter.sdi
+++ b/sw/sdi/swriter.sdi
@@ -3049,6 +3049,23 @@ SfxVoidItem InsertContentControl FN_INSERT_CONTENT_CONTROL
    GroupId = SfxGroupId::Insert;
]

SfxVoidItem InsertCheckboxContentControl FN_INSERT_CHECKBOX_CONTENT_CONTROL
()
[
    AutoUpdate = FALSE,
    FastCall = FALSE,
    ReadOnlyDoc = FALSE,
    Toggle = FALSE,
    Container = FALSE,
    RecordAbsolute = FALSE,
    RecordPerSet;

    AccelConfig = TRUE,
    MenuConfig = TRUE,
    ToolBoxConfig = TRUE,
    GroupId = SfxGroupId::Insert;
]

SfxVoidItem InsertMultiIndex FN_INSERT_MULTI_TOX
()
[
diff --git a/sw/source/uibase/inc/wrtsh.hxx b/sw/source/uibase/inc/wrtsh.hxx
index 37d8255..a80f42c 100644
--- a/sw/source/uibase/inc/wrtsh.hxx
+++ b/sw/source/uibase/inc/wrtsh.hxx
@@ -55,6 +55,7 @@ class SwFieldMgr;
class SfxRequest;
enum class SwLineBreakClear;
class SwContentControl;
enum class SwContentControlType;

namespace i18nutil {
    struct SearchOptions2;
@@ -316,7 +317,7 @@ typedef bool (SwWrtShell::*FNSimpleMove)();
    void    InsertPageBreak(const OUString *pPageDesc = nullptr, const ::std::optional<sal_uInt16>& rPgNum = std::nullopt);
    void InsertLineBreak(std::optional<SwLineBreakClear> oClear = std::nullopt);
    void    InsertColumnBreak();
    void InsertContentControl();
    void InsertContentControl(SwContentControlType eType);
    void    InsertFootnote(const OUString &, bool bEndNote = false, bool bEdit = true );
    void    SplitNode( bool bAutoFormat = false );
    bool    CanInsert();
diff --git a/sw/source/uibase/shells/textsh.cxx b/sw/source/uibase/shells/textsh.cxx
index 667f750..280e667 100644
--- a/sw/source/uibase/shells/textsh.cxx
+++ b/sw/source/uibase/shells/textsh.cxx
@@ -91,6 +91,7 @@ using namespace ::com::sun::star;
#include <com/sun/star/ui/dialogs/DialogClosedEvent.hpp>
#include <com/sun/star/ui/dialogs/ExecutableDialogResults.hpp>
#include <IDocumentUndoRedo.hxx>
#include <formatcontentcontrol.hxx>

SFX_IMPL_INTERFACE(SwTextShell, SwBaseShell)

@@ -219,7 +220,12 @@ void SwTextShell::ExecInsert(SfxRequest &rReq)
        break;

    case FN_INSERT_CONTENT_CONTROL:
        rSh.InsertContentControl();
        rSh.InsertContentControl(SwContentControlType::RICH_TEXT);
        rReq.Done();
        break;

    case FN_INSERT_CHECKBOX_CONTENT_CONTROL:
        rSh.InsertContentControl(SwContentControlType::CHECKBOX);
        rReq.Done();
        break;

diff --git a/sw/source/uibase/uiview/view.cxx b/sw/source/uibase/uiview/view.cxx
index 8aa7e37..4efcfc6 100644
--- a/sw/source/uibase/uiview/view.cxx
+++ b/sw/source/uibase/uiview/view.cxx
@@ -592,7 +592,7 @@ void SwView::CheckReadonlyState()
            SID_CHARMAP,                SID_EMOJI_CONTROL,          FN_INSERT_SOFT_HYPHEN,
            FN_INSERT_HARDHYPHEN,       FN_INSERT_HARD_SPACE,       FN_INSERT_NNBSP,
            FN_INSERT_BREAK,            FN_INSERT_LINEBREAK,        FN_INSERT_COLUMN_BREAK,
            FN_INSERT_BREAK_DLG,        FN_INSERT_CONTENT_CONTROL,
            FN_INSERT_BREAK_DLG,        FN_INSERT_CONTENT_CONTROL,  FN_INSERT_CHECKBOX_CONTENT_CONTROL,
            FN_DELETE_SENT,             FN_DELETE_BACK_SENT,        FN_DELETE_WORD,
            FN_DELETE_BACK_WORD,        FN_DELETE_LINE,             FN_DELETE_BACK_LINE,
            FN_DELETE_PARA,             FN_DELETE_BACK_PARA,        FN_DELETE_WHOLE_LINE,
diff --git a/sw/source/uibase/wrtsh/wrtsh1.cxx b/sw/source/uibase/wrtsh/wrtsh1.cxx
index daabc40..a0cd400 100644
--- a/sw/source/uibase/wrtsh/wrtsh1.cxx
+++ b/sw/source/uibase/wrtsh/wrtsh1.cxx
@@ -1012,7 +1012,7 @@ void SwWrtShell::InsertColumnBreak()
    EndUndo(SwUndoId::UI_INSERT_COLUMN_BREAK);
}

void SwWrtShell::InsertContentControl()
void SwWrtShell::InsertContentControl(SwContentControlType eType)
{
    if (!lcl_IsAllowed(this))
    {
@@ -1026,13 +1026,32 @@ void SwWrtShell::InsertContentControl()
    }

    auto pContentControl = std::make_shared<SwContentControl>(nullptr);
    pContentControl->SetShowingPlaceHolder(true);
    if (!HasSelection())
    OUString aPlaceholder;
    switch (eType)
    {
        OUString aPlaceholder = SwResId(STR_CONTENT_CONTROL_PLACEHOLDER);
        Insert(aPlaceholder);
        Left(CRSR_SKIP_CHARS, /*bSelect=*/true, aPlaceholder.getLength(), /*bBasicCall=*/false);
        case SwContentControlType::RICH_TEXT:
        {
            pContentControl->SetShowingPlaceHolder(true);
            if (!HasSelection())
            {
                aPlaceholder = SwResId(STR_CONTENT_CONTROL_PLACEHOLDER);
            }
            break;
        }
        case SwContentControlType::CHECKBOX:
        {
            pContentControl->SetCheckbox(true);
            // Ballot Box with X
            pContentControl->SetCheckedState(u"\u2612");
            // Ballot Box
            pContentControl->SetUncheckedState(OUString(u"\u2610"));
            aPlaceholder = u"\u2610";
            break;
        }
    }
    Insert(aPlaceholder);
    Left(CRSR_SKIP_CHARS, /*bSelect=*/true, aPlaceholder.getLength(),
            /*bBasicCall=*/false);
    SwFormatContentControl aContentControl(pContentControl, RES_TXTATR_CONTENTCONTROL);
    SetAttrItem(aContentControl);
}
diff --git a/sw/uiconfig/swriter/menubar/menubar.xml b/sw/uiconfig/swriter/menubar/menubar.xml
index 90c859a..e5d20fa 100644
--- a/sw/uiconfig/swriter/menubar/menubar.xml
+++ b/sw/uiconfig/swriter/menubar/menubar.xml
@@ -710,7 +710,12 @@
      <menu:menuitem menu:id=".uno:OpenReadOnly"/>
      <menu:menuitem menu:id=".uno:AutoControlFocus"/>
      <menu:menuseparator/>
      <menu:menuitem menu:id=".uno:InsertContentControl"/>
      <menu:menu menu:id=".uno:ContentControlsMenu">
        <menu:menupopup>
          <menu:menuitem menu:id=".uno:InsertContentControl"/>
          <menu:menuitem menu:id=".uno:InsertCheckboxContentControl"/>
        </menu:menupopup>
      </menu:menu>
    </menu:menupopup>
  </menu:menu>
  <menu:menu menu:id=".uno:ToolsMenu">