1/* Copyright (C) 2019 Red Hat, Inc.
2 *
3 * This library is free software; you can redistribute it and/or
4 * modify it under the terms of the GNU Lesser General Public
5 * License as published by the Free Software Foundation; either
6 * version 2 of the License, or (at your option) any later version.
7 *
8 * This library is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public
14 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
15 */
16#include <gtk/gtk.h>
17
18static void
19activate (GSimpleAction *action,
20 GVariant *parameter,
21 gpointer user_data)
22{
23 int *activated = user_data;
24 (*activated)++;
25}
26
27/* Test that inheriting actions along the widget
28 * hierarchy works as expected. Since GtkWidget does
29 * not expose the actions, we test this by observing
30 * the effect of activating them.
31 */
32static void
33test_inheritance (void)
34{
35 GtkWidget *window;
36 GtkWidget *box;
37 GtkWidget *button;
38 GSimpleActionGroup *win_actions;
39 GSimpleActionGroup *box_actions;
40 GActionEntry entries[] = {
41 { "action", activate, NULL, NULL, NULL },
42 };
43 gboolean found;
44 int win_activated;
45 int box_activated;
46
47 win_activated = 0;
48 box_activated = 0;
49
50 /* Our hierarchy looks like this:
51 *
52 * window win.action
53 * |
54 * box box.action
55 * |
56 * button
57 */
58 window = gtk_window_new ();
59 box = gtk_box_new (orientation: GTK_ORIENTATION_HORIZONTAL, spacing: 0);
60 button = gtk_button_new ();
61
62 gtk_window_set_child (GTK_WINDOW (window), child: box);
63 gtk_box_append (GTK_BOX (box), child: button);
64
65 win_actions = g_simple_action_group_new ();
66 g_action_map_add_action_entries (G_ACTION_MAP (win_actions),
67 entries, G_N_ELEMENTS (entries),
68 user_data: &win_activated);
69
70 box_actions = g_simple_action_group_new ();
71 g_action_map_add_action_entries (G_ACTION_MAP (box_actions),
72 entries, G_N_ELEMENTS (entries),
73 user_data: &box_activated);
74
75 gtk_widget_insert_action_group (widget: window, name: "win", G_ACTION_GROUP (win_actions));
76 gtk_widget_insert_action_group (widget: box, name: "box", G_ACTION_GROUP (box_actions));
77
78 g_assert_cmpint (win_activated, ==, 0);
79 g_assert_cmpint (box_activated, ==, 0);
80
81 found = gtk_widget_activate_action (widget: button, name: "win.action", NULL);
82
83 g_assert_true (found);
84 g_assert_cmpint (win_activated, ==, 1);
85 g_assert_cmpint (box_activated, ==, 0);
86
87 found = gtk_widget_activate_action (widget: box, name: "win.action", NULL);
88
89 g_assert_true (found);
90 g_assert_cmpint (win_activated, ==, 2);
91 g_assert_cmpint (box_activated, ==, 0);
92
93 found = gtk_widget_activate_action (widget: button, name: "box.action", NULL);
94
95 g_assert_true (found);
96 g_assert_cmpint (win_activated, ==, 2);
97 g_assert_cmpint (box_activated, ==, 1);
98
99 found = gtk_widget_activate_action (widget: window, name: "box.action", NULL);
100
101 g_assert_false (found);
102 g_assert_cmpint (win_activated, ==, 2);
103 g_assert_cmpint (box_activated, ==, 1);
104
105 gtk_window_destroy (GTK_WINDOW (window));
106 g_object_unref (object: win_actions);
107 g_object_unref (object: box_actions);
108}
109
110/* Test action inheritance with hierarchy changes */
111static void
112test_inheritance2 (void)
113{
114 GtkWidget *window;
115 GtkWidget *box;
116 GtkWidget *box1;
117 GtkWidget *box2;
118 GtkWidget *button;
119 GSimpleActionGroup *win_actions;
120 GSimpleActionGroup *box1_actions;
121 GSimpleActionGroup *box2_actions;
122 GActionEntry entries[] = {
123 { "action", activate, NULL, NULL, NULL },
124 };
125 gboolean found;
126 int win_activated;
127 int box1_activated;
128 int box2_activated;
129
130 win_activated = 0;
131 box1_activated = 0;
132 box2_activated = 0;
133
134 /* Our hierarchy looks like this:
135 *
136 * window win.action
137 * |
138 * box--------------------+
139 * | |
140 * box1 box1.action box2 box2.action;
141 * |
142 * button
143 */
144 window = gtk_window_new ();
145 box = gtk_box_new (orientation: GTK_ORIENTATION_HORIZONTAL, spacing: 0);
146 box1 = gtk_box_new (orientation: GTK_ORIENTATION_HORIZONTAL, spacing: 0);
147 box2 = gtk_box_new (orientation: GTK_ORIENTATION_HORIZONTAL, spacing: 0);
148 button = gtk_button_new ();
149
150 gtk_window_set_child (GTK_WINDOW (window), child: box);
151 gtk_box_append (GTK_BOX (box), child: box1);
152 gtk_box_append (GTK_BOX (box), child: box2);
153 gtk_box_append (GTK_BOX (box1), child: button);
154
155 win_actions = g_simple_action_group_new ();
156 g_action_map_add_action_entries (G_ACTION_MAP (win_actions),
157 entries, G_N_ELEMENTS (entries),
158 user_data: &win_activated);
159
160 box1_actions = g_simple_action_group_new ();
161 g_action_map_add_action_entries (G_ACTION_MAP (box1_actions),
162 entries, G_N_ELEMENTS (entries),
163 user_data: &box1_activated);
164
165 box2_actions = g_simple_action_group_new ();
166 g_action_map_add_action_entries (G_ACTION_MAP (box2_actions),
167 entries, G_N_ELEMENTS (entries),
168 user_data: &box2_activated);
169
170 gtk_widget_insert_action_group (widget: window, name: "win", G_ACTION_GROUP (win_actions));
171 gtk_widget_insert_action_group (widget: box1, name: "box1", G_ACTION_GROUP (box1_actions));
172 gtk_widget_insert_action_group (widget: box2, name: "box2", G_ACTION_GROUP (box2_actions));
173
174 g_assert_cmpint (win_activated, ==, 0);
175 g_assert_cmpint (box1_activated, ==, 0);
176 g_assert_cmpint (box2_activated, ==, 0);
177
178 found = gtk_widget_activate_action (widget: button, name: "win.action", NULL);
179
180 g_assert_true (found);
181 g_assert_cmpint (win_activated, ==, 1);
182 g_assert_cmpint (box1_activated, ==, 0);
183 g_assert_cmpint (box2_activated, ==, 0);
184
185 found = gtk_widget_activate_action (widget: button, name: "box1.action", NULL);
186
187 g_assert_true (found);
188 g_assert_cmpint (win_activated, ==, 1);
189 g_assert_cmpint (box1_activated, ==, 1);
190 g_assert_cmpint (box2_activated, ==, 0);
191
192 found = gtk_widget_activate_action (widget: button, name: "box2.action", NULL);
193
194 g_assert_false (found);
195 g_assert_cmpint (win_activated, ==, 1);
196 g_assert_cmpint (box1_activated, ==, 1);
197 g_assert_cmpint (box2_activated, ==, 0);
198
199 g_object_ref (button);
200 gtk_box_remove (GTK_BOX (box1), child: button);
201 gtk_box_append (GTK_BOX (box2), child: button);
202 g_object_unref (object: button);
203
204 found = gtk_widget_activate_action (widget: button, name: "win.action", NULL);
205
206 g_assert_true (found);
207 g_assert_cmpint (win_activated, ==, 2);
208 g_assert_cmpint (box1_activated, ==, 1);
209 g_assert_cmpint (box2_activated, ==, 0);
210
211 found = gtk_widget_activate_action (widget: button, name: "box1.action", NULL);
212
213 g_assert_false (found);
214 g_assert_cmpint (win_activated, ==, 2);
215 g_assert_cmpint (box1_activated, ==, 1);
216 g_assert_cmpint (box2_activated, ==, 0);
217
218 found = gtk_widget_activate_action (widget: button, name: "box2.action", NULL);
219
220 g_assert_true (found);
221 g_assert_cmpint (win_activated, ==, 2);
222 g_assert_cmpint (box1_activated, ==, 1);
223 g_assert_cmpint (box2_activated, ==, 1);
224
225 gtk_window_destroy (GTK_WINDOW (window));
226
227 g_object_unref (object: win_actions);
228 g_object_unref (object: box1_actions);
229 g_object_unref (object: box2_actions);
230}
231
232/* Similar to test_inheritance2, but using the actionable machinery
233 */
234static void
235test_inheritance3 (void)
236{
237 GtkWidget *window;
238 GtkWidget *box;
239 GtkWidget *box1;
240 GtkWidget *box2;
241 GtkWidget *button;
242 GSimpleActionGroup *win_actions;
243 GSimpleActionGroup *box1_actions;
244 GActionEntry entries[] = {
245 { "action", activate, NULL, NULL, NULL },
246 };
247 int activated;
248
249 /* Our hierarchy looks like this:
250 *
251 * window win.action
252 * |
253 * box--------------------+
254 * | |
255 * box1 box1.action box2
256 * |
257 * button
258 */
259 window = gtk_window_new ();
260 box = gtk_box_new (orientation: GTK_ORIENTATION_HORIZONTAL, spacing: 0);
261 box1 = gtk_box_new (orientation: GTK_ORIENTATION_HORIZONTAL, spacing: 0);
262 box2 = gtk_box_new (orientation: GTK_ORIENTATION_HORIZONTAL, spacing: 0);
263 button = gtk_button_new ();
264
265 gtk_window_set_child (GTK_WINDOW (window), child: box);
266 gtk_box_append (GTK_BOX (box), child: box1);
267 gtk_box_append (GTK_BOX (box), child: box2);
268 gtk_box_append (GTK_BOX (box1), child: button);
269
270 win_actions = g_simple_action_group_new ();
271 g_action_map_add_action_entries (G_ACTION_MAP (win_actions),
272 entries, G_N_ELEMENTS (entries),
273 user_data: &activated);
274
275 box1_actions = g_simple_action_group_new ();
276 g_action_map_add_action_entries (G_ACTION_MAP (box1_actions),
277 entries, G_N_ELEMENTS (entries),
278 user_data: &activated);
279
280 gtk_widget_insert_action_group (widget: window, name: "win", G_ACTION_GROUP (win_actions));
281 gtk_widget_insert_action_group (widget: box1, name: "box1", G_ACTION_GROUP (box1_actions));
282
283 gtk_actionable_set_action_name (GTK_ACTIONABLE (button), action_name: "box1.action");
284
285 g_assert_true (gtk_widget_get_sensitive (button));
286
287 g_object_ref (button);
288 gtk_box_remove (GTK_BOX (box1), child: button);
289 gtk_box_append (GTK_BOX (box2), child: button);
290 g_object_unref (object: button);
291
292 g_assert_false (gtk_widget_get_sensitive (button));
293
294 g_object_ref (button);
295 gtk_box_remove (GTK_BOX (box2), child: button);
296 gtk_box_append (GTK_BOX (box1), child: button);
297 g_object_unref (object: button);
298
299 g_assert_true (gtk_widget_get_sensitive (button));
300
301 g_object_ref (button);
302 gtk_box_remove (GTK_BOX (box1), child: button);
303 gtk_box_append (GTK_BOX (box2), child: button);
304 g_object_unref (object: button);
305
306 g_assert_false (gtk_widget_get_sensitive (button));
307
308 g_object_ref (box2);
309 gtk_box_remove (GTK_BOX (box), child: box2);
310 gtk_box_append (GTK_BOX (box1), child: box2);
311 g_object_unref (object: box2);
312
313 g_assert_true (gtk_widget_get_sensitive (button));
314
315 gtk_widget_insert_action_group (widget: box1, name: "box1", NULL);
316
317 g_assert_false (gtk_widget_get_sensitive (button));
318
319 gtk_widget_insert_action_group (widget: box1, name: "box1", G_ACTION_GROUP (box1_actions));
320
321 g_assert_true (gtk_widget_get_sensitive (button));
322
323 gtk_window_destroy (GTK_WINDOW (window));
324
325 g_object_unref (object: win_actions);
326 g_object_unref (object: box1_actions);
327}
328
329/* this checks a particular bug I've seen: when the action muxer
330 * hierarchy is already set up, adding action groups 'in the middle'
331 * does not properly update the muxer hierarchy, causing actions
332 * to be missed.
333 */
334static void
335test_inheritance4 (void)
336{
337 GtkWidget *window;
338 GtkWidget *box;
339 GtkWidget *button;
340 GSimpleActionGroup *win_actions;
341 GSimpleActionGroup *box_actions;
342 GActionEntry entries[] = {
343 { "action", activate, NULL, NULL, NULL },
344 };
345 int activated;
346
347 /* Our hierarchy looks like this:
348 *
349 * window win.action
350 * |
351 * box
352 * |
353 * button
354 */
355 window = gtk_window_new ();
356 box = gtk_box_new (orientation: GTK_ORIENTATION_HORIZONTAL, spacing: 0);
357 button = gtk_button_new ();
358
359 gtk_window_set_child (GTK_WINDOW (window), child: box);
360 gtk_box_append (GTK_BOX (box), child: button);
361
362 win_actions = g_simple_action_group_new ();
363 g_action_map_add_action_entries (G_ACTION_MAP (win_actions),
364 entries, G_N_ELEMENTS (entries),
365 user_data: &activated);
366
367 gtk_widget_insert_action_group (widget: window, name: "win", G_ACTION_GROUP (win_actions));
368
369 gtk_actionable_set_action_name (GTK_ACTIONABLE (button), action_name: "box.action");
370
371 /* no box1.action yet, but the action muxers are set up, with windows' muxer
372 * being the parent of button's, since box has no muxer yet.
373 */
374 g_assert_false (gtk_widget_get_sensitive (button));
375
376 box_actions = g_simple_action_group_new ();
377 g_action_map_add_action_entries (G_ACTION_MAP (box_actions),
378 entries, G_N_ELEMENTS (entries),
379 user_data: &activated);
380
381 gtk_widget_insert_action_group (widget: box, name: "box", G_ACTION_GROUP (box_actions));
382
383 /* now box has a muxer, and buttons muxer should be updated to inherit
384 * from it
385 */
386 g_assert_true (gtk_widget_get_sensitive (button));
387
388 gtk_window_destroy (GTK_WINDOW (window));
389
390 g_object_unref (object: win_actions);
391 g_object_unref (object: box_actions);
392}
393
394static int cut_activated;
395static int copy_activated;
396static int paste_activated;
397static int visibility_changed;
398
399static void
400cut_activate (GSimpleAction *action,
401 GVariant *parameter,
402 gpointer user_data)
403{
404 cut_activated++;
405}
406
407static void
408copy_activate (GSimpleAction *action,
409 GVariant *parameter,
410 gpointer user_data)
411{
412 copy_activated++;
413}
414
415static void
416paste_activate (GSimpleAction *action,
417 GVariant *parameter,
418 gpointer user_data)
419{
420 paste_activated++;
421}
422
423static void
424visibility_changed_cb (GObject *object,
425 GParamSpec *pspec,
426 gpointer user_data)
427{
428 visibility_changed++;
429}
430
431/* Spot-check that GtkText has the class actions
432 * for the context menu. Here we test that the clipboard
433 * actions are present, and that toggling visibility
434 * via the action works.
435 */
436static void
437test_text (void)
438{
439 GtkWidget *box;
440 GtkWidget *text;
441 GSimpleActionGroup *clipboard_actions;
442 GActionEntry clipboard_entries[] = {
443 { "cut", cut_activate, NULL, NULL, NULL },
444 { "copy", copy_activate, NULL, NULL, NULL },
445 { "paste", paste_activate, NULL, NULL, NULL },
446 };
447 box = gtk_box_new (orientation: GTK_ORIENTATION_HORIZONTAL, spacing: 0);
448 text = gtk_text_new ();
449
450 gtk_box_append (GTK_BOX (box), child: text);
451
452 clipboard_actions = g_simple_action_group_new ();
453 g_action_map_add_action_entries (G_ACTION_MAP (clipboard_actions),
454 entries: clipboard_entries,
455 G_N_ELEMENTS (clipboard_entries),
456 NULL);
457
458 gtk_widget_insert_action_group (widget: box, name: "clipboard", G_ACTION_GROUP (clipboard_actions));
459
460 gtk_widget_activate_action (widget: text, name: "cut.clipboard", NULL);
461 gtk_widget_activate_action (widget: text, name: "copy.clipboard", NULL);
462 gtk_widget_activate_action (widget: text, name: "paste.clipboard", NULL);
463
464 g_assert_cmpint (cut_activated, ==, 0);
465 g_assert_cmpint (copy_activated, ==, 0);
466 g_assert_cmpint (paste_activated, ==, 0);
467
468 g_signal_connect (text, "notify::visibility",
469 G_CALLBACK (visibility_changed_cb), NULL);
470
471 gtk_widget_activate_action (widget: text, name: "misc.toggle-visibility", NULL);
472
473 g_assert_cmpint (visibility_changed, ==, 1);
474
475 g_object_unref (g_object_ref_sink (box));
476 g_object_unref (object: clipboard_actions);
477}
478
479/* Test that inheritance works for individual actions
480 * even if they are in groups with the same prefix.
481 * This is a change from the way things work in GTK3.
482 */
483static void
484test_overlap (void)
485{
486 GtkWidget *window;
487 GtkWidget *box;
488 GActionEntry win_entries[] = {
489 { "win", activate, NULL, NULL, NULL },
490 };
491 GActionEntry box_entries[] = {
492 { "box", activate, NULL, NULL, NULL },
493 };
494 GSimpleActionGroup *win_actions;
495 GSimpleActionGroup *box_actions;
496 int win_activated;
497 int box_activated;
498
499 window = gtk_window_new ();
500 box = gtk_box_new (orientation: GTK_ORIENTATION_HORIZONTAL, spacing: 0);
501
502 gtk_window_set_child (GTK_WINDOW (window), child: box);
503
504 win_actions = g_simple_action_group_new ();
505 g_action_map_add_action_entries (G_ACTION_MAP (win_actions),
506 entries: win_entries,
507 G_N_ELEMENTS (win_entries),
508 user_data: &win_activated);
509
510 box_actions = g_simple_action_group_new ();
511 g_action_map_add_action_entries (G_ACTION_MAP (box_actions),
512 entries: box_entries,
513 G_N_ELEMENTS (box_entries),
514 user_data: &box_activated);
515
516 gtk_widget_insert_action_group (widget: window, name: "actions", G_ACTION_GROUP (win_actions));
517 gtk_widget_insert_action_group (widget: box, name: "actions", G_ACTION_GROUP (box_actions));
518
519 win_activated = 0;
520 box_activated = 0;
521
522 gtk_widget_activate_action (widget: box, name: "actions.win", NULL);
523
524 g_assert_cmpint (win_activated, ==, 1);
525 g_assert_cmpint (box_activated, ==, 0);
526
527 gtk_widget_activate_action (widget: box, name: "actions.box", NULL);
528
529 g_assert_cmpint (win_activated, ==, 1);
530 g_assert_cmpint (box_activated, ==, 1);
531
532 gtk_window_destroy (GTK_WINDOW (window));
533 g_object_unref (object: win_actions);
534 g_object_unref (object: box_actions);
535}
536
537static int toggled;
538static int act1;
539static int act2;
540
541static void
542visibility_toggled (GObject *object,
543 GParamSpec *pspec,
544 gpointer data)
545{
546 toggled++;
547}
548
549static void
550activate1 (GSimpleAction *action,
551 GVariant *parameter,
552 gpointer user_data)
553{
554 act1++;
555}
556
557static void
558activate2 (GSimpleAction *action,
559 GVariant *parameter,
560 gpointer user_data)
561{
562 act2++;
563}
564
565/* Test that overlap also works as expected between
566 * class action and inserted groups. Class actions
567 * take precedence over inserted groups in the same
568 * muxer, but inheritance works as normal between
569 * muxers.
570 */
571static void
572test_overlap2 (void)
573{
574 GtkWidget *text;
575 GtkWidget *child;
576 GSimpleActionGroup *group1;
577 GSimpleActionGroup *group2;
578 GActionEntry entries1[] = {
579 { "toggle-visibility", activate1, NULL, NULL, NULL },
580 };
581 GActionEntry entries2[] = {
582 { "toggle-visibility", activate2, NULL, NULL, NULL },
583 };
584
585 text = gtk_text_new ();
586 g_signal_connect (text, "notify::visibility",
587 G_CALLBACK (visibility_toggled), NULL);
588
589 child = gtk_label_new (str: "");
590 gtk_widget_set_parent (widget: child, parent: text);
591
592 g_assert_cmpint (toggled, ==, 0);
593 g_assert_cmpint (act1, ==, 0);
594 g_assert_cmpint (act2, ==, 0);
595
596 gtk_widget_activate_action (widget: child, name: "misc.toggle-visibility", NULL);
597
598 g_assert_cmpint (toggled, ==, 1);
599 g_assert_cmpint (act1, ==, 0);
600 g_assert_cmpint (act2, ==, 0);
601
602 group1 = g_simple_action_group_new ();
603 g_action_map_add_action_entries (G_ACTION_MAP (group1),
604 entries: entries1,
605 G_N_ELEMENTS (entries1),
606 NULL);
607 gtk_widget_insert_action_group (widget: text, name: "misc", G_ACTION_GROUP (group1));
608 gtk_widget_activate_action (widget: child, name: "misc.toggle-visibility", NULL);
609
610 g_assert_cmpint (toggled, ==, 2);
611 g_assert_cmpint (act1, ==, 0);
612 g_assert_cmpint (act2, ==, 0);
613
614 group2 = g_simple_action_group_new ();
615 g_action_map_add_action_entries (G_ACTION_MAP (group2),
616 entries: entries2,
617 G_N_ELEMENTS (entries2),
618 NULL);
619 gtk_widget_insert_action_group (widget: child, name: "misc", G_ACTION_GROUP (group2));
620
621 gtk_widget_activate_action (widget: child, name: "misc.toggle-visibility", NULL);
622
623 g_assert_cmpint (toggled, ==, 2);
624 g_assert_cmpint (act1, ==, 0);
625 g_assert_cmpint (act2, ==, 1);
626
627 g_object_unref (object: group1);
628 g_object_unref (object: group2);
629
630 gtk_widget_unparent (widget: child);
631 g_object_unref (g_object_ref_sink (text));
632}
633
634/* Test that gtk_widget_class_query_action
635 * yields the expected results
636 */
637static void
638test_introspection (void)
639{
640 GtkWidgetClass *class = g_type_class_ref (GTK_TYPE_TEXT);
641 guint i, j;
642 guint found;
643 GType owner;
644 const char *name;
645 const GVariantType *params;
646 const char *property;
647 struct {
648 GType owner;
649 const char *name;
650 const char *params;
651 const char *property;
652 } expected[] = {
653 { GTK_TYPE_TEXT, "misc.toggle-visibility", NULL, "visibility" },
654 { GTK_TYPE_TEXT, "misc.insert-emoji", NULL, NULL },
655 { GTK_TYPE_TEXT, "selection.select-all", NULL, NULL },
656 { GTK_TYPE_TEXT, "selection.delete", NULL, NULL },
657 { GTK_TYPE_TEXT, "clipboard.paste", NULL, NULL },
658 { GTK_TYPE_TEXT, "clipboard.copy", NULL, NULL },
659 { GTK_TYPE_TEXT, "clipboard.cut", NULL, NULL },
660 { GTK_TYPE_TEXT, "menu.popup", NULL, NULL },
661 { GTK_TYPE_TEXT, "text.redo", NULL, NULL },
662 { GTK_TYPE_TEXT, "text.undo", NULL, NULL },
663 };
664
665 j = 0;
666 found = 0;
667 while (gtk_widget_class_query_action (widget_class: class,
668 index_: j,
669 owner: &owner,
670 action_name: &name,
671 parameter_type: &params,
672 property_name: &property))
673 {
674 for (i = 0; i < G_N_ELEMENTS (expected); i++)
675 {
676 if (strcmp (s1: expected[i].name, s2: name) == 0)
677 {
678 found++;
679 g_assert_true (expected[i].owner == owner);
680 g_assert_cmpstr (expected[i].name, ==, name);
681 g_assert_cmpstr (expected[i].params, ==, params ? g_variant_type_peek_string (params) : NULL);
682 g_assert_cmpstr (expected[i].property, ==, property);
683 break;
684 }
685 }
686 if (i == G_N_ELEMENTS (expected))
687 g_error ("Unexpected GtkText action: %s", name);
688 j++;
689 }
690 g_assert_cmpuint (found, ==, G_N_ELEMENTS (expected));
691
692 g_type_class_unref (g_class: class);
693}
694
695/* Test that disabled actions don't get activated */
696static void
697test_enabled (void)
698{
699 GtkWidget *text;
700
701 text = gtk_text_new ();
702 g_signal_connect (text, "notify::visibility",
703 G_CALLBACK (visibility_toggled), NULL);
704
705 toggled = 0;
706
707 gtk_widget_activate_action (widget: text, name: "misc.toggle-visibility", NULL);
708
709 g_assert_cmpint (toggled, ==, 1);
710
711 gtk_widget_action_set_enabled (widget: text, action_name: "misc.toggle-visibility", FALSE);
712
713 gtk_widget_activate_action (widget: text, name: "misc.toggle-visibility", NULL);
714
715 g_assert_cmpint (toggled, ==, 1);
716
717 g_object_unref (g_object_ref_sink (text));
718}
719
720int
721main (int argc,
722 char *argv[])
723{
724 gtk_test_init (argcp: &argc, argvp: &argv);
725
726 g_test_add_func (testpath: "/action/inheritance", test_func: test_inheritance);
727 g_test_add_func (testpath: "/action/inheritance2", test_func: test_inheritance2);
728 g_test_add_func (testpath: "/action/inheritance3", test_func: test_inheritance3);
729 g_test_add_func (testpath: "/action/inheritance4", test_func: test_inheritance4);
730 g_test_add_func (testpath: "/action/text", test_func: test_text);
731 g_test_add_func (testpath: "/action/overlap", test_func: test_overlap);
732 g_test_add_func (testpath: "/action/overlap2", test_func: test_overlap2);
733 g_test_add_func (testpath: "/action/introspection", test_func: test_introspection);
734 g_test_add_func (testpath: "/action/enabled", test_func: test_enabled);
735
736 return g_test_run();
737}
738

source code of gtk/testsuite/gtk/action.c