]> Pileus Git - ~andy/gtk/blob - gtk/gtkmodelmenuitem.c
e6dd987cac3e1eb26f2941b8298da7ca66f3cfef
[~andy/gtk] / gtk / gtkmodelmenuitem.c
1 /*
2  * Copyright © 2011 Canonical Limited
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the licence, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  *
19  * Author: Ryan Lortie <desrt@desrt.ca>
20  */
21
22 #include "config.h"
23
24 #include "gtkmodelmenuitem.h"
25
26 #include "gtkaccelmapprivate.h"
27 #include "gtkmodelmenu.h"
28
29 struct _GtkModelMenuItem
30 {
31   GtkCheckMenuItem parent_instance;
32
33   GActionGroup *actions;
34   const gchar *action_name;
35   gboolean has_indicator;
36   gboolean can_activate;
37   GVariant *target;
38 };
39
40 typedef GtkCheckMenuItemClass GtkModelMenuItemClass;
41
42 static void gtk_model_menu_item_observer_iface_init (GActionObserverInterface *iface);
43 G_DEFINE_TYPE_WITH_CODE (GtkModelMenuItem, gtk_model_menu_item, GTK_TYPE_CHECK_MENU_ITEM,
44                          G_IMPLEMENT_INTERFACE (G_TYPE_ACTION_OBSERVER, gtk_model_menu_item_observer_iface_init))
45
46 static void
47 gtk_model_menu_item_activate (GtkMenuItem *menu_item)
48 {
49   GtkModelMenuItem *item = GTK_MODEL_MENU_ITEM (menu_item);
50
51   if (item->can_activate)
52     g_action_group_activate_action (item->actions, item->action_name, item->target);
53 }
54
55 static void
56 gtk_model_menu_item_toggle_size_request (GtkMenuItem *menu_item,
57                                          gint        *requisition)
58 {
59   GtkModelMenuItem *item = GTK_MODEL_MENU_ITEM (menu_item);
60
61   if (item->has_indicator)
62     GTK_MENU_ITEM_CLASS (gtk_model_menu_item_parent_class)
63       ->toggle_size_request (menu_item, requisition);
64
65   else
66     *requisition = 0;
67 }
68
69 static void
70 gtk_model_menu_item_draw_indicator (GtkCheckMenuItem *check_item,
71                                     cairo_t          *cr)
72 {
73   GtkModelMenuItem *item = GTK_MODEL_MENU_ITEM (check_item);
74
75   if (item->has_indicator)
76     GTK_CHECK_MENU_ITEM_CLASS (gtk_model_menu_item_parent_class)
77       ->draw_indicator (check_item, cr);
78 }
79
80 static void
81 gtk_model_menu_item_set_active (GtkModelMenuItem *item,
82                                 gboolean          active)
83 {
84   GtkCheckMenuItem *checkitem = GTK_CHECK_MENU_ITEM (item);
85
86   if (gtk_check_menu_item_get_active (checkitem) != active)
87     {
88       _gtk_check_menu_item_set_active (checkitem, active);
89       g_object_notify (G_OBJECT (checkitem), "active");
90       gtk_check_menu_item_toggled (checkitem);
91       gtk_widget_queue_draw (GTK_WIDGET (item));
92     }
93 }
94
95 static void
96 gtk_model_menu_item_action_added (GActionObserver    *observer,
97                                   GActionObservable  *observable,
98                                   const gchar        *action_name,
99                                   const GVariantType *parameter_type,
100                                   gboolean            enabled,
101                                   GVariant           *state)
102 {
103   GtkModelMenuItem *item = GTK_MODEL_MENU_ITEM (observer);
104
105   /* we can only activate the item if we have the correct type of parameter */
106   item->can_activate = (item->target == NULL && parameter_type == NULL) ||
107                        (item->target != NULL && parameter_type != NULL &&
108                         g_variant_is_of_type (item->target, parameter_type));
109
110   if (item->can_activate)
111     {
112       if (item->target != NULL && state != NULL)
113         {
114           /* actions with states and targets are radios */
115           gboolean selected;
116
117           selected = g_variant_equal (state, item->target);
118           gtk_check_menu_item_set_draw_as_radio (GTK_CHECK_MENU_ITEM (item), TRUE);
119           gtk_model_menu_item_set_active (item, selected);
120           item->has_indicator = TRUE;
121         }
122
123       else if (state != NULL && g_variant_is_of_type (state, G_VARIANT_TYPE_BOOLEAN))
124         {
125           /* boolean state actions without target are checks */
126           gtk_check_menu_item_set_draw_as_radio (GTK_CHECK_MENU_ITEM (item), FALSE);
127           gtk_model_menu_item_set_active (item, g_variant_get_boolean (state));
128           item->has_indicator = TRUE;
129         }
130
131       else
132         {
133           /* stateless items are just plain actions */
134           gtk_model_menu_item_set_active (item, FALSE);
135           item->has_indicator = FALSE;
136         }
137
138       gtk_widget_set_sensitive (GTK_WIDGET (item), enabled);
139       gtk_widget_queue_resize (GTK_WIDGET (item));
140     }
141 }
142
143 static void
144 gtk_model_menu_item_action_enabled_changed (GActionObserver   *observer,
145                                             GActionObservable *observable,
146                                             const gchar       *action_name,
147                                             gboolean           enabled)
148 {
149   GtkModelMenuItem *item = GTK_MODEL_MENU_ITEM (observer);
150
151   if (!item->can_activate)
152     return;
153
154   gtk_widget_set_sensitive (GTK_WIDGET (item), item->can_activate && enabled);
155 }
156
157 static void
158 gtk_model_menu_item_action_state_changed (GActionObserver   *observer,
159                                           GActionObservable *observable,
160                                           const gchar       *action_name,
161                                           GVariant          *state)
162 {
163   GtkModelMenuItem *item = GTK_MODEL_MENU_ITEM (observer);
164
165   if (!item->can_activate)
166     return;
167
168   if (item->target)
169     gtk_model_menu_item_set_active (item, g_variant_equal (state, item->target));
170
171   else if (g_variant_is_of_type (state, G_VARIANT_TYPE_BOOLEAN))
172     gtk_model_menu_item_set_active (item, g_variant_get_boolean (state));
173 }
174
175 static void
176 gtk_model_menu_item_action_removed (GActionObserver   *observer,
177                                     GActionObservable *observable,
178                                     const gchar       *action_name)
179 {
180   GtkModelMenuItem *item = GTK_MODEL_MENU_ITEM (observer);
181
182   if (!item->can_activate)
183     return;
184
185   gtk_widget_set_sensitive (GTK_WIDGET (item), FALSE);
186   gtk_model_menu_item_set_active (item, FALSE);
187   item->has_indicator = FALSE;
188
189   gtk_widget_queue_resize (GTK_WIDGET (item));
190 }
191
192 static void
193 gtk_model_menu_item_setup (GtkModelMenuItem  *item,
194                            GMenuModel        *model,
195                            gint               item_index,
196                            GActionObservable *actions,
197                            GtkAccelGroup     *accels)
198 {
199   GMenuAttributeIter *iter;
200   GMenuModel *submenu;
201   const gchar *key;
202   GVariant *value;
203
204   if ((submenu = g_menu_model_get_item_link (model, item_index, "submenu")))
205     {
206       gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), gtk_model_menu_create_menu (submenu, actions, accels));
207       g_object_unref (submenu);
208     }
209
210   iter = g_menu_model_iterate_item_attributes (model, item_index);
211   while (g_menu_attribute_iter_get_next (iter, &key, &value))
212     {
213       if (g_str_equal (key, "label") && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
214         gtk_menu_item_set_label (GTK_MENU_ITEM (item), g_variant_get_string (value, NULL));
215
216       else if (g_str_equal (key, "action") && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
217         item->action_name = g_variant_get_string (value, NULL);
218
219       else if (g_str_equal (key, "target"))
220         item->target = g_variant_ref (value);
221
222       g_variant_unref (value);
223     }
224   g_object_unref (iter);
225
226   gtk_menu_item_set_use_underline (GTK_MENU_ITEM (item), TRUE);
227
228   if (item->action_name)
229     {
230       const GVariantType *type;
231       gboolean enabled;
232       GVariant *state;
233       gchar *path;
234
235       /* observer already causes us to hold a hard ref on the group */
236       item->actions = G_ACTION_GROUP (actions);
237
238       if (actions)
239         {
240           g_action_observable_register_observer (actions, item->action_name, G_ACTION_OBSERVER (item));
241
242           if (g_action_group_query_action (G_ACTION_GROUP (actions), item->action_name, &enabled, &type, NULL, NULL, &state))
243             {
244               gtk_model_menu_item_action_added (G_ACTION_OBSERVER (item), actions, item->action_name, type, enabled, state);
245               if (state != NULL)
246                 g_variant_unref (state);
247             }
248           else
249             gtk_widget_set_sensitive (GTK_WIDGET (item), FALSE);
250         }
251       else
252         gtk_widget_set_sensitive (GTK_WIDGET (item), FALSE);
253
254       path = _gtk_accel_path_for_action (item->action_name, item->target);
255       gtk_menu_item_set_accel_path (GTK_MENU_ITEM (item), path);
256       g_free (path);
257     }
258 }
259
260 static void
261 gtk_model_menu_item_finalize (GObject *object)
262 {
263   G_OBJECT_CLASS (gtk_model_menu_item_parent_class)
264     ->finalize (object);
265 }
266
267 static void
268 gtk_model_menu_item_init (GtkModelMenuItem *item)
269 {
270 }
271
272 static void
273 gtk_model_menu_item_observer_iface_init (GActionObserverInterface *iface)
274 {
275   iface->action_added = gtk_model_menu_item_action_added;
276   iface->action_enabled_changed = gtk_model_menu_item_action_enabled_changed;
277   iface->action_state_changed = gtk_model_menu_item_action_state_changed;
278   iface->action_removed = gtk_model_menu_item_action_removed;
279 }
280
281 static void
282 gtk_model_menu_item_class_init (GtkModelMenuItemClass *class)
283 {
284   GtkCheckMenuItemClass *check_class = GTK_CHECK_MENU_ITEM_CLASS (class);
285   GtkMenuItemClass *item_class = GTK_MENU_ITEM_CLASS (class);
286   GObjectClass *object_class = G_OBJECT_CLASS (class);
287
288   check_class->draw_indicator = gtk_model_menu_item_draw_indicator;
289
290   item_class->activate = gtk_model_menu_item_activate;
291   item_class->toggle_size_request = gtk_model_menu_item_toggle_size_request;
292
293   object_class->finalize = gtk_model_menu_item_finalize;
294 }
295
296 GtkMenuItem *
297 gtk_model_menu_item_new (GMenuModel        *model,
298                          gint               item_index,
299                          GActionObservable *actions,
300                          GtkAccelGroup     *accels)
301 {
302   GtkModelMenuItem *item;
303
304   item = g_object_new (GTK_TYPE_MODEL_MENU_ITEM, NULL);
305
306   gtk_model_menu_item_setup (item, model, item_index, actions, accels);
307
308   return GTK_MENU_ITEM (item);
309 }