Back to index

indicator-appmenu  12.10.0
gtkmodelmenuitem.c
Go to the documentation of this file.
00001 /*
00002  * Copyright © 2011 Canonical Limited
00003  *
00004  * This library is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU Lesser General Public
00006  * License as published by the Free Software Foundation; either
00007  * version 2 of the licence, or (at your option) any later version.
00008  *
00009  * This library is distributed in the hope that it will be useful,
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012  * Lesser General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU Lesser General Public
00015  * License along with this library. If not, see <http://www.gnu.org/licenses/>.
00016  *
00017  * Author: Ryan Lortie <desrt@desrt.ca>
00018  */
00019 
00020 #include "config.h"
00021 
00022 #include "gtkmodelmenuitem.h"
00023 
00024 #include "gtkmodelmenu.h"
00025 
00026 struct _GtkModelMenuItem
00027 {
00028   GtkCheckMenuItem parent_instance;
00029 
00030   GActionGroup *actions;
00031   const gchar *action_name;
00032   gboolean has_indicator;
00033   gboolean can_activate;
00034   GVariant *target;
00035 };
00036 
00037 typedef GtkCheckMenuItemClass GtkModelMenuItemClass;
00038 
00039 static void gtk_model_menu_item_observer_iface_init (GActionObserverInterface *iface);
00040 G_DEFINE_TYPE_WITH_CODE (GtkModelMenuItem, gtk_model_menu_item, GTK_TYPE_CHECK_MENU_ITEM,
00041                          G_IMPLEMENT_INTERFACE (G_TYPE_ACTION_OBSERVER, gtk_model_menu_item_observer_iface_init))
00042 
00043 static void
00044 gtk_model_menu_item_activate (GtkMenuItem *menu_item)
00045 {
00046   GtkModelMenuItem *item = GTK_MODEL_MENU_ITEM (menu_item);
00047 
00048   if (item->can_activate)
00049     g_action_group_activate_action (item->actions, item->action_name, item->target);
00050 }
00051 
00052 static void
00053 gtk_model_menu_item_toggle_size_request (GtkMenuItem *menu_item,
00054                                          gint        *requisition)
00055 {
00056   GtkModelMenuItem *item = GTK_MODEL_MENU_ITEM (menu_item);
00057 
00058   if (item->has_indicator)
00059     GTK_MENU_ITEM_CLASS (gtk_model_menu_item_parent_class)
00060       ->toggle_size_request (menu_item, requisition);
00061 
00062   else
00063     *requisition = 0;
00064 }
00065 
00066 static void
00067 gtk_model_menu_item_draw_indicator (GtkCheckMenuItem *check_item,
00068                                     cairo_t          *cr)
00069 {
00070   GtkModelMenuItem *item = GTK_MODEL_MENU_ITEM (check_item);
00071 
00072   if (item->has_indicator)
00073     GTK_CHECK_MENU_ITEM_CLASS (gtk_model_menu_item_parent_class)
00074       ->draw_indicator (check_item, cr);
00075 }
00076 
00077 static void
00078 gtk_model_menu_item_set_active (GtkModelMenuItem *item,
00079                                 gboolean          active)
00080 {
00081   GtkCheckMenuItem *checkitem = GTK_CHECK_MENU_ITEM (item);
00082 
00083   if (gtk_check_menu_item_get_active (checkitem) != active)
00084     {
00085       // TODO _gtk_check_menu_item_set_active (checkitem, active);
00086       g_object_notify (G_OBJECT (checkitem), "active");
00087       gtk_check_menu_item_toggled (checkitem);
00088       gtk_widget_queue_draw (GTK_WIDGET (item));
00089     }
00090 }
00091 
00092 static void
00093 gtk_model_menu_item_action_added (GActionObserver    *observer,
00094                                   GActionObservable  *observable,
00095                                   const gchar        *action_name,
00096                                   const GVariantType *parameter_type,
00097                                   gboolean            enabled,
00098                                   GVariant           *state)
00099 {
00100   GtkModelMenuItem *item = GTK_MODEL_MENU_ITEM (observer);
00101 
00102   /* we can only activate the item if we have the correct type of parameter */
00103   item->can_activate = (item->target == NULL && parameter_type == NULL) ||
00104                        (item->target != NULL && parameter_type != NULL &&
00105                         g_variant_is_of_type (item->target, parameter_type));
00106 
00107   if (item->can_activate)
00108     {
00109       if (item->target != NULL && state != NULL)
00110         {
00111           /* actions with states and targets are radios */
00112           gboolean selected;
00113 
00114           selected = g_variant_equal (state, item->target);
00115           gtk_check_menu_item_set_draw_as_radio (GTK_CHECK_MENU_ITEM (item), TRUE);
00116           gtk_model_menu_item_set_active (item, selected);
00117           item->has_indicator = TRUE;
00118         }
00119 
00120       else if (state != NULL && g_variant_is_of_type (state, G_VARIANT_TYPE_BOOLEAN))
00121         {
00122           /* boolean state actions without target are checks */
00123           gtk_check_menu_item_set_draw_as_radio (GTK_CHECK_MENU_ITEM (item), FALSE);
00124           gtk_model_menu_item_set_active (item, g_variant_get_boolean (state));
00125           item->has_indicator = TRUE;
00126         }
00127 
00128       else
00129         {
00130           /* stateless items are just plain actions */
00131           gtk_model_menu_item_set_active (item, FALSE);
00132           item->has_indicator = FALSE;
00133         }
00134 
00135       gtk_widget_set_sensitive (GTK_WIDGET (item), enabled);
00136       gtk_widget_queue_resize (GTK_WIDGET (item));
00137     }
00138 }
00139 
00140 static void
00141 gtk_model_menu_item_action_enabled_changed (GActionObserver   *observer,
00142                                             GActionObservable *observable,
00143                                             const gchar       *action_name,
00144                                             gboolean           enabled)
00145 {
00146   GtkModelMenuItem *item = GTK_MODEL_MENU_ITEM (observer);
00147 
00148   if (!item->can_activate)
00149     return;
00150 
00151   gtk_widget_set_sensitive (GTK_WIDGET (item), item->can_activate && enabled);
00152 }
00153 
00154 static void
00155 gtk_model_menu_item_action_state_changed (GActionObserver   *observer,
00156                                           GActionObservable *observable,
00157                                           const gchar       *action_name,
00158                                           GVariant          *state)
00159 {
00160   GtkModelMenuItem *item = GTK_MODEL_MENU_ITEM (observer);
00161 
00162   if (!item->can_activate)
00163     return;
00164 
00165   if (item->target)
00166     gtk_model_menu_item_set_active (item, g_variant_equal (state, item->target));
00167 
00168   else if (g_variant_is_of_type (state, G_VARIANT_TYPE_BOOLEAN))
00169     gtk_model_menu_item_set_active (item, g_variant_get_boolean (state));
00170 }
00171 
00172 static void
00173 gtk_model_menu_item_action_removed (GActionObserver   *observer,
00174                                     GActionObservable *observable,
00175                                     const gchar       *action_name)
00176 {
00177   GtkModelMenuItem *item = GTK_MODEL_MENU_ITEM (observer);
00178 
00179   if (!item->can_activate)
00180     return;
00181 
00182   gtk_widget_set_sensitive (GTK_WIDGET (item), FALSE);
00183   gtk_model_menu_item_set_active (item, FALSE);
00184   item->has_indicator = FALSE;
00185 
00186   gtk_widget_queue_resize (GTK_WIDGET (item));
00187 }
00188 
00189 gchar *
00190 _gtk_accel_path_for_action (const gchar *action_name,
00191                             GVariant    *parameter)
00192 {
00193   GString *s;
00194 
00195   s = g_string_new ("<GAction>/");
00196   g_string_append (s, action_name);
00197   if (parameter)
00198     {    
00199       g_string_append_c (s, '/');
00200       g_variant_print_string (parameter, s, FALSE);
00201     }    
00202   return g_string_free (s, FALSE);
00203 }
00204 
00205 static void
00206 gtk_model_menu_item_setup (GtkModelMenuItem  *item,
00207                            GMenuModel        *model,
00208                            gint               item_index,
00209                            GActionObservable *actions,
00210                            GtkAccelGroup     *accels)
00211 {
00212   GMenuAttributeIter *iter;
00213   GMenuModel *submenu;
00214   const gchar *key;
00215   GVariant *value;
00216 
00217   if ((submenu = g_menu_model_get_item_link (model, item_index, "submenu")))
00218     {
00219       gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), gtk_model_menu_create_menu (submenu, actions, accels));
00220       g_object_unref (submenu);
00221     }
00222 
00223   iter = g_menu_model_iterate_item_attributes (model, item_index);
00224   while (g_menu_attribute_iter_get_next (iter, &key, &value))
00225     {
00226       if (g_str_equal (key, "label") && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
00227         gtk_menu_item_set_label (GTK_MENU_ITEM (item), g_variant_get_string (value, NULL));
00228 
00229       else if (g_str_equal (key, "action") && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
00230         item->action_name = g_variant_get_string (value, NULL);
00231 
00232       else if (g_str_equal (key, "target"))
00233         item->target = g_variant_ref (value);
00234 
00235       g_variant_unref (value);
00236     }
00237   g_object_unref (iter);
00238 
00239   gtk_menu_item_set_use_underline (GTK_MENU_ITEM (item), TRUE);
00240 
00241   if (item->action_name)
00242     {
00243       const GVariantType *type;
00244       gboolean enabled;
00245       GVariant *state;
00246       gchar *path;
00247 
00248       /* observer already causes us to hold a hard ref on the group */
00249       item->actions = G_ACTION_GROUP (actions);
00250 
00251       if (actions)
00252         {
00253           g_action_observable_register_observer (actions, item->action_name, G_ACTION_OBSERVER (item));
00254 
00255           if (g_action_group_query_action (G_ACTION_GROUP (actions), item->action_name, &enabled, &type, NULL, NULL, &state))
00256             {
00257               gtk_model_menu_item_action_added (G_ACTION_OBSERVER (item), actions, item->action_name, type, enabled, state);
00258               if (state != NULL)
00259                 g_variant_unref (state);
00260             }
00261           else
00262             gtk_widget_set_sensitive (GTK_WIDGET (item), FALSE);
00263         }
00264       else
00265         gtk_widget_set_sensitive (GTK_WIDGET (item), FALSE);
00266 
00267       path = _gtk_accel_path_for_action (item->action_name, item->target);
00268       gtk_menu_item_set_accel_path (GTK_MENU_ITEM (item), path);
00269       g_free (path);
00270     }
00271 }
00272 
00273 static void
00274 gtk_model_menu_item_finalize (GObject *object)
00275 {
00276   G_OBJECT_CLASS (gtk_model_menu_item_parent_class)
00277     ->finalize (object);
00278 }
00279 
00280 static void
00281 gtk_model_menu_item_init (GtkModelMenuItem *item)
00282 {
00283 }
00284 
00285 static void
00286 gtk_model_menu_item_observer_iface_init (GActionObserverInterface *iface)
00287 {
00288   iface->action_added = gtk_model_menu_item_action_added;
00289   iface->action_enabled_changed = gtk_model_menu_item_action_enabled_changed;
00290   iface->action_state_changed = gtk_model_menu_item_action_state_changed;
00291   iface->action_removed = gtk_model_menu_item_action_removed;
00292 }
00293 
00294 static void
00295 gtk_model_menu_item_class_init (GtkModelMenuItemClass *class)
00296 {
00297   GtkCheckMenuItemClass *check_class = GTK_CHECK_MENU_ITEM_CLASS (class);
00298   GtkMenuItemClass *item_class = GTK_MENU_ITEM_CLASS (class);
00299   GObjectClass *object_class = G_OBJECT_CLASS (class);
00300 
00301   check_class->draw_indicator = gtk_model_menu_item_draw_indicator;
00302 
00303   item_class->activate = gtk_model_menu_item_activate;
00304   item_class->toggle_size_request = gtk_model_menu_item_toggle_size_request;
00305 
00306   object_class->finalize = gtk_model_menu_item_finalize;
00307 }
00308 
00309 GtkMenuItem *
00310 gtk_model_menu_item_new (GMenuModel        *model,
00311                          gint               item_index,
00312                          GActionObservable *actions,
00313                          GtkAccelGroup     *accels)
00314 {
00315   GtkModelMenuItem *item;
00316 
00317   item = g_object_new (GTK_TYPE_MODEL_MENU_ITEM, NULL);
00318 
00319   gtk_model_menu_item_setup (item, model, item_index, actions, accels);
00320 
00321   return GTK_MENU_ITEM (item);
00322 }