[gtk+] Add API to create menus from models



commit 4240bfb74abf53adb07afc4135dadde4ab866b1f
Author: Matthias Clasen <mclasen redhat com>
Date:   Tue Jan 24 22:25:35 2012 -0500

    Add API to create menus from models
    
    This is needed to bring context menus, etc into the GAction world.

 docs/reference/gtk/gtk3-sections.txt |    2 +
 gtk/gtk.symbols                      |    2 +
 gtk/gtkapplicationprivate.h          |    7 ++
 gtk/gtkapplicationwindow.c           |   12 +++
 gtk/gtkmenu.h                        |    1 +
 gtk/gtkmenubar.h                     |    1 +
 gtk/gtkmodelmenu.c                   |  135 +++++++++++++++++++++++++++++++--
 gtk/gtkmodelmenu.h                   |    9 --
 gtk/gtkmodelmenuitem.c               |   18 +++--
 9 files changed, 162 insertions(+), 25 deletions(-)
---
diff --git a/docs/reference/gtk/gtk3-sections.txt b/docs/reference/gtk/gtk3-sections.txt
index 3b2317e1..41e42a9 100644
--- a/docs/reference/gtk/gtk3-sections.txt
+++ b/docs/reference/gtk/gtk3-sections.txt
@@ -2059,6 +2059,7 @@ gtk_link_button_get_type
 <TITLE>GtkMenu</TITLE>
 GtkMenu
 gtk_menu_new
+gtk_menu_new_from_model
 gtk_menu_set_screen
 gtk_menu_reorder_child
 gtk_menu_attach
@@ -2103,6 +2104,7 @@ gtk_menu_get_type
 <TITLE>GtkMenuBar</TITLE>
 GtkMenuBar
 gtk_menu_bar_new
+gtk_menu_bar_new_from_model
 GtkPackDirection
 gtk_menu_bar_set_pack_direction
 gtk_menu_bar_get_pack_direction
diff --git a/gtk/gtk.symbols b/gtk/gtk.symbols
index 93a4a17..3f377b1 100644
--- a/gtk/gtk.symbols
+++ b/gtk/gtk.symbols
@@ -1533,6 +1533,7 @@ gtk_menu_bar_get_child_pack_direction
 gtk_menu_bar_get_pack_direction
 gtk_menu_bar_get_type
 gtk_menu_bar_new
+gtk_menu_bar_new_from_model
 gtk_menu_bar_set_child_pack_direction
 gtk_menu_bar_set_pack_direction
 gtk_menu_detach
@@ -1569,6 +1570,7 @@ gtk_menu_item_set_use_underline
 gtk_menu_item_toggle_size_allocate
 gtk_menu_item_toggle_size_request
 gtk_menu_new
+gtk_menu_new_from_model
 gtk_menu_popdown
 gtk_menu_popup
 gtk_menu_popup_for_device
diff --git a/gtk/gtkapplicationprivate.h b/gtk/gtkapplicationprivate.h
index 4b24a53..10c5016 100644
--- a/gtk/gtkapplicationprivate.h
+++ b/gtk/gtkapplicationprivate.h
@@ -38,6 +38,13 @@ G_GNUC_INTERNAL
 GSimpleActionObserver * gtk_application_window_create_observer          (GtkApplicationWindow *window,
                                                                          const gchar          *action_name,
                                                                          GVariant             *target);
+
+G_GNUC_INTERNAL
+GActionObservable     * gtk_application_window_get_observable           (GtkApplicationWindow *window);
+
+G_GNUC_INTERNAL
+GtkAccelGroup         * gtk_application_window_get_accel_group          (GtkApplicationWindow *window);
+
 G_GNUC_INTERNAL
 const gchar *           gtk_application_get_dbus_object_path            (GtkApplication       *application);
 G_GNUC_INTERNAL
diff --git a/gtk/gtkapplicationwindow.c b/gtk/gtkapplicationwindow.c
index fabf8ef..5f8b1b3 100644
--- a/gtk/gtkapplicationwindow.c
+++ b/gtk/gtkapplicationwindow.c
@@ -1012,3 +1012,15 @@ gtk_application_window_create_observer (GtkApplicationWindow *window,
 
   return g_simple_action_observer_new (window->priv->muxer, action_name, target);
 }
+
+GActionObservable *
+gtk_application_window_get_observable (GtkApplicationWindow *window)
+{
+  return G_ACTION_OBSERVABLE (window->priv->muxer);
+}
+
+GtkAccelGroup *
+gtk_application_window_get_accel_group (GtkApplicationWindow *window)
+{
+  return window->priv->accels;
+}
diff --git a/gtk/gtkmenu.h b/gtk/gtkmenu.h
index 0228236..557949d 100644
--- a/gtk/gtkmenu.h
+++ b/gtk/gtkmenu.h
@@ -117,6 +117,7 @@ struct _GtkMenuClass
 
 GType	   gtk_menu_get_type		  (void) G_GNUC_CONST;
 GtkWidget* gtk_menu_new			  (void);
+GtkWidget* gtk_menu_new_from_model        (GMenuModel *model);
 
 /* Display the menu onscreen */
 void	   gtk_menu_popup		  (GtkMenu	       *menu,
diff --git a/gtk/gtkmenubar.h b/gtk/gtkmenubar.h
index bc1f66c..967b04d 100644
--- a/gtk/gtkmenubar.h
+++ b/gtk/gtkmenubar.h
@@ -71,6 +71,7 @@ struct _GtkMenuBarClass
 
 GType      gtk_menu_bar_get_type        (void) G_GNUC_CONST;
 GtkWidget* gtk_menu_bar_new             (void);
+GtkWidget* gtk_menu_bar_new_from_model  (GMenuModel *model);
 
 GtkPackDirection gtk_menu_bar_get_pack_direction (GtkMenuBar       *menubar);
 void             gtk_menu_bar_set_pack_direction (GtkMenuBar       *menubar,
diff --git a/gtk/gtkmodelmenu.c b/gtk/gtkmodelmenu.c
index d91d92b..df08f71 100644
--- a/gtk/gtkmodelmenu.c
+++ b/gtk/gtkmodelmenu.c
@@ -29,6 +29,7 @@
 #include "gtkmenubar.h"
 #include "gtkseparatormenuitem.h"
 #include "gtkmodelmenuitem.h"
+#include "gtkapplicationprivate.h"
 
 typedef struct {
   GActionObservable *actions;
@@ -65,7 +66,8 @@ gtk_model_menu_binding_free (gpointer data)
       binding->connected = g_slist_delete_link (binding->connected, binding->connected);
     }
 
-  g_object_unref (binding->actions);
+  if (binding->actions)
+    g_object_unref (binding->actions);
   g_object_unref (binding->model);
 
   g_slice_free (GtkModelMenuBinding, binding);
@@ -224,25 +226,38 @@ gtk_model_menu_binding_items_changed (GMenuModel *model,
     }
 }
 
-void
+static void
 gtk_model_menu_bind (GtkMenuShell      *shell,
                      GMenuModel        *model,
-                     GActionObservable *actions,
-                     GtkAccelGroup     *accels,
                      gboolean           with_separators)
 {
   GtkModelMenuBinding *binding;
 
   binding = g_slice_new (GtkModelMenuBinding);
   binding->model = g_object_ref (model);
-  binding->actions = g_object_ref (actions);
-  binding->accels = accels;
+  binding->actions = NULL;
+  binding->accels = NULL;
   binding->shell = shell;
   binding->update_idle = 0;
   binding->connected = NULL;
   binding->with_separators = with_separators;
 
   g_object_set_data_full (G_OBJECT (shell), "gtk-model-menu-binding", binding, gtk_model_menu_binding_free);
+}
+
+
+static void
+gtk_model_menu_populate (GtkMenuShell      *shell,
+                         GActionObservable *actions,
+                         GtkAccelGroup     *accels)
+{
+  GtkModelMenuBinding *binding;
+
+  binding = (GtkModelMenuBinding*) g_object_get_data (G_OBJECT (shell), "gtk-model-menu-binding");
+
+  binding->actions = g_object_ref (actions);
+  binding->accels = accels;
+
   gtk_model_menu_binding_populate (binding);
 }
 
@@ -254,8 +269,59 @@ gtk_model_menu_create_menu (GMenuModel        *model,
   GtkWidget *menu;
 
   menu = gtk_menu_new ();
-  gtk_menu_set_accel_group (GTK_MENU (menu), accels);
-  gtk_model_menu_bind (GTK_MENU_SHELL (menu), model, actions, accels, TRUE);
+
+  gtk_model_menu_bind (GTK_MENU_SHELL (menu), model, FALSE);
+  gtk_model_menu_populate (GTK_MENU_SHELL (menu), actions, accels);
+
+  return menu;
+}
+
+static void
+notify_attach (GtkMenu    *menu,
+               GParamSpec *pspec,
+               gpointer    data)
+{
+  GtkWidget *widget;
+  GtkWidget *toplevel;
+  GActionObservable *actions;
+  GtkAccelGroup *accels;
+
+  widget = gtk_menu_get_attach_widget (menu);
+  toplevel = gtk_widget_get_toplevel (widget);
+  if (GTK_IS_APPLICATION_WINDOW (toplevel))
+    {
+      actions = gtk_application_window_get_observable (GTK_APPLICATION_WINDOW (toplevel));
+      accels = gtk_application_window_get_accel_group (GTK_APPLICATION_WINDOW (toplevel));
+
+      gtk_model_menu_populate (GTK_MENU_SHELL (menu), actions, accels);
+    }
+}
+
+/**
+ * gtk_menu_new_from_model:
+ * @model: a #GMenuModel
+ *
+ * Creates a #GtkMenu and populates it with menu items and
+ * submenus according to @model.
+ *
+ * The created menu items are connected to actions found in the
+ * #GtkApplicationWindow to which the menu belongs - typically
+ * by means of being attached to a widget (see gtk_menu_attach_to_widget())
+ * that is contained within the #GtkApplicationWindows widget hierarchy.
+ *
+ * Returns: a new #GtkMenu
+ *
+ * Since: 3.4
+ */
+GtkWidget *
+gtk_menu_new_from_model (GMenuModel *model)
+{
+  GtkWidget *menu;
+
+  menu = gtk_menu_new ();
+  gtk_model_menu_bind (GTK_MENU_SHELL (menu), model, TRUE);
+  g_signal_connect (menu, "notify::attach-widget",
+                    G_CALLBACK (notify_attach), NULL);
 
   return menu;
 }
@@ -268,8 +334,59 @@ gtk_model_menu_create_menu_bar (GMenuModel        *model,
   GtkWidget *menubar;
 
   menubar = gtk_menu_bar_new ();
-  gtk_model_menu_bind (GTK_MENU_SHELL (menubar), model, actions, accels, FALSE);
+
+  gtk_model_menu_bind (GTK_MENU_SHELL (menubar), model, FALSE);
+  gtk_model_menu_populate (GTK_MENU_SHELL (menubar), actions, accels);
 
   return menubar;
 }
 
+static void
+hierarchy_changed (GtkMenuShell *shell,
+                   GObject      *previous_toplevel,
+                   gpointer      data)
+{
+  GtkWidget *toplevel;
+  GActionObservable *actions;
+  GtkAccelGroup *accels;
+
+  toplevel = gtk_widget_get_toplevel (GTK_WIDGET (shell));
+  if (GTK_IS_APPLICATION_WINDOW (toplevel))
+    {
+      actions = gtk_application_window_get_observable (GTK_APPLICATION_WINDOW (toplevel));
+      accels = gtk_application_window_get_accel_group (GTK_APPLICATION_WINDOW (toplevel));
+
+      gtk_model_menu_populate (shell, actions, accels);
+    }
+}
+
+/**
+ * gtk_menu_bar_new_from_model:
+ * @model: a #GMenuModel
+ *
+ * Creates a new #GtkMenuBar and populates it with menu items
+ * and submenus according to @model.
+ *
+ * The created menu items are connected to actions found in the
+ * #GtkApplicationWindow to which the menu bar belongs - typically
+ * by means of being contained within the #GtkApplicationWindows
+ * widget hierarchy.
+ *
+ * Returns: a new #GtkMenuBar
+ *
+ * Since: 3.4
+ */
+GtkWidget *
+gtk_menu_bar_new_from_model (GMenuModel *model)
+{
+  GtkWidget *menubar;
+
+  menubar = gtk_menu_bar_new ();
+
+  gtk_model_menu_bind (GTK_MENU_SHELL (menubar), model, FALSE);
+
+  g_signal_connect (menubar, "hierarchy-changed",
+                    G_CALLBACK (hierarchy_changed), NULL);
+
+  return menubar;
+}
diff --git a/gtk/gtkmodelmenu.h b/gtk/gtkmodelmenu.h
index 827d143..e1f388b 100644
--- a/gtk/gtkmodelmenu.h
+++ b/gtk/gtkmodelmenu.h
@@ -27,19 +27,10 @@
 #include <gtk/gtkaccelgroup.h>
 #include <gio/gio.h>
 
-G_GNUC_INTERNAL
-void                    gtk_model_menu_bind                             (GtkMenuShell      *shell,
-                                                                         GMenuModel        *model,
-                                                                         GActionObservable *actions,
-                                                                         GtkAccelGroup     *accels,
-                                                                         gboolean           with_separators);
-
-G_GNUC_INTERNAL
 GtkWidget *             gtk_model_menu_create_menu_bar                  (GMenuModel        *model,
                                                                          GActionObservable *actions,
                                                                          GtkAccelGroup     *accels);
 
-G_GNUC_INTERNAL
 GtkWidget *             gtk_model_menu_create_menu                      (GMenuModel        *model,
                                                                          GActionObservable *actions,
                                                                          GtkAccelGroup     *accels);
diff --git a/gtk/gtkmodelmenuitem.c b/gtk/gtkmodelmenuitem.c
index eb7b8f3..e6dd987 100644
--- a/gtk/gtkmodelmenuitem.c
+++ b/gtk/gtkmodelmenuitem.c
@@ -235,15 +235,19 @@ gtk_model_menu_item_setup (GtkModelMenuItem  *item,
       /* observer already causes us to hold a hard ref on the group */
       item->actions = G_ACTION_GROUP (actions);
 
-      g_action_observable_register_observer (actions, item->action_name, G_ACTION_OBSERVER (item));
-
-      if (g_action_group_query_action (G_ACTION_GROUP (actions), item->action_name, &enabled, &type, NULL, NULL, &state))
+      if (actions)
         {
-          gtk_model_menu_item_action_added (G_ACTION_OBSERVER (item), actions, item->action_name, type, enabled, state);
-          if (state != NULL)
-            g_variant_unref (state);
+          g_action_observable_register_observer (actions, item->action_name, G_ACTION_OBSERVER (item));
+
+          if (g_action_group_query_action (G_ACTION_GROUP (actions), item->action_name, &enabled, &type, NULL, NULL, &state))
+            {
+              gtk_model_menu_item_action_added (G_ACTION_OBSERVER (item), actions, item->action_name, type, enabled, state);
+              if (state != NULL)
+                g_variant_unref (state);
+            }
+          else
+            gtk_widget_set_sensitive (GTK_WIDGET (item), FALSE);
         }
-
       else
         gtk_widget_set_sensitive (GTK_WIDGET (item), FALSE);
 



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]