1 | /* testgmenu.c |
2 | * Copyright (C) 2011 Red Hat, Inc. |
3 | * Written by Matthias Clasen |
4 | * |
5 | * This library is free software; you can redistribute it and/or |
6 | * modify it under the terms of the GNU Library General Public |
7 | * License as published by the Free Software Foundation; either |
8 | * version 2 of the License, or (at your option) any later version. |
9 | * |
10 | * This library is distributed in the hope that it will be useful, |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
13 | * Library General Public License for more details. |
14 | * |
15 | * You should have received a copy of the GNU Library General Public |
16 | * License along with this library. If not, see <http://www.gnu.org/licenses/>. |
17 | */ |
18 | |
19 | #include <stdlib.h> |
20 | #include <string.h> |
21 | #include <gio/gio.h> |
22 | #include <gtk/gtk.h> |
23 | |
24 | /* TODO |
25 | * |
26 | * - Labeled sections |
27 | * |
28 | * - Focus changes. Verify that stopping subscriptions works. |
29 | * |
30 | * - Other attributes. What about icons ? |
31 | */ |
32 | |
33 | /* The example menu {{{1 */ |
34 | |
35 | static const char [] = |
36 | "<interface>\n" |
37 | "<menu id='edit-menu'>\n" |
38 | " <section>\n" |
39 | " <item>\n" |
40 | " <attribute name='action'>actions.undo</attribute>\n" |
41 | " <attribute name='label' translatable='yes' context='Stock label'>_Undo</attribute>\n" |
42 | " </item>\n" |
43 | " <item>\n" |
44 | " <attribute name='label' translatable='yes'>Redo</attribute>\n" |
45 | " <attribute name='action'>actions.redo</attribute>\n" |
46 | " </item>\n" |
47 | " </section>\n" |
48 | " <section/>\n" |
49 | " <section>\n" |
50 | " <attribute name='label' translatable='yes'>Copy & Paste</attribute>\n" |
51 | " <item>\n" |
52 | " <attribute name='label' translatable='yes'>Cut</attribute>\n" |
53 | " <attribute name='action'>actions.cut</attribute>\n" |
54 | " </item>\n" |
55 | " <item>\n" |
56 | " <attribute name='label' translatable='yes'>Copy</attribute>\n" |
57 | " <attribute name='action'>actions.copy</attribute>\n" |
58 | " </item>\n" |
59 | " <item>\n" |
60 | " <attribute name='label' translatable='yes'>Paste</attribute>\n" |
61 | " <attribute name='action'>actions.paste</attribute>\n" |
62 | " </item>\n" |
63 | " </section>\n" |
64 | " <section>\n" |
65 | " <item>\n" |
66 | " <attribute name='label' translatable='yes'>Bold</attribute>\n" |
67 | " <attribute name='action'>actions.bold</attribute>\n" |
68 | " </item>\n" |
69 | " <section id=\"size-placeholder\">\n" |
70 | " <attribute name=\"label\">Size</attribute>" |
71 | " </section>\n" |
72 | " <submenu>\n" |
73 | " <attribute name='label' translatable='yes'>Language</attribute>\n" |
74 | " <item>\n" |
75 | " <attribute name='label' translatable='yes'>Latin</attribute>\n" |
76 | " <attribute name='action'>actions.lang</attribute>\n" |
77 | " <attribute name='target'>latin</attribute>\n" |
78 | " </item>\n" |
79 | " <item>\n" |
80 | " <attribute name='label' translatable='yes'>Greek</attribute>\n" |
81 | " <attribute name='action'>actions.lang</attribute>\n" |
82 | " <attribute name='target'>greek</attribute>\n" |
83 | " </item>\n" |
84 | " <item>\n" |
85 | " <attribute name='label' translatable='yes'>Urdu</attribute>\n" |
86 | " <attribute name='action'>actions.lang</attribute>\n" |
87 | " <attribute name='target'>urdu</attribute>\n" |
88 | " </item>\n" |
89 | " </submenu>\n" |
90 | " </section>\n" |
91 | "</menu>\n" |
92 | "</interface>\n" ; |
93 | |
94 | static GMenuModel * |
95 | get_model (void) |
96 | { |
97 | GError *error = NULL; |
98 | GtkBuilder *builder; |
99 | GMenuModel *, *section; |
100 | float i; |
101 | |
102 | builder = gtk_builder_new (); |
103 | gtk_builder_add_from_string (builder, buffer: menu_markup, length: -1, error: &error); |
104 | g_assert_no_error (error); |
105 | |
106 | menu = G_MENU_MODEL (g_object_ref (gtk_builder_get_object (builder, "edit-menu" ))); |
107 | |
108 | section = G_MENU_MODEL (g_object_ref (gtk_builder_get_object (builder, "size-placeholder" ))); |
109 | g_object_unref (object: builder); |
110 | |
111 | for (i = 0.5; i <= 2.0; i += 0.5) |
112 | { |
113 | GMenuItem *item; |
114 | char *target; |
115 | char *label; |
116 | |
117 | target = g_strdup_printf (format: "actions.size::%.1f" , i); |
118 | label = g_strdup_printf (format: "x %.1f" , i); |
119 | item = g_menu_item_new (label, detailed_action: target); |
120 | g_menu_append_item (G_MENU (section), item); |
121 | g_free (mem: label); |
122 | g_free (mem: target); |
123 | } |
124 | |
125 | return menu; |
126 | } |
127 | |
128 | /* The example actions {{{1 */ |
129 | |
130 | static void |
131 | activate_action (GSimpleAction *action, GVariant *parameter, gpointer user_data) |
132 | { |
133 | g_print (format: "Action %s activated\n" , g_action_get_name (G_ACTION (action))); |
134 | } |
135 | |
136 | static void |
137 | activate_toggle (GSimpleAction *action, GVariant *parameter, gpointer user_data) |
138 | { |
139 | GVariant *old_state, *new_state; |
140 | |
141 | old_state = g_action_get_state (G_ACTION (action)); |
142 | new_state = g_variant_new_boolean (value: !g_variant_get_boolean (value: old_state)); |
143 | |
144 | g_print (format: "Toggle action %s activated, state changes from %d to %d\n" , |
145 | g_action_get_name (G_ACTION (action)), |
146 | g_variant_get_boolean (value: old_state), |
147 | g_variant_get_boolean (value: new_state)); |
148 | |
149 | g_simple_action_set_state (simple: action, value: new_state); |
150 | g_variant_unref (value: old_state); |
151 | } |
152 | |
153 | static void |
154 | activate_radio (GSimpleAction *action, GVariant *parameter, gpointer user_data) |
155 | { |
156 | GVariant *old_state, *new_state; |
157 | |
158 | old_state = g_action_get_state (G_ACTION (action)); |
159 | new_state = g_variant_new_string (string: g_variant_get_string (value: parameter, NULL)); |
160 | |
161 | g_print (format: "Radio action %s activated, state changes from %s to %s\n" , |
162 | g_action_get_name (G_ACTION (action)), |
163 | g_variant_get_string (value: old_state, NULL), |
164 | g_variant_get_string (value: new_state, NULL)); |
165 | |
166 | g_simple_action_set_state (simple: action, value: new_state); |
167 | g_variant_unref (value: old_state); |
168 | } |
169 | |
170 | static GActionEntry actions[] = { |
171 | { "undo" , activate_action, NULL, NULL, NULL }, |
172 | { "redo" , activate_action, NULL, NULL, NULL }, |
173 | { "cut" , activate_action, NULL, NULL, NULL }, |
174 | { "copy" , activate_action, NULL, NULL, NULL }, |
175 | { "paste" , activate_action, NULL, NULL, NULL }, |
176 | { "bold" , activate_toggle, NULL, "true" , NULL }, |
177 | { "lang" , activate_radio, "s" , "'latin'" , NULL }, |
178 | }; |
179 | |
180 | static GActionGroup * |
181 | get_group (void) |
182 | { |
183 | GSimpleActionGroup *group; |
184 | |
185 | group = g_simple_action_group_new (); |
186 | |
187 | g_action_map_add_action_entries (G_ACTION_MAP (group), entries: actions, G_N_ELEMENTS (actions), NULL); |
188 | |
189 | return G_ACTION_GROUP (group); |
190 | } |
191 | |
192 | /* The action treeview {{{1 */ |
193 | |
194 | static void |
195 | enabled_cell_func (GtkTreeViewColumn *column, |
196 | GtkCellRenderer *cell, |
197 | GtkTreeModel *model, |
198 | GtkTreeIter *iter, |
199 | gpointer data) |
200 | { |
201 | GActionGroup *group = data; |
202 | char *name; |
203 | gboolean enabled; |
204 | |
205 | gtk_tree_model_get (tree_model: model, iter, 0, &name, -1); |
206 | enabled = g_action_group_get_action_enabled (action_group: group, action_name: name); |
207 | g_free (mem: name); |
208 | |
209 | gtk_cell_renderer_toggle_set_active (GTK_CELL_RENDERER_TOGGLE (cell), setting: enabled); |
210 | } |
211 | |
212 | static void |
213 | state_cell_func (GtkTreeViewColumn *column, |
214 | GtkCellRenderer *cell, |
215 | GtkTreeModel *model, |
216 | GtkTreeIter *iter, |
217 | gpointer data) |
218 | { |
219 | GActionGroup *group = data; |
220 | char *name; |
221 | GVariant *state; |
222 | |
223 | gtk_tree_model_get (tree_model: model, iter, 0, &name, -1); |
224 | state = g_action_group_get_action_state (action_group: group, action_name: name); |
225 | g_free (mem: name); |
226 | |
227 | gtk_cell_renderer_set_visible (cell, FALSE); |
228 | g_object_set (object: cell, first_property_name: "mode" , GTK_CELL_RENDERER_MODE_INERT, NULL); |
229 | |
230 | if (state == NULL) |
231 | return; |
232 | |
233 | if (g_variant_is_of_type (value: state, G_VARIANT_TYPE_BOOLEAN) && |
234 | GTK_IS_CELL_RENDERER_TOGGLE (cell)) |
235 | { |
236 | gtk_cell_renderer_set_visible (cell, TRUE); |
237 | g_object_set (object: cell, first_property_name: "mode" , GTK_CELL_RENDERER_MODE_ACTIVATABLE, NULL); |
238 | gtk_cell_renderer_toggle_set_active (GTK_CELL_RENDERER_TOGGLE (cell), |
239 | setting: g_variant_get_boolean (value: state)); |
240 | } |
241 | else if (g_variant_is_of_type (value: state, G_VARIANT_TYPE_STRING) && |
242 | GTK_IS_CELL_RENDERER_COMBO (cell)) |
243 | { |
244 | gtk_cell_renderer_set_visible (cell, TRUE); |
245 | g_object_set (object: cell, first_property_name: "mode" , GTK_CELL_RENDERER_MODE_EDITABLE, NULL); |
246 | g_object_set (object: cell, first_property_name: "text" , g_variant_get_string (value: state, NULL), NULL); |
247 | } |
248 | |
249 | g_variant_unref (value: state); |
250 | } |
251 | |
252 | static void |
253 | enabled_cell_toggled (GtkCellRendererToggle *cell, |
254 | const char *path_str, |
255 | GtkTreeModel *model) |
256 | { |
257 | GActionGroup *group; |
258 | GAction *action; |
259 | char *name; |
260 | GtkTreePath *path; |
261 | GtkTreeIter iter; |
262 | gboolean enabled; |
263 | |
264 | group = g_object_get_data (G_OBJECT (model), key: "group" ); |
265 | path = gtk_tree_path_new_from_string (path: path_str); |
266 | gtk_tree_model_get_iter (tree_model: model, iter: &iter, path); |
267 | gtk_tree_model_get (tree_model: model, iter: &iter, 0, &name, -1); |
268 | |
269 | enabled = g_action_group_get_action_enabled (action_group: group, action_name: name); |
270 | action = g_action_map_lookup_action (G_ACTION_MAP (group), action_name: name); |
271 | g_simple_action_set_enabled (G_SIMPLE_ACTION (action), enabled: !enabled); |
272 | |
273 | gtk_tree_model_row_changed (tree_model: model, path, iter: &iter); |
274 | |
275 | g_free (mem: name); |
276 | gtk_tree_path_free (path); |
277 | } |
278 | |
279 | static void |
280 | state_cell_toggled (GtkCellRendererToggle *cell, |
281 | const char *path_str, |
282 | GtkTreeModel *model) |
283 | { |
284 | GActionGroup *group; |
285 | GAction *action; |
286 | char *name; |
287 | GtkTreePath *path; |
288 | GtkTreeIter iter; |
289 | GVariant *state; |
290 | |
291 | group = g_object_get_data (G_OBJECT (model), key: "group" ); |
292 | path = gtk_tree_path_new_from_string (path: path_str); |
293 | gtk_tree_model_get_iter (tree_model: model, iter: &iter, path); |
294 | gtk_tree_model_get (tree_model: model, iter: &iter, 0, &name, -1); |
295 | |
296 | state = g_action_group_get_action_state (action_group: group, action_name: name); |
297 | action = g_action_map_lookup_action (G_ACTION_MAP (group), action_name: name); |
298 | if (state && g_variant_is_of_type (value: state, G_VARIANT_TYPE_BOOLEAN)) |
299 | { |
300 | gboolean b; |
301 | |
302 | b = g_variant_get_boolean (value: state); |
303 | g_simple_action_set_state (G_SIMPLE_ACTION (action), value: g_variant_new_boolean (value: !b)); |
304 | } |
305 | else |
306 | { |
307 | /* nothing to do */ |
308 | } |
309 | |
310 | gtk_tree_model_row_changed (tree_model: model, path, iter: &iter); |
311 | |
312 | g_free (mem: name); |
313 | gtk_tree_path_free (path); |
314 | if (state) |
315 | g_variant_unref (value: state); |
316 | } |
317 | |
318 | static void |
319 | state_cell_edited (GtkCellRendererCombo *cell, |
320 | const char *path_str, |
321 | const char *new_text, |
322 | GtkTreeModel *model) |
323 | { |
324 | GActionGroup *group; |
325 | GAction *action; |
326 | char *name; |
327 | GtkTreePath *path; |
328 | GtkTreeIter iter; |
329 | |
330 | group = g_object_get_data (G_OBJECT (model), key: "group" ); |
331 | path = gtk_tree_path_new_from_string (path: path_str); |
332 | gtk_tree_model_get_iter (tree_model: model, iter: &iter, path); |
333 | gtk_tree_model_get (tree_model: model, iter: &iter, 0, &name, -1); |
334 | action = g_action_map_lookup_action (G_ACTION_MAP (group), action_name: name); |
335 | g_simple_action_set_state (G_SIMPLE_ACTION (action), value: g_variant_new_string (string: new_text)); |
336 | |
337 | gtk_tree_model_row_changed (tree_model: model, path, iter: &iter); |
338 | |
339 | g_free (mem: name); |
340 | gtk_tree_path_free (path); |
341 | } |
342 | |
343 | static GtkWidget * |
344 | create_action_treeview (GActionGroup *group) |
345 | { |
346 | GtkWidget *tv; |
347 | GtkListStore *store; |
348 | GtkListStore *values; |
349 | GtkTreeIter iter; |
350 | GtkTreeViewColumn *column; |
351 | GtkCellRenderer *cell; |
352 | char **group_actions; |
353 | int i; |
354 | |
355 | store = gtk_list_store_new (n_columns: 2, G_TYPE_STRING, G_TYPE_STRING); |
356 | group_actions = g_action_group_list_actions (action_group: group); |
357 | for (i = 0; group_actions[i]; i++) |
358 | { |
359 | gtk_list_store_append (list_store: store, iter: &iter); |
360 | gtk_list_store_set (list_store: store, iter: &iter, 0, group_actions[i], -1); |
361 | } |
362 | g_strfreev (str_array: group_actions); |
363 | g_object_set_data (G_OBJECT (store), key: "group" , data: group); |
364 | |
365 | tv = gtk_tree_view_new (); |
366 | |
367 | g_signal_connect_swapped (group, "action-enabled-changed" , |
368 | G_CALLBACK (gtk_widget_queue_draw), tv); |
369 | g_signal_connect_swapped (group, "action-state-changed" , |
370 | G_CALLBACK (gtk_widget_queue_draw), tv); |
371 | |
372 | gtk_tree_view_set_model (GTK_TREE_VIEW (tv), GTK_TREE_MODEL (store)); |
373 | |
374 | cell = gtk_cell_renderer_text_new (); |
375 | column = gtk_tree_view_column_new_with_attributes (title: "Action" , cell, |
376 | "text" , 0, |
377 | NULL); |
378 | gtk_tree_view_append_column (GTK_TREE_VIEW (tv), column); |
379 | |
380 | column = gtk_tree_view_column_new (); |
381 | gtk_tree_view_column_set_title (tree_column: column, title: "Enabled" ); |
382 | cell = gtk_cell_renderer_toggle_new (); |
383 | gtk_tree_view_column_pack_start (tree_column: column, cell, FALSE); |
384 | gtk_tree_view_column_set_cell_data_func (tree_column: column, cell_renderer: cell, func: enabled_cell_func, func_data: group, NULL); |
385 | g_signal_connect (cell, "toggled" , G_CALLBACK (enabled_cell_toggled), store); |
386 | gtk_tree_view_append_column (GTK_TREE_VIEW (tv), column); |
387 | |
388 | column = gtk_tree_view_column_new (); |
389 | gtk_tree_view_column_set_title (tree_column: column, title: "State" ); |
390 | cell = gtk_cell_renderer_toggle_new (); |
391 | gtk_tree_view_column_pack_start (tree_column: column, cell, FALSE); |
392 | gtk_tree_view_column_set_cell_data_func (tree_column: column, cell_renderer: cell, func: state_cell_func, func_data: group, NULL); |
393 | g_signal_connect (cell, "toggled" , G_CALLBACK (state_cell_toggled), store); |
394 | cell = gtk_cell_renderer_combo_new (); |
395 | values = gtk_list_store_new (n_columns: 1, G_TYPE_STRING); |
396 | gtk_list_store_append (list_store: values, iter: &iter); |
397 | gtk_list_store_set (list_store: values, iter: &iter, 0, "latin" , -1); |
398 | gtk_list_store_append (list_store: values, iter: &iter); |
399 | gtk_list_store_set (list_store: values, iter: &iter, 0, "greek" , -1); |
400 | gtk_list_store_append (list_store: values, iter: &iter); |
401 | gtk_list_store_set (list_store: values, iter: &iter, 0, "urdu" , -1); |
402 | gtk_list_store_append (list_store: values, iter: &iter); |
403 | gtk_list_store_set (list_store: values, iter: &iter, 0, "sumerian" , -1); |
404 | g_object_set (object: cell, |
405 | first_property_name: "has-entry" , FALSE, |
406 | "model" , values, |
407 | "text-column" , 0, |
408 | "editable" , TRUE, |
409 | NULL); |
410 | gtk_tree_view_column_pack_start (tree_column: column, cell, FALSE); |
411 | gtk_tree_view_column_set_cell_data_func (tree_column: column, cell_renderer: cell, func: state_cell_func, func_data: group, NULL); |
412 | g_signal_connect (cell, "edited" , G_CALLBACK (state_cell_edited), store); |
413 | gtk_tree_view_append_column (GTK_TREE_VIEW (tv), column); |
414 | |
415 | return tv; |
416 | } |
417 | |
418 | /* Dynamic menu changes {{{1 */ |
419 | |
420 | static void |
421 | toggle_sumerian (GtkCheckButton *button, gpointer data) |
422 | { |
423 | GMenuModel *model; |
424 | gboolean adding; |
425 | GMenuModel *m; |
426 | |
427 | model = g_object_get_data (G_OBJECT (button), key: "model" ); |
428 | |
429 | adding = gtk_check_button_get_active (self: button); |
430 | |
431 | m = g_menu_model_get_item_link (model, item_index: g_menu_model_get_n_items (model) - 1, G_MENU_LINK_SECTION); |
432 | m = g_menu_model_get_item_link (model: m, item_index: g_menu_model_get_n_items (model: m) - 1, G_MENU_LINK_SUBMENU); |
433 | if (adding) |
434 | g_menu_append (G_MENU (m), label: "Sumerian" , detailed_action: "lang::sumerian" ); |
435 | else |
436 | g_menu_remove (G_MENU (m), position: g_menu_model_get_n_items (model: m) - 1); |
437 | } |
438 | |
439 | static void |
440 | action_list_add (GtkTreeModel *store, |
441 | const char *action) |
442 | { |
443 | GtkTreeIter iter; |
444 | |
445 | gtk_list_store_append (GTK_LIST_STORE (store), iter: &iter); |
446 | gtk_list_store_set (GTK_LIST_STORE (store), iter: &iter, 0, action, -1); |
447 | } |
448 | |
449 | static void |
450 | action_list_remove (GtkTreeModel *store, |
451 | const char *action) |
452 | { |
453 | GtkTreeIter iter; |
454 | char *text; |
455 | |
456 | gtk_tree_model_get_iter_first (tree_model: store, iter: &iter); |
457 | do { |
458 | gtk_tree_model_get (tree_model: store, iter: &iter, 0, &text, -1); |
459 | if (g_strcmp0 (str1: action, str2: text) == 0) |
460 | { |
461 | g_free (mem: text); |
462 | gtk_list_store_remove (GTK_LIST_STORE (store), iter: &iter); |
463 | break; |
464 | } |
465 | g_free (mem: text); |
466 | } while (gtk_tree_model_iter_next (tree_model: store, iter: &iter)); |
467 | } |
468 | |
469 | static void |
470 | toggle_italic (GtkCheckButton *button, gpointer data) |
471 | { |
472 | GMenuModel *model; |
473 | GActionGroup *group; |
474 | GSimpleAction *action; |
475 | gboolean adding; |
476 | GMenuModel *m; |
477 | GtkTreeView *tv = data; |
478 | GtkTreeModel *store; |
479 | |
480 | model = g_object_get_data (G_OBJECT (button), key: "model" ); |
481 | group = g_object_get_data (G_OBJECT (button), key: "group" ); |
482 | |
483 | store = gtk_tree_view_get_model (tree_view: tv); |
484 | |
485 | adding = gtk_check_button_get_active (self: button); |
486 | |
487 | m = g_menu_model_get_item_link (model, item_index: g_menu_model_get_n_items (model) - 1, G_MENU_LINK_SECTION); |
488 | if (adding) |
489 | { |
490 | action = g_simple_action_new_stateful (name: "italic" , NULL, state: g_variant_new_boolean (FALSE)); |
491 | g_action_map_add_action (G_ACTION_MAP (group), G_ACTION (action)); |
492 | g_signal_connect (action, "activate" , G_CALLBACK (activate_toggle), NULL); |
493 | g_object_unref (object: action); |
494 | action_list_add (store, action: "italic" ); |
495 | g_menu_insert (G_MENU (m), position: 1, label: "Italic" , detailed_action: "italic" ); |
496 | } |
497 | else |
498 | { |
499 | g_action_map_remove_action (G_ACTION_MAP (group), action_name: "italic" ); |
500 | action_list_remove (store, action: "italic" ); |
501 | g_menu_remove (G_MENU (m), position: 1); |
502 | } |
503 | } |
504 | |
505 | static void |
506 | toggle_speed (GtkCheckButton *button, gpointer data) |
507 | { |
508 | GMenuModel *model; |
509 | GActionGroup *group; |
510 | GSimpleAction *action; |
511 | gboolean adding; |
512 | GMenuModel *m; |
513 | GMenu *; |
514 | GtkTreeView *tv = data; |
515 | GtkTreeModel *store; |
516 | |
517 | model = g_object_get_data (G_OBJECT (button), key: "model" ); |
518 | group = g_object_get_data (G_OBJECT (button), key: "group" ); |
519 | |
520 | store = gtk_tree_view_get_model (tree_view: tv); |
521 | |
522 | adding = gtk_check_button_get_active (self: button); |
523 | |
524 | m = g_menu_model_get_item_link (model, item_index: 1, G_MENU_LINK_SECTION); |
525 | if (adding) |
526 | { |
527 | action = g_simple_action_new (name: "faster" , NULL); |
528 | g_action_map_add_action (G_ACTION_MAP (group), G_ACTION (action)); |
529 | g_signal_connect (action, "activate" , G_CALLBACK (activate_action), NULL); |
530 | g_object_unref (object: action); |
531 | |
532 | action = g_simple_action_new (name: "slower" , NULL); |
533 | g_action_map_add_action (G_ACTION_MAP (group), G_ACTION (action)); |
534 | g_signal_connect (action, "activate" , G_CALLBACK (activate_action), NULL); |
535 | g_object_unref (object: action); |
536 | |
537 | action_list_add (store, action: "faster" ); |
538 | action_list_add (store, action: "slower" ); |
539 | |
540 | submenu = g_menu_new (); |
541 | g_menu_append (menu: submenu, label: "Faster" , detailed_action: "faster" ); |
542 | g_menu_append (menu: submenu, label: "Slower" , detailed_action: "slower" ); |
543 | g_menu_append_submenu (G_MENU (m), label: "Speed" , G_MENU_MODEL (submenu)); |
544 | } |
545 | else |
546 | { |
547 | g_action_map_remove_action (G_ACTION_MAP (group), action_name: "faster" ); |
548 | g_action_map_remove_action (G_ACTION_MAP (group), action_name: "slower" ); |
549 | |
550 | action_list_remove (store, action: "faster" ); |
551 | action_list_remove (store, action: "slower" ); |
552 | |
553 | g_menu_remove (G_MENU (m), position: g_menu_model_get_n_items (model: m) - 1); |
554 | } |
555 | } |
556 | static GtkWidget * |
557 | create_add_remove_buttons (GActionGroup *group, |
558 | GMenuModel *model, |
559 | GtkWidget *treeview) |
560 | { |
561 | GtkWidget *box; |
562 | GtkWidget *button; |
563 | |
564 | box = gtk_box_new (orientation: GTK_ORIENTATION_VERTICAL, spacing: 6); |
565 | |
566 | button = gtk_check_button_new_with_label (label: "Add Italic" ); |
567 | gtk_box_append (GTK_BOX (box), child: button); |
568 | |
569 | g_object_set_data (G_OBJECT (button), key: "group" , data: group); |
570 | g_object_set_data (G_OBJECT (button), key: "model" , data: model); |
571 | |
572 | g_signal_connect (button, "toggled" , |
573 | G_CALLBACK (toggle_italic), treeview); |
574 | |
575 | button = gtk_check_button_new_with_label (label: "Add Sumerian" ); |
576 | gtk_box_append (GTK_BOX (box), child: button); |
577 | |
578 | g_object_set_data (G_OBJECT (button), key: "group" , data: group); |
579 | g_object_set_data (G_OBJECT (button), key: "model" , data: model); |
580 | |
581 | g_signal_connect (button, "toggled" , |
582 | G_CALLBACK (toggle_sumerian), NULL); |
583 | |
584 | button = gtk_check_button_new_with_label (label: "Add Speed" ); |
585 | gtk_box_append (GTK_BOX (box), child: button); |
586 | |
587 | g_object_set_data (G_OBJECT (button), key: "group" , data: group); |
588 | g_object_set_data (G_OBJECT (button), key: "model" , data: model); |
589 | |
590 | g_signal_connect (button, "toggled" , |
591 | G_CALLBACK (toggle_speed), treeview); |
592 | return box; |
593 | } |
594 | |
595 | /* main {{{1 */ |
596 | |
597 | #define BUS_NAME "org.gtk.TestMenus" |
598 | #define OBJ_PATH "/org/gtk/TestMenus" |
599 | |
600 | static void |
601 | quit_cb (GtkWidget *widget, |
602 | gpointer data) |
603 | { |
604 | gboolean *done = data; |
605 | |
606 | *done = TRUE; |
607 | |
608 | g_main_context_wakeup (NULL); |
609 | } |
610 | |
611 | int |
612 | main (int argc, char *argv[]) |
613 | { |
614 | GtkWidget *window; |
615 | GtkWidget *box; |
616 | GtkWidget *button; |
617 | GtkWidget *tv; |
618 | GtkWidget *buttons; |
619 | GMenuModel *model; |
620 | GActionGroup *group; |
621 | GDBusConnection *bus; |
622 | GError *error = NULL; |
623 | gboolean do_export = FALSE; |
624 | gboolean do_import = FALSE; |
625 | GOptionEntry entries[] = { |
626 | { "export" , 0, 0, G_OPTION_ARG_NONE, &do_export, "Export actions and menus over D-Bus" , NULL }, |
627 | { "import" , 0, 0, G_OPTION_ARG_NONE, &do_import, "Use exported actions and menus" , NULL }, |
628 | { NULL, } |
629 | }; |
630 | GOptionContext *context; |
631 | gboolean done = FALSE; |
632 | |
633 | context = g_option_context_new (parameter_string: "" ); |
634 | g_option_context_add_main_entries (context, entries, NULL); |
635 | g_option_context_parse (context, argc: &argc, argv: &argv, NULL); |
636 | gtk_init (); |
637 | |
638 | if (do_export && do_import) |
639 | { |
640 | g_error ("can't have it both ways\n" ); |
641 | exit (status: 1); |
642 | } |
643 | |
644 | window = gtk_window_new (); |
645 | g_signal_connect (window, "destroy" , G_CALLBACK (quit_cb), &done); |
646 | box = gtk_box_new (orientation: GTK_ORIENTATION_VERTICAL, spacing: 6); |
647 | gtk_window_set_child (GTK_WINDOW (window), child: box); |
648 | |
649 | bus = g_bus_get_sync (bus_type: G_BUS_TYPE_SESSION, NULL, NULL); |
650 | |
651 | if (do_import) |
652 | { |
653 | g_print (format: "Getting menus from the bus...\n" ); |
654 | model = (GMenuModel*)g_dbus_menu_model_get (connection: bus, BUS_NAME, OBJ_PATH); |
655 | g_print (format: "Getting actions from the bus...\n" ); |
656 | group = (GActionGroup*)g_dbus_action_group_get (connection: bus, BUS_NAME, OBJ_PATH); |
657 | } |
658 | else |
659 | { |
660 | group = get_group (); |
661 | model = get_model (); |
662 | |
663 | tv = create_action_treeview (group); |
664 | gtk_box_append (GTK_BOX (box), child: tv); |
665 | buttons = create_add_remove_buttons (group, model, treeview: tv); |
666 | gtk_box_append (GTK_BOX (box), child: buttons); |
667 | } |
668 | |
669 | if (do_export) |
670 | { |
671 | g_print (format: "Exporting menus on the bus...\n" ); |
672 | if (!g_dbus_connection_export_menu_model (connection: bus, OBJ_PATH, menu: model, error: &error)) |
673 | { |
674 | g_warning ("Menu export failed: %s" , error->message); |
675 | exit (status: 1); |
676 | } |
677 | g_print (format: "Exporting actions on the bus...\n" ); |
678 | if (!g_dbus_connection_export_action_group (connection: bus, OBJ_PATH, action_group: group, error: &error)) |
679 | { |
680 | g_warning ("Action export failed: %s" , error->message); |
681 | exit (status: 1); |
682 | } |
683 | g_bus_own_name_on_connection (connection: bus, BUS_NAME, flags: 0, NULL, NULL, NULL, NULL); |
684 | } |
685 | else |
686 | { |
687 | button = gtk_menu_button_new (); |
688 | gtk_menu_button_set_label (GTK_MENU_BUTTON (button), label: "Click here" ); |
689 | gtk_menu_button_set_menu_model (GTK_MENU_BUTTON (button), menu_model: model); |
690 | gtk_widget_insert_action_group (widget: button, name: "actions" , group); |
691 | gtk_box_append (GTK_BOX (box), child: button); |
692 | } |
693 | |
694 | gtk_widget_show (widget: window); |
695 | |
696 | while (!done) |
697 | g_main_context_iteration (NULL, TRUE); |
698 | |
699 | return 0; |
700 | } |
701 | |
702 | /* Epilogue {{{1 */ |
703 | /* vim:set foldmethod=marker: */ |
704 | |