include/vcl/builder.hxx       |    6 -----
 include/vcl/builderbase.hxx   |    5 ++++
 include/vcl/widgetbuilder.hxx |    6 +++++
 vcl/inc/qt5/QtBuilder.hxx     |    2 +
 vcl/qt5/QtBuilder.cxx         |   28 +++++++++++++++++++++++++
 vcl/source/window/builder.cxx |   46 ++++++++++++++++++++++--------------------
 6 files changed, 67 insertions(+), 26 deletions(-)

New commits:
commit e32d069ba74229916c459842d0c27c5a89b82168
Author:     Michael Weghorn <m.wegh...@posteo.de>
AuthorDate: Fri Dec 13 22:55:50 2024 +0100
Commit:     Michael Weghorn <m.wegh...@posteo.de>
CommitDate: Sat Dec 14 01:03:50 2024 +0100

    tdf#130857 qt weld: Evaluate radio button groups
    
    Add support for radio button groups by implementing
    QtBuilder::setRadioButtonGroup.
    
    Qt provides QButtonGroup [1] to explicitly group
    buttons. Quoting from the QRadioButton doc [2]:
    
    > Radio buttons are autoExclusive by default. If auto-exclusive is
    > enabled, radio buttons that belong to the same parent widget behave as
    > if they were part of the same exclusive button group. If you need
    > multiple exclusive button groups for radio buttons that belong to the
    > same parent widget, put them into a QButtonGroup.
    
    To make sure exactly one button group exists and it gets
    freed automatically, make the radio button whose ID is set for
    the GtkRadioButton::group property [3] the parent (and thus the
    owner) of the group.
    Set a pointer of the group as a property for that button,
    so it can be retrieved directly from the button without requiring
    another map/... to keep track.
    
    With this in place, the radio buttons in Writer's
    "Tools" -> "Options" -> "LibreOfficeDev Writer"
    -> "Mail Merge Email" -> "Server Authentication"
    dialog are now grouped into 2 groups as expected in
    a WIP branch, rather than all of them being in one group (because
    they all have the same grid as a parent).
    Support for that dialog will be declared in an
    upcoming commit.
    
    [1] https://doc.qt.io/qt-6/qbuttongroup.html
    [2] https://doc.qt.io/qt-6/qradiobutton.html
    [3] https://docs.gtk.org/gtk3/property.RadioButton.group.html
    
    Change-Id: I60e8b5b525411c299ac50a8712361cc8572cc403
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/178444
    Tested-by: Jenkins
    Reviewed-by: Michael Weghorn <m.wegh...@posteo.de>

diff --git a/vcl/qt5/QtBuilder.cxx b/vcl/qt5/QtBuilder.cxx
index 963d8e71e861..7d78acb281f8 100644
--- a/vcl/qt5/QtBuilder.cxx
+++ b/vcl/qt5/QtBuilder.cxx
@@ -20,6 +20,7 @@
 #include <vcl/qt/QtUtils.hxx>
 
 #include <QtGui/QStandardItemModel>
+#include <QtWidgets/QButtonGroup>
 #include <QtWidgets/QCheckBox>
 #include <QtWidgets/QComboBox>
 #include <QtWidgets/QDialog>
@@ -275,6 +276,7 @@ QObject* QtBuilder::makeObject(QObject* pParent, 
std::u16string_view sName, std:
     else if (sName == u"GtkRadioButton")
     {
         pObject = new QRadioButton(pParentWidget);
+        extractRadioButtonGroup(sID, rMap);
     }
     else if (sName == u"GtkScrolledWindow")
     {
@@ -473,9 +475,30 @@ void QtBuilder::setMnemonicWidget(const OUString& 
rLabelId, const OUString& rMne
     pLabel->setBuddy(static_cast<QWidget*>(pBuddy));
 }
 
-void QtBuilder::setRadioButtonGroup(const OUString&, const OUString&)
+void QtBuilder::setRadioButtonGroup(const OUString& rRadioButtonId, const 
OUString& rRadioGroupId)
 {
-    SAL_WARN("vcl.qt", "Not implemented yet");
+    // insert all buttons into a button group owned by button whose matches 
the group's
+    QRadioButton* pGroupOwner = get<QRadioButton>(rRadioGroupId);
+    assert(pGroupOwner && "No radio button with the given group name");
+
+    QButtonGroup* pButtonGroup = nullptr;
+    static const char* const pPropertyKey = "PROPERTY_BUTTONGROUP";
+    QVariant aVariant = pGroupOwner->property(pPropertyKey);
+    if (aVariant.canConvert<QButtonGroup*>())
+    {
+        pButtonGroup = aVariant.value<QButtonGroup*>();
+    }
+    else
+    {
+        pButtonGroup = new QButtonGroup(pGroupOwner);
+        pButtonGroup->addButton(pGroupOwner);
+    }
+
+    QRadioButton* pRadioButton = get<QRadioButton>(rRadioButtonId);
+    assert(pRadioButton && "No radio button with given ID");
+    pButtonGroup->addButton(pRadioButton);
+
+    pGroupOwner->setProperty(pPropertyKey, QVariant::fromValue(pButtonGroup));
 }
 
 void QtBuilder::setPriority(QObject*, int) { SAL_WARN("vcl.qt", "Ignoring 
priority"); }
commit 7f64114f90c58599c459b9b5ce622e4d97bf9388
Author:     Michael Weghorn <m.wegh...@posteo.de>
AuthorDate: Fri Dec 13 22:28:02 2024 +0100
Commit:     Michael Weghorn <m.wegh...@posteo.de>
CommitDate: Sat Dec 14 01:03:45 2024 +0100

    tdf#130857 VclBuilder: Move more radio group logic to base class
    
    Add a purely virtual WidgetBuilder::setRadioButtonGroup method
    and call it for each of the entries in the map from
    WidgetBuilder::processUIFile, in line with what is already done
    for mnemonic widgets.
    Move the existing logic for handling such a pair to the new
    VclBuilder override, VclBuilder::setRadioButtonGroup.
    
    An "actual" implementation for QtBuilder will be added in a
    separate commit, just emit a warning there for now when the method
    gets called.
    
    Change-Id: I027278014bb18d9d5f3de8189cd2f515959bf3e7
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/178443
    Reviewed-by: Michael Weghorn <m.wegh...@posteo.de>
    Tested-by: Jenkins

diff --git a/include/vcl/builder.hxx b/include/vcl/builder.hxx
index 38e9aab01dcc..96136b04bb4c 100644
--- a/include/vcl/builder.hxx
+++ b/include/vcl/builder.hxx
@@ -270,6 +270,7 @@ private:
                             bool bToolbarItem) override;
 
     void setMnemonicWidget(const OUString& rLabelId, const OUString& 
rMnemonicWidgetId) override;
+    void setRadioButtonGroup(const OUString& rRadioButtonId, const OUString& 
rRadioGroupId) override;
     void setPriority(vcl::Window* pWindow, int nPriority) override;
     void setContext(vcl::Window* pWindow,
                     std::vector<vcl::EnumContext::Context>&& aContext) 
override;
diff --git a/include/vcl/widgetbuilder.hxx b/include/vcl/widgetbuilder.hxx
index 22641cb5d552..3b191b2f52f6 100644
--- a/include/vcl/widgetbuilder.hxx
+++ b/include/vcl/widgetbuilder.hxx
@@ -82,6 +82,10 @@ protected:
         {
             setMnemonicWidget(rMnemonic.m_sID, rMnemonic.m_sValue);
         }
+
+        // Set radiobutton groups when everything has been imported
+        for (const RadioButtonGroupMap& rGroup : getRadioButtonGroupMaps())
+            setRadioButtonGroup(rGroup.m_sID, rGroup.m_sValue);
     }
 
     // either pParent or pAtkProps must be set, pParent for a child of a 
widget, pAtkProps for
@@ -546,6 +550,8 @@ protected:
         = 0;
 
     virtual void setMnemonicWidget(const OUString& rLabelId, const OUString& 
rMnemonicWidgetId) = 0;
+    virtual void setRadioButtonGroup(const OUString& rRadioButtonId, const 
OUString& rRadioGroupId)
+        = 0;
     virtual void setPriority(Widget* pWidget, int nPriority) = 0;
     virtual void setContext(Widget* pWidget, 
std::vector<vcl::EnumContext::Context>&& aContext) = 0;
 
diff --git a/vcl/inc/qt5/QtBuilder.hxx b/vcl/inc/qt5/QtBuilder.hxx
index a69158e10a8f..743dc9961ec6 100644
--- a/vcl/inc/qt5/QtBuilder.hxx
+++ b/vcl/inc/qt5/QtBuilder.hxx
@@ -70,6 +70,8 @@ public:
 
     virtual void setMnemonicWidget(const OUString& rLabelId,
                                    const OUString& rMnemonicWidgetId) override;
+    virtual void setRadioButtonGroup(const OUString& rRadioButtonId,
+                                     const OUString& rRadioGroupId) override;
     virtual void setPriority(QObject* pObject, int nPriority) override;
     virtual void setContext(QObject* pObject,
                             std::vector<vcl::EnumContext::Context>&& aContext) 
override;
diff --git a/vcl/qt5/QtBuilder.cxx b/vcl/qt5/QtBuilder.cxx
index 98902bb9ab5e..963d8e71e861 100644
--- a/vcl/qt5/QtBuilder.cxx
+++ b/vcl/qt5/QtBuilder.cxx
@@ -473,6 +473,11 @@ void QtBuilder::setMnemonicWidget(const OUString& 
rLabelId, const OUString& rMne
     pLabel->setBuddy(static_cast<QWidget*>(pBuddy));
 }
 
+void QtBuilder::setRadioButtonGroup(const OUString&, const OUString&)
+{
+    SAL_WARN("vcl.qt", "Not implemented yet");
+}
+
 void QtBuilder::setPriority(QObject*, int) { SAL_WARN("vcl.qt", "Ignoring 
priority"); }
 
 void QtBuilder::setContext(QObject*, std::vector<vcl::EnumContext::Context>&&)
diff --git a/vcl/source/window/builder.cxx b/vcl/source/window/builder.cxx
index 8172e3d30e99..cec44574e48c 100644
--- a/vcl/source/window/builder.cxx
+++ b/vcl/source/window/builder.cxx
@@ -558,24 +558,6 @@ VclBuilder::VclBuilder(vcl::Window* pParent, 
std::u16string_view sUIDir, const O
         }
     }
 
-    //Set radiobutton groups when everything has been imported
-    for (auto const& elem : getRadioButtonGroupMaps())
-    {
-        RadioButton *pOne = get<RadioButton>(elem.m_sID);
-        RadioButton *pOther = get<RadioButton>(elem.m_sValue);
-        SAL_WARN_IF(!pOne || !pOther, "vcl", "missing member of radiobutton 
group");
-        if (pOne && pOther)
-        {
-            if (isLegacy())
-                pOne->group(*pOther);
-            else
-            {
-                pOther->group(*pOne);
-                std::stable_sort(pOther->m_xGroup->begin(), 
pOther->m_xGroup->end(), sortIntoBestTabTraversalOrder(this));
-            }
-        }
-    }
-
 #ifndef NDEBUG
     o3tl::sorted_vector<OUString> models;
 #endif
@@ -2809,6 +2791,23 @@ void VclBuilder::setMnemonicWidget(const OUString& 
rLabelId, const OUString& rMn
         pOne->set_mnemonic_widget(pOther);
 }
 
+void VclBuilder::setRadioButtonGroup(const OUString& rRadioButtonId, const 
OUString& rRadioGroupId)
+{
+    RadioButton *pOne = get<RadioButton>(rRadioButtonId);
+    RadioButton *pOther = get<RadioButton>(rRadioGroupId);
+    SAL_WARN_IF(!pOne || !pOther, "vcl", "missing member of radiobutton 
group");
+    if (pOne && pOther)
+    {
+        if (isLegacy())
+            pOne->group(*pOther);
+        else
+        {
+            pOther->group(*pOne);
+            std::stable_sort(pOther->m_xGroup->begin(), 
pOther->m_xGroup->end(), sortIntoBestTabTraversalOrder(this));
+        }
+    }
+}
+
 void VclBuilder::setPriority(vcl::Window* pWindow, int nPriority)
 {
     vcl::IPrioritable* pPrioritable = 
dynamic_cast<vcl::IPrioritable*>(pWindow);
commit ce67331daa101ce1d8fc81fe1c843d8810f7a33c
Author:     Michael Weghorn <m.wegh...@posteo.de>
AuthorDate: Fri Dec 13 22:11:34 2024 +0100
Commit:     Michael Weghorn <m.wegh...@posteo.de>
CommitDate: Sat Dec 14 01:03:39 2024 +0100

    tdf#130857 VclBuilder: Store radio button groups in BuilderBase
    
    Move the std::vector<RadioButtonGroupMap> and the method
    to extract and add to that map from VclBuilder to its base
    class, BuilderBase.
    
    Add a getter, BuilderBase::getRadioButtonGroupMaps instead
    of accessing the base class member directly in the VclBuilder subclass.
    
    Change-Id: Ic0d2abbd9faa41760d542ab22a80f2ba23136ccf
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/178442
    Tested-by: Jenkins
    Reviewed-by: Michael Weghorn <m.wegh...@posteo.de>

diff --git a/include/vcl/builder.hxx b/include/vcl/builder.hxx
index 254b4e52d328..38e9aab01dcc 100644
--- a/include/vcl/builder.hxx
+++ b/include/vcl/builder.hxx
@@ -127,8 +127,6 @@ private:
     };
     std::vector<WinAndId> m_aChildren;
 
-    typedef StringPair RadioButtonGroupMap;
-
     struct ButtonImageWidgetMap
     {
         OUString m_sID;
@@ -179,8 +177,6 @@ private:
 
     struct VclParserState
     {
-        std::vector<RadioButtonGroupMap> m_aGroupMaps;
-
         std::vector<ComboBoxModelMap> m_aModelMaps;
 
         std::vector<TextBufferMap> m_aTextBufferMaps;
@@ -254,7 +250,6 @@ private:
 
     static int  getImageSize(const stringmap &rMap);
 
-    void        extractGroup(const OUString &id, stringmap &rVec);
     void        extractModel(const OUString &id, stringmap &rVec);
     void        extractBuffer(const OUString &id, stringmap &rVec);
     static bool extractAdjustmentToMap(const OUString &id, stringmap &rVec, 
std::vector<WidgetAdjustmentMap>& rAdjustmentMap);
diff --git a/include/vcl/builderbase.hxx b/include/vcl/builderbase.hxx
index 573817315f72..eb3aa847199f 100644
--- a/include/vcl/builderbase.hxx
+++ b/include/vcl/builderbase.hxx
@@ -77,6 +77,7 @@ protected:
     };
 
     typedef StringPair MnemonicWidgetMap;
+    typedef StringPair RadioButtonGroupMap;
 
     static void collectPangoAttribute(xmlreader::XmlReader& reader, stringmap& 
rMap);
     static void collectAtkRelationAttribute(xmlreader::XmlReader& reader, 
stringmap& rMap);
@@ -112,6 +113,9 @@ protected:
     void extractMnemonicWidget(const OUString& id, stringmap& rMap);
     const std::vector<MnemonicWidgetMap>& getMnemonicWidgetMaps() const;
 
+    void extractRadioButtonGroup(const OUString& id, stringmap& rVec);
+    const std::vector<RadioButtonGroupMap>& getRadioButtonGroupMaps() const;
+
     OUString finalizeValue(const OString& rContext, const OString& rValue,
                            const bool bTranslate) const;
 
@@ -149,6 +153,7 @@ private:
         std::map<OUString, TextBuffer> m_aTextBuffers;
 
         std::vector<MnemonicWidgetMap> m_aMnemonicWidgetMaps;
+        std::vector<RadioButtonGroupMap> m_aRadioButtonGroupMaps;
     };
 
     std::unique_ptr<ParserState> m_pParserState;
diff --git a/vcl/source/window/builder.cxx b/vcl/source/window/builder.cxx
index ccfb68ca9f9e..8172e3d30e99 100644
--- a/vcl/source/window/builder.cxx
+++ b/vcl/source/window/builder.cxx
@@ -481,6 +481,11 @@ const std::vector<BuilderBase::MnemonicWidgetMap>& 
BuilderBase::getMnemonicWidge
     return m_pParserState->m_aMnemonicWidgetMaps;
 }
 
+const std::vector<BuilderBase::RadioButtonGroupMap>& 
BuilderBase::getRadioButtonGroupMaps() const {
+    assert(m_pParserState && "parser state no more valid");
+    return m_pParserState->m_aRadioButtonGroupMaps;
+}
+
 OUString BuilderBase::finalizeValue(const OString& rContext, const OString& 
rValue,
                                     const bool bTranslate) const
 {
@@ -554,7 +559,7 @@ VclBuilder::VclBuilder(vcl::Window* pParent, 
std::u16string_view sUIDir, const O
     }
 
     //Set radiobutton groups when everything has been imported
-    for (auto const& elem : m_pVclParserState->m_aGroupMaps)
+    for (auto const& elem : getRadioButtonGroupMaps())
     {
         RadioButton *pOne = get<RadioButton>(elem.m_sID);
         RadioButton *pOther = get<RadioButton>(elem.m_sValue);
@@ -1098,7 +1103,7 @@ namespace
     }
 }
 
-void VclBuilder::extractGroup(const OUString &id, stringmap &rMap)
+void BuilderBase::extractRadioButtonGroup(const OUString &id, stringmap &rMap)
 {
     VclBuilder::stringmap::iterator aFind = rMap.find(u"group"_ustr);
     if (aFind != rMap.end())
@@ -1107,7 +1112,7 @@ void VclBuilder::extractGroup(const OUString &id, 
stringmap &rMap)
         sal_Int32 nDelim = sID.indexOf(':');
         if (nDelim != -1)
             sID = sID.copy(0, nDelim);
-        m_pVclParserState->m_aGroupMaps.emplace_back(id, sID);
+        m_pParserState->m_aRadioButtonGroupMaps.emplace_back(id, sID);
         rMap.erase(aFind);
     }
 }
@@ -1603,7 +1608,7 @@ VclPtr<vcl::Window> VclBuilder::makeObject(vcl::Window 
*pParent, const OUString
     }
     else if (name == "GtkRadioButton")
     {
-        extractGroup(id, rMap);
+        extractRadioButtonGroup(id, rMap);
         WinBits nBits = WB_CLIPCHILDREN|WB_LEFT|WB_VCENTER|WB_3DLOOK;
         VclPtr<RadioButton> xButton = VclPtr<RadioButton>::Create(pParent, 
true, nBits);
         xButton->SetImageAlign(ImageAlign::Left); //default to left

Reply via email to