diff options
author | Ryan Lortie <desrt@desrt.ca> | 2011-12-01 20:39:11 -0500 |
---|---|---|
committer | Ryan Lortie <desrt@desrt.ca> | 2011-12-19 12:51:09 -0500 |
commit | 612e24dfc6fcd93c0b02db7ccffe51fd31cc247b (patch) | |
tree | 90eb586d454e6e8ee73c09bd34d0b5561f017fc0 /gtk/gtkmodelmenuitem.c | |
parent | ecfdb834c9e06a7f4e20d5273901f73819977c87 (diff) | |
download | gtk+-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.c | 247 |
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); +} |