vcl/unx/gtk3/gtkinst.cxx     |   74 ++++++++++++++++++++++++++++++++++++++-----
 vcl/unx/gtk4/convert3to4.cxx |   56 +++++++++++++++++++++++++++-----
 2 files changed, 113 insertions(+), 17 deletions(-)

New commits:
commit b32582d4bc21953a43c469872d58445ee1a5ff2b
Author:     Caolán McNamara <caol...@redhat.com>
AuthorDate: Fri Jun 4 14:44:44 2021 +0100
Commit:     Caolán McNamara <caol...@redhat.com>
CommitDate: Fri Jun 4 18:16:55 2021 +0200

    gtk4: support radio groups in GtkMenuButton menus
    
    Change-Id: I4e6b61e2c16b208a91490cfbaf5dc933d45c1df0
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/116718
    Tested-by: Caolán McNamara <caol...@redhat.com>
    Reviewed-by: Caolán McNamara <caol...@redhat.com>

diff --git a/vcl/unx/gtk3/gtkinst.cxx b/vcl/unx/gtk3/gtkinst.cxx
index 9615c36dac09..5b225ac558b3 100644
--- a/vcl/unx/gtk3/gtkinst.cxx
+++ b/vcl/unx/gtk3/gtkinst.cxx
@@ -9195,6 +9195,10 @@ private:
     GtkImage* m_pImage;
 #else
     GtkPicture* m_pImage;
+    o3tl::sorted_vector<OString> m_aInsertedActions; // must outlive 
m_aActionEntries
+    std::map<OString, OString> m_aIdToAction;
+    std::vector<GActionEntry> m_aActionEntries;
+    GActionGroup* m_pActionGroup;
 #endif
     GtkWidget* m_pLabel;
 #if !GTK_CHECK_VERSION(4, 0, 0)
@@ -9390,6 +9394,59 @@ private:
 #endif
     }
 
+#if GTK_CHECK_VERSION(4, 0, 0)
+    // build an action group for the menu, "action" is the normal menu entry 
case
+    // the others are radiogroups
+    void update_action_group_from_popover_model()
+    {
+        for (const auto& rAction : m_aActionEntries)
+            g_action_map_remove_action(G_ACTION_MAP(m_pActionGroup), 
rAction.name);
+        m_aActionEntries.clear();
+        m_aInsertedActions.clear();
+        m_aIdToAction.clear();
+
+        m_aActionEntries.push_back({"action", action_activated, "s", nullptr, 
nullptr, {}});
+
+        GtkPopover* pPopover = gtk_menu_button_get_popover(m_pMenuButton);
+        if (GMenuModel* pMenuModel = GTK_IS_POPOVER_MENU(pPopover) ?
+                                     
gtk_popover_menu_get_menu_model(GTK_POPOVER_MENU(pPopover)) :
+                                     nullptr)
+        {
+            for (int i = 0, nCount = g_menu_model_get_n_items(pMenuModel); i < 
nCount; ++i)
+            {
+                OString sAction, sTarget;
+                char *id;
+                if (g_menu_model_get_item_attribute(pMenuModel, i, "action", 
"s", &id))
+                {
+                    assert(OString(id).startsWith("menu."));
+
+                    sAction = OString(id + 5);
+
+                    auto res = m_aInsertedActions.insert(sAction);
+                    if (res.second)
+                    {
+                        // the const char* arg isn't copied by anything so it 
must continue to exist for the life time of
+                        // the action group
+                        m_aActionEntries.push_back({res.first->getStr(), 
action_activated, "s", "'none'", nullptr, {}});
+                    }
+
+                    g_free(id);
+                }
+
+                if (g_menu_model_get_item_attribute(pMenuModel, i, "target", 
"s", &id))
+                {
+                    sTarget = OString(id);
+                    g_free(id);
+                }
+
+                m_aIdToAction[sTarget] = sAction;
+            }
+        }
+
+        g_action_map_add_action_entries(G_ACTION_MAP(m_pActionGroup), 
m_aActionEntries.data(), m_aActionEntries.size(), this);
+    }
+#endif
+
 public:
 #if !GTK_CHECK_VERSION(4, 0, 0)
     GtkInstanceMenuButton(GtkMenuButton* pMenuButton, GtkWidget* pMenuAlign, 
GtkInstanceBuilder* pBuilder, bool bTakeOwnership)
@@ -9421,14 +9478,10 @@ public:
         m_pBox = formatMenuButton(m_pLabel);
 
 #if GTK_CHECK_VERSION(4, 0, 0)
-        static GActionEntry entries[] =
-        {
-            { "action", action_activated, "s", nullptr, nullptr }
-        };
+        m_pActionGroup = G_ACTION_GROUP(g_simple_action_group_new());
+        gtk_widget_insert_action_group(GTK_WIDGET(m_pMenuButton), "menu", 
m_pActionGroup);
 
-        GActionGroup* pActions = G_ACTION_GROUP(g_simple_action_group_new());
-        g_action_map_add_action_entries(G_ACTION_MAP(pActions), entries, 
SAL_N_ELEMENTS(entries), this);
-        gtk_widget_insert_action_group(GTK_WIDGET(m_pMenuButton), "menu", 
pActions);
+        update_action_group_from_popover_model();
 #endif
     }
 
@@ -9559,7 +9612,10 @@ public:
 
     virtual void set_item_active(const OString& rIdent, bool bActive) override
     {
-#if !GTK_CHECK_VERSION(4, 0, 0)
+#if GTK_CHECK_VERSION(4, 0, 0)
+        g_action_group_change_action_state(m_pActionGroup, 
m_aIdToAction[rIdent].getStr(),
+                                           
g_variant_new_string(rIdent.getStr()));
+#else
         MenuHelper::set_item_active(rIdent, bActive);
 #endif
     }
@@ -9633,6 +9689,7 @@ public:
 
 #if GTK_CHECK_VERSION(4, 0, 0)
         gtk_menu_button_set_popover(m_pMenuButton, m_pPopover);
+        update_action_group_from_popover_model();
         return;
 #else
 
@@ -21988,6 +22045,7 @@ weld::Builder* GtkInstance::CreateBuilder(weld::Widget* 
pParent, const OUString&
         rUIFile != "modules/smath/ui/fontsizedialog.ui" &&
         rUIFile != "modules/smath/ui/fonttypedialog.ui" &&
         rUIFile != "modules/smath/ui/savedefaultsdialog.ui" &&
+        rUIFile != "modules/smath/ui/spacingdialog.ui" &&
         rUIFile != "modules/swriter/ui/bibliographyentry.ui" &&
         rUIFile != "modules/swriter/ui/columndialog.ui" &&
         rUIFile != "modules/swriter/ui/columnpage.ui" &&
diff --git a/vcl/unx/gtk4/convert3to4.cxx b/vcl/unx/gtk4/convert3to4.cxx
index c2c2872b0897..33a3c4d5adc5 100644
--- a/vcl/unx/gtk4/convert3to4.cxx
+++ b/vcl/unx/gtk4/convert3to4.cxx
@@ -113,10 +113,26 @@ void AddBorderAsMargins(const 
css::uno::Reference<css::xml::dom::XNode>& xNode,
     xNode->insertBefore(CreateProperty(xDoc, "margin-start", rBorderWidth), 
xMarginEnd);
 }
 
-css::uno::Reference<css::xml::dom::XNode>
-ConvertMenu(const css::uno::Reference<css::xml::dom::XNode>& xMenu,
-            const css::uno::Reference<css::xml::dom::XNode>& xNode)
+struct MenuEntry
+{
+    bool m_bDrawAsRadio;
+    OUString m_sRadioGroup;
+    css::uno::Reference<css::xml::dom::XNode> m_xPropertyLabel;
+
+    MenuEntry(bool bDrawAsRadio, const OUString& rRadioGroup,
+              const css::uno::Reference<css::xml::dom::XNode>& rPropertyLabel)
+        : m_bDrawAsRadio(bDrawAsRadio)
+        , m_sRadioGroup(rRadioGroup)
+        , m_xPropertyLabel(rPropertyLabel)
+    {
+    }
+};
+
+MenuEntry ConvertMenu(const css::uno::Reference<css::xml::dom::XNode>& xMenu,
+                      const css::uno::Reference<css::xml::dom::XNode>& xNode)
 {
+    bool bDrawAsRadio = false;
+    OUString sRadioGroup;
     css::uno::Reference<css::xml::dom::XNode> xPropertyLabel;
 
     css::uno::Reference<css::xml::dom::XNode> xChild = xNode->getFirstChild();
@@ -132,14 +148,27 @@ ConvertMenu(const 
css::uno::Reference<css::xml::dom::XNode>& xMenu,
             {
                 xPropertyLabel = xChild;
             }
+            else if (sName == "draw-as-radio")
+            {
+                bDrawAsRadio = toBool(xChild->getFirstChild()->getNodeValue());
+            }
+            else if (sName == "group")
+            {
+                sRadioGroup = xChild->getFirstChild()->getNodeValue();
+            }
         }
 
         auto xNextChild = xChild->getNextSibling();
 
+        bool bChildDrawAsRadio = false;
+        OUString sChildRadioGroup;
         css::uno::Reference<css::xml::dom::XNode> xChildPropertyLabel;
         if (xChild->hasChildNodes())
         {
-            xChildPropertyLabel = ConvertMenu(xMenu, xChild);
+            MenuEntry aEntry = ConvertMenu(xMenu, xChild);
+            bChildDrawAsRadio = aEntry.m_bDrawAsRadio;
+            sChildRadioGroup = aEntry.m_sRadioGroup;
+            xChildPropertyLabel = aEntry.m_xPropertyLabel;
         }
 
         if (xChild->getNodeName() == "object")
@@ -150,8 +179,11 @@ ConvertMenu(const 
css::uno::Reference<css::xml::dom::XNode>& xMenu,
             css::uno::Reference<css::xml::dom::XNode> xClass = 
xMap->getNamedItem("class");
             OUString sClass(xClass->getNodeValue());
 
-            if (sClass == "GtkMenuItem")
+            if (sClass == "GtkMenuItem" || sClass == "GtkRadioMenuItem")
             {
+                css::uno::Reference<css::xml::dom::XNode> xId = 
xMap->getNamedItem("id");
+                OUString sId = xId->getNodeValue();
+
                 /*
                   <item>
                     <attribute name='label' 
translatable='yes'>whatever</attribute>
@@ -192,7 +224,14 @@ ConvertMenu(const 
css::uno::Reference<css::xml::dom::XNode>& xMenu,
                     = xDoc->createAttribute("name");
                 xActionName->setValue("action");
                 xActionAttr->setAttributeNode(xActionName);
-                xActionAttr->appendChild(xDoc->createTextNode("menu.action"));
+                if (bChildDrawAsRadio)
+                {
+                    if (sChildRadioGroup.isEmpty())
+                        sChildRadioGroup = sId;
+                    xActionAttr->appendChild(xDoc->createTextNode("menu." + 
sChildRadioGroup));
+                }
+                else
+                    
xActionAttr->appendChild(xDoc->createTextNode("menu.action"));
                 xItem->appendChild(xActionAttr);
 
                 css::uno::Reference<css::xml::dom::XElement> xTargetAttr
@@ -201,8 +240,7 @@ ConvertMenu(const 
css::uno::Reference<css::xml::dom::XNode>& xMenu,
                     = xDoc->createAttribute("name");
                 xTargetName->setValue("target");
                 xTargetAttr->setAttributeNode(xTargetName);
-                css::uno::Reference<css::xml::dom::XNode> xId = 
xMap->getNamedItem("id");
-                
xTargetAttr->appendChild(xDoc->createTextNode(xId->getNodeValue()));
+                xTargetAttr->appendChild(xDoc->createTextNode(sId));
                 xItem->appendChild(xTargetAttr);
             }
         }
@@ -210,7 +248,7 @@ ConvertMenu(const 
css::uno::Reference<css::xml::dom::XNode>& xMenu,
         xChild = xNextChild;
     }
 
-    return xPropertyLabel;
+    return MenuEntry(bDrawAsRadio, sRadioGroup, xPropertyLabel);
 }
 
 struct ConvertResult
_______________________________________________
Libreoffice-commits mailing list
libreoffice-comm...@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits

Reply via email to