]> Pileus Git - ~andy/gtk/blob - gtk/gtkmodelmenuitem.c
Split off GMenuModel -> GtkMenuBar code
[~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 "gtkmodelmenu.h"
27
28 struct _GtkModelMenuItem
29 {
30   GtkCheckMenuItem parent_instance;
31
32   GActionGroup *actions;
33   const gchar *action_name;
34   gboolean has_indicator;
35   gboolean can_activate;
36   GVariant *target;
37 };
38
39 typedef GtkCheckMenuItemClass GtkModelMenuItemClass;
40
41 static void gtk_model_menu_item_observer_iface_init (GActionObserverInterface *iface);
42 G_DEFINE_TYPE_WITH_CODE (GtkModelMenuItem, gtk_model_menu_item, GTK_TYPE_CHECK_MENU_ITEM,
43                          G_IMPLEMENT_INTERFACE (G_TYPE_ACTION_OBSERVER, gtk_model_menu_item_observer_iface_init))
44
45 static void
46 gtk_model_menu_item_activate (GtkMenuItem *menu_item)
47 {
48   GtkModelMenuItem *item = GTK_MODEL_MENU_ITEM (menu_item);
49
50   if (item->can_activate)
51     g_action_group_activate_action (item->actions, item->action_name, item->target);
52 }
53
54 static void
55 gtk_model_menu_item_toggle_size_request (GtkMenuItem *menu_item,
56                                          gint        *requisition)
57 {
58   GtkModelMenuItem *item = GTK_MODEL_MENU_ITEM (menu_item);
59
60   if (item->has_indicator)
61     GTK_MENU_ITEM_CLASS (gtk_model_menu_item_parent_class)
62       ->toggle_size_request (menu_item, requisition);
63
64   else
65     *requisition = 0;
66 }
67
68 static void
69 gtk_model_menu_item_draw_indicator (GtkCheckMenuItem *check_item,
70                                     cairo_t          *cr)
71 {
72   GtkModelMenuItem *item = GTK_MODEL_MENU_ITEM (check_item);
73
74   if (item->has_indicator)
75     GTK_CHECK_MENU_ITEM_CLASS (gtk_model_menu_item_parent_class)
76       ->draw_indicator (check_item, cr);
77 }
78
79 static void
80 gtk_model_menu_item_set_active (GtkModelMenuItem *item,
81                                 gboolean          active)
82 {
83   GtkCheckMenuItem *checkitem = GTK_CHECK_MENU_ITEM (item);
84
85   if (gtk_check_menu_item_get_active (checkitem) != active)
86     {
87       _gtk_check_menu_item_set_active (checkitem, active);
88       g_object_notify (G_OBJECT (checkitem), "active");
89       gtk_check_menu_item_toggled (checkitem);
90       gtk_widget_queue_draw (GTK_WIDGET (item));
91     }
92 }
93
94 static void
95 gtk_model_menu_item_action_added (GActionObserver    *observer,
96                                   GActionObservable  *observable,
97                                   const gchar        *action_name,
98                                   const GVariantType *parameter_type,
99                                   gboolean            enabled,
100                                   GVariant           *state)
101 {
102   GtkModelMenuItem *item = GTK_MODEL_MENU_ITEM (observer);
103
104   /* we can only activate the item if we have the correct type of parameter */
105   item->can_activate = (item->target == NULL && parameter_type == NULL) ||
106                        (item->target != NULL && parameter_type != NULL &&
107                         g_variant_is_of_type (item->target, parameter_type));
108
109   if (item->can_activate)
110     {
111       if (item->target != NULL && state != NULL)
112         {
113           /* actions with states and targets are radios */
114           gboolean selected;
115
116           selected = g_variant_equal (state, item->target);
117           gtk_check_menu_item_set_draw_as_radio (GTK_CHECK_MENU_ITEM (item), TRUE);
118           gtk_model_menu_item_set_active (item, selected);
119           item->has_indicator = TRUE;
120         }
121
122       else if (state != NULL && g_variant_is_of_type (state, G_VARIANT_TYPE_BOOLEAN))
123         {
124           /* boolean state actions without target are checks */
125           gtk_check_menu_item_set_draw_as_radio (GTK_CHECK_MENU_ITEM (item), FALSE);
126           gtk_model_menu_item_set_active (item, g_variant_get_boolean (state));
127           item->has_indicator = TRUE;
128         }
129
130       else
131         {
132           /* stateless items are just plain actions */
133           gtk_model_menu_item_set_active (item, FALSE);
134           item->has_indicator = FALSE;
135         }
136
137       gtk_widget_set_sensitive (GTK_WIDGET (item), enabled);
138       gtk_widget_queue_resize (GTK_WIDGET (item));
139     }
140 }
141
142 static void
143 gtk_model_menu_item_action_enabled_changed (GActionObserver   *observer,
144                                             GActionObservable *observable,
145                                             const gchar       *action_name,
146                                             gboolean           enabled)
147 {
148   GtkModelMenuItem *item = GTK_MODEL_MENU_ITEM (observer);
149
150   if (!item->can_activate)
151     return;
152
153   gtk_widget_set_sensitive (GTK_WIDGET (item), item->can_activate && enabled);
154 }
155
156 static void
157 gtk_model_menu_item_action_state_changed (GActionObserver   *observer,
158                                           GActionObservable *observable,
159                                           const gchar       *action_name,
160                                           GVariant          *state)
161 {
162   GtkModelMenuItem *item = GTK_MODEL_MENU_ITEM (observer);
163
164   if (!item->can_activate)
165     return;
166
167   if (item->target)
168     gtk_model_menu_item_set_active (item, g_variant_equal (state, item->target));
169
170   else if (g_variant_is_of_type (state, G_VARIANT_TYPE_BOOLEAN))
171     gtk_model_menu_item_set_active (item, g_variant_get_boolean (state));
172 }
173
174 static void
175 gtk_model_menu_item_action_removed (GActionObserver   *observer,
176                                     GActionObservable *observable,
177                                     const gchar       *action_name)
178 {
179   GtkModelMenuItem *item = GTK_MODEL_MENU_ITEM (observer);
180
181   if (!item->can_activate)
182     return;
183
184   gtk_widget_set_sensitive (GTK_WIDGET (item), FALSE);
185   gtk_model_menu_item_set_active (item, FALSE);
186   item->has_indicator = FALSE;
187
188   gtk_widget_queue_resize (GTK_WIDGET (item));
189 }
190
191 static void
192 gtk_model_menu_item_setup (GtkModelMenuItem  *item,
193                            GMenuModel        *model,
194                            gint               item_index,
195                            GActionObservable *actions)
196 {
197   GMenuAttributeIter *iter;
198   GMenuModel *submenu;
199   const gchar *key;
200   GVariant *value;
201
202   if ((submenu = g_menu_model_get_item_link (model, item_index, "submenu")))
203     {
204       gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), gtk_model_menu_create_menu (submenu, actions));
205       g_object_unref (submenu);
206     }
207
208   iter = g_menu_model_iterate_item_attributes (model, item_index);
209   while (g_menu_attribute_iter_get_next (iter, &key, &value))
210     {
211       if (g_str_equal (key, "label") && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
212         gtk_menu_item_set_label (GTK_MENU_ITEM (item), g_variant_get_string (value, NULL));
213
214       else if (g_str_equal (key, "action") && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
215         item->action_name = g_variant_get_string (value, NULL);
216
217       else if (g_str_equal (key, "target"))
218         item->target = g_variant_ref (value);
219
220       g_variant_unref (value);
221     }
222   g_object_unref (iter);
223
224   gtk_menu_item_set_use_underline (GTK_MENU_ITEM (item), TRUE);
225
226   if (item->action_name)
227     {
228       const GVariantType *type;
229       gboolean enabled;
230       GVariant *state;
231
232       /* observer already causes us to hold a hard ref on the group */
233       item->actions = G_ACTION_GROUP (actions);
234
235       g_action_observable_register_observer (actions, item->action_name, G_ACTION_OBSERVER (item));
236
237       if (g_action_group_query_action (G_ACTION_GROUP (actions), item->action_name, &enabled, &type, NULL, NULL, &state))
238         gtk_model_menu_item_action_added (G_ACTION_OBSERVER (item), actions, item->action_name, type, enabled, state);
239
240       else
241         gtk_widget_set_sensitive (GTK_WIDGET (item), FALSE);
242
243       if (state != NULL)
244         g_variant_unref (state);
245     }
246 }
247
248 static void
249 gtk_model_menu_item_finalize (GObject *object)
250 {
251   G_OBJECT_CLASS (gtk_model_menu_item_parent_class)
252     ->finalize (object);
253 }
254
255 static void
256 gtk_model_menu_item_init (GtkModelMenuItem *item)
257 {
258 }
259
260 static void
261 gtk_model_menu_item_observer_iface_init (GActionObserverInterface *iface)
262 {
263   iface->action_added = gtk_model_menu_item_action_added;
264   iface->action_enabled_changed = gtk_model_menu_item_action_enabled_changed;
265   iface->action_state_changed = gtk_model_menu_item_action_state_changed;
266   iface->action_removed = gtk_model_menu_item_action_removed;
267 }
268
269 static void
270 gtk_model_menu_item_class_init (GtkModelMenuItemClass *class)
271 {
272   GtkCheckMenuItemClass *check_class = GTK_CHECK_MENU_ITEM_CLASS (class);
273   GtkMenuItemClass *item_class = GTK_MENU_ITEM_CLASS (class);
274   GObjectClass *object_class = G_OBJECT_CLASS (class);
275
276   check_class->draw_indicator = gtk_model_menu_item_draw_indicator;
277
278   item_class->activate = gtk_model_menu_item_activate;
279   item_class->toggle_size_request = gtk_model_menu_item_toggle_size_request;
280
281   object_class->finalize = gtk_model_menu_item_finalize;
282 }
283
284 GtkMenuItem *
285 gtk_model_menu_item_new (GMenuModel        *model,
286                          gint               item_index,
287                          GActionObservable *actions)
288 {
289   GtkModelMenuItem *item;
290
291   item = g_object_new (GTK_TYPE_MODEL_MENU_ITEM, NULL);
292
293   gtk_model_menu_item_setup (item, model, item_index, actions);
294
295   return GTK_MENU_ITEM (item);
296 }