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 |
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 |
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 (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 */ |
111 | static 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 (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 | */ |
234 | static 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 (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 | */ |
334 | static 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 (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 | |
394 | static int cut_activated; |
395 | static int copy_activated; |
396 | static int paste_activated; |
397 | static int visibility_changed; |
398 | |
399 | static void |
400 | cut_activate (GSimpleAction *action, |
401 | GVariant *parameter, |
402 | gpointer user_data) |
403 | { |
404 | cut_activated++; |
405 | } |
406 | |
407 | static void |
408 | copy_activate (GSimpleAction *action, |
409 | GVariant *parameter, |
410 | gpointer user_data) |
411 | { |
412 | copy_activated++; |
413 | } |
414 | |
415 | static void |
416 | paste_activate (GSimpleAction *action, |
417 | GVariant *parameter, |
418 | gpointer user_data) |
419 | { |
420 | paste_activated++; |
421 | } |
422 | |
423 | static void |
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 |
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 (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 | */ |
483 | static 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 (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 | |
537 | static int toggled; |
538 | static int act1; |
539 | static int act2; |
540 | |
541 | static void |
542 | visibility_toggled (GObject *object, |
543 | GParamSpec *pspec, |
544 | gpointer data) |
545 | { |
546 | toggled++; |
547 | } |
548 | |
549 | static void |
550 | activate1 (GSimpleAction *action, |
551 | GVariant *parameter, |
552 | gpointer user_data) |
553 | { |
554 | act1++; |
555 | } |
556 | |
557 | static void |
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 |
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 (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 | */ |
637 | static 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 (widget_class: class, |
668 | index_: j, |
669 | owner: &owner, |
670 | action_name: &name, |
671 | parameter_type: ¶ms, |
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 */ |
696 | static 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 (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 | |
720 | int |
721 | main (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 | |