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
35static const char menu_markup[] =
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 &amp; 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
94static GMenuModel *
95get_model (void)
96{
97 GError *error = NULL;
98 GtkBuilder *builder;
99 GMenuModel *menu, *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
130static void
131activate_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
136static void
137activate_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
153static void
154activate_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
170static 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
180static GActionGroup *
181get_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
194static void
195enabled_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
212static void
213state_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
252static void
253enabled_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
279static void
280state_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
318static void
319state_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
343static GtkWidget *
344create_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
420static void
421toggle_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
439static void
440action_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
449static void
450action_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
469static void
470toggle_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
505static void
506toggle_speed (GtkCheckButton *button, gpointer data)
507{
508 GMenuModel *model;
509 GActionGroup *group;
510 GSimpleAction *action;
511 gboolean adding;
512 GMenuModel *m;
513 GMenu *submenu;
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}
556static GtkWidget *
557create_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
600static void
601quit_cb (GtkWidget *widget,
602 gpointer data)
603{
604 gboolean *done = data;
605
606 *done = TRUE;
607
608 g_main_context_wakeup (NULL);
609}
610
611int
612main (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

source code of gtk/tests/testgmenu.c