1/* gtkatspiselection.c: AT-SPI Selection implementation
2 *
3 * Copyright 2020 Red Hat, Inc.
4 *
5 * SPDX-License-Identifier: LGPL-2.1-or-later
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
19 */
20
21#include "config.h"
22
23#include "gtkatspiselectionprivate.h"
24
25#include "a11y/atspi/atspi-selection.h"
26
27#include "gtkatcontextprivate.h"
28#include "gtkatspicontextprivate.h"
29#include "gtkaccessibleprivate.h"
30#include "gtkdebug.h"
31#include "gtklistbase.h"
32#include "gtklistbox.h"
33#include "gtkflowbox.h"
34#include "gtkcombobox.h"
35#include "gtkstackswitcher.h"
36#include "gtknotebook.h"
37#include "gtklistview.h"
38#include "gtkgridview.h"
39#include "gtklistitem.h"
40#include "gtkbitset.h"
41#include "gtklistbaseprivate.h"
42#include "gtklistitemwidgetprivate.h"
43
44#include <gio/gio.h>
45
46typedef struct {
47 int n;
48 GtkWidget *child;
49} Counter;
50
51static void
52find_nth (GtkWidget *box,
53 GtkWidget *child,
54 gpointer data)
55{
56 Counter *counter = data;
57
58 if (counter->n == 0)
59 counter->child = child;
60
61 counter->n--;
62}
63
64/* {{{ GtkListbox */
65
66static void
67listbox_handle_method (GDBusConnection *connection,
68 const gchar *sender,
69 const gchar *object_path,
70 const gchar *interface_name,
71 const gchar *method_name,
72 GVariant *parameters,
73 GDBusMethodInvocation *invocation,
74 gpointer user_data)
75{
76 GtkATContext *self = user_data;
77 GtkAccessible *accessible = gtk_at_context_get_accessible (self);
78 GtkWidget *widget = GTK_WIDGET (accessible);
79
80 if (g_strcmp0 (str1: method_name, str2: "GetSelectedChild") == 0)
81 {
82 Counter counter;
83 int idx;
84
85 g_variant_get (value: parameters, format_string: "(i)", &idx);
86
87 counter.n = idx;
88 counter.child = NULL;
89
90 gtk_list_box_selected_foreach (GTK_LIST_BOX (widget), func: (GtkListBoxForeachFunc)find_nth, data: &counter);
91
92 if (counter.child == NULL)
93 g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, code: G_DBUS_ERROR_INVALID_ARGS, format: "No selected child for %d", idx);
94 else
95 {
96 GtkATContext *ctx = gtk_accessible_get_at_context (self: GTK_ACCESSIBLE (ptr: counter.child));
97 g_dbus_method_invocation_return_value (invocation, parameters: g_variant_new (format_string: "(@(so))", gtk_at_spi_context_to_ref (self: GTK_AT_SPI_CONTEXT (ptr: ctx))));
98 }
99 }
100 else if (g_strcmp0 (str1: method_name, str2: "SelectChild") == 0)
101 {
102 int idx;
103 GtkListBoxRow *row;
104
105 g_variant_get (value: parameters, format_string: "(i)", &idx);
106
107 row = gtk_list_box_get_row_at_index (GTK_LIST_BOX (widget), index_: idx);
108 if (!row)
109 g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, code: G_DBUS_ERROR_INVALID_ARGS, format: "No child at position %d", idx);
110 else
111 {
112 gboolean ret;
113
114 gtk_list_box_select_row (GTK_LIST_BOX (widget), row);
115 ret = gtk_list_box_row_is_selected (row);
116 g_dbus_method_invocation_return_value (invocation, parameters: g_variant_new (format_string: "(b)", ret));
117 }
118 }
119 else if (g_strcmp0 (str1: method_name, str2: "DeselectChild") == 0)
120 {
121 int idx;
122 GtkListBoxRow *row;
123
124 g_variant_get (value: parameters, format_string: "(i)", &idx);
125
126 row = gtk_list_box_get_row_at_index (GTK_LIST_BOX (widget), index_: idx);
127 if (!row)
128 g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, code: G_DBUS_ERROR_INVALID_ARGS, format: "No child at position %d", idx);
129 else
130 {
131 gboolean ret;
132
133 gtk_list_box_unselect_row (GTK_LIST_BOX (widget), row);
134 ret = !gtk_list_box_row_is_selected (row);
135 g_dbus_method_invocation_return_value (invocation, parameters: g_variant_new (format_string: "(b)", ret));
136 }
137 }
138 else if (g_strcmp0 (str1: method_name, str2: "DeselectSelectedChild") == 0)
139 {
140 Counter counter;
141 int idx;
142
143 g_variant_get (value: parameters, format_string: "(i)", &idx);
144
145 counter.n = idx;
146 counter.child = NULL;
147
148 gtk_list_box_selected_foreach (GTK_LIST_BOX (widget), func: (GtkListBoxForeachFunc)find_nth, data: &counter);
149
150 if (counter.child == NULL)
151 g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, code: G_DBUS_ERROR_INVALID_ARGS, format: "No selected child for %d", idx);
152 else
153 {
154 gboolean ret;
155
156 gtk_list_box_unselect_row (GTK_LIST_BOX (widget), GTK_LIST_BOX_ROW (counter.child));
157 ret = !gtk_list_box_row_is_selected (GTK_LIST_BOX_ROW (counter.child));
158 g_dbus_method_invocation_return_value (invocation, parameters: g_variant_new (format_string: "(b)", ret));
159 }
160 }
161 else if (g_strcmp0 (str1: method_name, str2: "IsChildSelected") == 0)
162 {
163 int idx;
164 GtkListBoxRow *row;
165
166 g_variant_get (value: parameters, format_string: "(i)", &idx);
167
168 row = gtk_list_box_get_row_at_index (GTK_LIST_BOX (widget), index_: idx);
169 if (!row)
170 g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, code: G_DBUS_ERROR_INVALID_ARGS, format: "No child at position %d", idx);
171 else
172 g_dbus_method_invocation_return_value (invocation, parameters: g_variant_new (format_string: "(b)", gtk_list_box_row_is_selected (row)));
173 }
174 else if (g_strcmp0 (str1: method_name, str2: "SelectAll") == 0)
175 {
176 gtk_list_box_select_all (GTK_LIST_BOX (widget));
177 g_dbus_method_invocation_return_value (invocation, parameters: g_variant_new (format_string: "(b)", TRUE));
178 }
179 else if (g_strcmp0 (str1: method_name, str2: "ClearSelection") == 0)
180 {
181 gtk_list_box_unselect_all (GTK_LIST_BOX (widget));
182 g_dbus_method_invocation_return_value (invocation, parameters: g_variant_new (format_string: "(b)", TRUE));
183 }
184}
185
186static void
187count_selected (GtkWidget *box,
188 GtkWidget *child,
189 gpointer data)
190{
191 *(int *)data += 1;
192}
193
194static GVariant *
195listbox_get_property (GDBusConnection *connection,
196 const gchar *sender,
197 const gchar *object_path,
198 const gchar *interface_name,
199 const gchar *property_name,
200 GError **error,
201 gpointer user_data)
202{
203 GtkATContext *self = GTK_AT_CONTEXT (ptr: user_data);
204 GtkAccessible *accessible = gtk_at_context_get_accessible (self);
205 GtkWidget *widget = GTK_WIDGET (accessible);
206
207 if (g_strcmp0 (str1: property_name, str2: "NSelectedChildren") == 0)
208 {
209 int count = 0;
210
211 gtk_list_box_selected_foreach (GTK_LIST_BOX (widget), func: (GtkListBoxForeachFunc)count_selected, data: &count);
212
213 return g_variant_new_int32 (value: count);
214 }
215
216 return NULL;
217}
218
219static const GDBusInterfaceVTable listbox_vtable = {
220 listbox_handle_method,
221 listbox_get_property,
222 NULL
223};
224
225/* }}} */
226/* {{{ GtkListView */
227
228static void
229listview_handle_method (GDBusConnection *connection,
230 const gchar *sender,
231 const gchar *object_path,
232 const gchar *interface_name,
233 const gchar *method_name,
234 GVariant *parameters,
235 GDBusMethodInvocation *invocation,
236 gpointer user_data)
237{
238 GtkATContext *self = user_data;
239 GtkAccessible *accessible = gtk_at_context_get_accessible (self);
240 GtkWidget *widget = GTK_WIDGET (accessible);
241 GtkSelectionModel *model = gtk_list_base_get_model (GTK_LIST_BASE (widget));
242
243 if (g_strcmp0 (str1: method_name, str2: "GetSelectedChild") == 0)
244 {
245 int idx;
246 GtkWidget *child;
247
248 g_variant_get (value: parameters, format_string: "(i)", &idx);
249
250 /* We are asked for the idx-the selected child *among the
251 * current children*
252 */
253 for (child = gtk_widget_get_first_child (widget);
254 child;
255 child = gtk_widget_get_next_sibling (widget: child))
256 {
257 if (gtk_list_item_widget_get_selected (GTK_LIST_ITEM_WIDGET (child)))
258 {
259 if (idx == 0)
260 break;
261 idx--;
262 }
263 }
264
265 if (child == NULL)
266 g_dbus_method_invocation_return_error (invocation,
267 G_DBUS_ERROR,
268 code: G_DBUS_ERROR_INVALID_ARGS,
269 format: "No selected child for %d", idx);
270 else
271 {
272 GtkATContext *ctx = gtk_accessible_get_at_context (self: GTK_ACCESSIBLE (ptr: child));
273 g_dbus_method_invocation_return_value (invocation,
274 parameters: g_variant_new (format_string: "(@(so))", gtk_at_spi_context_to_ref (self: GTK_AT_SPI_CONTEXT (ptr: ctx))));
275 }
276 }
277 else if (g_strcmp0 (str1: method_name, str2: "SelectChild") == 0)
278 {
279 int idx;
280 GtkWidget *child;
281
282 g_variant_get (value: parameters, format_string: "(i)", &idx);
283
284 for (child = gtk_widget_get_first_child (widget);
285 child;
286 child = gtk_widget_get_next_sibling (widget: child))
287 {
288 if (idx == 0)
289 break;
290 idx--;
291 }
292
293 if (child == NULL)
294 g_dbus_method_invocation_return_error (invocation,
295 G_DBUS_ERROR,
296 code: G_DBUS_ERROR_INVALID_ARGS,
297 format: "No child for %d", idx);
298 else
299 {
300 guint pos;
301 gboolean ret;
302
303 pos = gtk_list_item_widget_get_position (GTK_LIST_ITEM_WIDGET (child));
304 ret = gtk_selection_model_select_item (model, position: pos, FALSE);
305
306 g_dbus_method_invocation_return_value (invocation, parameters: g_variant_new (format_string: "(b)", ret));
307 }
308 }
309 else if (g_strcmp0 (str1: method_name, str2: "DeselectChild") == 0)
310 {
311 int idx;
312 GtkWidget *child;
313
314 g_variant_get (value: parameters, format_string: "(i)", &idx);
315
316 for (child = gtk_widget_get_first_child (widget);
317 child;
318 child = gtk_widget_get_next_sibling (widget: child))
319 {
320 if (idx == 0)
321 break;
322 idx--;
323 }
324
325 if (child == NULL)
326 g_dbus_method_invocation_return_error (invocation,
327 G_DBUS_ERROR,
328 code: G_DBUS_ERROR_INVALID_ARGS,
329 format: "No child for %d", idx);
330 else
331 {
332 guint pos;
333 gboolean ret;
334
335 pos = gtk_list_item_widget_get_position (GTK_LIST_ITEM_WIDGET (child));
336 ret = gtk_selection_model_unselect_item (model, position: pos);
337
338 g_dbus_method_invocation_return_value (invocation, parameters: g_variant_new (format_string: "(b)", ret));
339 }
340 }
341 else if (g_strcmp0 (str1: method_name, str2: "DeselectSelectedChild") == 0)
342 {
343 int idx;
344 GtkWidget *child;
345
346 g_variant_get (value: parameters, format_string: "(i)", &idx);
347
348 /* We are asked for the n-th selected child *among the current children* */
349 for (child = gtk_widget_get_first_child (widget);
350 child;
351 child = gtk_widget_get_next_sibling (widget: child))
352 {
353 if (gtk_list_item_widget_get_selected (GTK_LIST_ITEM_WIDGET (child)))
354 {
355 if (idx == 0)
356 break;
357 idx--;
358 }
359 }
360
361 if (child == NULL)
362 g_dbus_method_invocation_return_error (invocation,
363 G_DBUS_ERROR,
364 code: G_DBUS_ERROR_INVALID_ARGS,
365 format: "No selected child for %d", idx);
366 else
367 {
368 guint pos;
369 gboolean ret;
370
371 pos = gtk_list_item_widget_get_position (GTK_LIST_ITEM_WIDGET (child));
372 ret = gtk_selection_model_unselect_item (model, position: pos);
373
374 g_dbus_method_invocation_return_value (invocation, parameters: g_variant_new (format_string: "(b)", ret));
375 }
376 }
377 else if (g_strcmp0 (str1: method_name, str2: "IsChildSelected") == 0)
378 {
379 int idx;
380 GtkWidget *child;
381
382 g_variant_get (value: parameters, format_string: "(i)", &idx);
383
384 for (child = gtk_widget_get_first_child (widget);
385 child;
386 child = gtk_widget_get_next_sibling (widget: child))
387 {
388 if (idx == 0)
389 break;
390 idx--;
391 }
392
393 if (child == NULL)
394 g_dbus_method_invocation_return_error (invocation,
395 G_DBUS_ERROR,
396 code: G_DBUS_ERROR_INVALID_ARGS,
397 format: "No child for %d", idx);
398 else
399 {
400 gboolean ret;
401
402 ret = gtk_list_item_widget_get_selected (GTK_LIST_ITEM_WIDGET (child));
403
404 g_dbus_method_invocation_return_value (invocation, parameters: g_variant_new (format_string: "(b)", ret));
405 }
406 }
407 else if (g_strcmp0 (str1: method_name, str2: "SelectAll") == 0)
408 {
409 gboolean ret;
410
411 /* This is a bit inconsistent - the Selection interface is defined in terms
412 * of the current children, but this selects all items in the model, whether
413 * they are currently represented or not.
414 */
415 ret = gtk_selection_model_select_all (model);
416
417 g_dbus_method_invocation_return_value (invocation, parameters: g_variant_new (format_string: "(b)", ret));
418 }
419 else if (g_strcmp0 (str1: method_name, str2: "ClearSelection") == 0)
420 {
421 gboolean ret;
422
423 ret = gtk_selection_model_unselect_all (model);
424
425 g_dbus_method_invocation_return_value (invocation, parameters: g_variant_new (format_string: "(b)", ret));
426 }
427}
428
429static GVariant *
430listview_get_property (GDBusConnection *connection,
431 const gchar *sender,
432 const gchar *object_path,
433 const gchar *interface_name,
434 const gchar *property_name,
435 GError **error,
436 gpointer user_data)
437{
438 GtkATContext *self = GTK_AT_CONTEXT (ptr: user_data);
439 GtkAccessible *accessible = gtk_at_context_get_accessible (self);
440 GtkWidget *widget = GTK_WIDGET (accessible);
441 GtkSelectionModel *model = gtk_list_base_get_model (GTK_LIST_BASE (widget));
442
443 if (g_strcmp0 (str1: property_name, str2: "NSelectedChildren") == 0)
444 {
445 int count = 0;
446 GtkBitset *set;
447
448 set = gtk_selection_model_get_selection (model);
449 count = gtk_bitset_get_size (self: set);
450 gtk_bitset_unref (self: set);
451
452 return g_variant_new_int32 (value: count);
453 }
454
455 return NULL;
456}
457
458static const GDBusInterfaceVTable listview_vtable = {
459 listview_handle_method,
460 listview_get_property,
461 NULL
462};
463
464/* }}} */
465/* {{{ GtkFlowBox */
466
467static void
468flowbox_handle_method (GDBusConnection *connection,
469 const gchar *sender,
470 const gchar *object_path,
471 const gchar *interface_name,
472 const gchar *method_name,
473 GVariant *parameters,
474 GDBusMethodInvocation *invocation,
475 gpointer user_data)
476{
477 GtkATContext *self = user_data;
478 GtkAccessible *accessible = gtk_at_context_get_accessible (self);
479 GtkWidget *widget = GTK_WIDGET (accessible);
480
481 if (g_strcmp0 (str1: method_name, str2: "GetSelectedChild") == 0)
482 {
483 Counter counter;
484 int idx;
485
486 g_variant_get (value: parameters, format_string: "(i)", &idx);
487
488 counter.n = idx;
489 counter.child = NULL;
490
491 gtk_flow_box_selected_foreach (GTK_FLOW_BOX (widget), func: (GtkFlowBoxForeachFunc)find_nth, data: &counter);
492
493 if (counter.child == NULL)
494 g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, code: G_DBUS_ERROR_INVALID_ARGS, format: "No selected child for %d", idx);
495 else
496 {
497 GtkATContext *ctx = gtk_accessible_get_at_context (self: GTK_ACCESSIBLE (ptr: counter.child));
498 g_dbus_method_invocation_return_value (invocation, parameters: g_variant_new (format_string: "(@(so))", gtk_at_spi_context_to_ref (self: GTK_AT_SPI_CONTEXT (ptr: ctx))));
499 }
500 }
501 else if (g_strcmp0 (str1: method_name, str2: "SelectChild") == 0)
502 {
503 int idx;
504 GtkFlowBoxChild *child;
505
506 g_variant_get (value: parameters, format_string: "(i)", &idx);
507
508 child = gtk_flow_box_get_child_at_index (GTK_FLOW_BOX (widget), idx);
509 if (!child)
510 g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, code: G_DBUS_ERROR_INVALID_ARGS, format: "No child at position %d", idx);
511 else
512 {
513 gboolean ret;
514
515 gtk_flow_box_select_child (GTK_FLOW_BOX (widget), child);
516 ret = gtk_flow_box_child_is_selected (child);
517 g_dbus_method_invocation_return_value (invocation, parameters: g_variant_new (format_string: "(b)", ret));
518 }
519 }
520 else if (g_strcmp0 (str1: method_name, str2: "DeselectChild") == 0)
521 {
522 int idx;
523 GtkFlowBoxChild *child;
524
525 g_variant_get (value: parameters, format_string: "(i)", &idx);
526
527 child = gtk_flow_box_get_child_at_index (GTK_FLOW_BOX (widget), idx);
528 if (!child)
529 g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, code: G_DBUS_ERROR_INVALID_ARGS, format: "No child at position %d", idx);
530 else
531 {
532 gboolean ret;
533
534 gtk_flow_box_unselect_child (GTK_FLOW_BOX (widget), child);
535 ret = !gtk_flow_box_child_is_selected (child);
536 g_dbus_method_invocation_return_value (invocation, parameters: g_variant_new (format_string: "(b)", ret));
537 }
538 }
539 else if (g_strcmp0 (str1: method_name, str2: "DeselectSelectedChild") == 0)
540 {
541 Counter counter;
542 int idx;
543
544 g_variant_get (value: parameters, format_string: "(i)", &idx);
545
546 counter.n = idx;
547 counter.child = NULL;
548
549 gtk_flow_box_selected_foreach (GTK_FLOW_BOX (widget), func: (GtkFlowBoxForeachFunc)find_nth, data: &counter);
550
551 if (counter.child == NULL)
552 g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, code: G_DBUS_ERROR_INVALID_ARGS, format: "No selected child for %d", idx);
553 else
554 {
555 gboolean ret;
556
557 gtk_flow_box_unselect_child (GTK_FLOW_BOX (widget), GTK_FLOW_BOX_CHILD (counter.child));
558 ret = !gtk_flow_box_child_is_selected (GTK_FLOW_BOX_CHILD (counter.child));
559 g_dbus_method_invocation_return_value (invocation, parameters: g_variant_new (format_string: "(b)", ret));
560 }
561 }
562 else if (g_strcmp0 (str1: method_name, str2: "IsChildSelected") == 0)
563 {
564 int idx;
565 GtkFlowBoxChild *child;
566
567 g_variant_get (value: parameters, format_string: "(i)", &idx);
568
569 child = gtk_flow_box_get_child_at_index (GTK_FLOW_BOX (widget), idx);
570 if (!child)
571 g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, code: G_DBUS_ERROR_INVALID_ARGS, format: "No child at position %d", idx);
572 else
573 g_dbus_method_invocation_return_value (invocation, parameters: g_variant_new (format_string: "(b)", gtk_flow_box_child_is_selected (child)));
574 }
575 else if (g_strcmp0 (str1: method_name, str2: "SelectAll") == 0)
576 {
577 gtk_flow_box_select_all (GTK_FLOW_BOX (widget));
578 g_dbus_method_invocation_return_value (invocation, parameters: g_variant_new (format_string: "(b)", TRUE));
579 }
580 else if (g_strcmp0 (str1: method_name, str2: "ClearSelection") == 0)
581 {
582 gtk_flow_box_unselect_all (GTK_FLOW_BOX (widget));
583 g_dbus_method_invocation_return_value (invocation, parameters: g_variant_new (format_string: "(b)", TRUE));
584 }
585}
586
587static GVariant *
588flowbox_get_property (GDBusConnection *connection,
589 const gchar *sender,
590 const gchar *object_path,
591 const gchar *interface_name,
592 const gchar *property_name,
593 GError **error,
594 gpointer user_data)
595{
596 GtkATContext *self = GTK_AT_CONTEXT (ptr: user_data);
597 GtkAccessible *accessible = gtk_at_context_get_accessible (self);
598 GtkWidget *widget = GTK_WIDGET (accessible);
599
600 if (g_strcmp0 (str1: property_name, str2: "NSelectedChildren") == 0)
601 {
602 int count = 0;
603
604 gtk_flow_box_selected_foreach (GTK_FLOW_BOX (widget), func: (GtkFlowBoxForeachFunc)count_selected, data: &count);
605
606 return g_variant_new_int32 (value: count);
607 }
608
609 return NULL;
610}
611
612static const GDBusInterfaceVTable flowbox_vtable = {
613 flowbox_handle_method,
614 flowbox_get_property,
615 NULL
616};
617
618/* }}} */
619/* {{{ GtkComboBox */
620
621static void
622combobox_handle_method (GDBusConnection *connection,
623 const gchar *sender,
624 const gchar *object_path,
625 const gchar *interface_name,
626 const gchar *method_name,
627 GVariant *parameters,
628 GDBusMethodInvocation *invocation,
629 gpointer user_data)
630{
631 GtkATContext *self = user_data;
632 GtkAccessible *accessible = gtk_at_context_get_accessible (self);
633 GtkWidget *widget = GTK_WIDGET (accessible);
634
635 if (g_strcmp0 (str1: method_name, str2: "GetSelectedChild") == 0)
636 {
637 /* Need to figure out what to do here */
638 g_dbus_method_invocation_return_error_literal (invocation, G_DBUS_ERROR, code: G_DBUS_ERROR_NOT_SUPPORTED, message: "");
639 }
640 else if (g_strcmp0 (str1: method_name, str2: "SelectChild") == 0)
641 {
642 int idx;
643
644 g_variant_get (value: parameters, format_string: "(i)", &idx);
645 gtk_combo_box_set_active (GTK_COMBO_BOX (widget), index_: idx);
646 g_dbus_method_invocation_return_value (invocation, parameters: g_variant_new (format_string: "(b)", TRUE));
647 }
648 else if (g_strcmp0 (str1: method_name, str2: "DeselectChild") == 0)
649 {
650 int idx;
651
652 g_variant_get (value: parameters, format_string: "(i)", &idx);
653
654 gtk_combo_box_set_active (GTK_COMBO_BOX (widget), index_: -1);
655 g_dbus_method_invocation_return_value (invocation, parameters: g_variant_new (format_string: "(b)", TRUE));
656 }
657 else if (g_strcmp0 (str1: method_name, str2: "DeselectSelectedChild") == 0)
658 {
659 int idx;
660
661 g_variant_get (value: parameters, format_string: "(i)", &idx);
662 if (idx == 0)
663 gtk_combo_box_set_active (GTK_COMBO_BOX (widget), index_: -1);
664 g_dbus_method_invocation_return_value (invocation, parameters: g_variant_new (format_string: "(b)", idx == 0));
665 }
666 else if (g_strcmp0 (str1: method_name, str2: "IsChildSelected") == 0)
667 {
668 int idx;
669 gboolean active;
670
671 g_variant_get (value: parameters, format_string: "(i)", &idx);
672 active = idx = gtk_combo_box_get_active (GTK_COMBO_BOX (widget));
673 g_dbus_method_invocation_return_value (invocation, parameters: g_variant_new (format_string: "(b)", active));
674 }
675 else if (g_strcmp0 (str1: method_name, str2: "SelectAll") == 0)
676 {
677 g_dbus_method_invocation_return_value (invocation, parameters: g_variant_new (format_string: "(b)", FALSE));
678 }
679 else if (g_strcmp0 (str1: method_name, str2: "ClearSelection") == 0)
680 {
681 gtk_combo_box_set_active (GTK_COMBO_BOX (widget), index_: -1);
682 g_dbus_method_invocation_return_value (invocation, parameters: g_variant_new (format_string: "(b)", TRUE));
683 }
684}
685
686static GVariant *
687combobox_get_property (GDBusConnection *connection,
688 const gchar *sender,
689 const gchar *object_path,
690 const gchar *interface_name,
691 const gchar *property_name,
692 GError **error,
693 gpointer user_data)
694{
695 GtkATContext *self = GTK_AT_CONTEXT (ptr: user_data);
696 GtkAccessible *accessible = gtk_at_context_get_accessible (self);
697 GtkWidget *widget = GTK_WIDGET (accessible);
698
699 if (g_strcmp0 (str1: property_name, str2: "NSelectedChildren") == 0)
700 {
701 if (gtk_combo_box_get_active (GTK_COMBO_BOX (widget)))
702 return g_variant_new_int32 (value: 1);
703 else
704 return g_variant_new_int32 (value: 0);
705 }
706
707 return NULL;
708}
709
710static const GDBusInterfaceVTable combobox_vtable = {
711 combobox_handle_method,
712 combobox_get_property,
713 NULL
714};
715
716/* }}} */
717/* {{{ GtkStackSwitcher */
718
719static void
720stackswitcher_handle_method (GDBusConnection *connection,
721 const gchar *sender,
722 const gchar *object_path,
723 const gchar *interface_name,
724 const gchar *method_name,
725 GVariant *parameters,
726 GDBusMethodInvocation *invocation,
727 gpointer user_data)
728{
729 GtkATContext *self = user_data;
730 GtkAccessible *accessible = gtk_at_context_get_accessible (self);
731 GtkWidget *widget = GTK_WIDGET (accessible);
732 GtkStack *stack = gtk_stack_switcher_get_stack (GTK_STACK_SWITCHER (widget));
733
734 if (g_strcmp0 (str1: method_name, str2: "GetSelectedChild") == 0)
735 {
736 guint i, n;
737 GtkSelectionModel *pages;
738 GtkWidget *child;
739
740 pages = gtk_stack_get_pages (stack);
741 n = g_list_model_get_n_items (list: G_LIST_MODEL (ptr: pages));
742 for (i = 0, child = gtk_widget_get_first_child (widget);
743 i < n && child;
744 i++, child = gtk_widget_get_next_sibling (widget: child))
745 {
746 if (gtk_selection_model_is_selected (model: pages, position: i))
747 break;
748 }
749 g_object_unref (object: pages);
750
751 if (child == NULL)
752 g_dbus_method_invocation_return_error_literal (invocation,
753 G_DBUS_ERROR,
754 code: G_DBUS_ERROR_INVALID_ARGS,
755 message: "No selected child");
756 else
757 {
758 GtkATContext *ctx = gtk_accessible_get_at_context (self: GTK_ACCESSIBLE (ptr: child));
759 g_dbus_method_invocation_return_value (invocation,
760 parameters: g_variant_new (format_string: "(@(so))", gtk_at_spi_context_to_ref (self: GTK_AT_SPI_CONTEXT (ptr: ctx))));
761 }
762 }
763 else if (g_strcmp0 (str1: method_name, str2: "SelectChild") == 0)
764 {
765 int idx;
766 GtkSelectionModel *pages;
767
768 g_variant_get (value: parameters, format_string: "(i)", &idx);
769
770 pages = gtk_stack_get_pages (stack);
771 gtk_selection_model_select_item (model: pages, position: idx, TRUE);
772 g_object_unref (object: pages);
773
774 g_dbus_method_invocation_return_value (invocation, parameters: g_variant_new (format_string: "(b)", TRUE));
775 }
776 else if (g_strcmp0 (str1: method_name, str2: "DeselectChild") == 0)
777 {
778 g_dbus_method_invocation_return_value (invocation, parameters: g_variant_new (format_string: "(b)", FALSE));
779 }
780 else if (g_strcmp0 (str1: method_name, str2: "DeselectSelectedChild") == 0)
781 {
782 g_dbus_method_invocation_return_value (invocation, parameters: g_variant_new (format_string: "(b)", FALSE));
783 }
784 else if (g_strcmp0 (str1: method_name, str2: "IsChildSelected") == 0)
785 {
786 int idx;
787 GtkSelectionModel *pages;
788 gboolean active;
789
790 g_variant_get (value: parameters, format_string: "(i)", &idx);
791
792 pages = gtk_stack_get_pages (stack);
793 active = gtk_selection_model_is_selected (model: pages, position: idx);
794 g_object_unref (object: pages);
795
796 g_dbus_method_invocation_return_value (invocation, parameters: g_variant_new (format_string: "(b)", active));
797 }
798 else if (g_strcmp0 (str1: method_name, str2: "SelectAll") == 0)
799 {
800 g_dbus_method_invocation_return_value (invocation, parameters: g_variant_new (format_string: "(b)", FALSE));
801 }
802 else if (g_strcmp0 (str1: method_name, str2: "ClearSelection") == 0)
803 {
804 g_dbus_method_invocation_return_value (invocation, parameters: g_variant_new (format_string: "(b)", FALSE));
805 }
806}
807
808static GVariant *
809stackswitcher_get_property (GDBusConnection *connection,
810 const gchar *sender,
811 const gchar *object_path,
812 const gchar *interface_name,
813 const gchar *property_name,
814 GError **error,
815 gpointer user_data)
816{
817 GtkATContext *self = GTK_AT_CONTEXT (ptr: user_data);
818 GtkAccessible *accessible = gtk_at_context_get_accessible (self);
819 GtkWidget *widget = GTK_WIDGET (accessible);
820
821 if (g_strcmp0 (str1: property_name, str2: "NSelectedChildren") == 0)
822 {
823 GtkStack *stack = gtk_stack_switcher_get_stack (GTK_STACK_SWITCHER (widget));
824
825 if (stack == NULL || gtk_stack_get_visible_child (stack) == NULL)
826 return g_variant_new_int32 (value: 0);
827 else
828 return g_variant_new_int32 (value: 1);
829 }
830
831 return NULL;
832}
833
834static const GDBusInterfaceVTable stackswitcher_vtable = {
835 stackswitcher_handle_method,
836 stackswitcher_get_property,
837 NULL
838};
839
840/* }}} */
841/* {{{ GtkNotebook */
842
843static void
844notebook_handle_method (GDBusConnection *connection,
845 const gchar *sender,
846 const gchar *object_path,
847 const gchar *interface_name,
848 const gchar *method_name,
849 GVariant *parameters,
850 GDBusMethodInvocation *invocation,
851 gpointer user_data)
852{
853 GtkATContext *self = user_data;
854 GtkAccessible *accessible = gtk_at_context_get_accessible (self);
855 GtkWidget *widget = GTK_WIDGET (accessible);
856 GtkWidget *notebook = gtk_widget_get_parent (widget: gtk_widget_get_parent (widget));
857
858 if (g_strcmp0 (str1: method_name, str2: "GetSelectedChild") == 0)
859 {
860 int i;
861 GtkWidget *child;
862
863 i = gtk_notebook_get_current_page (GTK_NOTEBOOK (notebook));
864
865 for (child = gtk_widget_get_first_child (widget);
866 child;
867 child = gtk_widget_get_next_sibling (widget: child))
868 {
869 /* skip actions */
870 if (gtk_accessible_get_accessible_role (self: GTK_ACCESSIBLE (ptr: child)) != GTK_ACCESSIBLE_ROLE_TAB)
871 continue;
872
873 if (i == 0)
874 break;
875
876 i--;
877 }
878
879 if (child == NULL)
880 {
881 g_dbus_method_invocation_return_error_literal (invocation,
882 G_DBUS_ERROR,
883 code: G_DBUS_ERROR_INVALID_ARGS,
884 message: "No selected child");
885 }
886 else
887 {
888 GtkATContext *ctx = gtk_accessible_get_at_context (self: GTK_ACCESSIBLE (ptr: child));
889 g_dbus_method_invocation_return_value (invocation,
890 parameters: g_variant_new (format_string: "(@(so))", gtk_at_spi_context_to_ref (self: GTK_AT_SPI_CONTEXT (ptr: ctx))));
891 }
892 }
893 else if (g_strcmp0 (str1: method_name, str2: "SelectChild") == 0)
894 {
895 int i;
896 GtkWidget *child;
897
898 g_variant_get (value: parameters, format_string: "(i)", &i);
899
900 /* skip an action widget */
901 child = gtk_widget_get_first_child (widget);
902 if (gtk_accessible_get_accessible_role (self: GTK_ACCESSIBLE (ptr: child)) != GTK_ACCESSIBLE_ROLE_TAB)
903 i--;
904
905 gtk_notebook_set_current_page (GTK_NOTEBOOK (notebook), page_num: i);
906
907 g_dbus_method_invocation_return_value (invocation, parameters: g_variant_new (format_string: "(b)", TRUE));
908 }
909 else if (g_strcmp0 (str1: method_name, str2: "DeselectChild") == 0)
910 {
911 g_dbus_method_invocation_return_value (invocation, parameters: g_variant_new (format_string: "(b)", FALSE));
912 }
913 else if (g_strcmp0 (str1: method_name, str2: "DeselectSelectedChild") == 0)
914 {
915 g_dbus_method_invocation_return_value (invocation, parameters: g_variant_new (format_string: "(b)", FALSE));
916 }
917 else if (g_strcmp0 (str1: method_name, str2: "IsChildSelected") == 0)
918 {
919 int i;
920 gboolean active;
921 GtkWidget *child;
922
923 g_variant_get (value: parameters, format_string: "(i)", &i);
924
925 /* skip an action widget */
926 child = gtk_widget_get_first_child (widget);
927 if (gtk_accessible_get_accessible_role (self: GTK_ACCESSIBLE (ptr: child)) != GTK_ACCESSIBLE_ROLE_TAB)
928 i--;
929
930 active = i == gtk_notebook_get_current_page (GTK_NOTEBOOK (notebook));
931
932 g_dbus_method_invocation_return_value (invocation, parameters: g_variant_new (format_string: "(b)", active));
933 }
934 else if (g_strcmp0 (str1: method_name, str2: "SelectAll") == 0)
935 {
936 g_dbus_method_invocation_return_value (invocation, parameters: g_variant_new (format_string: "(b)", FALSE));
937 }
938 else if (g_strcmp0 (str1: method_name, str2: "ClearSelection") == 0)
939 {
940 g_dbus_method_invocation_return_value (invocation, parameters: g_variant_new (format_string: "(b)", FALSE));
941 }
942}
943
944static GVariant *
945notebook_get_property (GDBusConnection *connection,
946 const gchar *sender,
947 const gchar *object_path,
948 const gchar *interface_name,
949 const gchar *property_name,
950 GError **error,
951 gpointer user_data)
952{
953 if (g_strcmp0 (str1: property_name, str2: "NSelectedChildren") == 0)
954 {
955 return g_variant_new_int32 (value: 1);
956 }
957
958 return NULL;
959}
960
961static const GDBusInterfaceVTable notebook_vtable = {
962 notebook_handle_method,
963 notebook_get_property,
964 NULL
965};
966
967/* }}} */
968
969#define IS_NOTEBOOK_TAB_LIST(s,r) \
970 ((r == GTK_ACCESSIBLE_ROLE_TAB_LIST) && \
971 (gtk_widget_get_parent (GTK_WIDGET (s)) != NULL) && \
972 GTK_IS_NOTEBOOK (gtk_widget_get_parent (gtk_widget_get_parent (GTK_WIDGET (s)))))
973
974const GDBusInterfaceVTable *
975gtk_atspi_get_selection_vtable (GtkAccessible *accessible,
976 GtkAccessibleRole role)
977{
978 if (GTK_IS_LIST_BOX (accessible))
979 return &listbox_vtable;
980 else if (GTK_IS_LIST_VIEW (accessible) ||
981 GTK_IS_GRID_VIEW (accessible))
982 return &listview_vtable;
983 else if (GTK_IS_FLOW_BOX (accessible))
984 return &flowbox_vtable;
985 else if (GTK_IS_COMBO_BOX (accessible))
986 return &combobox_vtable;
987 else if (GTK_IS_STACK_SWITCHER (accessible))
988 return &stackswitcher_vtable;
989 else if (IS_NOTEBOOK_TAB_LIST (accessible, role))
990 return &notebook_vtable;
991
992 return NULL;
993}
994
995/* {{{ GtkListView notification */
996
997typedef struct {
998 GtkAtspiSelectionCallback *changed;
999 gpointer data;
1000} SelectionChanged;
1001
1002typedef struct {
1003 GtkSelectionModel *model;
1004 GtkAtspiSelectionCallback *changed;
1005 gpointer data;
1006} ListViewData;
1007
1008static void
1009update_model (ListViewData *data,
1010 GtkSelectionModel *model)
1011{
1012 if (data->model)
1013 g_signal_handlers_disconnect_by_func (data->model, data->changed, data->data);
1014
1015 g_set_object (&data->model, model);
1016
1017 if (data->model)
1018 g_signal_connect_swapped (data->model, "selection-changed", G_CALLBACK (data->changed), data->data);
1019}
1020
1021static void
1022list_view_data_free (gpointer user_data)
1023{
1024 ListViewData *data = user_data;
1025 update_model (data, NULL);
1026 g_free (mem: data);
1027}
1028
1029static void
1030model_changed (GtkListBase *list,
1031 GParamSpec *pspec,
1032 gpointer unused)
1033{
1034 ListViewData *data;
1035
1036 data = (ListViewData *)g_object_get_data (G_OBJECT (list), key: "accessible-selection-data");
1037 update_model (data, model: gtk_list_base_get_model (self: list));
1038}
1039
1040/* }}} */
1041
1042void
1043gtk_atspi_connect_selection_signals (GtkAccessible *accessible,
1044 GtkAtspiSelectionCallback selection_changed,
1045 gpointer data)
1046{
1047 if (GTK_IS_LIST_BOX (accessible))
1048 {
1049 SelectionChanged *changed;
1050
1051 changed = g_new (SelectionChanged, 1);
1052 changed->changed = selection_changed;
1053 changed->data = data;
1054
1055 g_object_set_data_full (G_OBJECT (accessible), key: "accessible-selection-data", data: changed, destroy: g_free);
1056
1057 g_signal_connect_swapped (accessible, "selected-rows-changed", G_CALLBACK (selection_changed), data);
1058 }
1059 else if (GTK_IS_FLOW_BOX (accessible))
1060 {
1061 SelectionChanged *changed;
1062
1063 changed = g_new (SelectionChanged, 1);
1064 changed->changed = selection_changed;
1065 changed->data = data;
1066
1067 g_object_set_data_full (G_OBJECT (accessible), key: "accessible-selection-data", data: changed, destroy: g_free);
1068
1069 g_signal_connect_swapped (accessible, "selected-children-changed", G_CALLBACK (selection_changed), data);
1070 }
1071 else if (GTK_IS_COMBO_BOX (accessible))
1072 {
1073 SelectionChanged *changed;
1074
1075 changed = g_new (SelectionChanged, 1);
1076 changed->changed = selection_changed;
1077 changed->data = data;
1078
1079 g_object_set_data_full (G_OBJECT (accessible), key: "accessible-selection-data", data: changed, destroy: g_free);
1080
1081 g_signal_connect_swapped (accessible, "changed", G_CALLBACK (selection_changed), data);
1082 }
1083 else if (GTK_IS_STACK_SWITCHER (accessible))
1084 {
1085 SelectionChanged *changed;
1086
1087 changed = g_new (SelectionChanged, 1);
1088 changed->changed = selection_changed;
1089 changed->data = data;
1090
1091 g_object_set_data_full (G_OBJECT (accessible), key: "accessible-selection-data", data: changed, destroy: g_free);
1092
1093 g_signal_connect_swapped (accessible, "notify::visible-child", G_CALLBACK (selection_changed), data);
1094 }
1095 else if (IS_NOTEBOOK_TAB_LIST (accessible, GTK_AT_CONTEXT (data)->accessible_role))
1096 {
1097 GtkWidget *notebook = gtk_widget_get_parent (widget: gtk_widget_get_parent (GTK_WIDGET (accessible)));
1098 SelectionChanged *changed;
1099
1100 changed = g_new (SelectionChanged, 1);
1101 changed->changed = selection_changed;
1102 changed->data = data;
1103
1104 g_object_set_data_full (G_OBJECT (accessible), key: "accessible-selection-data", data: changed, destroy: g_free);
1105
1106 g_signal_connect_swapped (notebook, "notify::page", G_CALLBACK (selection_changed), data);
1107 }
1108 else if (GTK_IS_LIST_VIEW (accessible) ||
1109 GTK_IS_GRID_VIEW (accessible))
1110 {
1111 ListViewData *changed;
1112
1113 changed = g_new0 (ListViewData, 1);
1114 changed->changed = selection_changed;
1115 changed->data = data;
1116
1117 g_object_set_data_full (G_OBJECT (accessible), key: "accessible-selection-data", data: changed, destroy: list_view_data_free);
1118
1119 g_signal_connect (accessible, "notify::model", G_CALLBACK (model_changed), NULL);
1120 model_changed (GTK_LIST_BASE (accessible), NULL, unused: changed);
1121 }
1122}
1123
1124void
1125gtk_atspi_disconnect_selection_signals (GtkAccessible *accessible)
1126{
1127 if (GTK_IS_LIST_BOX (accessible) ||
1128 GTK_IS_FLOW_BOX (accessible) ||
1129 GTK_IS_COMBO_BOX (accessible) ||
1130 GTK_IS_STACK_SWITCHER (accessible))
1131 {
1132 SelectionChanged *changed;
1133
1134 changed = g_object_get_data (G_OBJECT (accessible), key: "accessible-selection-data");
1135 if (changed == NULL)
1136 return;
1137
1138 g_signal_handlers_disconnect_by_func (accessible, changed->changed, changed->data);
1139
1140 g_object_set_data (G_OBJECT (accessible), key: "accessible-selection-data", NULL);
1141 }
1142 else if (IS_NOTEBOOK_TAB_LIST (accessible, gtk_accessible_get_accessible_role (accessible)))
1143 {
1144 GtkWidget *notebook = gtk_widget_get_parent (widget: gtk_widget_get_parent (GTK_WIDGET (accessible)));
1145 SelectionChanged *changed;
1146
1147 changed = g_object_get_data (G_OBJECT (accessible), key: "accessible-selection-data");
1148 if (changed == NULL)
1149 return;
1150
1151 g_signal_handlers_disconnect_by_func (notebook, changed->changed, changed->data);
1152
1153 g_object_set_data (G_OBJECT (accessible), key: "accessible-selection-data", NULL);
1154 }
1155 else if (GTK_IS_LIST_VIEW (accessible) ||
1156 GTK_IS_GRID_VIEW (accessible))
1157 {
1158 g_signal_handlers_disconnect_by_func (accessible, model_changed, NULL);
1159
1160 g_object_set_data (G_OBJECT (accessible), key: "accessible-selection-data", NULL);
1161 }
1162}
1163
1164/* vim:set foldmethod=marker expandtab: */
1165
1166

source code of gtk/gtk/a11y/gtkatspiselection.c