]> Pileus Git - ~andy/gtk/blobdiff - gtk/gtkmodelmenu.c
Really keep GtkBubbleWindow private
[~andy/gtk] / gtk / gtkmodelmenu.c
index df08f7187d224f5da35b464d97ab713de51e2af0..0c3cc5e96ccd94d8fc9baf2c55f261312d16cc83 100644 (file)
@@ -13,9 +13,7 @@
  * Lesser General Public License for more details.
  *
  * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
  *
  * Author: Matthias Clasen <mclasen@redhat.com>
  *         Ryan Lortie <desrt@desrt.ca>
 
 #include "config.h"
 
-#include "gtkmodelmenu.h"
-
-#include "gtkmenu.h"
+#include "gtkmenushell.h"
 #include "gtkmenubar.h"
+#include "gtkmenu.h"
+
 #include "gtkseparatormenuitem.h"
 #include "gtkmodelmenuitem.h"
 #include "gtkapplicationprivate.h"
 
+#define MODEL_MENU_WIDGET_DATA "gtk-model-menu-widget-data"
+
 typedef struct {
-  GActionObservable *actions;
   GMenuModel        *model;
-  GtkAccelGroup     *accels;
   GtkMenuShell      *shell;
   guint              update_idle;
   GSList            *connected;
   gboolean           with_separators;
   gint               n_items;
+  gchar             *action_namespace;
 } GtkModelMenuBinding;
 
 static void
@@ -50,6 +49,7 @@ gtk_model_menu_binding_items_changed (GMenuModel *model,
                                       gpointer    user_data);
 static void gtk_model_menu_binding_append_model (GtkModelMenuBinding *binding,
                                                  GMenuModel *model,
+                                                 const gchar *action_namespace,
                                                  gboolean with_separators);
 
 static void
@@ -66,9 +66,8 @@ gtk_model_menu_binding_free (gpointer data)
       binding->connected = g_slist_delete_link (binding->connected, binding->connected);
     }
 
-  if (binding->actions)
-    g_object_unref (binding->actions);
   g_object_unref (binding->model);
+  g_free (binding->action_namespace);
 
   g_slice_free (GtkModelMenuBinding, binding);
 }
@@ -76,6 +75,7 @@ gtk_model_menu_binding_free (gpointer data)
 static void
 gtk_model_menu_binding_append_item (GtkModelMenuBinding  *binding,
                                     GMenuModel           *model,
+                                    const gchar          *action_namespace,
                                     gint                  item_index,
                                     gchar               **heading)
 {
@@ -83,14 +83,30 @@ gtk_model_menu_binding_append_item (GtkModelMenuBinding  *binding,
 
   if ((section = g_menu_model_get_item_link (model, item_index, "section")))
     {
+      gchar *section_namespace = NULL;
+
       g_menu_model_get_item_attribute (model, item_index, "label", "s", heading);
-      gtk_model_menu_binding_append_model (binding, section, FALSE);
+      g_menu_model_get_item_attribute (model, item_index, "action-namespace", "s", &section_namespace);
+
+      if (action_namespace)
+        {
+          gchar *namespace = g_strjoin (".", action_namespace, section_namespace, NULL);
+          gtk_model_menu_binding_append_model (binding, section, namespace, FALSE);
+          g_free (namespace);
+        }
+      else
+        {
+          gtk_model_menu_binding_append_model (binding, section, section_namespace, FALSE);
+        }
+
+      g_free (section_namespace);
+      g_object_unref (section);
     }
   else
     {
       GtkMenuItem *item;
 
-      item = gtk_model_menu_item_new (model, item_index, binding->actions, binding->accels);
+      item = gtk_model_menu_item_new (model, item_index, action_namespace);
       gtk_menu_shell_append (binding->shell, GTK_WIDGET (item));
       gtk_widget_show (GTK_WIDGET (item));
       binding->n_items++;
@@ -100,6 +116,7 @@ gtk_model_menu_binding_append_item (GtkModelMenuBinding  *binding,
 static void
 gtk_model_menu_binding_append_model (GtkModelMenuBinding *binding,
                                      GMenuModel          *model,
+                                     const gchar         *action_namespace,
                                      gboolean             with_separators)
 {
   gint n, i;
@@ -142,7 +159,7 @@ gtk_model_menu_binding_append_model (GtkModelMenuBinding *binding,
       gint our_position = binding->n_items;
       gchar *heading = NULL;
 
-      gtk_model_menu_binding_append_item (binding, model, i, &heading);
+      gtk_model_menu_binding_append_item (binding, model, action_namespace, i, &heading);
 
       if (with_separators && our_position < binding->n_items)
         {
@@ -184,7 +201,7 @@ gtk_model_menu_binding_populate (GtkModelMenuBinding *binding)
   binding->n_items = 0;
 
   /* add new items from the model */
-  gtk_model_menu_binding_append_model (binding, binding->model, binding->with_separators);
+  gtk_model_menu_binding_append_model (binding, binding->model, binding->action_namespace, binding->with_separators);
 }
 
 static gboolean
@@ -226,167 +243,80 @@ gtk_model_menu_binding_items_changed (GMenuModel *model,
     }
 }
 
-static void
-gtk_model_menu_bind (GtkMenuShell      *shell,
-                     GMenuModel        *model,
-                     gboolean           with_separators)
-{
-  GtkModelMenuBinding *binding;
-
-  binding = g_slice_new (GtkModelMenuBinding);
-  binding->model = g_object_ref (model);
-  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);
-}
-
-GtkWidget *
-gtk_model_menu_create_menu (GMenuModel        *model,
-                            GActionObservable *actions,
-                            GtkAccelGroup     *accels)
-{
-  GtkWidget *menu;
-
-  menu = gtk_menu_new ();
-
-  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
+ * gtk_menu_shell_bind_model:
+ * @menu_shell: a #GtkMenuShell
+ * @model: (allow-none): the #GMenuModel to bind to or %NULL to remove
+ *   binding
+ * @action_namespace: (allow-none): the namespace for actions in @model
+ * @with_separators: %TRUE if toplevel items in @shell should have
+ *   separators between them
+ *
+ * Establishes a binding between a #GtkMenuShell and a #GMenuModel.
  *
- * Creates a #GtkMenu and populates it with menu items and
- * submenus according to @model.
+ * The contents of @shell are removed and then refilled with menu items
+ * according to @model.  When @model changes, @shell is updated.
+ * Calling this function twice on @shell with different @model will
+ * cause the first binding to be replaced with a binding to the new
+ * model. If @model is %NULL then any previous binding is undone and
+ * all children are removed.
  *
- * 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.
+ * @with_separators determines if toplevel items (eg: sections) have
+ * separators inserted between them.  This is typically desired for
+ * menus but doesn't make sense for menubars.
  *
- * Returns: a new #GtkMenu
+ * If @action_namespace is non-%NULL then the effect is as if all
+ * actions mentioned in the @model have their names prefixed with the
+ * namespace, plus a dot.  For example, if the action "quit" is
+ * mentioned and @action_namespace is "app" then the effective action
+ * name is "app.quit".
  *
- * Since: 3.4
+ * For most cases you are probably better off using
+ * gtk_menu_new_from_model() or gtk_menu_bar_new_from_model() or just
+ * directly passing the #GMenuModel to gtk_application_set_app_menu() or
+ * gtk_application_set_menu_bar().
+ *
+ * Since: 3.6
  */
-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;
-}
-
-GtkWidget *
-gtk_model_menu_create_menu_bar (GMenuModel        *model,
-                                GActionObservable *actions,
-                                GtkAccelGroup     *accels)
+void
+gtk_menu_shell_bind_model (GtkMenuShell *shell,
+                           GMenuModel   *model,
+                           const gchar  *action_namespace,
+                           gboolean      with_separators)
 {
-  GtkWidget *menubar;
-
-  menubar = gtk_menu_bar_new ();
+  g_return_if_fail (GTK_IS_MENU_SHELL (shell));
+  g_return_if_fail (model == NULL || G_IS_MENU_MODEL (model));
 
-  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))
+  if (model)
     {
-      actions = gtk_application_window_get_observable (GTK_APPLICATION_WINDOW (toplevel));
-      accels = gtk_application_window_get_accel_group (GTK_APPLICATION_WINDOW (toplevel));
+      GtkModelMenuBinding *binding;
 
-      gtk_model_menu_populate (shell, actions, accels);
-    }
-}
+      binding = g_slice_new (GtkModelMenuBinding);
+      binding->model = g_object_ref (model);
+      binding->shell = shell;
+      binding->update_idle = 0;
+      binding->connected = NULL;
+      binding->with_separators = with_separators;
+      binding->action_namespace = g_strdup (action_namespace);
 
-/**
- * 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;
+      g_object_set_data_full (G_OBJECT (shell), "gtk-model-menu-binding", binding, gtk_model_menu_binding_free);
 
-  menubar = gtk_menu_bar_new ();
+      gtk_model_menu_binding_populate (binding);
+    }
 
-  gtk_model_menu_bind (GTK_MENU_SHELL (menubar), model, FALSE);
+  else
+    {
+      GList *children;
 
-  g_signal_connect (menubar, "hierarchy-changed",
-                    G_CALLBACK (hierarchy_changed), NULL);
+      /* break existing binding */
+      g_object_set_data (G_OBJECT (shell), "gtk-model-menu-binding", NULL);
 
-  return menubar;
+      /* remove all children */
+      children = gtk_container_get_children (GTK_CONTAINER (shell));
+      while (children)
+        {
+          gtk_container_remove (GTK_CONTAINER (shell), children->data);
+          children = g_list_delete_link (children, children);
+        }
+    }
 }