1 /* GTK+ - accessibility implementations
2 * Copyright 2001, 2002, 2003 Sun Microsystems Inc.
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 /* Preamble {{{1 */
19
20 #include "config.h"
21
22 #include <gtk/gtk.h>
23 #include <gtk/gtkpango.h>
24 #include "gtkwidgetprivate.h"
25 #include "gtklabelprivate.h"
26 #include "gtklabelaccessible.h"
27 #include "gtklabelaccessibleprivate.h"
28 #include "gtkstylecontextprivate.h"
29
30 struct _GtkLabelAccessiblePrivate
31 {
32 gint cursor_position;
33 gint selection_bound;
34
35 GList *links;
36 };
37
38 typedef struct _GtkLabelAccessibleLink GtkLabelAccessibleLink;
39 typedef struct _GtkLabelAccessibleLinkClass GtkLabelAccessibleLinkClass;
40
41 struct _GtkLabelAccessibleLink
42 {
43 AtkHyperlink parent;
44
45 GtkLabelAccessible *label;
46 gint index;
47 gboolean focused;
48 };
49
50 struct _GtkLabelAccessibleLinkClass
51 {
52 AtkHyperlinkClass parent_class;
53 };
54
55 static GtkLabelAccessibleLink *gtk_label_accessible_link_new (GtkLabelAccessible *label,
56 gint idx);
57
58 typedef struct _GtkLabelAccessibleLinkImpl GtkLabelAccessibleLinkImpl;
59 typedef struct _GtkLabelAccessibleLinkImplClass GtkLabelAccessibleLinkImplClass;
60
61 struct _GtkLabelAccessibleLinkImpl
62 {
63 AtkObject parent;
64
65 GtkLabelAccessibleLink *link;
66 };
67
68 struct _GtkLabelAccessibleLinkImplClass
69 {
70 AtkObjectClass parent_class;
71 };
72
73 /* GtkLabelAccessibleLinkImpl {{{1 */
74
75 GType _gtk_label_accessible_link_impl_get_type (void);
76
77 static void atk_hyperlink_impl_interface_init (AtkHyperlinkImplIface *iface);
78
G_DEFINE_TYPE_WITH_CODE(GtkLabelAccessibleLinkImpl,_gtk_label_accessible_link_impl,ATK_TYPE_OBJECT,G_IMPLEMENT_INTERFACE (ATK_TYPE_HYPERLINK_IMPL,atk_hyperlink_impl_interface_init))79 G_DEFINE_TYPE_WITH_CODE (GtkLabelAccessibleLinkImpl, _gtk_label_accessible_link_impl, ATK_TYPE_OBJECT,
80 G_IMPLEMENT_INTERFACE (ATK_TYPE_HYPERLINK_IMPL, atk_hyperlink_impl_interface_init))
81
82 static AtkHyperlink *
83 gtk_label_accessible_link_impl_get_hyperlink (AtkHyperlinkImpl *atk_impl)
84 {
85 GtkLabelAccessibleLinkImpl *impl = (GtkLabelAccessibleLinkImpl *)atk_impl;
86
87 return ATK_HYPERLINK (g_object_ref (impl->link));
88 }
89
90 static void
atk_hyperlink_impl_interface_init(AtkHyperlinkImplIface * iface)91 atk_hyperlink_impl_interface_init (AtkHyperlinkImplIface *iface)
92 {
93 iface->get_hyperlink = gtk_label_accessible_link_impl_get_hyperlink;
94 }
95
96 static AtkStateSet *
gtk_label_accessible_link_impl_ref_state_set(AtkObject * obj)97 gtk_label_accessible_link_impl_ref_state_set (AtkObject *obj)
98 {
99 AtkStateSet *state_set;
100 GtkLabelAccessibleLink *link;
101 GtkWidget *widget;
102
103 link = ((GtkLabelAccessibleLinkImpl *)obj)->link;
104
105 state_set = atk_object_ref_state_set (atk_object_get_parent (obj));
106
107 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (atk_object_get_parent (obj)));
108 if (widget)
109 {
110 if (gtk_widget_get_can_focus (widget))
111 {
112 atk_state_set_add_state (state_set, ATK_STATE_FOCUSABLE);
113 if (_gtk_label_get_link_focused (GTK_LABEL (widget), link->index))
114 atk_state_set_add_state (state_set, ATK_STATE_FOCUSED);
115 else
116 atk_state_set_remove_state (state_set, ATK_STATE_FOCUSED);
117 }
118
119 if (_gtk_label_get_link_visited (GTK_LABEL (widget), link->index))
120 atk_state_set_add_state (state_set, ATK_STATE_VISITED);
121 }
122
123 return state_set;
124 }
125
126 static void
_gtk_label_accessible_link_impl_init(GtkLabelAccessibleLinkImpl * impl)127 _gtk_label_accessible_link_impl_init (GtkLabelAccessibleLinkImpl *impl)
128 {
129 atk_object_set_role (ATK_OBJECT (impl), ATK_ROLE_LINK);
130 }
131
132 static void
_gtk_label_accessible_link_impl_finalize(GObject * obj)133 _gtk_label_accessible_link_impl_finalize (GObject *obj)
134 {
135 GtkLabelAccessibleLinkImpl *impl = (GtkLabelAccessibleLinkImpl *)obj;
136
137 g_object_unref (impl->link);
138
139 G_OBJECT_CLASS (_gtk_label_accessible_link_impl_parent_class)->finalize (obj);
140 }
141
142 static void
_gtk_label_accessible_link_impl_class_init(GtkLabelAccessibleLinkImplClass * class)143 _gtk_label_accessible_link_impl_class_init (GtkLabelAccessibleLinkImplClass *class)
144 {
145 GObjectClass *object_class = G_OBJECT_CLASS (class);
146 AtkObjectClass *atk_obj_class = ATK_OBJECT_CLASS (class);
147
148 object_class->finalize = _gtk_label_accessible_link_impl_finalize;
149 atk_obj_class->ref_state_set = gtk_label_accessible_link_impl_ref_state_set;
150 }
151
152 /* 'Public' API {{{2 */
153
154 static GtkLabelAccessibleLinkImpl *
gtk_label_accessible_link_impl_new(GtkLabelAccessible * label,gint idx)155 gtk_label_accessible_link_impl_new (GtkLabelAccessible *label,
156 gint idx)
157 {
158 GtkLabelAccessibleLinkImpl *impl;
159
160 impl = g_object_new (_gtk_label_accessible_link_impl_get_type (), NULL);
161 impl->link = gtk_label_accessible_link_new (label, idx);
162 atk_object_set_parent (ATK_OBJECT (impl), ATK_OBJECT (label));
163
164 return impl;
165 }
166
167 /* GtkLabelAccessibleLink {{{1 */
168
169 GType _gtk_label_accessible_link_get_type (void);
170
171 static void atk_action_interface_init (AtkActionIface *iface);
172
G_DEFINE_TYPE_WITH_CODE(GtkLabelAccessibleLink,_gtk_label_accessible_link,ATK_TYPE_HYPERLINK,G_IMPLEMENT_INTERFACE (ATK_TYPE_ACTION,atk_action_interface_init))173 G_DEFINE_TYPE_WITH_CODE (GtkLabelAccessibleLink, _gtk_label_accessible_link, ATK_TYPE_HYPERLINK,
174 G_IMPLEMENT_INTERFACE (ATK_TYPE_ACTION, atk_action_interface_init))
175
176 static gchar *
177 gtk_label_accessible_link_get_uri (AtkHyperlink *atk_link,
178 gint i)
179 {
180 GtkLabelAccessibleLink *link = (GtkLabelAccessibleLink *)atk_link;
181 GtkWidget *widget;
182 const gchar *uri;
183
184 g_return_val_if_fail (i == 0, NULL);
185
186 if (link->label == NULL)
187 return NULL;
188
189 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (link->label));
190 uri = _gtk_label_get_link_uri (GTK_LABEL (widget), link->index);
191
192 return g_strdup (uri);
193 }
194
195 static gint
gtk_label_accessible_link_get_n_anchors(AtkHyperlink * atk_link)196 gtk_label_accessible_link_get_n_anchors (AtkHyperlink *atk_link)
197 {
198 return 1;
199 }
200
201 static gboolean
gtk_label_accessible_link_is_valid(AtkHyperlink * atk_link)202 gtk_label_accessible_link_is_valid (AtkHyperlink *atk_link)
203 {
204 return TRUE;
205 }
206
207 static AtkObject *
gtk_label_accessible_link_get_object(AtkHyperlink * atk_link,gint i)208 gtk_label_accessible_link_get_object (AtkHyperlink *atk_link,
209 gint i)
210 {
211 GtkLabelAccessibleLink *link = (GtkLabelAccessibleLink *)atk_link;
212
213 g_return_val_if_fail (i == 0, NULL);
214
215 return ATK_OBJECT (link->label);
216 }
217
218 static gint
gtk_label_accessible_link_get_start_index(AtkHyperlink * atk_link)219 gtk_label_accessible_link_get_start_index (AtkHyperlink *atk_link)
220 {
221 GtkLabelAccessibleLink *link = (GtkLabelAccessibleLink *)atk_link;
222 GtkWidget *widget;
223 gint start, end;
224
225 if (link->label == NULL)
226 return 0;
227
228 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (link->label));
229 _gtk_label_get_link_extent (GTK_LABEL (widget), link->index, &start, &end);
230
231 return start;
232 }
233
234 static gint
gtk_label_accessible_link_get_end_index(AtkHyperlink * atk_link)235 gtk_label_accessible_link_get_end_index (AtkHyperlink *atk_link)
236 {
237 GtkLabelAccessibleLink *link = (GtkLabelAccessibleLink *)atk_link;
238 GtkWidget *widget;
239 gint start, end;
240
241 if (link->label == NULL)
242 return 0;
243
244 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (link->label));
245 _gtk_label_get_link_extent (GTK_LABEL (widget), link->index, &start, &end);
246
247 return end;
248 }
249
250 static void
_gtk_label_accessible_link_init(GtkLabelAccessibleLink * link)251 _gtk_label_accessible_link_init (GtkLabelAccessibleLink *link)
252 {
253 }
254
255 static void
_gtk_label_accessible_link_class_init(GtkLabelAccessibleLinkClass * class)256 _gtk_label_accessible_link_class_init (GtkLabelAccessibleLinkClass *class)
257 {
258 AtkHyperlinkClass *atk_link_class = ATK_HYPERLINK_CLASS (class);
259
260 atk_link_class->get_uri = gtk_label_accessible_link_get_uri;
261 atk_link_class->get_n_anchors = gtk_label_accessible_link_get_n_anchors;
262 atk_link_class->is_valid = gtk_label_accessible_link_is_valid;
263 atk_link_class->get_object = gtk_label_accessible_link_get_object;
264 atk_link_class->get_start_index = gtk_label_accessible_link_get_start_index;
265 atk_link_class->get_end_index = gtk_label_accessible_link_get_end_index;
266 }
267
268 /* 'Public' API {{{2 */
269
270 static GtkLabelAccessibleLink *
gtk_label_accessible_link_new(GtkLabelAccessible * label,gint idx)271 gtk_label_accessible_link_new (GtkLabelAccessible *label,
272 gint idx)
273 {
274 GtkLabelAccessibleLink *link;
275
276 link = g_object_new (_gtk_label_accessible_link_get_type (), NULL);
277 link->label = label;
278 link->index = idx;
279
280 return link;
281 }
282
283 /* AtkAction implementation {{{2 */
284
285 static gboolean
gtk_label_accessible_link_do_action(AtkAction * action,gint i)286 gtk_label_accessible_link_do_action (AtkAction *action,
287 gint i)
288 {
289 GtkLabelAccessibleLink *link = (GtkLabelAccessibleLink *)action;
290 GtkWidget *widget;
291
292 if (i != 0)
293 return FALSE;
294
295 if (link->label == NULL)
296 return FALSE;
297
298 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (link->label));
299 if (widget == NULL)
300 return FALSE;
301
302 if (!gtk_widget_is_sensitive (widget) || !gtk_widget_get_visible (widget))
303 return FALSE;
304
305 _gtk_label_activate_link (GTK_LABEL (widget), link->index);
306
307 return TRUE;
308 }
309
310 static gint
gtk_label_accessible_link_get_n_actions(AtkAction * action)311 gtk_label_accessible_link_get_n_actions (AtkAction *action)
312 {
313 return 1;
314 }
315
316 static const gchar *
gtk_label_accessible_link_get_name(AtkAction * action,gint i)317 gtk_label_accessible_link_get_name (AtkAction *action,
318 gint i)
319 {
320 if (i != 0)
321 return NULL;
322
323 return "activate";
324 }
325
326 static void
atk_action_interface_init(AtkActionIface * iface)327 atk_action_interface_init (AtkActionIface *iface)
328 {
329 iface->do_action = gtk_label_accessible_link_do_action;
330 iface->get_n_actions = gtk_label_accessible_link_get_n_actions;
331 iface->get_name = gtk_label_accessible_link_get_name;
332 }
333
334 /* GtkLabelAccessible {{{1 */
335
336 static void atk_text_interface_init (AtkTextIface *iface);
337 static void atk_hypertext_interface_init (AtkHypertextIface *iface);
338
G_DEFINE_TYPE_WITH_CODE(GtkLabelAccessible,gtk_label_accessible,GTK_TYPE_WIDGET_ACCESSIBLE,G_ADD_PRIVATE (GtkLabelAccessible)G_IMPLEMENT_INTERFACE (ATK_TYPE_TEXT,atk_text_interface_init)G_IMPLEMENT_INTERFACE (ATK_TYPE_HYPERTEXT,atk_hypertext_interface_init))339 G_DEFINE_TYPE_WITH_CODE (GtkLabelAccessible, gtk_label_accessible, GTK_TYPE_WIDGET_ACCESSIBLE,
340 G_ADD_PRIVATE (GtkLabelAccessible)
341 G_IMPLEMENT_INTERFACE (ATK_TYPE_TEXT, atk_text_interface_init)
342 G_IMPLEMENT_INTERFACE (ATK_TYPE_HYPERTEXT, atk_hypertext_interface_init))
343
344 static void
345 gtk_label_accessible_init (GtkLabelAccessible *label)
346 {
347 label->priv = gtk_label_accessible_get_instance_private (label);
348 }
349
350 static void
gtk_label_accessible_initialize(AtkObject * obj,gpointer data)351 gtk_label_accessible_initialize (AtkObject *obj,
352 gpointer data)
353 {
354 GtkWidget *widget;
355
356 ATK_OBJECT_CLASS (gtk_label_accessible_parent_class)->initialize (obj, data);
357
358 widget = GTK_WIDGET (data);
359
360 _gtk_label_accessible_update_links (GTK_LABEL (widget));
361
362 /* Check whether ancestor of GtkLabel is a GtkButton
363 * and if so set accessible parent for GtkLabelAccessible
364 */
365 while (widget != NULL)
366 {
367 widget = gtk_widget_get_parent (widget);
368 if (GTK_IS_BUTTON (widget))
369 {
370 atk_object_set_parent (obj, gtk_widget_get_accessible (widget));
371 break;
372 }
373 }
374
375 obj->role = ATK_ROLE_LABEL;
376 }
377
378 static gboolean
check_for_selection_change(GtkLabelAccessible * accessible,GtkLabel * label)379 check_for_selection_change (GtkLabelAccessible *accessible,
380 GtkLabel *label)
381 {
382 gboolean ret_val = FALSE;
383 gint start, end;
384
385 if (gtk_label_get_selection_bounds (label, &start, &end))
386 {
387 if (end != accessible->priv->cursor_position ||
388 start != accessible->priv->selection_bound)
389 ret_val = TRUE;
390 }
391 else
392 {
393 ret_val = (accessible->priv->cursor_position != accessible->priv->selection_bound);
394 }
395
396 accessible->priv->cursor_position = end;
397 accessible->priv->selection_bound = start;
398
399 return ret_val;
400 }
401
402 static void
gtk_label_accessible_notify_gtk(GObject * obj,GParamSpec * pspec)403 gtk_label_accessible_notify_gtk (GObject *obj,
404 GParamSpec *pspec)
405 {
406 GtkWidget *widget = GTK_WIDGET (obj);
407 AtkObject* atk_obj = gtk_widget_get_accessible (widget);
408 GtkLabelAccessible *accessible;
409
410 accessible = GTK_LABEL_ACCESSIBLE (atk_obj);
411
412 if (g_strcmp0 (pspec->name, "cursor-position") == 0)
413 {
414 g_signal_emit_by_name (atk_obj, "text-caret-moved",
415 _gtk_label_get_cursor_position (GTK_LABEL (widget)));
416 if (check_for_selection_change (accessible, GTK_LABEL (widget)))
417 g_signal_emit_by_name (atk_obj, "text-selection-changed");
418 }
419 else if (g_strcmp0 (pspec->name, "selection-bound") == 0)
420 {
421 if (check_for_selection_change (accessible, GTK_LABEL (widget)))
422 g_signal_emit_by_name (atk_obj, "text-selection-changed");
423 }
424 else
425 GTK_WIDGET_ACCESSIBLE_CLASS (gtk_label_accessible_parent_class)->notify_gtk (obj, pspec);
426 }
427
428 /* atkobject.h */
429
430 static AtkStateSet *
gtk_label_accessible_ref_state_set(AtkObject * accessible)431 gtk_label_accessible_ref_state_set (AtkObject *accessible)
432 {
433 AtkStateSet *state_set;
434 GtkWidget *widget;
435
436 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible));
437 if (widget == NULL)
438 return NULL;
439
440 state_set = ATK_OBJECT_CLASS (gtk_label_accessible_parent_class)->ref_state_set (accessible);
441 atk_state_set_add_state (state_set, ATK_STATE_MULTI_LINE);
442
443 return state_set;
444 }
445
446 static AtkRelationSet *
gtk_label_accessible_ref_relation_set(AtkObject * obj)447 gtk_label_accessible_ref_relation_set (AtkObject *obj)
448 {
449 GtkWidget *widget;
450 AtkRelationSet *relation_set;
451
452 g_return_val_if_fail (GTK_IS_LABEL_ACCESSIBLE (obj), NULL);
453
454 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (obj));
455 if (widget == NULL)
456 return NULL;
457
458 relation_set = ATK_OBJECT_CLASS (gtk_label_accessible_parent_class)->ref_relation_set (obj);
459
460 if (!atk_relation_set_contains (relation_set, ATK_RELATION_LABEL_FOR))
461 {
462 /* Get the mnemonic widget.
463 * The relation set is not updated if the mnemonic widget is changed
464 */
465 GtkWidget *mnemonic_widget;
466
467 mnemonic_widget = gtk_label_get_mnemonic_widget (GTK_LABEL (widget));
468
469 if (mnemonic_widget)
470 {
471 AtkObject *accessible_array[1];
472 AtkRelation* relation;
473
474 if (!gtk_widget_get_can_focus (mnemonic_widget))
475 {
476 /*
477 * Handle the case where a GtkFileChooserButton is specified
478 * as the mnemonic widget. use the combobox which is a child of the
479 * GtkFileChooserButton as the mnemonic widget. See bug #359843.
480 */
481 if (GTK_IS_BOX (mnemonic_widget))
482 {
483 GList *list, *tmpl;
484
485 list = gtk_container_get_children (GTK_CONTAINER (mnemonic_widget));
486 if (g_list_length (list) == 2)
487 {
488 tmpl = g_list_last (list);
489 if (GTK_IS_COMBO_BOX(tmpl->data))
490 {
491 mnemonic_widget = GTK_WIDGET(tmpl->data);
492 }
493 }
494 g_list_free (list);
495 }
496 }
497 accessible_array[0] = gtk_widget_get_accessible (mnemonic_widget);
498 relation = atk_relation_new (accessible_array, 1,
499 ATK_RELATION_LABEL_FOR);
500 atk_relation_set_add (relation_set, relation);
501 /*
502 * Unref the relation so that it is not leaked.
503 */
504 g_object_unref (relation);
505 }
506 }
507 return relation_set;
508 }
509
510 static const gchar*
gtk_label_accessible_get_name(AtkObject * accessible)511 gtk_label_accessible_get_name (AtkObject *accessible)
512 {
513 const gchar *name;
514
515 g_return_val_if_fail (GTK_IS_LABEL_ACCESSIBLE (accessible), NULL);
516
517 name = ATK_OBJECT_CLASS (gtk_label_accessible_parent_class)->get_name (accessible);
518 if (name != NULL)
519 return name;
520 else
521 {
522 /*
523 * Get the text on the label
524 */
525 GtkWidget *widget;
526
527 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible));
528 if (widget == NULL)
529 return NULL;
530
531 g_return_val_if_fail (GTK_IS_LABEL (widget), NULL);
532
533 return gtk_label_get_text (GTK_LABEL (widget));
534 }
535 }
536
537 static gint
gtk_label_accessible_get_n_children(AtkObject * obj)538 gtk_label_accessible_get_n_children (AtkObject *obj)
539 {
540 GtkLabelAccessible *accessible = GTK_LABEL_ACCESSIBLE (obj);
541
542 return g_list_length (accessible->priv->links);
543 }
544
545 static AtkObject *
gtk_label_accessible_ref_child(AtkObject * obj,gint idx)546 gtk_label_accessible_ref_child (AtkObject *obj,
547 gint idx)
548 {
549 GtkLabelAccessible *accessible = GTK_LABEL_ACCESSIBLE (obj);
550 AtkObject *child;
551
552 child = g_list_nth_data (accessible->priv->links, idx);
553
554 if (child)
555 g_object_ref (child);
556
557 return child;
558 }
559
560 static void clear_links (GtkLabelAccessible *accessible);
561
562 static void
gtk_label_accessible_finalize(GObject * obj)563 gtk_label_accessible_finalize (GObject *obj)
564 {
565 GtkLabelAccessible *accessible = GTK_LABEL_ACCESSIBLE (obj);
566
567 clear_links (accessible);
568
569 G_OBJECT_CLASS (gtk_label_accessible_parent_class)->finalize (obj);
570 }
571
572 static void
gtk_label_accessible_class_init(GtkLabelAccessibleClass * klass)573 gtk_label_accessible_class_init (GtkLabelAccessibleClass *klass)
574 {
575 GObjectClass *object_class = G_OBJECT_CLASS (klass);
576 AtkObjectClass *class = ATK_OBJECT_CLASS (klass);
577 GtkWidgetAccessibleClass *widget_class = GTK_WIDGET_ACCESSIBLE_CLASS (klass);
578
579 object_class->finalize = gtk_label_accessible_finalize;
580
581 class->get_name = gtk_label_accessible_get_name;
582 class->ref_state_set = gtk_label_accessible_ref_state_set;
583 class->ref_relation_set = gtk_label_accessible_ref_relation_set;
584 class->initialize = gtk_label_accessible_initialize;
585
586 class->get_n_children = gtk_label_accessible_get_n_children;
587 class->ref_child = gtk_label_accessible_ref_child;
588
589 widget_class->notify_gtk = gtk_label_accessible_notify_gtk;
590 }
591
592 /* 'Public' API {{{2 */
593
594 void
_gtk_label_accessible_text_deleted(GtkLabel * label)595 _gtk_label_accessible_text_deleted (GtkLabel *label)
596 {
597 AtkObject *obj;
598 const char *text;
599 guint length;
600
601 obj = _gtk_widget_peek_accessible (GTK_WIDGET (label));
602 if (obj == NULL)
603 return;
604
605 text = gtk_label_get_text (label);
606 length = g_utf8_strlen (text, -1);
607 if (length > 0)
608 g_signal_emit_by_name (obj, "text-changed::delete", 0, length);
609 }
610
611 void
_gtk_label_accessible_text_inserted(GtkLabel * label)612 _gtk_label_accessible_text_inserted (GtkLabel *label)
613 {
614 AtkObject *obj;
615 const char *text;
616 guint length;
617
618 obj = _gtk_widget_peek_accessible (GTK_WIDGET (label));
619 if (obj == NULL)
620 return;
621
622 text = gtk_label_get_text (label);
623 length = g_utf8_strlen (text, -1);
624 if (length > 0)
625 g_signal_emit_by_name (obj, "text-changed::insert", 0, length);
626
627 if (obj->name == NULL)
628 /* The label has changed so notify a change in accessible-name */
629 g_object_notify (G_OBJECT (obj), "accessible-name");
630
631 g_signal_emit_by_name (obj, "visible-data-changed");
632 }
633
634 static void
clear_links(GtkLabelAccessible * accessible)635 clear_links (GtkLabelAccessible *accessible)
636 {
637 GList *l;
638 gint i;
639 GtkLabelAccessibleLinkImpl *impl;
640
641 for (l = accessible->priv->links, i = 0; l; l = l->next, i++)
642 {
643 impl = l->data;
644 g_signal_emit_by_name (accessible, "children-changed::remove", i, impl, NULL);
645 atk_object_set_parent (ATK_OBJECT (impl), NULL);
646 impl->link->label = NULL;
647 }
648 g_list_free_full (accessible->priv->links, g_object_unref);
649 accessible->priv->links = NULL;
650 }
651
652 static void
create_links(GtkLabelAccessible * accessible)653 create_links (GtkLabelAccessible *accessible)
654 {
655 GtkWidget *widget;
656 gint n, i;
657 GtkLabelAccessibleLinkImpl *impl;
658
659 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible));
660
661 n = _gtk_label_get_n_links (GTK_LABEL (widget));
662 for (i = 0; i < n; i++)
663 {
664 impl = gtk_label_accessible_link_impl_new (accessible, i);
665 accessible->priv->links = g_list_append (accessible->priv->links, impl);
666 g_signal_emit_by_name (accessible, "children-changed::add", i, impl, NULL);
667 }
668 }
669
670 void
_gtk_label_accessible_update_links(GtkLabel * label)671 _gtk_label_accessible_update_links (GtkLabel *label)
672 {
673 AtkObject *obj;
674
675 obj = _gtk_widget_peek_accessible (GTK_WIDGET (label));
676 if (obj == NULL)
677 return;
678
679 clear_links (GTK_LABEL_ACCESSIBLE (obj));
680 create_links (GTK_LABEL_ACCESSIBLE (obj));
681 }
682
683 void
_gtk_label_accessible_focus_link_changed(GtkLabel * label)684 _gtk_label_accessible_focus_link_changed (GtkLabel *label)
685 {
686 AtkObject *obj;
687 GtkLabelAccessible *accessible;
688 GList *l;
689 GtkLabelAccessibleLinkImpl *impl;
690 gboolean focused;
691
692 obj = _gtk_widget_peek_accessible (GTK_WIDGET (label));
693 if (obj == NULL)
694 return;
695
696 accessible = GTK_LABEL_ACCESSIBLE (obj);
697
698 for (l = accessible->priv->links; l; l = l->next)
699 {
700 impl = l->data;
701 focused = _gtk_label_get_link_focused (label, impl->link->index);
702 if (impl->link->focused != focused)
703 {
704 impl->link->focused = focused;
705 atk_object_notify_state_change (ATK_OBJECT (impl), ATK_STATE_FOCUSED, focused);
706 }
707 }
708 }
709
710 /* AtkText implementation {{{2 */
711
712 static gchar*
gtk_label_accessible_get_text(AtkText * atk_text,gint start_pos,gint end_pos)713 gtk_label_accessible_get_text (AtkText *atk_text,
714 gint start_pos,
715 gint end_pos)
716 {
717 GtkWidget *widget;
718 const gchar *text;
719
720 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (atk_text));
721 if (widget == NULL)
722 return NULL;
723
724 text = gtk_label_get_text (GTK_LABEL (widget));
725
726 if (text)
727 {
728 guint length;
729 const gchar *start, *end;
730
731 length = g_utf8_strlen (text, -1);
732 if (end_pos < 0 || end_pos > length)
733 end_pos = length;
734 if (start_pos > length)
735 start_pos = length;
736 if (end_pos <= start_pos)
737 return g_strdup ("");
738 start = g_utf8_offset_to_pointer (text, start_pos);
739 end = g_utf8_offset_to_pointer (start, end_pos - start_pos);
740 return g_strndup (start, end - start);
741 }
742
743 return NULL;
744 }
745
746 static gchar *
gtk_label_accessible_get_text_before_offset(AtkText * text,gint offset,AtkTextBoundary boundary_type,gint * start_offset,gint * end_offset)747 gtk_label_accessible_get_text_before_offset (AtkText *text,
748 gint offset,
749 AtkTextBoundary boundary_type,
750 gint *start_offset,
751 gint *end_offset)
752 {
753 GtkWidget *widget;
754
755 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
756 if (widget == NULL)
757 return NULL;
758
759 return _gtk_pango_get_text_before (gtk_label_get_layout (GTK_LABEL (widget)),
760 boundary_type, offset,
761 start_offset, end_offset);
762 }
763
764 static gchar*
gtk_label_accessible_get_text_at_offset(AtkText * text,gint offset,AtkTextBoundary boundary_type,gint * start_offset,gint * end_offset)765 gtk_label_accessible_get_text_at_offset (AtkText *text,
766 gint offset,
767 AtkTextBoundary boundary_type,
768 gint *start_offset,
769 gint *end_offset)
770 {
771 GtkWidget *widget;
772
773 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
774 if (widget == NULL)
775 return NULL;
776
777 return _gtk_pango_get_text_at (gtk_label_get_layout (GTK_LABEL (widget)),
778 boundary_type, offset,
779 start_offset, end_offset);
780 }
781
782 static gchar*
gtk_label_accessible_get_text_after_offset(AtkText * text,gint offset,AtkTextBoundary boundary_type,gint * start_offset,gint * end_offset)783 gtk_label_accessible_get_text_after_offset (AtkText *text,
784 gint offset,
785 AtkTextBoundary boundary_type,
786 gint *start_offset,
787 gint *end_offset)
788 {
789 GtkWidget *widget;
790
791 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
792 if (widget == NULL)
793 return NULL;
794
795 return _gtk_pango_get_text_after (gtk_label_get_layout (GTK_LABEL (widget)),
796 boundary_type, offset,
797 start_offset, end_offset);
798 }
799
800 static gint
gtk_label_accessible_get_character_count(AtkText * atk_text)801 gtk_label_accessible_get_character_count (AtkText *atk_text)
802 {
803 GtkWidget *widget;
804 const gchar *text;
805
806 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (atk_text));
807 if (widget == NULL)
808 return 0;
809
810 text = gtk_label_get_text (GTK_LABEL (widget));
811
812 if (text)
813 return g_utf8_strlen (text, -1);
814
815 return 0;
816 }
817
818 static gint
gtk_label_accessible_get_caret_offset(AtkText * text)819 gtk_label_accessible_get_caret_offset (AtkText *text)
820 {
821 GtkWidget *widget;
822
823 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
824 if (widget == NULL)
825 return 0;
826
827 return _gtk_label_get_cursor_position (GTK_LABEL (widget));
828 }
829
830 static gboolean
gtk_label_accessible_set_caret_offset(AtkText * text,gint offset)831 gtk_label_accessible_set_caret_offset (AtkText *text,
832 gint offset)
833 {
834 GtkWidget *widget;
835 GtkLabel *label;
836
837 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
838 if (widget == NULL)
839 return FALSE;
840
841 label = GTK_LABEL (widget);
842
843 if (!gtk_label_get_selectable (label))
844 return FALSE;
845
846 gtk_label_select_region (label, offset, offset);
847
848 return TRUE;
849 }
850
851 static gint
gtk_label_accessible_get_n_selections(AtkText * text)852 gtk_label_accessible_get_n_selections (AtkText *text)
853 {
854 GtkWidget *widget;
855
856 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
857 if (widget == NULL)
858 return 0;
859
860 if (gtk_label_get_selection_bounds (GTK_LABEL (widget), NULL, NULL))
861 return 1;
862
863 return 0;
864 }
865
866 static gchar *
gtk_label_accessible_get_selection(AtkText * atk_text,gint selection_num,gint * start_pos,gint * end_pos)867 gtk_label_accessible_get_selection (AtkText *atk_text,
868 gint selection_num,
869 gint *start_pos,
870 gint *end_pos)
871 {
872 GtkWidget *widget;
873 GtkLabel *label;
874
875 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (atk_text));
876 if (widget == NULL)
877 return NULL;
878
879 if (selection_num != 0)
880 return NULL;
881
882 label = GTK_LABEL (widget);
883
884 if (gtk_label_get_selection_bounds (label, start_pos, end_pos))
885 {
886 const gchar *text;
887
888 text = gtk_label_get_text (label);
889
890 if (text)
891 return g_utf8_substring (text, *start_pos, *end_pos);
892 }
893
894 return NULL;
895 }
896
897 static gboolean
gtk_label_accessible_add_selection(AtkText * text,gint start_pos,gint end_pos)898 gtk_label_accessible_add_selection (AtkText *text,
899 gint start_pos,
900 gint end_pos)
901 {
902 GtkWidget *widget;
903 GtkLabel *label;
904 gint start, end;
905
906 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
907 if (widget == NULL)
908 return FALSE;
909
910 label = GTK_LABEL (widget);
911
912 if (!gtk_label_get_selectable (label))
913 return FALSE;
914
915 if (!gtk_label_get_selection_bounds (label, &start, &end))
916 {
917 gtk_label_select_region (label, start_pos, end_pos);
918 return TRUE;
919 }
920 else
921 return FALSE;
922 }
923
924 static gboolean
gtk_label_accessible_remove_selection(AtkText * text,gint selection_num)925 gtk_label_accessible_remove_selection (AtkText *text,
926 gint selection_num)
927 {
928 GtkWidget *widget;
929 GtkLabel *label;
930 gint start, end;
931
932 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
933 if (widget == NULL)
934 return FALSE;
935
936 if (selection_num != 0)
937 return FALSE;
938
939 label = GTK_LABEL (widget);
940
941 if (!gtk_label_get_selectable (label))
942 return FALSE;
943
944 if (gtk_label_get_selection_bounds (label, &start, &end))
945 {
946 gtk_label_select_region (label, end, end);
947 return TRUE;
948 }
949 else
950 return FALSE;
951 }
952
953 static gboolean
gtk_label_accessible_set_selection(AtkText * text,gint selection_num,gint start_pos,gint end_pos)954 gtk_label_accessible_set_selection (AtkText *text,
955 gint selection_num,
956 gint start_pos,
957 gint end_pos)
958 {
959 GtkWidget *widget;
960 GtkLabel *label;
961 gint start, end;
962
963 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
964 if (widget == NULL)
965 return FALSE;
966
967 if (selection_num != 0)
968 return FALSE;
969
970 label = GTK_LABEL (widget);
971
972 if (!gtk_label_get_selectable (label))
973 return FALSE;
974
975 if (gtk_label_get_selection_bounds (label, &start, &end))
976 {
977 gtk_label_select_region (label, start_pos, end_pos);
978 return TRUE;
979 }
980 else
981 return FALSE;
982 }
983
984 static void
gtk_label_accessible_get_character_extents(AtkText * text,gint offset,gint * x,gint * y,gint * width,gint * height,AtkCoordType coords)985 gtk_label_accessible_get_character_extents (AtkText *text,
986 gint offset,
987 gint *x,
988 gint *y,
989 gint *width,
990 gint *height,
991 AtkCoordType coords)
992 {
993 GtkWidget *widget;
994 GtkLabel *label;
995 PangoRectangle char_rect;
996 const gchar *label_text;
997 gint index, x_layout, y_layout;
998 GdkWindow *window;
999 gint x_window, y_window;
1000
1001 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
1002 if (widget == NULL)
1003 return;
1004
1005 label = GTK_LABEL (widget);
1006
1007 gtk_label_get_layout_offsets (label, &x_layout, &y_layout);
1008 label_text = gtk_label_get_text (label);
1009 index = g_utf8_offset_to_pointer (label_text, offset) - label_text;
1010 pango_layout_index_to_pos (gtk_label_get_layout (label), index, &char_rect);
1011 pango_extents_to_pixels (&char_rect, NULL);
1012
1013 window = gtk_widget_get_window (widget);
1014 gdk_window_get_origin (window, &x_window, &y_window);
1015
1016 *x = x_window + x_layout + char_rect.x;
1017 *y = y_window + y_layout + char_rect.y;
1018 *width = char_rect.width;
1019 *height = char_rect.height;
1020
1021 if (coords == ATK_XY_WINDOW)
1022 {
1023 window = gdk_window_get_toplevel (window);
1024 gdk_window_get_origin (window, &x_window, &y_window);
1025
1026 *x -= x_window;
1027 *y -= y_window;
1028 }
1029 }
1030
1031 static gint
gtk_label_accessible_get_offset_at_point(AtkText * atk_text,gint x,gint y,AtkCoordType coords)1032 gtk_label_accessible_get_offset_at_point (AtkText *atk_text,
1033 gint x,
1034 gint y,
1035 AtkCoordType coords)
1036 {
1037 GtkWidget *widget;
1038 GtkLabel *label;
1039 const gchar *text;
1040 gint index, x_layout, y_layout;
1041 gint x_window, y_window;
1042 gint x_local, y_local;
1043 GdkWindow *window;
1044
1045 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (atk_text));
1046 if (widget == NULL)
1047 return -1;
1048
1049 label = GTK_LABEL (widget);
1050
1051 gtk_label_get_layout_offsets (label, &x_layout, &y_layout);
1052
1053 window = gtk_widget_get_window (widget);
1054 gdk_window_get_origin (window, &x_window, &y_window);
1055
1056 x_local = x - x_layout - x_window;
1057 y_local = y - y_layout - y_window;
1058
1059 if (coords == ATK_XY_WINDOW)
1060 {
1061 window = gdk_window_get_toplevel (window);
1062 gdk_window_get_origin (window, &x_window, &y_window);
1063
1064 x_local += x_window;
1065 y_local += y_window;
1066 }
1067
1068 if (!pango_layout_xy_to_index (gtk_label_get_layout (label),
1069 x_local * PANGO_SCALE,
1070 y_local * PANGO_SCALE,
1071 &index, NULL))
1072 {
1073 if (x_local < 0 || y_local < 0)
1074 index = 0;
1075 else
1076 index = -1;
1077 }
1078
1079 if (index != -1)
1080 {
1081 text = gtk_label_get_text (label);
1082 return g_utf8_pointer_to_offset (text, text + index);
1083 }
1084
1085 return -1;
1086 }
1087
1088 static AtkAttributeSet *
add_attribute(AtkAttributeSet * attributes,AtkTextAttribute attr,const gchar * value)1089 add_attribute (AtkAttributeSet *attributes,
1090 AtkTextAttribute attr,
1091 const gchar *value)
1092 {
1093 AtkAttribute *at;
1094
1095 at = g_new (AtkAttribute, 1);
1096 at->name = g_strdup (atk_text_attribute_get_name (attr));
1097 at->value = g_strdup (value);
1098
1099 return g_slist_prepend (attributes, at);
1100 }
1101
1102 static AtkAttributeSet*
gtk_label_accessible_get_run_attributes(AtkText * text,gint offset,gint * start_offset,gint * end_offset)1103 gtk_label_accessible_get_run_attributes (AtkText *text,
1104 gint offset,
1105 gint *start_offset,
1106 gint *end_offset)
1107 {
1108 GtkWidget *widget;
1109 AtkAttributeSet *attributes;
1110
1111 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
1112 if (widget == NULL)
1113 return NULL;
1114
1115 attributes = NULL;
1116 attributes = add_attribute (attributes, ATK_TEXT_ATTR_DIRECTION,
1117 atk_text_attribute_get_value (ATK_TEXT_ATTR_DIRECTION,
1118 gtk_widget_get_direction (widget)));
1119 attributes = _gtk_pango_get_run_attributes (attributes,
1120 gtk_label_get_layout (GTK_LABEL (widget)),
1121 offset,
1122 start_offset,
1123 end_offset);
1124
1125 return attributes;
1126 }
1127
1128 static AtkAttributeSet *
gtk_label_accessible_get_default_attributes(AtkText * text)1129 gtk_label_accessible_get_default_attributes (AtkText *text)
1130 {
1131 GtkWidget *widget;
1132 AtkAttributeSet *attributes;
1133
1134 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
1135 if (widget == NULL)
1136 return NULL;
1137
1138 attributes = NULL;
1139 attributes = add_attribute (attributes, ATK_TEXT_ATTR_DIRECTION,
1140 atk_text_attribute_get_value (ATK_TEXT_ATTR_DIRECTION,
1141 gtk_widget_get_direction (widget)));
1142 attributes = _gtk_pango_get_default_attributes (attributes,
1143 gtk_label_get_layout (GTK_LABEL (widget)));
1144 attributes = _gtk_style_context_get_attributes (attributes,
1145 gtk_widget_get_style_context (widget),
1146 gtk_widget_get_state_flags (widget));
1147
1148 return attributes;
1149 }
1150
1151 static gunichar
gtk_label_accessible_get_character_at_offset(AtkText * atk_text,gint offset)1152 gtk_label_accessible_get_character_at_offset (AtkText *atk_text,
1153 gint offset)
1154 {
1155 GtkWidget *widget;
1156 const gchar *text;
1157 gchar *index;
1158
1159 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (atk_text));
1160 if (widget == NULL)
1161 return '\0';
1162
1163 text = gtk_label_get_text (GTK_LABEL (widget));
1164 if (offset >= g_utf8_strlen (text, -1))
1165 return '\0';
1166
1167 index = g_utf8_offset_to_pointer (text, offset);
1168
1169 return g_utf8_get_char (index);
1170 }
1171
1172 static void
atk_text_interface_init(AtkTextIface * iface)1173 atk_text_interface_init (AtkTextIface *iface)
1174 {
1175 iface->get_text = gtk_label_accessible_get_text;
1176 iface->get_character_at_offset = gtk_label_accessible_get_character_at_offset;
1177 iface->get_text_before_offset = gtk_label_accessible_get_text_before_offset;
1178 iface->get_text_at_offset = gtk_label_accessible_get_text_at_offset;
1179 iface->get_text_after_offset = gtk_label_accessible_get_text_after_offset;
1180 iface->get_character_count = gtk_label_accessible_get_character_count;
1181 iface->get_caret_offset = gtk_label_accessible_get_caret_offset;
1182 iface->set_caret_offset = gtk_label_accessible_set_caret_offset;
1183 iface->get_n_selections = gtk_label_accessible_get_n_selections;
1184 iface->get_selection = gtk_label_accessible_get_selection;
1185 iface->add_selection = gtk_label_accessible_add_selection;
1186 iface->remove_selection = gtk_label_accessible_remove_selection;
1187 iface->set_selection = gtk_label_accessible_set_selection;
1188 iface->get_character_extents = gtk_label_accessible_get_character_extents;
1189 iface->get_offset_at_point = gtk_label_accessible_get_offset_at_point;
1190 iface->get_run_attributes = gtk_label_accessible_get_run_attributes;
1191 iface->get_default_attributes = gtk_label_accessible_get_default_attributes;
1192 }
1193
1194 /* AtkHypertext implementation {{{2 */
1195
1196 static AtkHyperlink *
gtk_label_accessible_get_link(AtkHypertext * hypertext,gint idx)1197 gtk_label_accessible_get_link (AtkHypertext *hypertext,
1198 gint idx)
1199 {
1200 GtkLabelAccessible *label = GTK_LABEL_ACCESSIBLE (hypertext);
1201 GtkLabelAccessibleLinkImpl *impl;
1202
1203 impl = (GtkLabelAccessibleLinkImpl *)g_list_nth_data (label->priv->links, idx);
1204
1205 if (impl)
1206 return ATK_HYPERLINK (impl->link);
1207
1208 return NULL;
1209 }
1210
1211 static gint
gtk_label_accessible_get_n_links(AtkHypertext * hypertext)1212 gtk_label_accessible_get_n_links (AtkHypertext *hypertext)
1213 {
1214 GtkWidget *widget;
1215
1216 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (hypertext));
1217
1218 return _gtk_label_get_n_links (GTK_LABEL (widget));
1219 }
1220
1221 static gint
gtk_label_accessible_get_link_index(AtkHypertext * hypertext,gint char_index)1222 gtk_label_accessible_get_link_index (AtkHypertext *hypertext,
1223 gint char_index)
1224 {
1225 GtkWidget *widget;
1226
1227 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (hypertext));
1228
1229 return _gtk_label_get_link_at (GTK_LABEL (widget), char_index);
1230 }
1231
1232 static void
atk_hypertext_interface_init(AtkHypertextIface * iface)1233 atk_hypertext_interface_init (AtkHypertextIface *iface)
1234 {
1235 iface->get_link = gtk_label_accessible_get_link;
1236 iface->get_n_links = gtk_label_accessible_get_n_links;
1237 iface->get_link_index = gtk_label_accessible_get_link_index;
1238 }
1239
1240 /* vim:set foldmethod=marker expandtab: */
1241