* 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
gpointer user_data);
static void gtk_model_menu_binding_append_model (GtkModelMenuBinding *binding,
GMenuModel *model,
+ const gchar *action_namespace,
gboolean with_separators);
static void
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);
}
static void
gtk_model_menu_binding_append_item (GtkModelMenuBinding *binding,
GMenuModel *model,
+ const gchar *action_namespace,
gint item_index,
gchar **heading)
{
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", §ion_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++;
static void
gtk_model_menu_binding_append_model (GtkModelMenuBinding *binding,
GMenuModel *model,
+ const gchar *action_namespace,
gboolean with_separators)
{
gint n, i;
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)
{
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
}
}
-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);
+ }
+ }
}