2 * Copyright © 2011 Red Hat, Inc.
3 * Copyright © 2011 Canonical Limited
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the licence, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
18 * Author: Matthias Clasen <mclasen@redhat.com>
19 * Ryan Lortie <desrt@desrt.ca>
24 #include "gtkmodelmenu.h"
27 #include "gtkmenubar.h"
28 #include "gtkseparatormenuitem.h"
29 #include "gtkmodelmenuitem.h"
30 #include "gtkapplicationprivate.h"
32 #define MODEL_MENU_WIDGET_DATA "gtk-model-menu-widget-data"
36 GtkAccelGroup *accels;
40 gboolean with_separators;
42 gchar *action_namespace;
43 } GtkModelMenuBinding;
46 gtk_model_menu_binding_items_changed (GMenuModel *model,
51 static void gtk_model_menu_binding_append_model (GtkModelMenuBinding *binding,
53 const gchar *action_namespace,
54 gboolean with_separators);
57 gtk_model_menu_binding_free (gpointer data)
59 GtkModelMenuBinding *binding = data;
61 /* disconnect all existing signal handlers */
62 while (binding->connected)
64 g_signal_handlers_disconnect_by_func (binding->connected->data, gtk_model_menu_binding_items_changed, binding);
65 g_object_unref (binding->connected->data);
67 binding->connected = g_slist_delete_link (binding->connected, binding->connected);
70 g_object_unref (binding->model);
71 g_free (binding->action_namespace);
73 g_slice_free (GtkModelMenuBinding, binding);
77 gtk_model_menu_binding_append_item (GtkModelMenuBinding *binding,
79 const gchar *action_namespace,
85 if ((section = g_menu_model_get_item_link (model, item_index, "section")))
87 const gchar *section_namespace = NULL;
89 g_menu_model_get_item_attribute (model, item_index, "label", "s", heading);
90 g_menu_model_get_item_attribute (model, item_index, "action-namespace", "s", §ion_namespace);
94 gchar *namespace = g_strjoin (".", action_namespace, section_namespace, NULL);
95 gtk_model_menu_binding_append_model (binding, section, namespace, FALSE);
100 gtk_model_menu_binding_append_model (binding, section, section_namespace, FALSE);
103 g_free (section_namespace);
104 g_object_unref (section);
110 item = gtk_model_menu_item_new (model, item_index, action_namespace, binding->accels);
111 gtk_menu_shell_append (binding->shell, GTK_WIDGET (item));
112 gtk_widget_show (GTK_WIDGET (item));
118 gtk_model_menu_binding_append_model (GtkModelMenuBinding *binding,
120 const gchar *action_namespace,
121 gboolean with_separators)
125 g_signal_connect (model, "items-changed", G_CALLBACK (gtk_model_menu_binding_items_changed), binding);
126 binding->connected = g_slist_prepend (binding->connected, g_object_ref (model));
128 /* Deciding if we should show a separator is a bit difficult. There
129 * are two types of separators:
131 * - section headings (when sections have 'label' property)
133 * - normal separators automatically put between sections
135 * The easiest way to think about it is that a section usually has a
136 * separator (or heading) immediately before it.
138 * There are three exceptions to this general rule:
140 * - empty sections don't get separators or headings
142 * - sections only get separators and headings at the toplevel of a
143 * menu (ie: no separators on nested sections or in menubars)
145 * - the first section in the menu doesn't get a normal separator,
146 * but it can get a header (if it's not empty)
148 * Unfortunately, we cannot simply check the size of the section in
149 * order to determine if we should place a header: the section may
150 * contain other sections that are themselves empty. Instead, we need
151 * to append the section, and check if we ended up with any actual
152 * content. If we did, then we need to insert before that content.
153 * We use 'our_position' to keep track of this.
156 n = g_menu_model_get_n_items (model);
158 for (i = 0; i < n; i++)
160 gint our_position = binding->n_items;
161 gchar *heading = NULL;
163 gtk_model_menu_binding_append_item (binding, model, action_namespace, i, &heading);
165 if (with_separators && our_position < binding->n_items)
167 GtkWidget *separator = NULL;
171 separator = gtk_menu_item_new_with_label (heading);
172 gtk_widget_set_sensitive (separator, FALSE);
174 else if (our_position > 0)
175 separator = gtk_separator_menu_item_new ();
179 gtk_menu_shell_insert (binding->shell, separator, our_position);
180 gtk_widget_show (separator);
190 gtk_model_menu_binding_populate (GtkModelMenuBinding *binding)
194 /* remove current children */
195 children = gtk_container_get_children (GTK_CONTAINER (binding->shell));
198 gtk_container_remove (GTK_CONTAINER (binding->shell), children->data);
199 children = g_list_delete_link (children, children);
202 binding->n_items = 0;
204 /* add new items from the model */
205 gtk_model_menu_binding_append_model (binding, binding->model, binding->action_namespace, binding->with_separators);
209 gtk_model_menu_binding_handle_changes (gpointer user_data)
211 GtkModelMenuBinding *binding = user_data;
213 /* disconnect all existing signal handlers */
214 while (binding->connected)
216 g_signal_handlers_disconnect_by_func (binding->connected->data, gtk_model_menu_binding_items_changed, binding);
217 g_object_unref (binding->connected->data);
219 binding->connected = g_slist_delete_link (binding->connected, binding->connected);
222 gtk_model_menu_binding_populate (binding);
224 binding->update_idle = 0;
226 g_object_unref (binding->shell);
228 return G_SOURCE_REMOVE;
232 gtk_model_menu_binding_items_changed (GMenuModel *model,
238 GtkModelMenuBinding *binding = user_data;
240 if (binding->update_idle == 0)
242 binding->update_idle = gdk_threads_add_idle (gtk_model_menu_binding_handle_changes, user_data);
243 g_object_ref (binding->shell);
248 gtk_model_menu_bind (GtkMenuShell *shell,
250 const gchar *action_namespace,
251 gboolean with_separators)
253 GtkModelMenuBinding *binding;
255 binding = g_slice_new (GtkModelMenuBinding);
256 binding->model = g_object_ref (model);
257 binding->accels = NULL;
258 binding->shell = shell;
259 binding->update_idle = 0;
260 binding->connected = NULL;
261 binding->with_separators = with_separators;
262 binding->action_namespace = g_strdup (action_namespace);
264 g_object_set_data_full (G_OBJECT (shell), "gtk-model-menu-binding", binding, gtk_model_menu_binding_free);
269 gtk_model_menu_populate (GtkMenuShell *shell,
270 GtkAccelGroup *accels)
272 GtkModelMenuBinding *binding;
274 binding = (GtkModelMenuBinding*) g_object_get_data (G_OBJECT (shell), "gtk-model-menu-binding");
276 binding->accels = accels;
278 gtk_model_menu_binding_populate (binding);
282 gtk_model_menu_create_menu (GMenuModel *model,
283 const gchar *action_namespace,
284 GtkAccelGroup *accels)
288 menu = gtk_menu_new ();
289 gtk_menu_set_accel_group (GTK_MENU (menu), accels);
291 gtk_model_menu_bind (GTK_MENU_SHELL (menu), model, action_namespace, TRUE);
292 gtk_model_menu_populate (GTK_MENU_SHELL (menu), accels);
298 * gtk_menu_new_from_model:
299 * @model: a #GMenuModel
301 * Creates a #GtkMenu and populates it with menu items and
302 * submenus according to @model.
304 * The created menu items are connected to actions found in the
305 * #GtkApplicationWindow to which the menu belongs - typically
306 * by means of being attached to a widget (see gtk_menu_attach_to_widget())
307 * that is contained within the #GtkApplicationWindows widget hierarchy.
309 * Returns: a new #GtkMenu
314 gtk_menu_new_from_model (GMenuModel *model)
318 menu = gtk_menu_new ();
319 gtk_model_menu_bind (GTK_MENU_SHELL (menu), model, NULL, TRUE);
320 gtk_model_menu_populate (GTK_MENU_SHELL (menu), NULL);
326 gtk_model_menu_create_menu_bar (GMenuModel *model,
327 GtkAccelGroup *accels)
331 menubar = gtk_menu_bar_new ();
333 gtk_model_menu_bind (GTK_MENU_SHELL (menubar), model, NULL, FALSE);
334 gtk_model_menu_populate (GTK_MENU_SHELL (menubar), accels);
340 * gtk_menu_bar_new_from_model:
341 * @model: a #GMenuModel
343 * Creates a new #GtkMenuBar and populates it with menu items
344 * and submenus according to @model.
346 * The created menu items are connected to actions found in the
347 * #GtkApplicationWindow to which the menu bar belongs - typically
348 * by means of being contained within the #GtkApplicationWindows
351 * Returns: a new #GtkMenuBar
356 gtk_menu_bar_new_from_model (GMenuModel *model)
360 menubar = gtk_menu_bar_new ();
362 gtk_model_menu_bind (GTK_MENU_SHELL (menubar), model, NULL, FALSE);
363 gtk_model_menu_populate (GTK_MENU_SHELL (menubar), NULL);