officecfg/registry/schema/org/openoffice/Office/Writer.xcs |   13 ++
 sw/inc/cmdid.h                                             |    2 
 sw/inc/viewopt.hxx                                         |   79 +++++++-----
 sw/qa/extras/uiwriter/uiwriter9.cxx                        |   83 +++++++++++++
 sw/source/ui/config/optpage.cxx                            |   43 ++++++
 sw/source/uibase/app/appopt.cxx                            |    9 +
 sw/source/uibase/config/cfgitems.cxx                       |   30 ++++
 sw/source/uibase/config/usrpref.cxx                        |   71 +++++++++++
 sw/source/uibase/config/viewopt.cxx                        |    3 
 sw/source/uibase/docvw/edtwin.cxx                          |   41 ++++++
 sw/source/uibase/inc/cfgitems.hxx                          |   21 +++
 sw/source/uibase/inc/optpage.hxx                           |    4 
 sw/source/uibase/inc/usrpref.hxx                           |   30 ++++
 sw/source/uibase/inc/wrtsh.hxx                             |    1 
 sw/source/uibase/wrtsh/wrtsh1.cxx                          |   11 +
 sw/uiconfig/swriter/ui/optformataidspage.ui                |   75 +++++++++++
 16 files changed, 478 insertions(+), 38 deletions(-)

New commits:
commit a7dd2e5a4712083cc5934e1d677e7dff86ceb7e6
Author:     Yiğit Akçay <yigit.ak...@icloud.com>
AuthorDate: Thu Jan 25 23:05:59 2024 +0300
Commit:     Andreas Heinisch <andreas.heini...@yahoo.de>
CommitDate: Wed Feb 28 10:37:50 2024 +0100

    tdf#151710 Enable enclosing of selected text with characters
    
    This patch implements a new setting in Tools -> Options -> Writer ->
    Formatting Aids -> Autocomplete -> Enclose with characters.
    
    When this option is enabled (default), selected text is enclosed
    with parentheses, square brackets, curly braces or quotation marks,
    depending on which character is pressed.
    
    For example, if the selected text is "abcd", the option is enabled and
    the button for the character '(' is hit, the text is replaced with
    "(abcd)".
    
    Change-Id: Ibc5b7be3cc96f00217dd068971e7c07e68439700
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/162583
    Reviewed-by: Andreas Heinisch <andreas.heini...@yahoo.de>
    Tested-by: Jenkins

diff --git a/officecfg/registry/schema/org/openoffice/Office/Writer.xcs 
b/officecfg/registry/schema/org/openoffice/Office/Writer.xcs
index 22f1481286b9..8497cc87bb67 100644
--- a/officecfg/registry/schema/org/openoffice/Office/Writer.xcs
+++ b/officecfg/registry/schema/org/openoffice/Office/Writer.xcs
@@ -2061,6 +2061,19 @@
         </prop>
       </group>
     </group>
+    <group oor:name="FmtAidsAutocomplete">
+      <info>
+        <desc>Contains formatting aids auto complete options.</desc>
+      </info>
+      <prop oor:name="EncloseWithCharacters" oor:type="xs:boolean" 
oor:nillable="false">
+        <!-- UIHints: Tools - Options - Formatting Aids - Enclose with 
characters -->
+        <info>
+          <desc>Specifies whether selected text will be enclosed with 
parentheses, square brackets, curly braces or quotation marks, depending on 
which button is pressed.</desc>
+          <label>Enclose with characters</label>
+        </info>
+        <value>true</value>
+      </prop>
+    </group>
     <group oor:name="Revision">
       <info>
         <desc>Contains settings for change recording.</desc>
diff --git a/sw/inc/cmdid.h b/sw/inc/cmdid.h
index e8521380c62c..3e611005c15e 100644
--- a/sw/inc/cmdid.h
+++ b/sw/inc/cmdid.h
@@ -850,7 +850,7 @@ class SwUINumRuleItem;
 #define FN_PARAM_CRSR_IN_PROTECTED      
TypedWhichId<SfxBoolItem>(FN_PARAM2+13) /* Cursor in protected areas */
 #define FN_PARAM_TOX_TYPE               
TypedWhichId<SfxUInt16Item>(FN_PARAM2+14) /* TOX type in tox dialog*/
 #define FN_PARAM_LINK_DISPLAY_NAME      (FN_PARAM2+15) /* LinkDisplayName 
property*/
-// free
+#define FN_PARAM_FMT_AIDS_AUTOCOMPL     
TypedWhichId<SwFmtAidsAutoComplItem>(FN_PARAM2+16) /* Formatting aids 
autocomplete options */
 #define FN_PARAM_CONTOUR_PP             (FN_PARAM2+17) /* contour PolyPolygon*/
 
 #define FN_ANCHOR_POSITION              (FN_PARAM2+18) /* AnchorPosition 
property */
diff --git a/sw/inc/viewopt.hxx b/sw/inc/viewopt.hxx
index 8aac21e745b3..d58c361e24c9 100644
--- a/sw/inc/viewopt.hxx
+++ b/sw/inc/viewopt.hxx
@@ -252,39 +252,40 @@ class SW_DLLPUBLIC SwViewOption
 
     static sal_uInt16   s_nPixelTwips;// 1 Pixel == ? Twips
 
-    OUString        m_sSymbolFont;        // Symbolfont.
-    ViewOptFlags1   m_nCoreOptions;       // Bits for SwViewShell.
-    ViewOptCoreFlags2 m_nCore2Options;    // Bits for SwViewShell.
-    ViewOptFlags2   m_nUIOptions;         // UI-Bits
-    Color           m_aRetouchColor;      // DefaultBackground for BrowseView
-    Size            m_aSnapSize;          // Describes horizontal and vertical 
snap.
-    sal_uInt16      mnViewLayoutColumns;  // # columns for edit view
-    short           m_nDivisionX;         // Grid division.
-    short           m_nDivisionY;
-    sal_uInt8       m_nPagePreviewRow;       // Page Preview Row/Columns.
-    sal_uInt8       m_nPagePreviewCol;       // Page Preview Row/Columns.
-    SwFillMode      m_nShadowCursorFillMode;  // FillMode for ShadowCursor.
-    bool            m_bReadonly : 1;      // Readonly-Doc.
-    bool            m_bStarOneSetting : 1;// Prevent from UI automatics (no 
scrollbars in readonly documents).
-    bool            m_bIsPagePreview : 1; // The preview mustn't print 
field/footnote/... shadings.
-    bool            m_bSelectionInReadonly : 1; // Determines whether 
selection is switched on in readonly documents.
-    bool            mbFormView : 1;
-    bool            mbBrowseMode : 1;
-    bool            mbBookView : 1;      // View mode for page preview.
-    bool            mbViewLayoutBookMode : 1; // Book view mode for edit view.
-    bool            mbHideWhitespaceMode : 1; // Hide header, footer, and 
pagebreak.
-    bool            m_bShowPlaceHolderFields : 1; // Only used in printing!
-    mutable bool    m_bIdle;
-    sal_Int32       m_nDefaultAnchor;     // GetDefaultAnchorType() to convert 
int to RndStdIds
+    OUString          m_sSymbolFont;                // Symbolfont.
+    ViewOptFlags1     m_nCoreOptions;               // Bits for SwViewShell.
+    ViewOptCoreFlags2 m_nCore2Options;              // Bits for SwViewShell.
+    ViewOptFlags2     m_nUIOptions;                 // UI-Bits
+    Color             m_aRetouchColor;              // DefaultBackground for 
BrowseView
+    Size              m_aSnapSize;                  // Describes horizontal 
and vertical snap.
+    sal_uInt16        mnViewLayoutColumns;          // # columns for edit view
+    short             m_nDivisionX;                 // Grid division.
+    short             m_nDivisionY;
+    sal_uInt8         m_nPagePreviewRow;            // Page Preview 
Row/Columns.
+    sal_uInt8         m_nPagePreviewCol;            // Page Preview 
Row/Columns.
+    SwFillMode        m_nShadowCursorFillMode;      // FillMode for 
ShadowCursor.
+    bool              m_bReadonly : 1;              // Readonly-Doc.
+    bool              m_bStarOneSetting : 1;        // Prevent from UI 
automatics (no scrollbars in readonly documents).
+    bool              m_bIsPagePreview : 1;         // The preview mustn't 
print field/footnote/... shadings.
+    bool              m_bSelectionInReadonly : 1;   // Determines whether 
selection is switched on in readonly documents.
+    bool              mbFormView : 1;
+    bool              mbBrowseMode : 1;
+    bool              mbBookView : 1;               // View mode for page 
preview.
+    bool              mbViewLayoutBookMode : 1;     // Book view mode for edit 
view.
+    bool              mbHideWhitespaceMode : 1;     // Hide header, footer, 
and pagebreak.
+    bool              m_bShowPlaceHolderFields : 1; // Only used in printing!
+    bool              m_bEncloseWithCharactersOn : 1;
+    mutable bool      m_bIdle;
+    sal_Int32         m_nDefaultAnchor;             // GetDefaultAnchorType() 
to convert int to RndStdIds
     // tdf#135266 - tox dialog: remember last used entry level depending on 
the index type
-    sal_uInt8 m_nTocEntryLvl;
-    sal_uInt8 m_nIdxEntryLvl;
+    sal_uInt8         m_nTocEntryLvl;
+    sal_uInt8         m_nIdxEntryLvl;
 
     // Scale
-    sal_uInt16          m_nZoom;          // In percent.
-    SvxZoomType     m_eZoom;              // 'enum' for zoom.
+    sal_uInt16        m_nZoom;                     // In percent.
+    SvxZoomType       m_eZoom;                     // 'enum' for zoom.
 
-    sal_uInt8            m_nTableDestination;      // Destination for table 
background.
+    sal_uInt8         m_nTableDestination;         // Destination for table 
background.
 
 #ifdef DBG_UTIL
     // Corresponds to statements in ui/config/cfgvw.src.
@@ -792,6 +793,25 @@ public:
     void   SetShadowCursor(bool b)
         { SetUIOption(b, ViewOptFlags2::ShadowCursor); }
 
+    // Enclose with characters autocomplete, switch on/off
+    bool IsEncloseWithCharactersOn() const { return 
m_bEncloseWithCharactersOn; }
+    void SetEncloseWithCharactersOn(bool b) { m_bEncloseWithCharactersOn = b; }
+
+    static bool IsEncloseWithCharactersTrigger(sal_Unicode cChar)
+    {
+        switch (cChar)
+        {
+            case '(':  [[fallthrough]];
+            case '{':  [[fallthrough]];
+            case '[':  [[fallthrough]];
+            case '\'': [[fallthrough]];
+            case '\"':
+                return true;
+            default:
+                return false;
+        }
+    }
+
     //move vertical ruler to the right
     bool    IsVRulerRight()    const
         { return bool(m_nUIOptions & ViewOptFlags2::VRulerRight); }
@@ -877,6 +897,7 @@ inline void SwViewOption::SetUIOptions( const SwViewOption& 
rVOpt )
     m_nUIOptions = rVOpt.m_nUIOptions;
     m_nTableDestination = rVOpt.m_nTableDestination;
     m_nShadowCursorFillMode = rVOpt.m_nShadowCursorFillMode;
+    m_bEncloseWithCharactersOn = rVOpt.m_bEncloseWithCharactersOn;
 }
 
 // Helper function for checking HTML-capabilities.
diff --git a/sw/qa/extras/uiwriter/uiwriter9.cxx 
b/sw/qa/extras/uiwriter/uiwriter9.cxx
index b3d5d04a7f85..879c7cc15f08 100644
--- a/sw/qa/extras/uiwriter/uiwriter9.cxx
+++ b/sw/qa/extras/uiwriter/uiwriter9.cxx
@@ -21,6 +21,8 @@
 #include <comphelper/propertysequence.hxx>
 #include <swdtflvr.hxx>
 #include <o3tl/string_view.hxx>
+#include <editeng/acorrcfg.hxx>
+#include <swacorr.hxx>
 
 #include <view.hxx>
 #include <wrtsh.hxx>
@@ -313,6 +315,87 @@ CPPUNIT_TEST_FIXTURE(SwUiWriterTest9, testTdf159816)
     xTransfer->PrivateDrop(*pWrtShell, ptTo, /*bMove=*/true, 
/*bXSelection=*/true);
 }
 
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest9, testTdf151710)
+{
+    createSwDoc();
+    SwXTextDocument* pTextDoc = 
dynamic_cast<SwXTextDocument*>(mxComponent.get());
+    CPPUNIT_ASSERT(pTextDoc);
+
+    // Check that the particular setting is turned on by default
+    const SwViewOption* pVwOpt = 
pTextDoc->GetDocShell()->GetWrtShell()->GetViewOptions();
+    CPPUNIT_ASSERT(pVwOpt);
+    CPPUNIT_ASSERT(pVwOpt->IsEncloseWithCharactersOn());
+
+    // Localized quotation marks
+    SvxAutoCorrect* pACorr = SvxAutoCorrCfg::Get().GetAutoCorrect();
+    CPPUNIT_ASSERT(pACorr);
+    LanguageType eLang = 
Application::GetSettings().GetLanguageTag().getLanguageType();
+    OUString sStartSingleQuote{ pACorr->GetQuote('\'', true, eLang) };
+    OUString sEndSingleQuote{ pACorr->GetQuote('\'', false, eLang) };
+    OUString sStartDoubleQuote{ pACorr->GetQuote('\"', true, eLang) };
+    OUString sEndDoubleQuote{ pACorr->GetQuote('\"', false, eLang) };
+
+    // Insert some text to work with
+    uno::Sequence<beans::PropertyValue> aArgsInsert(
+        comphelper::InitPropertySequence({ { "Text", 
uno::Any(OUString("abcd")) } }));
+    dispatchCommand(mxComponent, ".uno:InsertText", aArgsInsert);
+    CPPUNIT_ASSERT_EQUAL(OUString("abcd"), pTextDoc->getText()->getString());
+
+    // Successfully enclose the text; afterwards the selection should exist 
with the new
+    // enclosed text
+    dispatchCommand(mxComponent, ".uno:SelectAll", {});
+    pTextDoc->postKeyEvent(LOK_KEYEVENT_KEYINPUT, '(', 0);
+    Scheduler::ProcessEventsToIdle();
+    CPPUNIT_ASSERT_EQUAL(OUString("(abcd)"), pTextDoc->getText()->getString());
+
+    pTextDoc->postKeyEvent(LOK_KEYEVENT_KEYINPUT, '[', 0);
+    Scheduler::ProcessEventsToIdle();
+    CPPUNIT_ASSERT_EQUAL(OUString("[(abcd)]"), 
pTextDoc->getText()->getString());
+
+    pTextDoc->postKeyEvent(LOK_KEYEVENT_KEYINPUT, '{', 0);
+    Scheduler::ProcessEventsToIdle();
+    CPPUNIT_ASSERT_EQUAL(OUString("{[(abcd)]}"), 
pTextDoc->getText()->getString());
+
+    pTextDoc->postKeyEvent(LOK_KEYEVENT_KEYINPUT, '\'', 0);
+    Scheduler::ProcessEventsToIdle();
+    CPPUNIT_ASSERT_EQUAL(OUString(sStartSingleQuote + "{[(abcd)]}" + 
sEndSingleQuote),
+                         pTextDoc->getText()->getString());
+
+    pTextDoc->postKeyEvent(LOK_KEYEVENT_KEYINPUT, '\"', 0);
+    Scheduler::ProcessEventsToIdle();
+    CPPUNIT_ASSERT_EQUAL(OUString(sStartDoubleQuote + sStartSingleQuote + 
"{[(abcd)]}"
+                                  + sEndSingleQuote + sEndDoubleQuote),
+                         pTextDoc->getText()->getString());
+
+    // Disable the setting and check that enclosing doesn't happen anymore
+    const_cast<SwViewOption*>(pVwOpt)->SetEncloseWithCharactersOn(false);
+    CPPUNIT_ASSERT(!pVwOpt->IsEncloseWithCharactersOn());
+
+    pTextDoc->postKeyEvent(LOK_KEYEVENT_KEYINPUT, '(', 0);
+    Scheduler::ProcessEventsToIdle();
+    CPPUNIT_ASSERT_EQUAL(OUString("("), pTextDoc->getText()->getString());
+
+    dispatchCommand(mxComponent, ".uno:SelectAll", {});
+    pTextDoc->postKeyEvent(LOK_KEYEVENT_KEYINPUT, '[', 0);
+    Scheduler::ProcessEventsToIdle();
+    CPPUNIT_ASSERT_EQUAL(OUString("["), pTextDoc->getText()->getString());
+
+    dispatchCommand(mxComponent, ".uno:SelectAll", {});
+    pTextDoc->postKeyEvent(LOK_KEYEVENT_KEYINPUT, '{', 0);
+    Scheduler::ProcessEventsToIdle();
+    CPPUNIT_ASSERT_EQUAL(OUString("{"), pTextDoc->getText()->getString());
+
+    dispatchCommand(mxComponent, ".uno:SelectAll", {});
+    pTextDoc->postKeyEvent(LOK_KEYEVENT_KEYINPUT, '\'', 0);
+    Scheduler::ProcessEventsToIdle();
+    CPPUNIT_ASSERT_EQUAL(sStartSingleQuote, pTextDoc->getText()->getString());
+
+    dispatchCommand(mxComponent, ".uno:SelectAll", {});
+    pTextDoc->postKeyEvent(LOK_KEYEVENT_KEYINPUT, '\"', 0);
+    Scheduler::ProcessEventsToIdle();
+    CPPUNIT_ASSERT_EQUAL(sStartDoubleQuote, pTextDoc->getText()->getString());
+}
+
 } // end of anonymous namespace
 CPPUNIT_PLUGIN_IMPLEMENT();
 
diff --git a/sw/source/ui/config/optpage.cxx b/sw/source/ui/config/optpage.cxx
index 6051a53b75f2..6ca432acc34e 100644
--- a/sw/source/ui/config/optpage.cxx
+++ b/sw/source/ui/config/optpage.cxx
@@ -1642,6 +1642,9 @@ 
SwShdwCursorOptionsTabPage::SwShdwCursorOptionsTabPage(weld::Container* pPage, w
     , m_xDefaultAnchorTypeImg(m_xBuilder->weld_widget("lockAnchor"))
     , m_xMathBaselineAlignmentCB(m_xBuilder->weld_check_button("mathbaseline"))
     , m_xMathBaselineAlignmentImg(m_xBuilder->weld_widget("lockmathbaseline"))
+    , 
m_xFmtAidsAutoComplFrame(m_xBuilder->weld_frame("fmtaidsautocompleteframe"))
+    , 
m_xEncloseWithCharactersCB(m_xBuilder->weld_check_button("enclosewithcharacters"))
+    , 
m_xEncloseWithCharactersImg(m_xBuilder->weld_widget("lockenclosewithcharacters"))
 {
     SwFillMode eMode = SwFillMode::Tab;
     bool bIsOn = false;
@@ -1653,6 +1656,13 @@ 
SwShdwCursorOptionsTabPage::SwShdwCursorOptionsTabPage(weld::Container* pPage, w
     }
     m_xOnOffCB->set_active( bIsOn );
 
+    bool bIsEncloseWithCharactersOn = false;
+    if (const SwFmtAidsAutoComplItem* pItem = 
rSet.GetItemIfSet(FN_PARAM_FMT_AIDS_AUTOCOMPL, false))
+    {
+        bIsEncloseWithCharactersOn = pItem->IsEncloseWithCharactersOn();
+    }
+    m_xEncloseWithCharactersCB->set_active(bIsEncloseWithCharactersOn);
+
     m_xDirectCursorFillMode->set_active( static_cast<int>(eMode) );
     const SfxUInt16Item* pHtmlModeItem = rSet.GetItemIfSet(SID_HTML_MODE, 
false);
     if(!pHtmlModeItem || !(pHtmlModeItem->GetValue() & HTMLMODE_ON))
@@ -1672,6 +1682,9 @@ 
SwShdwCursorOptionsTabPage::SwShdwCursorOptionsTabPage(weld::Container* pPage, w
     m_xCursorProtFrame->hide();
     m_xCursorInProtCB->hide();
     m_xImageFrame->hide();
+
+    m_xFmtAidsAutoComplFrame->hide();
+    m_xEncloseWithCharactersCB->hide();
 }
 
 SwShdwCursorOptionsTabPage::~SwShdwCursorOptionsTabPage()
@@ -1693,8 +1706,8 @@ void SwShdwCursorOptionsTabPage::PageCreated( const 
SfxAllItemSet& aSet )
 OUString SwShdwCursorOptionsTabPage::GetAllStrings()
 {
     OUString sAllStrings;
-    OUString labels[] = { "layoutopt", "displayfl", "cursoropt",      
"cursorlabel",
-                          "fillmode",  "lbImage",   "lbDefaultAnchor" };
+    OUString labels[] = { "layoutopt", "displayfl", "cursoropt",       
"cursorlabel",
+                          "fillmode",  "lbImage",   "lbDefaultAnchor", 
"autocomplete" };
 
     for (const auto& label : labels)
     {
@@ -1703,8 +1716,9 @@ OUString SwShdwCursorOptionsTabPage::GetAllStrings()
     }
 
     OUString checkButton[]
-        = { "mathbaseline", "paragraph",  "hyphens",   "spaces",       
"nonbreak",   "tabs",
-            "break",        "hiddentext", "bookmarks", "cursorinprot", 
"cursoronoff" };
+        = { "mathbaseline", "paragraph",    "hyphens",     "spaces",
+            "nonbreak",     "tabs",         "break",       "hiddentext",
+            "bookmarks",    "cursorinprot", "cursoronoff", 
"enclosewithcharacters" };
 
     for (const auto& check : checkButton)
     {
@@ -1743,6 +1757,16 @@ bool SwShdwCursorOptionsTabPage::FillItemSet( 
SfxItemSet* rSet )
         bRet = true;
     }
 
+    SwFmtAidsAutoComplItem aFmtAidsAutoComplOpt;
+    
aFmtAidsAutoComplOpt.SetEncloseWithCharactersOn(m_xEncloseWithCharactersCB->get_active());
+    if (const SwFmtAidsAutoComplItem* pFmtAidsAutoComplItem
+        = rSet->GetItemIfSet(FN_PARAM_FMT_AIDS_AUTOCOMPL, false);
+        !pFmtAidsAutoComplItem || *pFmtAidsAutoComplItem != 
aFmtAidsAutoComplOpt)
+    {
+        rSet->Put(aFmtAidsAutoComplOpt);
+        bRet = true;
+    }
+
     const SwDocDisplayItem* pOldAttr = GetOldItem(GetItemSet(), 
FN_PARAM_DOCDISP);
 
     SwDocDisplayItem aDisp;
@@ -1796,6 +1820,17 @@ void SwShdwCursorOptionsTabPage::Reset( const 
SfxItemSet* rSet )
         m_xMathBaselineAlignmentCB->hide();
     }
 
+    bool bIsEncloseWithCharactersOn = false;
+    if (const SwFmtAidsAutoComplItem* pItem
+        = rSet->GetItemIfSet(FN_PARAM_FMT_AIDS_AUTOCOMPL, false))
+    {
+        bIsEncloseWithCharactersOn = pItem->IsEncloseWithCharactersOn();
+    }
+    bReadOnly = 
officecfg::Office::Writer::FmtAidsAutocomplete::EncloseWithCharacters::isReadOnly();
+    m_xEncloseWithCharactersCB->set_active(bIsEncloseWithCharactersOn);
+    m_xEncloseWithCharactersCB->set_sensitive(!bReadOnly);
+    m_xEncloseWithCharactersImg->set_visible(bReadOnly);
+
     if( const SfxBoolItem* pItem = rSet->GetItemIfSet( 
FN_PARAM_CRSR_IN_PROTECTED, false ) )
         m_xCursorInProtCB->set_active(pItem->GetValue());
     bReadOnly = 
officecfg::Office::Writer::Cursor::Option::ProtectedArea::isReadOnly();
diff --git a/sw/source/uibase/app/appopt.cxx b/sw/source/uibase/app/appopt.cxx
index 7be59a1dab9a..f21fc733c92a 100644
--- a/sw/source/uibase/app/appopt.cxx
+++ b/sw/source/uibase/app/appopt.cxx
@@ -102,7 +102,8 @@ std::optional<SfxItemSet> SwModule::CreateItemSet( 
sal_uInt16 nId )
             FN_PARAM_PRINTER, FN_PARAM_STDFONTS,
             FN_PARAM_WRTSHELL, FN_PARAM_WRTSHELL,
             FN_PARAM_SHADOWCURSOR, FN_PARAM_SHADOWCURSOR,
-            FN_PARAM_CRSR_IN_PROTECTED, FN_PARAM_CRSR_IN_PROTECTED>
+            FN_PARAM_CRSR_IN_PROTECTED, FN_PARAM_CRSR_IN_PROTECTED,
+            FN_PARAM_FMT_AIDS_AUTOCOMPL, FN_PARAM_FMT_AIDS_AUTOCOMPL>
         aRet(GetPool());
 
     aRet.Put( SwDocDisplayItem( aViewOpt ) );
@@ -111,6 +112,7 @@ std::optional<SfxItemSet> SwModule::CreateItemSet( 
sal_uInt16 nId )
     {
         aRet.Put( SwShadowCursorItem( aViewOpt ));
         aRet.Put( SfxBoolItem(FN_PARAM_CRSR_IN_PROTECTED, 
aViewOpt.IsCursorInProtectedArea()));
+        aRet.Put(SwFmtAidsAutoComplItem(aViewOpt));
     }
 
     if( pAppView )
@@ -393,6 +395,11 @@ void SwModule::ApplyItemSet( sal_uInt16 nId, const 
SfxItemSet& rSet )
         aViewOpt.SetCursorInProtectedArea(pItem->GetValue());
     }
 
+    if (const SwFmtAidsAutoComplItem* pItem = 
rSet.GetItemIfSet(FN_PARAM_FMT_AIDS_AUTOCOMPL, false))
+    {
+        
aViewOpt.SetEncloseWithCharactersOn(pItem->IsEncloseWithCharactersOn());
+    }
+
     // set elements for the current view and shell
     ApplyUsrPref( aViewOpt, pAppView, bTextDialog? SvViewOpt::DestText : 
SvViewOpt::DestWeb);
 
diff --git a/sw/source/uibase/config/cfgitems.cxx 
b/sw/source/uibase/config/cfgitems.cxx
index e32c86e14c52..fc8c0e1c4ddc 100644
--- a/sw/source/uibase/config/cfgitems.cxx
+++ b/sw/source/uibase/config/cfgitems.cxx
@@ -232,6 +232,36 @@ void SwShadowCursorItem::FillViewOptions( SwViewOption& 
rVOpt ) const
     rVOpt.SetShdwCursorFillMode( m_eMode );
 }
 
+SwFmtAidsAutoComplItem::SwFmtAidsAutoComplItem()
+    : SfxPoolItem(FN_PARAM_FMT_AIDS_AUTOCOMPL)
+    , m_bEncloseWithCharactersOn(true)
+{
+}
+
+SwFmtAidsAutoComplItem::SwFmtAidsAutoComplItem(const SwViewOption& rVOpt)
+    : SfxPoolItem(FN_PARAM_FMT_AIDS_AUTOCOMPL)
+    , m_bEncloseWithCharactersOn(rVOpt.IsEncloseWithCharactersOn())
+{
+}
+
+SwFmtAidsAutoComplItem* SwFmtAidsAutoComplItem::Clone(SfxItemPool*) const
+{
+    return new SwFmtAidsAutoComplItem(*this);
+}
+
+bool SwFmtAidsAutoComplItem::operator==(const SfxPoolItem& rCmp) const
+{
+    assert(SfxPoolItem::operator==(rCmp));
+    const SwFmtAidsAutoComplItem& rItem = static_cast<const 
SwFmtAidsAutoComplItem&>(rCmp);
+
+    return m_bEncloseWithCharactersOn == rItem.m_bEncloseWithCharactersOn;
+}
+
+void SwFmtAidsAutoComplItem::FillViewOptions(SwViewOption& rVOpt) const
+{
+    rVOpt.SetEncloseWithCharactersOn(m_bEncloseWithCharactersOn);
+}
+
 #ifdef DBG_UTIL
 SwTestItem* SwTestItem::Clone( SfxItemPool* ) const
 {
diff --git a/sw/source/uibase/config/usrpref.cxx 
b/sw/source/uibase/config/usrpref.cxx
index 8928e8e5470f..0d1ee2859a5d 100644
--- a/sw/source/uibase/config/usrpref.cxx
+++ b/sw/source/uibase/config/usrpref.cxx
@@ -52,6 +52,7 @@ SwMasterUsrPref::SwMasterUsrPref(bool bWeb) :
     m_aGridConfig(bWeb, *this),
     m_aCursorConfig(*this),
     m_pWebColorConfig(bWeb ? new SwWebColorConfig(*this) : nullptr),
+    m_aFmtAidsAutoComplConfig(*this),
     m_bApplyCharUnit(false)
 {
     if (comphelper::IsFuzzing())
@@ -73,6 +74,7 @@ SwMasterUsrPref::SwMasterUsrPref(bool bWeb) :
     m_aCursorConfig.Load();
     if(m_pWebColorConfig)
         m_pWebColorConfig->Load();
+    m_aFmtAidsAutoComplConfig.Load();
 }
 
 SwMasterUsrPref::~SwMasterUsrPref()
@@ -568,6 +570,75 @@ void SwCursorConfig::Load()
 
 void SwCursorConfig::Notify( const css::uno::Sequence< OUString >& ) {}
 
+Sequence<OUString> SwFmtAidsAutoComplConfig::GetPropertyNames()
+{
+    static const char* aPropNames[] = {
+        "EncloseWithCharacters", // 0
+    };
+    const int nCount = SAL_N_ELEMENTS(aPropNames);
+    Sequence<OUString> aNames(nCount);
+    OUString* pNames = aNames.getArray();
+    for (int i = 0; i < nCount; i++)
+        pNames[i] = OUString::createFromAscii(aPropNames[i]);
+    return aNames;
+}
+
+SwFmtAidsAutoComplConfig::SwFmtAidsAutoComplConfig(SwMasterUsrPref& rPar)
+    : ConfigItem("Office.Writer/FmtAidsAutocomplete", 
ConfigItemMode::ReleaseTree)
+    , m_rParent(rPar)
+{
+}
+
+SwFmtAidsAutoComplConfig::~SwFmtAidsAutoComplConfig() {}
+
+void SwFmtAidsAutoComplConfig::ImplCommit()
+{
+    Sequence<OUString> aNames = GetPropertyNames();
+
+    Sequence<Any> aValues(aNames.getLength());
+    Any* pValues = aValues.getArray();
+
+    for (int nProp = 0; nProp < aNames.getLength(); nProp++)
+    {
+        switch (nProp)
+        {
+            case 0:
+                pValues[nProp] <<= m_rParent.IsEncloseWithCharactersOn();
+                break; // "FmtAidsAutocomplete/EncloseWithCharacters"
+        }
+    }
+    PutProperties(aNames, aValues);
+}
+
+void SwFmtAidsAutoComplConfig::Load()
+{
+    Sequence<OUString> aNames = GetPropertyNames();
+    Sequence<Any> aValues = GetProperties(aNames);
+    const Any* pValues = aValues.getConstArray();
+    OSL_ENSURE(aValues.getLength() == aNames.getLength(), "GetProperties 
failed");
+    if (aValues.getLength() != aNames.getLength())
+        return;
+
+    for (int nProp = 0; nProp < aNames.getLength(); nProp++)
+    {
+        if (pValues[nProp].hasValue())
+        {
+            switch (nProp)
+            {
+                case 0:
+                {
+                    bool bSet = false;
+                    pValues[nProp] >>= bSet;
+                    m_rParent.SetEncloseWithCharactersOn(bSet);
+                    break; // "FmtAidsAutocomplete/EncloseWithCharacters"
+                }
+            }
+        }
+    }
+}
+
+void SwFmtAidsAutoComplConfig::Notify(const css::uno::Sequence<OUString>&) {}
+
 SwWebColorConfig::SwWebColorConfig(SwMasterUsrPref& rPar) :
     ConfigItem("Office.WriterWeb/Background", ConfigItemMode::ReleaseTree),
     m_rParent(rPar),
diff --git a/sw/source/uibase/config/viewopt.cxx 
b/sw/source/uibase/config/viewopt.cxx
index 96ac6531e14d..d37f3bfecc32 100644
--- a/sw/source/uibase/config/viewopt.cxx
+++ b/sw/source/uibase/config/viewopt.cxx
@@ -251,6 +251,7 @@ SwViewOption::SwViewOption() :
     mbViewLayoutBookMode(false),
     mbHideWhitespaceMode(false),
     m_bShowPlaceHolderFields( true ),
+    m_bEncloseWithCharactersOn( true ),
     m_nZoom( 100 ),
     m_eZoom( SvxZoomType::PERCENT ),
     m_nTableDestination(TBL_DEST_CELL)
@@ -327,6 +328,7 @@ SwViewOption::SwViewOption(const SwViewOption& rVOpt)
     mbViewLayoutBookMode = rVOpt.mbViewLayoutBookMode;
     mbHideWhitespaceMode = rVOpt.mbHideWhitespaceMode;
     m_bShowPlaceHolderFields = rVOpt.m_bShowPlaceHolderFields;
+    m_bEncloseWithCharactersOn = rVOpt.m_bEncloseWithCharactersOn;
     m_bIdle           = rVOpt.m_bIdle;
     m_nDefaultAnchor  = rVOpt.m_nDefaultAnchor;
     m_nTocEntryLvl = rVOpt.m_nTocEntryLvl;
@@ -373,6 +375,7 @@ SwViewOption& SwViewOption::operator=( const SwViewOption 
&rVOpt )
     mbViewLayoutBookMode = rVOpt.mbViewLayoutBookMode;
     mbHideWhitespaceMode = rVOpt.mbHideWhitespaceMode;
     m_bShowPlaceHolderFields = rVOpt.m_bShowPlaceHolderFields;
+    m_bEncloseWithCharactersOn = rVOpt.m_bEncloseWithCharactersOn;
     m_bIdle           = rVOpt.m_bIdle;
     m_nDefaultAnchor  = rVOpt.m_nDefaultAnchor;
     m_aColorConfig    = rVOpt.m_aColorConfig;
diff --git a/sw/source/uibase/docvw/edtwin.cxx 
b/sw/source/uibase/docvw/edtwin.cxx
index 9c4a3e6c69ee..b3f8c6855e43 100644
--- a/sw/source/uibase/docvw/edtwin.cxx
+++ b/sw/source/uibase/docvw/edtwin.cxx
@@ -2578,8 +2578,45 @@ KEYINPUT_CHECKTABLE_INSDEL:
                     m_aInBuffer += " ";
                 }
 
-                const bool bIsAutoCorrectChar =  
SvxAutoCorrect::IsAutoCorrectChar( aCh );
-                if( !aKeyEvent.GetRepeat() && pACorr && ( bIsAutoCorrectChar 
|| rSh.IsNbspRunNext() ) &&
+                const SwViewOption& rVwOpt = 
SwViewOption::GetCurrentViewOptions();
+                const bool bIsAutoCorrectChar = 
SvxAutoCorrect::IsAutoCorrectChar(aCh);
+                if (!aKeyEvent.GetRepeat() && rSh.HasSelection()
+                    && rVwOpt.IsEncloseWithCharactersOn()
+                    && SwViewOption::IsEncloseWithCharactersTrigger(aCh))
+                {
+                    FlushInBuffer();
+                    switch (aCh)
+                    {
+                        case '(':
+                            rSh.InsertEnclosingChars(u"(", u")");
+                            break;
+                        case '[':
+                            rSh.InsertEnclosingChars(u"[", u"]");
+                            break;
+                        case '{':
+                            rSh.InsertEnclosingChars(u"{", u"}");
+                            break;
+                        case '\"':
+                        {
+                            LanguageType eLang
+                                = 
Application::GetSettings().GetLanguageTag().getLanguageType();
+                            OUString sStartQuote{ pACorr->GetQuote('\"', true, 
eLang) };
+                            OUString sEndQuote{ pACorr->GetQuote('\"', false, 
eLang) };
+                            rSh.InsertEnclosingChars(sStartQuote, sEndQuote);
+                            break;
+                        }
+                        case '\'':
+                        {
+                            LanguageType eLang
+                                = 
Application::GetSettings().GetLanguageTag().getLanguageType();
+                            OUString sStartQuote{ pACorr->GetQuote('\'', true, 
eLang) };
+                            OUString sEndQuote{ pACorr->GetQuote('\'', false, 
eLang) };
+                            rSh.InsertEnclosingChars(sStartQuote, sEndQuote);
+                            break;
+                        }
+                    }
+                }
+                else if( !aKeyEvent.GetRepeat() && pACorr && ( 
bIsAutoCorrectChar || rSh.IsNbspRunNext() ) &&
                         pACfg->IsAutoFormatByInput() &&
                     (( pACorr->IsAutoCorrFlag( ACFlags::ChgWeightUnderl ) &&
                         ( '*' == aCh || '_' == aCh ) ) ||
diff --git a/sw/source/uibase/inc/cfgitems.hxx 
b/sw/source/uibase/inc/cfgitems.hxx
index 34ffa2d24e12..6ee5ea620cda 100644
--- a/sw/source/uibase/inc/cfgitems.hxx
+++ b/sw/source/uibase/inc/cfgitems.hxx
@@ -133,6 +133,27 @@ public:
     void SetOn( bool bFlag )            { m_bOn = bFlag; }
 };
 
+class SW_DLLPUBLIC SwFmtAidsAutoComplItem final : public SfxPoolItem
+{
+    friend class SwShdwCursorOptionsTabPage;
+    friend class SwModule;
+
+    bool m_bEncloseWithCharactersOn;
+
+public:
+    SwFmtAidsAutoComplItem();
+    SwFmtAidsAutoComplItem(const SwViewOption& rVOpt);
+
+    virtual SwFmtAidsAutoComplItem* Clone(SfxItemPool* pPool = nullptr) const 
override;
+    virtual bool operator==(const SfxPoolItem&) const override;
+
+    void FillViewOptions(SwViewOption& rVOpt) const;
+
+    bool IsEncloseWithCharactersOn() const { return 
m_bEncloseWithCharactersOn; }
+
+    void SetEncloseWithCharactersOn(bool bFlag) { m_bEncloseWithCharactersOn = 
bFlag; }
+};
+
 #ifdef DBG_UTIL
 
 // Item for settings dialog - test settings
diff --git a/sw/source/uibase/inc/optpage.hxx b/sw/source/uibase/inc/optpage.hxx
index e2641fd7fc2b..68192a64f1d4 100644
--- a/sw/source/uibase/inc/optpage.hxx
+++ b/sw/source/uibase/inc/optpage.hxx
@@ -318,6 +318,10 @@ class SwShdwCursorOptionsTabPage final : public SfxTabPage
     std::unique_ptr<weld::CheckButton> m_xMathBaselineAlignmentCB;
     std::unique_ptr<weld::Widget> m_xMathBaselineAlignmentImg;
 
+    std::unique_ptr<weld::Frame> m_xFmtAidsAutoComplFrame;
+    std::unique_ptr<weld::CheckButton> m_xEncloseWithCharactersCB;
+    std::unique_ptr<weld::Widget> m_xEncloseWithCharactersImg;
+
 public:
     SwShdwCursorOptionsTabPage(weld::Container* pPage, weld::DialogController* 
pController, const SfxItemSet& rSet);
     virtual ~SwShdwCursorOptionsTabPage() override;
diff --git a/sw/source/uibase/inc/usrpref.hxx b/sw/source/uibase/inc/usrpref.hxx
index 1464c90bce3c..24d9c6a7a34b 100644
--- a/sw/source/uibase/inc/usrpref.hxx
+++ b/sw/source/uibase/inc/usrpref.hxx
@@ -103,6 +103,24 @@ public:
     using ConfigItem::SetModified;
 };
 
+class SwFmtAidsAutoComplConfig final : public utl::ConfigItem
+{
+private:
+    SwMasterUsrPref& m_rParent;
+
+    static css::uno::Sequence<OUString> GetPropertyNames();
+
+    virtual void ImplCommit() override;
+
+public:
+    SwFmtAidsAutoComplConfig(SwMasterUsrPref& rParent);
+    virtual ~SwFmtAidsAutoComplConfig() override;
+
+    virtual void Notify(const css::uno::Sequence<OUString>& aPropertyNames) 
override;
+    void Load();
+    using ConfigItem::SetModified;
+};
+
 class SwWebColorConfig final : public utl::ConfigItem
 {
 private:
@@ -127,6 +145,7 @@ class SwMasterUsrPref : public SwViewOption
     friend class SwGridConfig;
     friend class SwCursorConfig;
     friend class SwWebColorConfig;
+    friend class SwFmtAidsAutoComplConfig;
 
     SwFieldUpdateFlags m_eFieldUpdateFlags;    //update of fields and charts
     sal_Int32   m_nLinkUpdateMode;
@@ -146,6 +165,7 @@ class SwMasterUsrPref : public SwViewOption
     SwGridConfig        m_aGridConfig;
     SwCursorConfig      m_aCursorConfig;
     std::unique_ptr<SwWebColorConfig>   m_pWebColorConfig;
+    SwFmtAidsAutoComplConfig m_aFmtAidsAutoComplConfig;
 
     bool m_bApplyCharUnit; // apply_char_unit
 public:
@@ -162,6 +182,7 @@ public:
             m_aCursorConfig.SetModified();
             if(m_pWebColorConfig)
                 m_pWebColorConfig->SetModified();
+            m_aFmtAidsAutoComplConfig.SetModified();
         }
 
     void SetUpdateLinkMode(sal_Int32 nSet, bool bNoModify = false)
@@ -266,6 +287,15 @@ public:
                         m_aLayoutConfig.SetModified();
                     }
                 }
+
+    void SetEncloseWithCharactersOn(bool bVal, bool noModify = false)
+    {
+        this->SwViewOption::SetEncloseWithCharactersOn(bVal);
+        if (!noModify)
+        {
+            m_aFmtAidsAutoComplConfig.SetModified();
+        }
+    }
 };
 
 #endif
diff --git a/sw/source/uibase/inc/wrtsh.hxx b/sw/source/uibase/inc/wrtsh.hxx
index dd97160b140b..8b8c85753e96 100644
--- a/sw/source/uibase/inc/wrtsh.hxx
+++ b/sw/source/uibase/inc/wrtsh.hxx
@@ -318,6 +318,7 @@ typedef bool (SwWrtShell::*FNSimpleMove)();
 
     void    InsertByWord( const OUString & );
     SW_DLLPUBLIC void InsertPageBreak(const OUString *pPageDesc = nullptr, 
const ::std::optional<sal_uInt16>& rPgNum = std::nullopt);
+    void InsertEnclosingChars(std::u16string_view sStartStr, 
std::u16string_view sEndStr);
     SW_DLLPUBLIC void InsertLineBreak(std::optional<SwLineBreakClear> oClear = 
std::nullopt);
     void    InsertColumnBreak();
     SW_DLLPUBLIC void InsertContentControl(SwContentControlType eType);
diff --git a/sw/source/uibase/wrtsh/wrtsh1.cxx 
b/sw/source/uibase/wrtsh/wrtsh1.cxx
index 0054eca4cb34..2063029680df 100644
--- a/sw/source/uibase/wrtsh/wrtsh1.cxx
+++ b/sw/source/uibase/wrtsh/wrtsh1.cxx
@@ -986,6 +986,17 @@ void SwWrtShell::InsertPageBreak(const OUString 
*pPageDesc, const ::std::optiona
     collectUIInformation("BREAK_PAGE", "parameter");
 }
 
+// Insert enclosing characters
+// Selections will be overwritten
+void SwWrtShell::InsertEnclosingChars(std::u16string_view sStartStr, 
std::u16string_view sEndStr)
+{
+    for (SwPaM& rPaM : SwWrtShell::GetCursor()->GetRingContainer())
+    {
+        const OUString aStr = sStartStr + rPaM.GetText() + sEndStr;
+        SwViewShell::getIDocumentContentOperations().ReplaceRange(rPaM, aStr, 
false);
+    }
+}
+
 // Insert hard page break;
 // Selections will be overwritten
 
diff --git a/sw/uiconfig/swriter/ui/optformataidspage.ui 
b/sw/uiconfig/swriter/ui/optformataidspage.ui
index 4825b51a356e..a454c39b872e 100644
--- a/sw/uiconfig/swriter/ui/optformataidspage.ui
+++ b/sw/uiconfig/swriter/ui/optformataidspage.ui
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!-- Generated with glade 3.38.2 -->
+<!-- Generated with glade 3.40.0 -->
 <interface domain="sw">
   <requires lib="gtk+" version="3.20"/>
   <object class="GtkBox" id="OptFormatAidsPage">
@@ -551,6 +551,7 @@
             <property name="visible">True</property>
             <property name="can-focus">False</property>
             <property name="orientation">vertical</property>
+            <property name="spacing">12</property>
             <child>
               <object class="GtkFrame" id="directcrsrframe">
                 <property name="visible">True</property>
@@ -758,6 +759,78 @@
                 <property name="position">1</property>
               </packing>
             </child>
+            <child>
+              <object class="GtkFrame" id="fmtaidsautocompleteframe">
+                <property name="visible">True</property>
+                <property name="can-focus">False</property>
+                <property name="valign">start</property>
+                <property name="label-xalign">0</property>
+                <property name="shadow-type">none</property>
+                <child>
+                  <!-- n-columns=2 n-rows=1 -->
+                  <object class="GtkGrid" id="grid7">
+                    <property name="visible">True</property>
+                    <property name="can-focus">False</property>
+                    <property name="margin-start">12</property>
+                    <property name="margin-top">6</property>
+                    <property name="row-spacing">6</property>
+                    <child>
+                      <object class="GtkCheckButton" 
id="enclosewithcharacters">
+                        <property name="label" translatable="yes" 
context="optformataidspage|enclosewithcharacters">Enclose with 
characters</property>
+                        <property name="visible">True</property>
+                        <property name="can-focus">True</property>
+                        <property name="receives-default">False</property>
+                        <property name="tooltip-text" translatable="yes" 
context="optformataidspage|enclosewithcharacters|tooltip_text">When enabled you 
can enclose selected text with parentheses, square brackets, curly braces or 
double/single quotation marks when pressing the respective button.</property>
+                        <property name="use-underline">True</property>
+                        <property name="draw-indicator">True</property>
+                        <child internal-child="accessible">
+                          <object class="AtkObject" 
id="enclosewithcharacters-atkobject">
+                            <property name="AtkObject::accessible-description" 
translatable="yes" context="extended_tip|enclosewithcharacters">Specifies that 
you can enclose selected text with parentheses, square brackets, curly braces 
or double/single quotation marks when pressing the respective button.</property>
+                          </object>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="left-attach">1</property>
+                        <property name="top-attach">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkImage" id="lockenclosewithcharacters">
+                        <property name="can-focus">False</property>
+                        <property name="no-show-all">True</property>
+                        <property name="halign">center</property>
+                        <property name="valign">center</property>
+                        <property name="icon-name">res/lock.png</property>
+                        <child internal-child="accessible">
+                          <object class="AtkObject" 
id="lockenclosewithcharacters-atkobject">
+                            <property 
name="AtkObject::accessible-description">Specifies that you can enclose 
selected text with parentheses, square brackets, curly braces or quotation 
marks when pressing the respective button.</property>
+                          </object>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="left-attach">0</property>
+                        <property name="top-attach">0</property>
+                      </packing>
+                    </child>
+                  </object>
+                </child>
+                <child type="label">
+                  <object class="GtkLabel" id="autocomplete">
+                    <property name="visible">True</property>
+                    <property name="can-focus">False</property>
+                    <property name="label" translatable="yes" 
context="optformataidspage|autocomplete">Auto complete</property>
+                    <attributes>
+                      <attribute name="weight" value="bold"/>
+                    </attributes>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">3</property>
+              </packing>
+            </child>
           </object>
           <packing>
             <property name="left-attach">1</property>

Reply via email to