1 | /* gtkatcontext.c: Assistive technology context |
2 | * |
3 | * Copyright 2020 GNOME Foundation |
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 | /** |
22 | * GtkATContext: |
23 | * |
24 | * `GtkATContext` is an abstract class provided by GTK to communicate to |
25 | * platform-specific assistive technologies API. |
26 | * |
27 | * Each platform supported by GTK implements a `GtkATContext` subclass, and |
28 | * is responsible for updating the accessible state in response to state |
29 | * changes in `GtkAccessible`. |
30 | */ |
31 | |
32 | #include "config.h" |
33 | |
34 | #include "gtkatcontextprivate.h" |
35 | |
36 | #include "gtkaccessiblevalueprivate.h" |
37 | #include "gtkaccessibleprivate.h" |
38 | #include "gtkdebug.h" |
39 | #include "gtktestatcontextprivate.h" |
40 | #include "gtktypebuiltins.h" |
41 | |
42 | #if defined(GDK_WINDOWING_X11) || defined(GDK_WINDOWING_WAYLAND) |
43 | #include "a11y/gtkatspicontextprivate.h" |
44 | #endif |
45 | |
46 | G_DEFINE_ABSTRACT_TYPE (GtkATContext, gtk_at_context, G_TYPE_OBJECT) |
47 | |
48 | enum |
49 | { |
50 | PROP_ACCESSIBLE_ROLE = 1, |
51 | PROP_ACCESSIBLE, |
52 | PROP_DISPLAY, |
53 | |
54 | N_PROPS |
55 | }; |
56 | |
57 | enum |
58 | { |
59 | STATE_CHANGE, |
60 | |
61 | LAST_SIGNAL |
62 | }; |
63 | |
64 | static GParamSpec *obj_props[N_PROPS]; |
65 | |
66 | static guint obj_signals[LAST_SIGNAL]; |
67 | |
68 | static void |
69 | gtk_at_context_finalize (GObject *gobject) |
70 | { |
71 | GtkATContext *self = GTK_AT_CONTEXT (ptr: gobject); |
72 | |
73 | gtk_accessible_attribute_set_unref (self: self->properties); |
74 | gtk_accessible_attribute_set_unref (self: self->relations); |
75 | gtk_accessible_attribute_set_unref (self: self->states); |
76 | |
77 | G_OBJECT_CLASS (gtk_at_context_parent_class)->finalize (gobject); |
78 | } |
79 | |
80 | static void |
81 | gtk_at_context_dispose (GObject *gobject) |
82 | { |
83 | GtkATContext *self = GTK_AT_CONTEXT (ptr: gobject); |
84 | |
85 | gtk_at_context_unrealize (self); |
86 | |
87 | G_OBJECT_CLASS (gtk_at_context_parent_class)->dispose (gobject); |
88 | } |
89 | |
90 | static void |
91 | gtk_at_context_set_property (GObject *gobject, |
92 | guint prop_id, |
93 | const GValue *value, |
94 | GParamSpec *pspec) |
95 | { |
96 | GtkATContext *self = GTK_AT_CONTEXT (ptr: gobject); |
97 | |
98 | switch (prop_id) |
99 | { |
100 | case PROP_ACCESSIBLE_ROLE: |
101 | if (!self->realized) |
102 | self->accessible_role = g_value_get_enum (value); |
103 | else |
104 | g_critical ("The accessible role cannot be set on a realized AT context" ); |
105 | break; |
106 | |
107 | case PROP_ACCESSIBLE: |
108 | self->accessible = g_value_get_object (value); |
109 | break; |
110 | |
111 | case PROP_DISPLAY: |
112 | gtk_at_context_set_display (self, display: g_value_get_object (value)); |
113 | break; |
114 | |
115 | default: |
116 | G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); |
117 | } |
118 | } |
119 | |
120 | static void |
121 | gtk_at_context_get_property (GObject *gobject, |
122 | guint prop_id, |
123 | GValue *value, |
124 | GParamSpec *pspec) |
125 | { |
126 | GtkATContext *self = GTK_AT_CONTEXT (ptr: gobject); |
127 | |
128 | switch (prop_id) |
129 | { |
130 | case PROP_ACCESSIBLE_ROLE: |
131 | g_value_set_enum (value, v_enum: self->accessible_role); |
132 | break; |
133 | |
134 | case PROP_ACCESSIBLE: |
135 | g_value_set_object (value, v_object: self->accessible); |
136 | break; |
137 | |
138 | case PROP_DISPLAY: |
139 | g_value_set_object (value, v_object: self->display); |
140 | break; |
141 | |
142 | default: |
143 | G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); |
144 | } |
145 | } |
146 | |
147 | static void |
148 | gtk_at_context_real_state_change (GtkATContext *self, |
149 | GtkAccessibleStateChange changed_states, |
150 | GtkAccessiblePropertyChange changed_properties, |
151 | GtkAccessibleRelationChange changed_relations, |
152 | GtkAccessibleAttributeSet *states, |
153 | GtkAccessibleAttributeSet *properties, |
154 | GtkAccessibleAttributeSet *relations) |
155 | { |
156 | } |
157 | |
158 | static void |
159 | gtk_at_context_real_platform_change (GtkATContext *self, |
160 | GtkAccessiblePlatformChange change) |
161 | { |
162 | } |
163 | |
164 | static void |
165 | gtk_at_context_real_bounds_change (GtkATContext *self) |
166 | { |
167 | } |
168 | |
169 | static void |
170 | gtk_at_context_real_child_change (GtkATContext *self, |
171 | GtkAccessibleChildChange change, |
172 | GtkAccessible *child) |
173 | { |
174 | } |
175 | |
176 | static void |
177 | gtk_at_context_real_realize (GtkATContext *self) |
178 | { |
179 | } |
180 | |
181 | static void |
182 | gtk_at_context_real_unrealize (GtkATContext *self) |
183 | { |
184 | } |
185 | |
186 | static void |
187 | gtk_at_context_class_init (GtkATContextClass *klass) |
188 | { |
189 | GObjectClass *gobject_class = G_OBJECT_CLASS (klass); |
190 | |
191 | gobject_class->set_property = gtk_at_context_set_property; |
192 | gobject_class->get_property = gtk_at_context_get_property; |
193 | gobject_class->dispose = gtk_at_context_dispose; |
194 | gobject_class->finalize = gtk_at_context_finalize; |
195 | |
196 | klass->realize = gtk_at_context_real_realize; |
197 | klass->unrealize = gtk_at_context_real_unrealize; |
198 | klass->state_change = gtk_at_context_real_state_change; |
199 | klass->platform_change = gtk_at_context_real_platform_change; |
200 | klass->bounds_change = gtk_at_context_real_bounds_change; |
201 | klass->child_change = gtk_at_context_real_child_change; |
202 | |
203 | /** |
204 | * GtkATContext:accessible-role: (attributes org.gtk.Property.get=gtk_at_context_get_accessible_role) |
205 | * |
206 | * The accessible role used by the AT context. |
207 | * |
208 | * Depending on the given role, different states and properties can be |
209 | * set or retrieved. |
210 | */ |
211 | obj_props[PROP_ACCESSIBLE_ROLE] = |
212 | g_param_spec_enum (name: "accessible-role" , |
213 | nick: "Accessible Role" , |
214 | blurb: "The accessible role of the AT context" , |
215 | enum_type: GTK_TYPE_ACCESSIBLE_ROLE, |
216 | default_value: GTK_ACCESSIBLE_ROLE_NONE, |
217 | flags: G_PARAM_READWRITE | |
218 | G_PARAM_CONSTRUCT | |
219 | G_PARAM_STATIC_STRINGS); |
220 | |
221 | /** |
222 | * GtkATContext:accessible: (attributes org.gtk.Property.get=gtk_at_context_get_accessible) |
223 | * |
224 | * The `GtkAccessible` that created the `GtkATContext` instance. |
225 | */ |
226 | obj_props[PROP_ACCESSIBLE] = |
227 | g_param_spec_object (name: "accessible" , |
228 | nick: "Accessible" , |
229 | blurb: "The accessible implementation" , |
230 | GTK_TYPE_ACCESSIBLE, |
231 | flags: G_PARAM_READWRITE | |
232 | G_PARAM_CONSTRUCT_ONLY | |
233 | G_PARAM_STATIC_STRINGS); |
234 | |
235 | /** |
236 | * GtkATContext:display: |
237 | * |
238 | * The `GdkDisplay` for the `GtkATContext`. |
239 | */ |
240 | obj_props[PROP_DISPLAY] = |
241 | g_param_spec_object (name: "display" , |
242 | nick: "Display" , |
243 | blurb: "The display connection" , |
244 | GDK_TYPE_DISPLAY, |
245 | flags: G_PARAM_READWRITE | |
246 | G_PARAM_STATIC_STRINGS | |
247 | G_PARAM_EXPLICIT_NOTIFY); |
248 | |
249 | /** |
250 | * GtkATContext::state-change: |
251 | * @self: the `GtkATContext` |
252 | * |
253 | * Emitted when the attributes of the accessible for the |
254 | * `GtkATContext` instance change. |
255 | */ |
256 | obj_signals[STATE_CHANGE] = |
257 | g_signal_new (signal_name: "state-change" , |
258 | G_TYPE_FROM_CLASS (gobject_class), |
259 | signal_flags: G_SIGNAL_RUN_FIRST, |
260 | class_offset: 0, |
261 | NULL, NULL, |
262 | NULL, |
263 | G_TYPE_NONE, n_params: 0); |
264 | |
265 | g_object_class_install_properties (oclass: gobject_class, n_pspecs: N_PROPS, pspecs: obj_props); |
266 | } |
267 | |
268 | #define N_PROPERTIES (GTK_ACCESSIBLE_PROPERTY_VALUE_TEXT + 1) |
269 | #define N_RELATIONS (GTK_ACCESSIBLE_RELATION_SET_SIZE + 1) |
270 | #define N_STATES (GTK_ACCESSIBLE_STATE_SELECTED + 1) |
271 | |
272 | static const char *property_attrs[] = { |
273 | [GTK_ACCESSIBLE_PROPERTY_AUTOCOMPLETE] = "autocomplete" , |
274 | [GTK_ACCESSIBLE_PROPERTY_DESCRIPTION] = "description" , |
275 | [GTK_ACCESSIBLE_PROPERTY_HAS_POPUP] = "haspopup" , |
276 | [GTK_ACCESSIBLE_PROPERTY_KEY_SHORTCUTS] = "keyshortcuts" , |
277 | [GTK_ACCESSIBLE_PROPERTY_LABEL] = "label" , |
278 | [GTK_ACCESSIBLE_PROPERTY_LEVEL] = "level" , |
279 | [GTK_ACCESSIBLE_PROPERTY_MODAL] = "modal" , |
280 | [GTK_ACCESSIBLE_PROPERTY_MULTI_LINE] = "multiline" , |
281 | [GTK_ACCESSIBLE_PROPERTY_MULTI_SELECTABLE] = "multiselectable" , |
282 | [GTK_ACCESSIBLE_PROPERTY_ORIENTATION] = "orientation" , |
283 | [GTK_ACCESSIBLE_PROPERTY_PLACEHOLDER] = "placeholder" , |
284 | [GTK_ACCESSIBLE_PROPERTY_READ_ONLY] = "readonly" , |
285 | [GTK_ACCESSIBLE_PROPERTY_REQUIRED] = "required" , |
286 | [GTK_ACCESSIBLE_PROPERTY_ROLE_DESCRIPTION] = "roledescription" , |
287 | [GTK_ACCESSIBLE_PROPERTY_SORT] = "sort" , |
288 | [GTK_ACCESSIBLE_PROPERTY_VALUE_MAX] = "valuemax" , |
289 | [GTK_ACCESSIBLE_PROPERTY_VALUE_MIN] = "valuemin" , |
290 | [GTK_ACCESSIBLE_PROPERTY_VALUE_NOW] = "valuenow" , |
291 | [GTK_ACCESSIBLE_PROPERTY_VALUE_TEXT] = "valuetext" , |
292 | }; |
293 | |
294 | /*< private > |
295 | * gtk_accessible_property_get_attribute_name: |
296 | * @property: a `GtkAccessibleProperty` |
297 | * |
298 | * Retrieves the name of a `GtkAccessibleProperty`. |
299 | * |
300 | * Returns: (transfer none): the name of the accessible property |
301 | */ |
302 | const char * |
303 | gtk_accessible_property_get_attribute_name (GtkAccessibleProperty property) |
304 | { |
305 | g_return_val_if_fail (property >= GTK_ACCESSIBLE_PROPERTY_AUTOCOMPLETE && |
306 | property <= GTK_ACCESSIBLE_PROPERTY_VALUE_TEXT, |
307 | "<none>" ); |
308 | |
309 | return property_attrs[property]; |
310 | } |
311 | |
312 | static const char *relation_attrs[] = { |
313 | [GTK_ACCESSIBLE_RELATION_ACTIVE_DESCENDANT] = "activedescendant" , |
314 | [GTK_ACCESSIBLE_RELATION_COL_COUNT] = "colcount" , |
315 | [GTK_ACCESSIBLE_RELATION_COL_INDEX] = "colindex" , |
316 | [GTK_ACCESSIBLE_RELATION_COL_INDEX_TEXT] = "colindextext" , |
317 | [GTK_ACCESSIBLE_RELATION_COL_SPAN] = "colspan" , |
318 | [GTK_ACCESSIBLE_RELATION_CONTROLS] = "controls" , |
319 | [GTK_ACCESSIBLE_RELATION_DESCRIBED_BY] = "describedby" , |
320 | [GTK_ACCESSIBLE_RELATION_DETAILS] = "details" , |
321 | [GTK_ACCESSIBLE_RELATION_ERROR_MESSAGE] = "errormessage" , |
322 | [GTK_ACCESSIBLE_RELATION_FLOW_TO] = "flowto" , |
323 | [GTK_ACCESSIBLE_RELATION_LABELLED_BY] = "labelledby" , |
324 | [GTK_ACCESSIBLE_RELATION_OWNS] = "owns" , |
325 | [GTK_ACCESSIBLE_RELATION_POS_IN_SET] = "posinset" , |
326 | [GTK_ACCESSIBLE_RELATION_ROW_COUNT] = "rowcount" , |
327 | [GTK_ACCESSIBLE_RELATION_ROW_INDEX] = "rowindex" , |
328 | [GTK_ACCESSIBLE_RELATION_ROW_INDEX_TEXT] = "rowindextext" , |
329 | [GTK_ACCESSIBLE_RELATION_ROW_SPAN] = "rowspan" , |
330 | [GTK_ACCESSIBLE_RELATION_SET_SIZE] = "setsize" , |
331 | }; |
332 | |
333 | /*< private > |
334 | * gtk_accessible_relation_get_attribute_name: |
335 | * @relation: a `GtkAccessibleRelation` |
336 | * |
337 | * Retrieves the name of a `GtkAccessibleRelation`. |
338 | * |
339 | * Returns: (transfer none): the name of the accessible relation |
340 | */ |
341 | const char * |
342 | gtk_accessible_relation_get_attribute_name (GtkAccessibleRelation relation) |
343 | { |
344 | g_return_val_if_fail (relation >= GTK_ACCESSIBLE_RELATION_ACTIVE_DESCENDANT && |
345 | relation <= GTK_ACCESSIBLE_RELATION_SET_SIZE, |
346 | "<none>" ); |
347 | |
348 | return relation_attrs[relation]; |
349 | } |
350 | |
351 | static const char *state_attrs[] = { |
352 | [GTK_ACCESSIBLE_STATE_BUSY] = "busy" , |
353 | [GTK_ACCESSIBLE_STATE_CHECKED] = "checked" , |
354 | [GTK_ACCESSIBLE_STATE_DISABLED] = "disabled" , |
355 | [GTK_ACCESSIBLE_STATE_EXPANDED] = "expanded" , |
356 | [GTK_ACCESSIBLE_STATE_HIDDEN] = "hidden" , |
357 | [GTK_ACCESSIBLE_STATE_INVALID] = "invalid" , |
358 | [GTK_ACCESSIBLE_STATE_PRESSED] = "pressed" , |
359 | [GTK_ACCESSIBLE_STATE_SELECTED] = "selected" , |
360 | }; |
361 | |
362 | /*< private > |
363 | * gtk_accessible_state_get_attribute_name: |
364 | * @state: a `GtkAccessibleState` |
365 | * |
366 | * Retrieves the name of a `GtkAccessibleState`. |
367 | * |
368 | * Returns: (transfer none): the name of the accessible state |
369 | */ |
370 | const char * |
371 | gtk_accessible_state_get_attribute_name (GtkAccessibleState state) |
372 | { |
373 | g_return_val_if_fail (state >= GTK_ACCESSIBLE_STATE_BUSY && |
374 | state <= GTK_ACCESSIBLE_STATE_SELECTED, |
375 | "<none>" ); |
376 | |
377 | return state_attrs[state]; |
378 | } |
379 | |
380 | static void |
381 | gtk_at_context_init (GtkATContext *self) |
382 | { |
383 | self->accessible_role = GTK_ACCESSIBLE_ROLE_NONE; |
384 | |
385 | self->properties = |
386 | gtk_accessible_attribute_set_new (G_N_ELEMENTS (property_attrs), |
387 | name_func: (GtkAccessibleAttributeNameFunc) gtk_accessible_property_get_attribute_name, |
388 | default_func: (GtkAccessibleAttributeDefaultFunc) gtk_accessible_value_get_default_for_property); |
389 | self->relations = |
390 | gtk_accessible_attribute_set_new (G_N_ELEMENTS (relation_attrs), |
391 | name_func: (GtkAccessibleAttributeNameFunc) gtk_accessible_relation_get_attribute_name, |
392 | default_func: (GtkAccessibleAttributeDefaultFunc) gtk_accessible_value_get_default_for_relation); |
393 | self->states = |
394 | gtk_accessible_attribute_set_new (G_N_ELEMENTS (state_attrs), |
395 | name_func: (GtkAccessibleAttributeNameFunc) gtk_accessible_state_get_attribute_name, |
396 | default_func: (GtkAccessibleAttributeDefaultFunc) gtk_accessible_value_get_default_for_state); |
397 | } |
398 | |
399 | /** |
400 | * gtk_at_context_get_accessible: (attributes org.gtk.Method.get_property=accessible) |
401 | * @self: a `GtkATContext` |
402 | * |
403 | * Retrieves the `GtkAccessible` using this context. |
404 | * |
405 | * Returns: (transfer none): a `GtkAccessible` |
406 | */ |
407 | GtkAccessible * |
408 | gtk_at_context_get_accessible (GtkATContext *self) |
409 | { |
410 | g_return_val_if_fail (GTK_IS_AT_CONTEXT (self), NULL); |
411 | |
412 | return self->accessible; |
413 | } |
414 | |
415 | /*< private > |
416 | * gtk_at_context_set_accessible_role: |
417 | * @self: a `GtkATContext` |
418 | * @role: the accessible role for the context |
419 | * |
420 | * Sets the accessible role for the given `GtkATContext`. |
421 | * |
422 | * This function can only be called if the `GtkATContext` is unrealized. |
423 | */ |
424 | void |
425 | gtk_at_context_set_accessible_role (GtkATContext *self, |
426 | GtkAccessibleRole role) |
427 | { |
428 | g_return_if_fail (GTK_IS_AT_CONTEXT (self)); |
429 | g_return_if_fail (!self->realized); |
430 | |
431 | if (self->accessible_role == role) |
432 | return; |
433 | |
434 | self->accessible_role = role; |
435 | |
436 | g_object_notify_by_pspec (G_OBJECT (self), pspec: obj_props[PROP_ACCESSIBLE_ROLE]); |
437 | } |
438 | |
439 | /** |
440 | * gtk_at_context_get_accessible_role: (attributes org.gtk.Method.get_property=accessible-role) |
441 | * @self: a `GtkATContext` |
442 | * |
443 | * Retrieves the accessible role of this context. |
444 | * |
445 | * Returns: a `GtkAccessibleRole` |
446 | */ |
447 | GtkAccessibleRole |
448 | gtk_at_context_get_accessible_role (GtkATContext *self) |
449 | { |
450 | g_return_val_if_fail (GTK_IS_AT_CONTEXT (self), GTK_ACCESSIBLE_ROLE_NONE); |
451 | |
452 | return self->accessible_role; |
453 | } |
454 | |
455 | /*< private > |
456 | * gtk_at_context_set_display: |
457 | * @self: a `GtkATContext` |
458 | * @display: a `GdkDisplay` |
459 | * |
460 | * Sets the `GdkDisplay` used by the `GtkATContext`. |
461 | * |
462 | * This function can only be called if the `GtkATContext` is |
463 | * not realized. |
464 | */ |
465 | void |
466 | gtk_at_context_set_display (GtkATContext *self, |
467 | GdkDisplay *display) |
468 | { |
469 | g_return_if_fail (GTK_IS_AT_CONTEXT (self)); |
470 | g_return_if_fail (display == NULL || GDK_IS_DISPLAY (display)); |
471 | |
472 | if (self->display == display) |
473 | return; |
474 | |
475 | if (self->realized) |
476 | return; |
477 | |
478 | self->display = display; |
479 | |
480 | g_object_notify_by_pspec (G_OBJECT (self), pspec: obj_props[PROP_DISPLAY]); |
481 | } |
482 | |
483 | /*< private > |
484 | * gtk_at_context_get_display: |
485 | * @self: a `GtkATContext` |
486 | * |
487 | * Retrieves the `GdkDisplay` used to create the context. |
488 | * |
489 | * Returns: (transfer none): a `GdkDisplay` |
490 | */ |
491 | GdkDisplay * |
492 | gtk_at_context_get_display (GtkATContext *self) |
493 | { |
494 | g_return_val_if_fail (GTK_IS_AT_CONTEXT (self), NULL); |
495 | |
496 | return self->display; |
497 | } |
498 | |
499 | static const struct { |
500 | const char *name; |
501 | const char *env_name; |
502 | GtkATContext * (* create_context) (GtkAccessibleRole accessible_role, |
503 | GtkAccessible *accessible, |
504 | GdkDisplay *display); |
505 | } a11y_backends[] = { |
506 | #if defined(GDK_WINDOWING_WAYLAND) |
507 | { "AT-SPI (Wayland)" , "atspi" , gtk_at_spi_create_context }, |
508 | #endif |
509 | #if defined(GDK_WINDOWING_X11) |
510 | { "AT-SPI (X11)" , "atspi" , gtk_at_spi_create_context }, |
511 | #endif |
512 | { "Test" , "test" , gtk_test_at_context_new }, |
513 | { NULL, NULL, NULL }, |
514 | }; |
515 | |
516 | /** |
517 | * gtk_at_context_create: (constructor) |
518 | * @accessible_role: the accessible role used by the `GtkATContext` |
519 | * @accessible: the `GtkAccessible` implementation using the `GtkATContext` |
520 | * @display: the `GdkDisplay` used by the `GtkATContext` |
521 | * |
522 | * Creates a new `GtkATContext` instance for the given accessible role, |
523 | * accessible instance, and display connection. |
524 | * |
525 | * The `GtkATContext` implementation being instantiated will depend on the |
526 | * platform. |
527 | * |
528 | * Returns: (nullable) (transfer full): the `GtkATContext` |
529 | */ |
530 | GtkATContext * |
531 | gtk_at_context_create (GtkAccessibleRole accessible_role, |
532 | GtkAccessible *accessible, |
533 | GdkDisplay *display) |
534 | { |
535 | static const char *gtk_a11y_env; |
536 | |
537 | if (gtk_a11y_env == NULL) |
538 | { |
539 | gtk_a11y_env = g_getenv (variable: "GTK_A11Y" ); |
540 | if (gtk_a11y_env == NULL) |
541 | gtk_a11y_env = "0" ; |
542 | |
543 | if (g_ascii_strcasecmp (s1: gtk_a11y_env, s2: "help" ) == 0) |
544 | { |
545 | g_print (format: "Supported arguments for GTK_A11Y environment variable:\n" ); |
546 | |
547 | #if defined(GDK_WINDOWING_X11) || defined(GDK_WINDOWING_WAYLAND) |
548 | g_print (format: " atspi - Use the AT-SPI accessibility backend\n" ); |
549 | #endif |
550 | g_print (format: " test - Use the test accessibility backend\n" ); |
551 | g_print (format: " none - Disable the accessibility backend\n" ); |
552 | g_print (format: " help - Print this help\n\n" ); |
553 | g_print (format: "Other arguments will cause a warning and be ignored.\n" ); |
554 | |
555 | gtk_a11y_env = "0" ; |
556 | } |
557 | } |
558 | |
559 | /* Short-circuit disabling the accessibility support */ |
560 | if (g_ascii_strcasecmp (s1: gtk_a11y_env, s2: "none" ) == 0) |
561 | return NULL; |
562 | |
563 | GtkATContext *res = NULL; |
564 | |
565 | for (guint i = 0; i < G_N_ELEMENTS (a11y_backends); i++) |
566 | { |
567 | if (a11y_backends[i].name == NULL) |
568 | break; |
569 | |
570 | if (a11y_backends[i].create_context != NULL && |
571 | (*gtk_a11y_env == '0' || g_ascii_strcasecmp (s1: a11y_backends[i].env_name, s2: gtk_a11y_env) == 0)) |
572 | { |
573 | res = a11y_backends[i].create_context (accessible_role, accessible, display); |
574 | if (res != NULL) |
575 | break; |
576 | } |
577 | } |
578 | |
579 | if (*gtk_a11y_env != '0' && res == NULL) |
580 | g_warning ("Unrecognized accessibility backend \"%s\". Try GTK_A11Y=help" , gtk_a11y_env); |
581 | |
582 | /* Fall back to the test context, so we can get debugging data */ |
583 | if (res == NULL) |
584 | res = g_object_new (GTK_TYPE_TEST_AT_CONTEXT, |
585 | first_property_name: "accessible_role" , accessible_role, |
586 | "accessible" , accessible, |
587 | "display" , display, |
588 | NULL); |
589 | |
590 | return res; |
591 | } |
592 | |
593 | /*< private > |
594 | * gtk_at_context_clone: (constructor) |
595 | * @self: the `GtkATContext` to clone |
596 | * @role: the accessible role of the clone, or %GTK_ACCESSIBLE_ROLE_NONE to |
597 | * use the same accessible role of @self |
598 | * @accessible: (nullable): the accessible creating the context, or %NULL to |
599 | * use the same `GtkAccessible` of @self |
600 | * @display: (nullable): the display connection, or %NULL to use the same |
601 | * `GdkDisplay` of @self |
602 | * |
603 | * Clones the state of the given `GtkATContext`, using @role, @accessible, |
604 | * and @display. |
605 | * |
606 | * If @self is realized, the returned `GtkATContext` will also be realized. |
607 | * |
608 | * Returns: (transfer full): the newly created `GtkATContext` |
609 | */ |
610 | GtkATContext * |
611 | gtk_at_context_clone (GtkATContext *self, |
612 | GtkAccessibleRole role, |
613 | GtkAccessible *accessible, |
614 | GdkDisplay *display) |
615 | { |
616 | g_return_val_if_fail (self == NULL || GTK_IS_AT_CONTEXT (self), NULL); |
617 | g_return_val_if_fail (accessible == NULL || GTK_IS_ACCESSIBLE (accessible), NULL); |
618 | g_return_val_if_fail (display == NULL || GDK_IS_DISPLAY (display), NULL); |
619 | |
620 | if (self != NULL && role == GTK_ACCESSIBLE_ROLE_NONE) |
621 | role = self->accessible_role; |
622 | |
623 | if (self != NULL && accessible == NULL) |
624 | accessible = self->accessible; |
625 | |
626 | if (self != NULL && display == NULL) |
627 | display = self->display; |
628 | |
629 | GtkATContext *res = gtk_at_context_create (accessible_role: role, accessible, display); |
630 | |
631 | if (self != NULL) |
632 | { |
633 | g_clear_pointer (&res->states, gtk_accessible_attribute_set_unref); |
634 | g_clear_pointer (&res->properties, gtk_accessible_attribute_set_unref); |
635 | g_clear_pointer (&res->relations, gtk_accessible_attribute_set_unref); |
636 | |
637 | res->states = gtk_accessible_attribute_set_ref (self: self->states); |
638 | res->properties = gtk_accessible_attribute_set_ref (self: self->properties); |
639 | res->relations = gtk_accessible_attribute_set_ref (self: self->relations); |
640 | |
641 | if (self->realized) |
642 | gtk_at_context_realize (self: res); |
643 | } |
644 | |
645 | return res; |
646 | } |
647 | |
648 | gboolean |
649 | gtk_at_context_is_realized (GtkATContext *self) |
650 | { |
651 | return self->realized; |
652 | } |
653 | |
654 | void |
655 | gtk_at_context_realize (GtkATContext *self) |
656 | { |
657 | if (self->realized) |
658 | return; |
659 | |
660 | GTK_NOTE (A11Y, g_message ("Realizing AT context '%s'" , G_OBJECT_TYPE_NAME (self))); |
661 | GTK_AT_CONTEXT_GET_CLASS (ptr: self)->realize (self); |
662 | |
663 | self->realized = TRUE; |
664 | } |
665 | |
666 | void |
667 | gtk_at_context_unrealize (GtkATContext *self) |
668 | { |
669 | if (!self->realized) |
670 | return; |
671 | |
672 | GTK_NOTE (A11Y, g_message ("Unrealizing AT context '%s'" , G_OBJECT_TYPE_NAME (self))); |
673 | GTK_AT_CONTEXT_GET_CLASS (ptr: self)->unrealize (self); |
674 | |
675 | self->realized = FALSE; |
676 | } |
677 | |
678 | /*< private > |
679 | * gtk_at_context_update: |
680 | * @self: a `GtkATContext` |
681 | * |
682 | * Notifies the AT connected to this `GtkATContext` that the accessible |
683 | * state and its properties have changed. |
684 | */ |
685 | void |
686 | gtk_at_context_update (GtkATContext *self) |
687 | { |
688 | g_return_if_fail (GTK_IS_AT_CONTEXT (self)); |
689 | |
690 | if (!self->realized) |
691 | return; |
692 | |
693 | /* There's no point in notifying of state changes if there weren't any */ |
694 | if (self->updated_properties == 0 && |
695 | self->updated_relations == 0 && |
696 | self->updated_states == 0) |
697 | return; |
698 | |
699 | GTK_AT_CONTEXT_GET_CLASS (ptr: self)->state_change (self, |
700 | self->updated_states, self->updated_properties, self->updated_relations, |
701 | self->states, self->properties, self->relations); |
702 | g_signal_emit (instance: self, signal_id: obj_signals[STATE_CHANGE], detail: 0); |
703 | |
704 | self->updated_properties = 0; |
705 | self->updated_relations = 0; |
706 | self->updated_states = 0; |
707 | } |
708 | |
709 | /*< private > |
710 | * gtk_at_context_set_accessible_state: |
711 | * @self: a `GtkATContext` |
712 | * @state: a `GtkAccessibleState` |
713 | * @value: (nullable): `GtkAccessibleValue` |
714 | * |
715 | * Sets the @value for the given @state of a `GtkATContext`. |
716 | * |
717 | * If @value is %NULL, the state is unset. |
718 | * |
719 | * This function will accumulate state changes until gtk_at_context_update() |
720 | * is called. |
721 | */ |
722 | void |
723 | gtk_at_context_set_accessible_state (GtkATContext *self, |
724 | GtkAccessibleState state, |
725 | GtkAccessibleValue *value) |
726 | { |
727 | g_return_if_fail (GTK_IS_AT_CONTEXT (self)); |
728 | |
729 | gboolean res = FALSE; |
730 | |
731 | if (value != NULL) |
732 | res = gtk_accessible_attribute_set_add (self: self->states, attribute: state, value); |
733 | else |
734 | res = gtk_accessible_attribute_set_remove (self: self->states, state); |
735 | |
736 | if (res) |
737 | self->updated_states |= (1 << state); |
738 | } |
739 | |
740 | /*< private > |
741 | * gtk_at_context_has_accessible_state: |
742 | * @self: a `GtkATContext` |
743 | * @state: a `GtkAccessibleState` |
744 | * |
745 | * Checks whether a `GtkATContext` has the given @state set. |
746 | * |
747 | * Returns: %TRUE, if the accessible state is set |
748 | */ |
749 | gboolean |
750 | gtk_at_context_has_accessible_state (GtkATContext *self, |
751 | GtkAccessibleState state) |
752 | { |
753 | g_return_val_if_fail (GTK_IS_AT_CONTEXT (self), FALSE); |
754 | |
755 | return gtk_accessible_attribute_set_contains (self: self->states, state); |
756 | } |
757 | |
758 | /*< private > |
759 | * gtk_at_context_get_accessible_state: |
760 | * @self: a `GtkATContext` |
761 | * @state: a `GtkAccessibleState` |
762 | * |
763 | * Retrieves the value for the accessible state of a `GtkATContext`. |
764 | * |
765 | * Returns: (transfer none): the value for the given state |
766 | */ |
767 | GtkAccessibleValue * |
768 | gtk_at_context_get_accessible_state (GtkATContext *self, |
769 | GtkAccessibleState state) |
770 | { |
771 | g_return_val_if_fail (GTK_IS_AT_CONTEXT (self), NULL); |
772 | |
773 | return gtk_accessible_attribute_set_get_value (self: self->states, state); |
774 | } |
775 | |
776 | /*< private > |
777 | * gtk_at_context_set_accessible_property: |
778 | * @self: a `GtkATContext` |
779 | * @property: a `GtkAccessibleProperty` |
780 | * @value: (nullable): `GtkAccessibleValue` |
781 | * |
782 | * Sets the @value for the given @property of a `GtkATContext`. |
783 | * |
784 | * If @value is %NULL, the property is unset. |
785 | * |
786 | * This function will accumulate property changes until gtk_at_context_update() |
787 | * is called. |
788 | */ |
789 | void |
790 | gtk_at_context_set_accessible_property (GtkATContext *self, |
791 | GtkAccessibleProperty property, |
792 | GtkAccessibleValue *value) |
793 | { |
794 | g_return_if_fail (GTK_IS_AT_CONTEXT (self)); |
795 | |
796 | gboolean res = FALSE; |
797 | |
798 | if (value != NULL) |
799 | res = gtk_accessible_attribute_set_add (self: self->properties, attribute: property, value); |
800 | else |
801 | res = gtk_accessible_attribute_set_remove (self: self->properties, state: property); |
802 | |
803 | if (res) |
804 | self->updated_properties |= (1 << property); |
805 | } |
806 | |
807 | /*< private > |
808 | * gtk_at_context_has_accessible_property: |
809 | * @self: a `GtkATContext` |
810 | * @property: a `GtkAccessibleProperty` |
811 | * |
812 | * Checks whether a `GtkATContext` has the given @property set. |
813 | * |
814 | * Returns: %TRUE, if the accessible property is set |
815 | */ |
816 | gboolean |
817 | gtk_at_context_has_accessible_property (GtkATContext *self, |
818 | GtkAccessibleProperty property) |
819 | { |
820 | g_return_val_if_fail (GTK_IS_AT_CONTEXT (self), FALSE); |
821 | |
822 | return gtk_accessible_attribute_set_contains (self: self->properties, state: property); |
823 | } |
824 | |
825 | /*< private > |
826 | * gtk_at_context_get_accessible_property: |
827 | * @self: a `GtkATContext` |
828 | * @property: a `GtkAccessibleProperty` |
829 | * |
830 | * Retrieves the value for the accessible property of a `GtkATContext`. |
831 | * |
832 | * Returns: (transfer none): the value for the given property |
833 | */ |
834 | GtkAccessibleValue * |
835 | gtk_at_context_get_accessible_property (GtkATContext *self, |
836 | GtkAccessibleProperty property) |
837 | { |
838 | g_return_val_if_fail (GTK_IS_AT_CONTEXT (self), NULL); |
839 | |
840 | return gtk_accessible_attribute_set_get_value (self: self->properties, state: property); |
841 | } |
842 | |
843 | /*< private > |
844 | * gtk_at_context_set_accessible_relation: |
845 | * @self: a `GtkATContext` |
846 | * @relation: a `GtkAccessibleRelation` |
847 | * @value: (nullable): `GtkAccessibleValue` |
848 | * |
849 | * Sets the @value for the given @relation of a `GtkATContext`. |
850 | * |
851 | * If @value is %NULL, the relation is unset. |
852 | * |
853 | * This function will accumulate relation changes until gtk_at_context_update() |
854 | * is called. |
855 | */ |
856 | void |
857 | gtk_at_context_set_accessible_relation (GtkATContext *self, |
858 | GtkAccessibleRelation relation, |
859 | GtkAccessibleValue *value) |
860 | { |
861 | g_return_if_fail (GTK_IS_AT_CONTEXT (self)); |
862 | |
863 | gboolean res = FALSE; |
864 | |
865 | if (value != NULL) |
866 | res = gtk_accessible_attribute_set_add (self: self->relations, attribute: relation, value); |
867 | else |
868 | res = gtk_accessible_attribute_set_remove (self: self->relations, state: relation); |
869 | |
870 | if (res) |
871 | self->updated_relations |= (1 << relation); |
872 | } |
873 | |
874 | /*< private > |
875 | * gtk_at_context_has_accessible_relation: |
876 | * @self: a `GtkATContext` |
877 | * @relation: a `GtkAccessibleRelation` |
878 | * |
879 | * Checks whether a `GtkATContext` has the given @relation set. |
880 | * |
881 | * Returns: %TRUE, if the accessible relation is set |
882 | */ |
883 | gboolean |
884 | gtk_at_context_has_accessible_relation (GtkATContext *self, |
885 | GtkAccessibleRelation relation) |
886 | { |
887 | g_return_val_if_fail (GTK_IS_AT_CONTEXT (self), FALSE); |
888 | |
889 | return gtk_accessible_attribute_set_contains (self: self->relations, state: relation); |
890 | } |
891 | |
892 | /*< private > |
893 | * gtk_at_context_get_accessible_relation: |
894 | * @self: a `GtkATContext` |
895 | * @relation: a `GtkAccessibleRelation` |
896 | * |
897 | * Retrieves the value for the accessible relation of a `GtkATContext`. |
898 | * |
899 | * Returns: (transfer none): the value for the given relation |
900 | */ |
901 | GtkAccessibleValue * |
902 | gtk_at_context_get_accessible_relation (GtkATContext *self, |
903 | GtkAccessibleRelation relation) |
904 | { |
905 | g_return_val_if_fail (GTK_IS_AT_CONTEXT (self), NULL); |
906 | |
907 | return gtk_accessible_attribute_set_get_value (self: self->relations, state: relation); |
908 | } |
909 | |
910 | static gboolean |
911 | is_structural_role (GtkAccessibleRole role) |
912 | { |
913 | /* Keep the switch small while avoiding the compiler warning for |
914 | * unhandled enumeration values |
915 | */ |
916 | switch ((int) role) |
917 | { |
918 | case GTK_ACCESSIBLE_ROLE_FORM: |
919 | case GTK_ACCESSIBLE_ROLE_GROUP: |
920 | case GTK_ACCESSIBLE_ROLE_GENERIC: |
921 | case GTK_ACCESSIBLE_ROLE_LANDMARK: |
922 | case GTK_ACCESSIBLE_ROLE_LIST_ITEM: |
923 | case GTK_ACCESSIBLE_ROLE_REGION: |
924 | case GTK_ACCESSIBLE_ROLE_SEARCH: |
925 | case GTK_ACCESSIBLE_ROLE_SEPARATOR: |
926 | return TRUE; |
927 | |
928 | default: |
929 | break; |
930 | } |
931 | |
932 | return FALSE; |
933 | } |
934 | |
935 | /* See the WAI-ARIA ยง 4.3, "Accessible Name and Description Computation" */ |
936 | static void |
937 | gtk_at_context_get_name_accumulate (GtkATContext *self, |
938 | GPtrArray *names, |
939 | gboolean recurse) |
940 | { |
941 | GtkAccessibleValue *value = NULL; |
942 | |
943 | if (gtk_accessible_attribute_set_contains (self: self->properties, state: GTK_ACCESSIBLE_PROPERTY_LABEL)) |
944 | { |
945 | value = gtk_accessible_attribute_set_get_value (self: self->properties, state: GTK_ACCESSIBLE_PROPERTY_LABEL); |
946 | |
947 | g_ptr_array_add (array: names, data: (char *) gtk_string_accessible_value_get (value)); |
948 | } |
949 | |
950 | if (recurse && gtk_accessible_attribute_set_contains (self: self->relations, state: GTK_ACCESSIBLE_RELATION_LABELLED_BY)) |
951 | { |
952 | value = gtk_accessible_attribute_set_get_value (self: self->relations, state: GTK_ACCESSIBLE_RELATION_LABELLED_BY); |
953 | |
954 | GList *list = gtk_reference_list_accessible_value_get (value); |
955 | |
956 | for (GList *l = list; l != NULL; l = l->next) |
957 | { |
958 | GtkAccessible *rel = GTK_ACCESSIBLE (ptr: l->data); |
959 | GtkATContext *rel_context = gtk_accessible_get_at_context (self: rel); |
960 | |
961 | gtk_at_context_get_name_accumulate (self: rel_context, names, FALSE); |
962 | } |
963 | } |
964 | |
965 | GtkAccessibleRole role = gtk_at_context_get_accessible_role (self); |
966 | |
967 | switch ((int) role) |
968 | { |
969 | case GTK_ACCESSIBLE_ROLE_RANGE: |
970 | { |
971 | int range_attrs[] = { |
972 | GTK_ACCESSIBLE_PROPERTY_VALUE_TEXT, |
973 | GTK_ACCESSIBLE_PROPERTY_VALUE_NOW, |
974 | }; |
975 | |
976 | value = NULL; |
977 | for (int i = 0; i < G_N_ELEMENTS (range_attrs); i++) |
978 | { |
979 | if (gtk_accessible_attribute_set_contains (self: self->properties, state: range_attrs[i])) |
980 | { |
981 | value = gtk_accessible_attribute_set_get_value (self: self->properties, state: range_attrs[i]); |
982 | break; |
983 | } |
984 | } |
985 | |
986 | if (value != NULL) |
987 | g_ptr_array_add (array: names, data: (char *) gtk_string_accessible_value_get (value)); |
988 | } |
989 | break; |
990 | |
991 | default: |
992 | break; |
993 | } |
994 | |
995 | /* If there is no label or labelled-by attribute, hidden elements |
996 | * have no name |
997 | */ |
998 | if (gtk_accessible_attribute_set_contains (self: self->states, state: GTK_ACCESSIBLE_STATE_HIDDEN)) |
999 | { |
1000 | value = gtk_accessible_attribute_set_get_value (self: self->states, state: GTK_ACCESSIBLE_STATE_HIDDEN); |
1001 | |
1002 | if (gtk_boolean_accessible_value_get (value)) |
1003 | return; |
1004 | } |
1005 | |
1006 | /* This fallback is in place only for unlabelled elements */ |
1007 | if (names->len != 0) |
1008 | return; |
1009 | |
1010 | /* Ignore structural elements, namely: generic containers */ |
1011 | if (self->accessible != NULL && !is_structural_role (role)) |
1012 | g_ptr_array_add (array: names, data: (char *)G_OBJECT_TYPE_NAME (self->accessible)); |
1013 | } |
1014 | |
1015 | static void |
1016 | gtk_at_context_get_description_accumulate (GtkATContext *self, |
1017 | GPtrArray *labels, |
1018 | gboolean recurse) |
1019 | { |
1020 | GtkAccessibleValue *value = NULL; |
1021 | |
1022 | if (gtk_accessible_attribute_set_contains (self: self->properties, state: GTK_ACCESSIBLE_PROPERTY_DESCRIPTION)) |
1023 | { |
1024 | value = gtk_accessible_attribute_set_get_value (self: self->properties, state: GTK_ACCESSIBLE_PROPERTY_DESCRIPTION); |
1025 | |
1026 | g_ptr_array_add (array: labels, data: (char *) gtk_string_accessible_value_get (value)); |
1027 | } |
1028 | |
1029 | if (recurse && gtk_accessible_attribute_set_contains (self: self->relations, state: GTK_ACCESSIBLE_RELATION_DESCRIBED_BY)) |
1030 | { |
1031 | value = gtk_accessible_attribute_set_get_value (self: self->relations, state: GTK_ACCESSIBLE_RELATION_DESCRIBED_BY); |
1032 | |
1033 | GList *list = gtk_reference_list_accessible_value_get (value); |
1034 | |
1035 | for (GList *l = list; l != NULL; l = l->next) |
1036 | { |
1037 | GtkAccessible *rel = GTK_ACCESSIBLE (ptr: l->data); |
1038 | GtkATContext *rel_context = gtk_accessible_get_at_context (self: rel); |
1039 | |
1040 | gtk_at_context_get_description_accumulate (self: rel_context, labels, FALSE); |
1041 | } |
1042 | } |
1043 | |
1044 | GtkAccessibleRole role = gtk_at_context_get_accessible_role (self); |
1045 | |
1046 | switch ((int) role) |
1047 | { |
1048 | case GTK_ACCESSIBLE_ROLE_RANGE: |
1049 | { |
1050 | int range_attrs[] = { |
1051 | GTK_ACCESSIBLE_PROPERTY_VALUE_TEXT, |
1052 | GTK_ACCESSIBLE_PROPERTY_VALUE_NOW, |
1053 | }; |
1054 | |
1055 | value = NULL; |
1056 | for (int i = 0; i < G_N_ELEMENTS (range_attrs); i++) |
1057 | { |
1058 | if (gtk_accessible_attribute_set_contains (self: self->properties, state: range_attrs[i])) |
1059 | { |
1060 | value = gtk_accessible_attribute_set_get_value (self: self->properties, state: range_attrs[i]); |
1061 | break; |
1062 | } |
1063 | } |
1064 | |
1065 | if (value != NULL) |
1066 | g_ptr_array_add (array: labels, data: (char *) gtk_string_accessible_value_get (value)); |
1067 | } |
1068 | break; |
1069 | |
1070 | default: |
1071 | break; |
1072 | } |
1073 | |
1074 | /* If there is no description or described-by attribute, hidden elements |
1075 | * have no description |
1076 | */ |
1077 | if (gtk_accessible_attribute_set_contains (self: self->states, state: GTK_ACCESSIBLE_STATE_HIDDEN)) |
1078 | { |
1079 | value = gtk_accessible_attribute_set_get_value (self: self->states, state: GTK_ACCESSIBLE_STATE_HIDDEN); |
1080 | |
1081 | if (gtk_boolean_accessible_value_get (value)) |
1082 | return; |
1083 | } |
1084 | } |
1085 | |
1086 | /*< private > |
1087 | * gtk_at_context_get_name: |
1088 | * @self: a `GtkATContext` |
1089 | * |
1090 | * Retrieves the accessible name of the `GtkATContext`. |
1091 | * |
1092 | * This is a convenience function meant to be used by `GtkATContext` implementations. |
1093 | * |
1094 | * Returns: (transfer full): the label of the `GtkATContext` |
1095 | */ |
1096 | char * |
1097 | gtk_at_context_get_name (GtkATContext *self) |
1098 | { |
1099 | g_return_val_if_fail (GTK_IS_AT_CONTEXT (self), NULL); |
1100 | |
1101 | GPtrArray *names = g_ptr_array_new (); |
1102 | |
1103 | gtk_at_context_get_name_accumulate (self, names, TRUE); |
1104 | |
1105 | if (names->len == 0) |
1106 | { |
1107 | g_ptr_array_unref (array: names); |
1108 | return g_strdup (str: "" ); |
1109 | } |
1110 | |
1111 | GString *res = g_string_new (init: "" ); |
1112 | g_string_append (string: res, g_ptr_array_index (names, 0)); |
1113 | |
1114 | for (guint i = 1; i < names->len; i++) |
1115 | { |
1116 | g_string_append (string: res, val: " " ); |
1117 | g_string_append (string: res, g_ptr_array_index (names, i)); |
1118 | } |
1119 | |
1120 | g_ptr_array_unref (array: names); |
1121 | |
1122 | return g_string_free (string: res, FALSE); |
1123 | } |
1124 | |
1125 | /*< private > |
1126 | * gtk_at_context_get_description: |
1127 | * @self: a `GtkATContext` |
1128 | * |
1129 | * Retrieves the accessible description of the `GtkATContext`. |
1130 | * |
1131 | * This is a convenience function meant to be used by `GtkATContext` implementations. |
1132 | * |
1133 | * Returns: (transfer full): the label of the `GtkATContext` |
1134 | */ |
1135 | char * |
1136 | gtk_at_context_get_description (GtkATContext *self) |
1137 | { |
1138 | g_return_val_if_fail (GTK_IS_AT_CONTEXT (self), NULL); |
1139 | |
1140 | GPtrArray *names = g_ptr_array_new (); |
1141 | |
1142 | gtk_at_context_get_description_accumulate (self, labels: names, TRUE); |
1143 | |
1144 | if (names->len == 0) |
1145 | { |
1146 | g_ptr_array_unref (array: names); |
1147 | return g_strdup (str: "" ); |
1148 | } |
1149 | |
1150 | GString *res = g_string_new (init: "" ); |
1151 | g_string_append (string: res, g_ptr_array_index (names, 0)); |
1152 | |
1153 | for (guint i = 1; i < names->len; i++) |
1154 | { |
1155 | g_string_append (string: res, val: " " ); |
1156 | g_string_append (string: res, g_ptr_array_index (names, i)); |
1157 | } |
1158 | |
1159 | g_ptr_array_unref (array: names); |
1160 | |
1161 | return g_string_free (string: res, FALSE); |
1162 | } |
1163 | |
1164 | void |
1165 | gtk_at_context_platform_changed (GtkATContext *self, |
1166 | GtkAccessiblePlatformChange change) |
1167 | { |
1168 | gtk_at_context_realize (self); |
1169 | |
1170 | GTK_AT_CONTEXT_GET_CLASS (ptr: self)->platform_change (self, change); |
1171 | } |
1172 | |
1173 | void |
1174 | gtk_at_context_bounds_changed (GtkATContext *self) |
1175 | { |
1176 | if (!self->realized) |
1177 | return; |
1178 | |
1179 | GTK_AT_CONTEXT_GET_CLASS (ptr: self)->bounds_change (self); |
1180 | } |
1181 | |
1182 | void |
1183 | gtk_at_context_child_changed (GtkATContext *self, |
1184 | GtkAccessibleChildChange change, |
1185 | GtkAccessible *child) |
1186 | { |
1187 | if (!self->realized) |
1188 | return; |
1189 | |
1190 | GTK_AT_CONTEXT_GET_CLASS (ptr: self)->child_change (self, change, child); |
1191 | } |
1192 | |