summaryrefslogtreecommitdiff
path: root/gtk/gtkmodelmenuitem.c
diff options
context:
space:
mode:
authorRyan Lortie <desrt@desrt.ca>2011-12-01 20:39:11 -0500
committerRyan Lortie <desrt@desrt.ca>2011-12-19 12:51:09 -0500
commit612e24dfc6fcd93c0b02db7ccffe51fd31cc247b (patch)
tree90eb586d454e6e8ee73c09bd34d0b5561f017fc0 /gtk/gtkmodelmenuitem.c
parentecfdb834c9e06a7f4e20d5273901f73819977c87 (diff)
downloadgtk+-612e24dfc6fcd93c0b02db7ccffe51fd31cc247b.tar.gz
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.
Diffstat (limited to 'gtk/gtkmodelmenuitem.c')
-rw-r--r--gtk/gtkmodelmenuitem.c247
1 files changed, 247 insertions, 0 deletions
diff --git a/gtk/gtkmodelmenuitem.c b/gtk/gtkmodelmenuitem.c
new file mode 100644
index 0000000000..c76c9db945
--- /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);
+}