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 
18 static void
activate(GSimpleAction * action,GVariant * parameter,gpointer user_data)19 activate (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  */
32 static void
test_inheritance(void)33 test_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 (GTK_ORIENTATION_HORIZONTAL, 0);
60   button = gtk_button_new ();
61 
62   gtk_window_set_child (GTK_WINDOW (window), box);
63   gtk_box_append (GTK_BOX (box), 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                                    &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                                    &box_activated);
74 
75   gtk_widget_insert_action_group (window, "win", G_ACTION_GROUP (win_actions));
76   gtk_widget_insert_action_group (box, "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 (button, "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 (box, "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 (button, "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 (window, "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 (win_actions);
107   g_object_unref (box_actions);
108 }
109 
110 /* Test action inheritance with hierarchy changes */
111 static void
test_inheritance2(void)112 test_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 (GTK_ORIENTATION_HORIZONTAL, 0);
146   box1 = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
147   box2 = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
148   button = gtk_button_new ();
149 
150   gtk_window_set_child (GTK_WINDOW (window), box);
151   gtk_box_append (GTK_BOX (box), box1);
152   gtk_box_append (GTK_BOX (box), box2);
153   gtk_box_append (GTK_BOX (box1), 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                                    &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                                    &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                                    &box2_activated);
169 
170   gtk_widget_insert_action_group (window, "win", G_ACTION_GROUP (win_actions));
171   gtk_widget_insert_action_group (box1, "box1", G_ACTION_GROUP (box1_actions));
172   gtk_widget_insert_action_group (box2, "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 (button, "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 (button, "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 (button, "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), button);
201   gtk_box_append (GTK_BOX (box2), button);
202   g_object_unref (button);
203 
204   found = gtk_widget_activate_action (button, "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 (button, "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 (button, "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 (win_actions);
228   g_object_unref (box1_actions);
229   g_object_unref (box2_actions);
230 }
231 
232 /* Similar to test_inheritance2, but using the actionable machinery
233  */
234 static void
test_inheritance3(void)235 test_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 (GTK_ORIENTATION_HORIZONTAL, 0);
261   box1 = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
262   box2 = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
263   button = gtk_button_new ();
264 
265   gtk_window_set_child (GTK_WINDOW (window), box);
266   gtk_box_append (GTK_BOX (box), box1);
267   gtk_box_append (GTK_BOX (box), box2);
268   gtk_box_append (GTK_BOX (box1), 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                                    &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                                    &activated);
279 
280   gtk_widget_insert_action_group (window, "win", G_ACTION_GROUP (win_actions));
281   gtk_widget_insert_action_group (box1, "box1", G_ACTION_GROUP (box1_actions));
282 
283   gtk_actionable_set_action_name (GTK_ACTIONABLE (button), "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), button);
289   gtk_box_append (GTK_BOX (box2), button);
290   g_object_unref (button);
291 
292   g_assert_false (gtk_widget_get_sensitive (button));
293 
294   g_object_ref (button);
295   gtk_box_remove (GTK_BOX (box2), button);
296   gtk_box_append (GTK_BOX (box1), button);
297   g_object_unref (button);
298 
299   g_assert_true (gtk_widget_get_sensitive (button));
300 
301   g_object_ref (button);
302   gtk_box_remove (GTK_BOX (box1), button);
303   gtk_box_append (GTK_BOX (box2), button);
304   g_object_unref (button);
305 
306   g_assert_false (gtk_widget_get_sensitive (button));
307 
308   g_object_ref (box2);
309   gtk_box_remove (GTK_BOX (box), box2);
310   gtk_box_append (GTK_BOX (box1), box2);
311   g_object_unref (box2);
312 
313   g_assert_true (gtk_widget_get_sensitive (button));
314 
315   gtk_widget_insert_action_group (box1, "box1", NULL);
316 
317   g_assert_false (gtk_widget_get_sensitive (button));
318 
319   gtk_widget_insert_action_group (box1, "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 (win_actions);
326   g_object_unref (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  */
334 static void
test_inheritance4(void)335 test_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 (GTK_ORIENTATION_HORIZONTAL, 0);
357   button = gtk_button_new ();
358 
359   gtk_window_set_child (GTK_WINDOW (window), box);
360   gtk_box_append (GTK_BOX (box), 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                                    &activated);
366 
367   gtk_widget_insert_action_group (window, "win", G_ACTION_GROUP (win_actions));
368 
369   gtk_actionable_set_action_name (GTK_ACTIONABLE (button), "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                                    &activated);
380 
381   gtk_widget_insert_action_group (box, "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 (win_actions);
391   g_object_unref (box_actions);
392 }
393 
394 static int cut_activated;
395 static int copy_activated;
396 static int paste_activated;
397 static int visibility_changed;
398 
399 static void
cut_activate(GSimpleAction * action,GVariant * parameter,gpointer user_data)400 cut_activate (GSimpleAction *action,
401               GVariant      *parameter,
402               gpointer       user_data)
403 {
404   cut_activated++;
405 }
406 
407 static void
copy_activate(GSimpleAction * action,GVariant * parameter,gpointer user_data)408 copy_activate (GSimpleAction *action,
409                GVariant      *parameter,
410                gpointer       user_data)
411 {
412   copy_activated++;
413 }
414 
415 static void
paste_activate(GSimpleAction * action,GVariant * parameter,gpointer user_data)416 paste_activate (GSimpleAction *action,
417                 GVariant      *parameter,
418                 gpointer       user_data)
419 {
420   paste_activated++;
421 }
422 
423 static void
visibility_changed_cb(GObject * object,GParamSpec * pspec,gpointer user_data)424 visibility_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  */
436 static void
test_text(void)437 test_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 (GTK_ORIENTATION_HORIZONTAL, 0);
448   text = gtk_text_new ();
449 
450   gtk_box_append (GTK_BOX (box), text);
451 
452   clipboard_actions = g_simple_action_group_new ();
453   g_action_map_add_action_entries (G_ACTION_MAP (clipboard_actions),
454                                    clipboard_entries,
455                                    G_N_ELEMENTS (clipboard_entries),
456                                    NULL);
457 
458   gtk_widget_insert_action_group (box, "clipboard", G_ACTION_GROUP (clipboard_actions));
459 
460   gtk_widget_activate_action (text, "cut.clipboard", NULL);
461   gtk_widget_activate_action (text, "copy.clipboard", NULL);
462   gtk_widget_activate_action (text, "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 (text, "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 (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  */
483 static void
test_overlap(void)484 test_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 (GTK_ORIENTATION_HORIZONTAL, 0);
501 
502   gtk_window_set_child (GTK_WINDOW (window), box);
503 
504   win_actions = g_simple_action_group_new ();
505   g_action_map_add_action_entries (G_ACTION_MAP (win_actions),
506                                    win_entries,
507                                    G_N_ELEMENTS (win_entries),
508                                    &win_activated);
509 
510   box_actions = g_simple_action_group_new ();
511   g_action_map_add_action_entries (G_ACTION_MAP (box_actions),
512                                    box_entries,
513                                    G_N_ELEMENTS (box_entries),
514                                    &box_activated);
515 
516   gtk_widget_insert_action_group (window, "actions", G_ACTION_GROUP (win_actions));
517   gtk_widget_insert_action_group (box, "actions", G_ACTION_GROUP (box_actions));
518 
519   win_activated = 0;
520   box_activated = 0;
521 
522   gtk_widget_activate_action (box, "actions.win", NULL);
523 
524   g_assert_cmpint (win_activated, ==, 1);
525   g_assert_cmpint (box_activated, ==, 0);
526 
527   gtk_widget_activate_action (box, "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 (win_actions);
534   g_object_unref (box_actions);
535 }
536 
537 static int toggled;
538 static int act1;
539 static int act2;
540 
541 static void
visibility_toggled(GObject * object,GParamSpec * pspec,gpointer data)542 visibility_toggled (GObject *object,
543                     GParamSpec *pspec,
544                     gpointer data)
545 {
546   toggled++;
547 }
548 
549 static void
activate1(GSimpleAction * action,GVariant * parameter,gpointer user_data)550 activate1 (GSimpleAction *action,
551            GVariant      *parameter,
552            gpointer       user_data)
553 {
554   act1++;
555 }
556 
557 static void
activate2(GSimpleAction * action,GVariant * parameter,gpointer user_data)558 activate2 (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  */
571 static void
test_overlap2(void)572 test_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 ("");
590   gtk_widget_set_parent (child, 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 (child, "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                                    entries1,
605                                    G_N_ELEMENTS (entries1),
606                                    NULL);
607   gtk_widget_insert_action_group (text, "misc", G_ACTION_GROUP (group1));
608   gtk_widget_activate_action (child, "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                                    entries2,
617                                    G_N_ELEMENTS (entries2),
618                                    NULL);
619   gtk_widget_insert_action_group (child, "misc", G_ACTION_GROUP (group2));
620 
621   gtk_widget_activate_action (child, "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 (group1);
628   g_object_unref (group2);
629 
630   gtk_widget_unparent (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  */
637 static void
test_introspection(void)638 test_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 (class,
668                                         j,
669                                         &owner,
670                                         &name,
671                                         &params,
672                                         &property))
673     {
674       for (i = 0; i < G_N_ELEMENTS (expected); i++)
675         {
676           if (strcmp (expected[i].name, 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 (class);
693 }
694 
695 /* Test that disabled actions don't get activated */
696 static void
test_enabled(void)697 test_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 (text, "misc.toggle-visibility", NULL);
708 
709   g_assert_cmpint (toggled, ==, 1);
710 
711   gtk_widget_action_set_enabled (text, "misc.toggle-visibility", FALSE);
712 
713   gtk_widget_activate_action (text, "misc.toggle-visibility", NULL);
714 
715   g_assert_cmpint (toggled, ==, 1);
716 
717   g_object_unref (g_object_ref_sink (text));
718 }
719 
720 int
main(int argc,char * argv[])721 main (int   argc,
722       char *argv[])
723 {
724   gtk_test_init (&argc, &argv);
725 
726   g_test_add_func ("/action/inheritance", test_inheritance);
727   g_test_add_func ("/action/inheritance2", test_inheritance2);
728   g_test_add_func ("/action/inheritance3", test_inheritance3);
729   g_test_add_func ("/action/inheritance4", test_inheritance4);
730   g_test_add_func ("/action/text", test_text);
731   g_test_add_func ("/action/overlap", test_overlap);
732   g_test_add_func ("/action/overlap2", test_overlap2);
733   g_test_add_func ("/action/introspection", test_introspection);
734   g_test_add_func ("/action/enabled", test_enabled);
735 
736   return g_test_run();
737 }
738