]> Pileus Git - ~andy/gtk/blob - gtk/gtkmodelmenuitem.c
stylecontext: Do invalidation on first resize container
[~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, see <http://www.gnu.org/licenses/>.
16  *
17  * Author: Ryan Lortie <desrt@desrt.ca>
18  */
19
20 #include "config.h"
21
22 #include "gtkmodelmenuitem.h"
23
24 #include "gtkaccelmapprivate.h"
25 #include "gtkactionhelper.h"
26 #include "gtkwidgetprivate.h"
27 #include "gtkaccellabel.h"
28
29 struct _GtkModelMenuItem
30 {
31   GtkCheckMenuItem parent_instance;
32   GtkActionHelperRole role;
33   gboolean has_indicator;
34 };
35
36 typedef GtkCheckMenuItemClass GtkModelMenuItemClass;
37
38 G_DEFINE_TYPE (GtkModelMenuItem, gtk_model_menu_item, GTK_TYPE_CHECK_MENU_ITEM)
39
40 #define PROP_ACTION_ROLE 1
41
42 static void
43 gtk_model_menu_item_toggle_size_request (GtkMenuItem *menu_item,
44                                          gint        *requisition)
45 {
46   GtkModelMenuItem *item = GTK_MODEL_MENU_ITEM (menu_item);
47
48   if (item->has_indicator)
49     GTK_MENU_ITEM_CLASS (gtk_model_menu_item_parent_class)
50       ->toggle_size_request (menu_item, requisition);
51
52   else
53     *requisition = 0;
54 }
55
56 static void
57 gtk_model_menu_item_draw_indicator (GtkCheckMenuItem *check_item,
58                                     cairo_t          *cr)
59 {
60   GtkModelMenuItem *item = GTK_MODEL_MENU_ITEM (check_item);
61
62   if (item->has_indicator)
63     GTK_CHECK_MENU_ITEM_CLASS (gtk_model_menu_item_parent_class)
64       ->draw_indicator (check_item, cr);
65 }
66
67 static void
68 gtk_actionable_set_namespaced_action_name (GtkActionable *actionable,
69                                            const gchar   *namespace,
70                                            const gchar   *action_name)
71 {
72   if (namespace)
73     {
74       gchar *name = g_strdup_printf ("%s.%s", namespace, action_name);
75       gtk_actionable_set_action_name (actionable, name);
76       g_free (name);
77     }
78   else
79     {
80       gtk_actionable_set_action_name (actionable, action_name);
81     }
82 }
83
84 static void
85 gtk_model_menu_item_submenu_shown (GtkWidget *widget,
86                                    gpointer   user_data)
87 {
88   const gchar *action_name = user_data;
89   GActionMuxer *muxer;
90
91   muxer = _gtk_widget_get_action_muxer (widget);
92   g_action_group_change_action_state (G_ACTION_GROUP (muxer), action_name, g_variant_new_boolean (TRUE));
93 }
94
95 static void
96 gtk_model_menu_item_submenu_hidden (GtkWidget *widget,
97                                     gpointer   user_data)
98 {
99   const gchar *action_name = user_data;
100   GActionMuxer *muxer;
101
102   muxer = _gtk_widget_get_action_muxer (widget);
103   g_action_group_change_action_state (G_ACTION_GROUP (muxer), action_name, g_variant_new_boolean (FALSE));
104 }
105
106 static void
107 gtk_model_menu_item_setup (GtkModelMenuItem  *item,
108                            GMenuModel        *model,
109                            gint               item_index,
110                            const gchar       *action_namespace)
111 {
112   GMenuAttributeIter *iter;
113   GMenuModel *submenu;
114   const gchar *key;
115   GVariant *value;
116
117   if ((submenu = g_menu_model_get_item_link (model, item_index, "submenu")))
118     {
119       gchar *section_namespace = NULL;
120       GtkWidget *menu;
121
122       g_menu_model_get_item_attribute (model, item_index, "action-namespace", "s", &section_namespace);
123       menu = gtk_menu_new ();
124
125       if (action_namespace)
126         {
127           gchar *namespace = g_strjoin (".", action_namespace, section_namespace, NULL);
128           gtk_menu_shell_bind_model (GTK_MENU_SHELL (menu), submenu, namespace, TRUE);
129           g_free (namespace);
130         }
131       else
132         gtk_menu_shell_bind_model (GTK_MENU_SHELL (menu), submenu, section_namespace, TRUE);
133
134       gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), menu);
135
136       g_free (section_namespace);
137       g_object_unref (submenu);
138     }
139
140   iter = g_menu_model_iterate_item_attributes (model, item_index);
141   while (g_menu_attribute_iter_get_next (iter, &key, &value))
142     {
143       if (g_str_equal (key, "label") && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
144         gtk_menu_item_set_label (GTK_MENU_ITEM (item), g_variant_get_string (value, NULL));
145
146       else if (g_str_equal (key, "accel") && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
147         {
148           GdkModifierType modifiers;
149           guint key;
150
151           gtk_accelerator_parse (g_variant_get_string (value, NULL), &key, &modifiers);
152
153           if (key)
154             {
155               GtkAccelLabel *accel_label;
156
157               /* Ensure that the GtkAccelLabel has been created... */
158               (void) gtk_menu_item_get_label (GTK_MENU_ITEM (item));
159               accel_label = GTK_ACCEL_LABEL (gtk_bin_get_child (GTK_BIN (item)));
160               g_assert (accel_label);
161
162               gtk_accel_label_set_accel (accel_label, key, modifiers);
163             }
164         }
165
166       else if (g_str_equal (key, "action") && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
167         gtk_actionable_set_namespaced_action_name (GTK_ACTIONABLE (item), action_namespace,
168                                                    g_variant_get_string (value, NULL));
169
170       else if (g_str_equal (key, "target"))
171         gtk_actionable_set_action_target_value (GTK_ACTIONABLE (item), value);
172
173       else if (g_str_equal (key, "submenu-action") && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
174         {
175           GtkWidget *submenu;
176
177           submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (item));
178
179           if (submenu != NULL)
180             {
181               const gchar *action = g_variant_get_string (value, NULL);
182               gchar *full_action;
183
184               if (action_namespace)
185                 full_action = g_strjoin (".", action_namespace, action, NULL);
186               else
187                 full_action = g_strdup (action);
188
189               g_object_set_data_full (G_OBJECT (submenu), "gtkmodelmenu-visibility-action", full_action, g_free);
190               g_signal_connect (submenu, "show", G_CALLBACK (gtk_model_menu_item_submenu_shown), full_action);
191               g_signal_connect (submenu, "hide", G_CALLBACK (gtk_model_menu_item_submenu_hidden), full_action);
192             }
193         }
194
195       g_variant_unref (value);
196     }
197   g_object_unref (iter);
198
199   gtk_menu_item_set_use_underline (GTK_MENU_ITEM (item), TRUE);
200 }
201
202 static void
203 gtk_model_menu_item_set_has_indicator (GtkModelMenuItem *item,
204                                        gboolean          has_indicator)
205 {
206   if (has_indicator == item->has_indicator)
207     return;
208
209   item->has_indicator = has_indicator;
210
211   gtk_widget_queue_resize (GTK_WIDGET (item));
212 }
213
214 static void
215 gtk_model_menu_item_set_property (GObject *object, guint prop_id,
216                                   const GValue *value, GParamSpec *pspec)
217 {
218   GtkModelMenuItem *item = GTK_MODEL_MENU_ITEM (object);
219   GtkActionHelperRole role;
220   AtkObject *accessible;
221   AtkRole a11y_role;
222
223   g_assert (prop_id == PROP_ACTION_ROLE);
224
225   role = g_value_get_uint (value);
226
227   if (role == item->role)
228     return;
229
230   gtk_check_menu_item_set_draw_as_radio (GTK_CHECK_MENU_ITEM (item), role == GTK_ACTION_HELPER_ROLE_RADIO);
231   gtk_model_menu_item_set_has_indicator (item, role != GTK_ACTION_HELPER_ROLE_NORMAL);
232
233   accessible = gtk_widget_get_accessible (GTK_WIDGET (item));
234   switch (role)
235     {
236     case GTK_ACTION_HELPER_ROLE_NORMAL:
237       a11y_role = ATK_ROLE_MENU_ITEM;
238       break;
239
240     case GTK_ACTION_HELPER_ROLE_TOGGLE:
241       a11y_role = ATK_ROLE_CHECK_MENU_ITEM;
242       break;
243
244     case GTK_ACTION_HELPER_ROLE_RADIO:
245       a11y_role = ATK_ROLE_RADIO_MENU_ITEM;
246       break;
247
248     default:
249       g_assert_not_reached ();
250     }
251
252   atk_object_set_role (accessible, a11y_role);
253 }
254
255 static void
256 gtk_model_menu_item_init (GtkModelMenuItem *item)
257 {
258 }
259
260 static void
261 gtk_model_menu_item_class_init (GtkModelMenuItemClass *class)
262 {
263   GtkCheckMenuItemClass *check_class = GTK_CHECK_MENU_ITEM_CLASS (class);
264   GtkMenuItemClass *item_class = GTK_MENU_ITEM_CLASS (class);
265   GObjectClass *object_class = G_OBJECT_CLASS (class);
266
267   check_class->draw_indicator = gtk_model_menu_item_draw_indicator;
268
269   item_class->toggle_size_request = gtk_model_menu_item_toggle_size_request;
270
271   object_class->set_property = gtk_model_menu_item_set_property;
272
273   g_object_class_install_property (object_class, PROP_ACTION_ROLE,
274                                    g_param_spec_uint ("action-role", "action role", "action role",
275                                                       0, 2, 0, G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
276 }
277
278 GtkMenuItem *
279 gtk_model_menu_item_new (GMenuModel        *model,
280                          gint               item_index,
281                          const gchar       *action_namespace)
282 {
283   GtkModelMenuItem *item;
284
285   item = g_object_new (GTK_TYPE_MODEL_MENU_ITEM, NULL);
286
287   gtk_model_menu_item_setup (item, model, item_index, action_namespace);
288
289   return GTK_MENU_ITEM (item);
290 }