[gtk+/wip/gmenu] introduce GtkModelMenuItem
- From: Ryan Lortie <ryanl src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk+/wip/gmenu] introduce GtkModelMenuItem
- Date: Fri, 2 Dec 2011 01:40:21 +0000 (UTC)
commit d8abb8981714964149367bba68b2cd8c88b6a5f8
Author: Ryan Lortie <desrt desrt ca>
Date: Thu Dec 1 20:39:11 2011 -0500
introduce GtkModelMenuItem
This GtkMenuItem subclass (and GActionObserver implementation) contains
all the knowledge necessary for converting a GMenuModel item description
into a GtkMenuItem.
Remove much of the code that used to do this from
gtkapplicationwindow.c.
gtk/Makefile.am | 2 +
gtk/gtkapplicationwindow.c | 189 +--------------------------------
gtk/gtkmodelmenuitem.c | 247 ++++++++++++++++++++++++++++++++++++++++++++
gtk/gtkmodelmenuitem.h | 44 ++++++++
4 files changed, 299 insertions(+), 183 deletions(-)
---
diff --git a/gtk/Makefile.am b/gtk/Makefile.am
index 6bb5eba..fa6f464 100644
--- a/gtk/Makefile.am
+++ b/gtk/Makefile.am
@@ -427,6 +427,7 @@ gtk_private_h_sources = \
gtkmenuitemprivate.h \
gtkmenushellprivate.h \
gtkmnemonichash.h \
+ gtkmodelmenuitem.h \
gtkmodifierstyle.h \
gtkmodulesprivate.h \
gtkmountoperationprivate.h \
@@ -624,6 +625,7 @@ gtk_base_c_sources = \
gtkmessagedialog.c \
gtkmisc.c \
gtkmnemonichash.c \
+ gtkmodelmenuitem.c \
gtkmodifierstyle.c \
gtkmodules.c \
gtkmountoperation.c \
diff --git a/gtk/gtkapplicationwindow.c b/gtk/gtkapplicationwindow.c
index 87acbef..56733ed 100644
--- a/gtk/gtkapplicationwindow.c
+++ b/gtk/gtkapplicationwindow.c
@@ -24,6 +24,7 @@
#include "gtkapplicationwindow.h"
#include "gtkseparatormenuitem.h"
+#include "gtkmodelmenuitem.h"
#include "gtkcheckmenuitem.h"
#include "gtkmenubar.h"
#include "gactionmuxer.h"
@@ -516,184 +517,6 @@ gtk_application_window_set_show_menubar (GtkApplicationWindow *window,
/* GtkMenu construction {{{1 */
-typedef struct {
- GActionGroup *group;
- gchar *name;
- gchar *target;
- gulong enabled_changed_id;
- gulong state_changed_id;
- gulong activate_handler;
-} ActionData;
-
-static void
-action_data_free (gpointer data)
-{
- ActionData *a = data;
-
- if (a->enabled_changed_id)
- g_signal_handler_disconnect (a->group, a->enabled_changed_id);
-
- if (a->state_changed_id)
- g_signal_handler_disconnect (a->group, a->state_changed_id);
-
- g_object_unref (a->group);
- g_free (a->name);
- g_free (a->target);
-
- g_free (a);
-}
-
-static void
-enabled_changed (GActionGroup *group,
- const gchar *action_name,
- gboolean enabled,
- GtkWidget *widget)
-{
- gtk_widget_set_sensitive (widget, enabled);
-}
-
-static void
-item_activated (GtkWidget *w,
- gpointer data)
-{
- ActionData *a;
- GVariant *parameter;
-
- a = g_object_get_data (G_OBJECT (w), "action");
- if (a->target)
- parameter = g_variant_ref_sink (g_variant_new_string (a->target));
- else
- parameter = NULL;
- g_action_group_activate_action (a->group, a->name, parameter);
- if (parameter)
- g_variant_unref (parameter);
-}
-
-static void
-toggle_state_changed (GActionGroup *group,
- const gchar *name,
- GVariant *state,
- GtkCheckMenuItem *w)
-{
- ActionData *a;
-
- a = g_object_get_data (G_OBJECT (w), "action");
- g_signal_handler_block (w, a->activate_handler);
- gtk_check_menu_item_set_active (w, g_variant_get_boolean (state));
- g_signal_handler_unblock (w, a->activate_handler);
-}
-
-static void
-radio_state_changed (GActionGroup *group,
- const gchar *name,
- GVariant *state,
- GtkCheckMenuItem *w)
-{
- ActionData *a;
- gboolean b;
-
- a = g_object_get_data (G_OBJECT (w), "action");
- g_signal_handler_block (w, a->activate_handler);
- b = g_strcmp0 (a->target, g_variant_get_string (state, NULL)) == 0;
- gtk_check_menu_item_set_active (w, b);
- g_signal_handler_unblock (w, a->activate_handler);
-}
-
-static GtkWidget *
-create_menuitem_from_model (GMenuModel *model,
- gint item,
- GActionGroup *group)
-{
- GtkWidget *w;
- gchar *label;
- gchar *action;
- gchar *target;
- gchar *s;
- ActionData *a;
- const GVariantType *type;
- GVariant *v;
-
- label = NULL;
- g_menu_model_get_item_attribute (model, item, G_MENU_ATTRIBUTE_LABEL, "s", &label);
-
- action = NULL;
- g_menu_model_get_item_attribute (model, item, G_MENU_ATTRIBUTE_ACTION, "s", &action);
-
- if (action != NULL)
- type = g_action_group_get_action_state_type (group, action);
- else
- type = NULL;
-
- if (type == NULL)
- w = gtk_menu_item_new_with_label (label);
- else if (g_variant_type_equal (type, G_VARIANT_TYPE_BOOLEAN))
- w = gtk_check_menu_item_new_with_label (label);
- else if (g_variant_type_equal (type, G_VARIANT_TYPE_STRING))
- {
- w = gtk_check_menu_item_new_with_label (label);
- gtk_check_menu_item_set_draw_as_radio (GTK_CHECK_MENU_ITEM (w), TRUE);
- }
- else
- g_assert_not_reached ();
-
- gtk_menu_item_set_use_underline (GTK_MENU_ITEM (w), TRUE);
-
- if (action != NULL)
- {
- a = g_new0 (ActionData, 1);
- a->group = g_object_ref (group);
- a->name = g_strdup (action);
- g_object_set_data_full (G_OBJECT (w), "action", a, action_data_free);
-
- if (!g_action_group_get_action_enabled (group, action))
- gtk_widget_set_sensitive (w, FALSE);
-
- s = g_strconcat ("action-enabled-changed::", action, NULL);
- a->enabled_changed_id = g_signal_connect (group, s,
- G_CALLBACK (enabled_changed), w);
- g_free (s);
- a->activate_handler = g_signal_connect (w, "activate",
- G_CALLBACK (item_activated), NULL);
-
- if (type == NULL)
- {
- /* all set */
- }
- else if (g_variant_type_equal (type, G_VARIANT_TYPE_BOOLEAN))
- {
- s = g_strconcat ("action-state-changed::", action, NULL);
- a->state_changed_id = g_signal_connect (group, s,
- G_CALLBACK (toggle_state_changed), w);
- g_free (s);
- v = g_action_group_get_action_state (group, action);
- gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (w),
- g_variant_get_boolean (v));
- g_variant_unref (v);
- }
- else if (g_variant_type_equal (type, G_VARIANT_TYPE_STRING))
- {
- s = g_strconcat ("action-state-changed::", action, NULL);
- a->state_changed_id = g_signal_connect (group, s,
- G_CALLBACK (radio_state_changed), w);
- g_free (s);
- g_menu_model_get_item_attribute (model, item, G_MENU_ATTRIBUTE_TARGET, "s", &target);
- a->target = g_strdup (target);
- v = g_action_group_get_action_state (group, action);
- gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (w),
- g_strcmp0 (g_variant_get_string (v, NULL), target) == 0);
- g_variant_unref (v);
- g_free (target);
- }
- else
- g_assert_not_reached ();
- }
-
- g_free (label);
- g_free (action);
-
- return w;
-}
-
static void populate_menu_from_model (GtkMenuShell *menu,
GMenuModel *model,
GActionGroup *group);
@@ -708,7 +531,7 @@ append_items_from_model (GtkMenuShell *menu,
gint n;
gint i;
GtkWidget *w;
- GtkWidget *menuitem;
+ GtkMenuItem *menuitem;
GtkWidget *submenu;
GMenuModel *m;
gchar *label;
@@ -752,18 +575,18 @@ append_items_from_model (GtkMenuShell *menu,
continue;
}
- menuitem = create_menuitem_from_model (model, i, group);
+ menuitem = gtk_model_menu_item_new (model, i, G_ACTION_OBSERVABLE (group));
if ((m = g_menu_model_get_item_link (model, i, G_MENU_LINK_SUBMENU)))
{
submenu = gtk_menu_new ();
populate_menu_from_model (GTK_MENU_SHELL (submenu), m, group);
- gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem), submenu);
+ gtk_menu_item_set_submenu (menuitem, submenu);
g_object_unref (m);
}
- gtk_widget_show (menuitem);
- gtk_menu_shell_append (menu, menuitem);
+ gtk_widget_show (GTK_WIDGET (menuitem));
+ gtk_menu_shell_append (menu, GTK_WIDGET (menuitem));
*need_separator = TRUE;
}
diff --git a/gtk/gtkmodelmenuitem.c b/gtk/gtkmodelmenuitem.c
new file mode 100644
index 0000000..c76c9db
--- /dev/null
+++ b/gtk/gtkmodelmenuitem.c
@@ -0,0 +1,247 @@
+/*
+ * Copyright  2011 Canonical Limited
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the licence, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * 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.
+ *
+ * Author: Ryan Lortie <desrt desrt ca>
+ */
+
+#include "config.h"
+
+#include "gtkmodelmenuitem.h"
+
+struct _GtkModelMenuItem
+{
+ GtkCheckMenuItem parent_instance;
+
+ GActionGroup *actions;
+ const gchar *action_name;
+ gboolean can_activate;
+ GVariant *target;
+};
+
+typedef GtkCheckMenuItemClass GtkModelMenuItemClass;
+
+static void gtk_model_menu_item_observer_iface_init (GActionObserverInterface *iface);
+G_DEFINE_TYPE_WITH_CODE (GtkModelMenuItem, gtk_model_menu_item, GTK_TYPE_CHECK_MENU_ITEM,
+ G_IMPLEMENT_INTERFACE (G_TYPE_ACTION_OBSERVER, gtk_model_menu_item_observer_iface_init))
+
+static void
+gtk_model_menu_item_activate (GtkMenuItem *menu_item)
+{
+ GtkModelMenuItem *item = GTK_MODEL_MENU_ITEM (menu_item);
+
+ if (item->can_activate)
+ g_action_group_activate_action (item->actions, item->action_name, item->target);
+}
+
+static void
+gtk_model_menu_item_set_active (GtkModelMenuItem *item,
+ gboolean active)
+{
+ GtkCheckMenuItem *checkitem = GTK_CHECK_MENU_ITEM (item);
+
+ if (gtk_check_menu_item_get_active (checkitem) != active)
+ {
+ _gtk_check_menu_item_set_active (checkitem, active);
+ g_object_notify (G_OBJECT (checkitem), "active");
+ gtk_check_menu_item_toggled (checkitem);
+ gtk_widget_queue_draw (GTK_WIDGET (item));
+ }
+}
+
+static void
+gtk_model_menu_item_action_added (GActionObserver *observer,
+ GActionObservable *observable,
+ const gchar *action_name,
+ const GVariantType *parameter_type,
+ gboolean enabled,
+ GVariant *state)
+{
+ GtkModelMenuItem *item = GTK_MODEL_MENU_ITEM (observer);
+
+ /* we can only activate the item if we have the correct type of parameter */
+ item->can_activate = (item->target == NULL && parameter_type == NULL) ||
+ (item->target != NULL && parameter_type != NULL &&
+ g_variant_is_of_type (item->target, parameter_type));
+
+ if (item->can_activate)
+ {
+ if (item->target != NULL && state != NULL)
+ {
+ /* actions with states and targets are radios */
+ gboolean selected;
+
+ selected = g_variant_equal (state, item->target);
+ gtk_check_menu_item_set_draw_as_radio (GTK_CHECK_MENU_ITEM (item), TRUE);
+ gtk_model_menu_item_set_active (item, selected);
+ }
+
+ else if (state != NULL && g_variant_is_of_type (state, G_VARIANT_TYPE_BOOLEAN))
+ {
+ /* boolean state actions without target are checks */
+ gtk_check_menu_item_set_draw_as_radio (GTK_CHECK_MENU_ITEM (item), FALSE);
+ gtk_model_menu_item_set_active (item, g_variant_get_boolean (state));
+ }
+
+ else
+ {
+ /* stateless items are just plain actions */
+ gtk_model_menu_item_set_active (item, FALSE);
+ }
+
+ gtk_widget_set_sensitive (GTK_WIDGET (item), enabled);
+ }
+}
+
+static void
+gtk_model_menu_item_action_enabled_changed (GActionObserver *observer,
+ GActionObservable *observable,
+ const gchar *action_name,
+ gboolean enabled)
+{
+ GtkModelMenuItem *item = GTK_MODEL_MENU_ITEM (observer);
+
+ if (!item->can_activate)
+ return;
+
+ gtk_widget_set_sensitive (GTK_WIDGET (item), item->can_activate && enabled);
+}
+
+static void
+gtk_model_menu_item_action_state_changed (GActionObserver *observer,
+ GActionObservable *observable,
+ const gchar *action_name,
+ GVariant *state)
+{
+ GtkModelMenuItem *item = GTK_MODEL_MENU_ITEM (observer);
+
+ if (!item->can_activate)
+ return;
+
+ if (item->target)
+ gtk_model_menu_item_set_active (item, g_variant_equal (state, item->target));
+
+ else if (g_variant_is_of_type (state, G_VARIANT_TYPE_BOOLEAN))
+ gtk_model_menu_item_set_active (item, g_variant_get_boolean (state));
+}
+
+static void
+gtk_model_menu_item_action_removed (GActionObserver *observer,
+ GActionObservable *observable,
+ const gchar *action_name)
+{
+ GtkModelMenuItem *item = GTK_MODEL_MENU_ITEM (observer);
+
+ if (!item->can_activate)
+ return;
+
+ gtk_widget_set_sensitive (GTK_WIDGET (item), FALSE);
+ gtk_model_menu_item_set_active (item, FALSE);
+}
+
+static void
+gtk_model_menu_item_setup (GtkModelMenuItem *item,
+ GMenuModel *model,
+ gint item_index,
+ GActionObservable *actions)
+{
+ GMenuAttributeIter *iter;
+ const gchar *key;
+ GVariant *value;
+
+ iter = g_menu_model_iterate_item_attributes (model, item_index);
+ while (g_menu_attribute_iter_get_next (iter, &key, &value))
+ {
+ if (g_str_equal (key, "label") && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
+ gtk_menu_item_set_label (GTK_MENU_ITEM (item), g_variant_get_string (value, NULL));
+
+ else if (g_str_equal (key, "action") && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
+ item->action_name = g_variant_get_string (value, NULL);
+
+ else if (g_str_equal (key, "target"))
+ item->target = g_variant_ref (value);
+
+ g_variant_unref (value);
+ }
+ g_object_unref (iter);
+
+ gtk_menu_item_set_use_underline (GTK_MENU_ITEM (item), TRUE);
+
+ if (item->action_name)
+ {
+ const GVariantType *type;
+ gboolean enabled;
+ GVariant *state;
+
+ /* 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))
+ gtk_model_menu_item_action_added (G_ACTION_OBSERVER (item), actions, item->action_name, type, enabled, state);
+
+ else
+ gtk_widget_set_sensitive (GTK_WIDGET (item), FALSE);
+ }
+}
+
+static void
+gtk_model_menu_item_finalize (GObject *object)
+{
+ G_OBJECT_CLASS (gtk_model_menu_item_parent_class)
+ ->finalize (object);
+}
+
+static void
+gtk_model_menu_item_init (GtkModelMenuItem *item)
+{
+}
+
+static void
+gtk_model_menu_item_observer_iface_init (GActionObserverInterface *iface)
+{
+ iface->action_added = gtk_model_menu_item_action_added;
+ iface->action_enabled_changed = gtk_model_menu_item_action_enabled_changed;
+ iface->action_state_changed = gtk_model_menu_item_action_state_changed;
+ iface->action_removed = gtk_model_menu_item_action_removed;
+}
+
+static void
+gtk_model_menu_item_class_init (GtkModelMenuItemClass *class)
+{
+ GtkMenuItemClass *item_class = GTK_MENU_ITEM_CLASS (class);
+ GObjectClass *object_class = G_OBJECT_CLASS (class);
+
+ item_class->activate = gtk_model_menu_item_activate;
+
+ object_class->finalize = gtk_model_menu_item_finalize;
+}
+
+GtkMenuItem *
+gtk_model_menu_item_new (GMenuModel *model,
+ gint item_index,
+ GActionObservable *actions)
+{
+ GtkModelMenuItem *item;
+
+ item = g_object_new (GTK_TYPE_MODEL_MENU_ITEM, NULL);
+
+ gtk_model_menu_item_setup (item, model, item_index, actions);
+
+ return GTK_MENU_ITEM (item);
+}
diff --git a/gtk/gtkmodelmenuitem.h b/gtk/gtkmodelmenuitem.h
new file mode 100644
index 0000000..4146bf9
--- /dev/null
+++ b/gtk/gtkmodelmenuitem.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright  2011 Canonical Limited
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the licence, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * 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.
+ *
+ * Author: Ryan Lortie <desrt desrt ca>
+ */
+
+#ifndef __GTK_MODEL_MENU_ITEM_H__
+#define __GTK_MODEL_MENU_ITEM_H__
+
+#include <gtk/gactionobservable.h>
+#include <gtk/gtkcheckmenuitem.h>
+
+#define GTK_TYPE_MODEL_MENU_ITEM (gtk_model_menu_item_get_type ())
+#define GTK_MODEL_MENU_ITEM(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \
+ GTK_TYPE_MODEL_MENU_ITEM, GtkModelMenuItem))
+#define GTK_IS_MODEL_MENU_ITEM(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), \
+ GTK_TYPE_MODEL_MENU_ITEM))
+
+typedef struct _GtkModelMenuItem GtkModelMenuItem;
+
+G_GNUC_INTERNAL
+GType gtk_model_menu_item_get_type (void) G_GNUC_CONST;
+
+G_GNUC_INTERNAL
+GtkMenuItem * gtk_model_menu_item_new (GMenuModel *model,
+ gint item_index,
+ GActionObservable *actions);
+
+#endif /* __GTK_MODEL_MENU_ITEM_H__ */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]