]> Pileus Git - ~andy/gtk/blob - gtk/a11y/gtklabelaccessible.c
Initial conversion of GailWidget to GtkWidgetAccessible
[~andy/gtk] / gtk / a11y / gtklabelaccessible.c
1 /* GAIL - The GNOME Accessibility Enabling Library
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, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 #include "config.h"
21
22 #include <string.h>
23
24 #include <gtk/gtk.h>
25 #include <gtk/gtkpango.h>
26 #include "gtklabelaccessible.h"
27
28
29 static void atk_text_interface_init (AtkTextIface *iface);
30
31 G_DEFINE_TYPE_WITH_CODE (GtkLabelAccessible, gtk_label_accessible, GTK_TYPE_WIDGET_ACCESSIBLE,
32                          G_IMPLEMENT_INTERFACE (ATK_TYPE_TEXT, atk_text_interface_init))
33
34 static void
35 gtk_label_accessible_init (GtkLabelAccessible *label)
36 {
37 }
38
39 static void
40 gtk_label_accessible_initialize (AtkObject *obj,
41                                  gpointer   data)
42 {
43   GtkWidget  *widget;
44   GtkLabelAccessible *accessible;
45
46   ATK_OBJECT_CLASS (gtk_label_accessible_parent_class)->initialize (obj, data);
47
48   accessible = GTK_LABEL_ACCESSIBLE (obj);
49
50   widget = GTK_WIDGET (data);
51
52   accessible->text = g_strdup (gtk_label_get_text (GTK_LABEL (widget)));
53
54   /*
55    * Check whether ancestor of GtkLabel is a GtkButton and if so
56    * set accessible parent for GtkLabelAccessible
57    */
58   while (widget != NULL)
59     {
60       widget = gtk_widget_get_parent (widget);
61       if (GTK_IS_BUTTON (widget))
62         {
63           atk_object_set_parent (obj, gtk_widget_get_accessible (widget));
64           break;
65         }
66     }
67
68   if (GTK_IS_ACCEL_LABEL (widget))
69     obj->role = ATK_ROLE_ACCEL_LABEL;
70   else
71     obj->role = ATK_ROLE_LABEL;
72 }
73
74 static void
75 gtk_label_accessible_notify_gtk (GObject    *obj,
76                                  GParamSpec *pspec)
77 {
78   GtkWidget *widget = GTK_WIDGET (obj);
79   AtkObject* atk_obj = gtk_widget_get_accessible (widget);
80   GtkLabelAccessible *accessible;
81   gint length;
82
83   accessible = GTK_LABEL_ACCESSIBLE (atk_obj);
84
85   if (strcmp (pspec->name, "label") == 0)
86     {
87       const gchar *text;
88
89       text = gtk_label_get_text (GTK_LABEL (widget));
90       if (strcmp (accessible->text, text) == 0)
91         return;
92
93       /* Create a delete text and an insert text signal */
94       length = g_utf8_strlen (accessible->text, -1);
95       if (length > 0)
96         g_signal_emit_by_name (atk_obj, "text_changed::delete", 0, length);
97
98       g_free (accessible->text);
99       accessible->text = g_strdup (text);
100
101       length = g_utf8_strlen (accessible->text, -1);
102       if (length > 0)
103         g_signal_emit_by_name (atk_obj, "text_changed::insert", 0, length);
104
105       if (atk_obj->name == NULL)
106         /* The label has changed so notify a change in accessible-name */
107         g_object_notify (G_OBJECT (atk_obj), "accessible-name");
108
109       g_signal_emit_by_name (atk_obj, "visible_data_changed");
110     }
111   else if (strcmp (pspec->name, "cursor-position") == 0)
112     {
113       g_signal_emit_by_name (atk_obj, "text_caret_moved",
114                              _gtk_label_get_cursor_position (GTK_LABEL (widget)));
115       g_signal_emit_by_name (atk_obj, "text_selection_changed");
116     }
117   else if (strcmp (pspec->name, "selection-bound") == 0)
118     {
119       g_signal_emit_by_name (atk_obj, "text_selection_changed");
120     }
121   else
122     GTK_WIDGET_ACCESSIBLE_CLASS (gtk_label_accessible_parent_class)->notify_gtk (obj, pspec);
123 }
124
125 static void
126 gtk_label_accessible_finalize (GObject *object)
127 {
128   GtkLabelAccessible *accessible = GTK_LABEL_ACCESSIBLE (object);
129
130   g_free (accessible->text);
131
132   G_OBJECT_CLASS (gtk_label_accessible_parent_class)->finalize (object);
133 }
134
135
136 /* atkobject.h */
137
138 static AtkStateSet *
139 gtk_label_accessible_ref_state_set (AtkObject *accessible)
140 {
141   AtkStateSet *state_set;
142   GtkWidget *widget;
143
144   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible));
145   if (widget == NULL)
146     return NULL;
147
148   state_set = ATK_OBJECT_CLASS (gtk_label_accessible_parent_class)->ref_state_set (accessible);
149   atk_state_set_add_state (state_set, ATK_STATE_MULTI_LINE);
150
151   return state_set;
152 }
153
154 AtkRelationSet *
155 gtk_label_accessible_ref_relation_set (AtkObject *obj)
156 {
157   GtkWidget *widget;
158   AtkRelationSet *relation_set;
159
160   g_return_val_if_fail (GTK_IS_LABEL_ACCESSIBLE (obj), NULL);
161
162   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (obj));
163   if (widget == NULL)
164     return NULL;
165
166   relation_set = ATK_OBJECT_CLASS (gtk_label_accessible_parent_class)->ref_relation_set (obj);
167
168   if (!atk_relation_set_contains (relation_set, ATK_RELATION_LABEL_FOR))
169     {
170       /* Get the mnemonic widget.
171        * The relation set is not updated if the mnemonic widget is changed
172        */
173       GtkWidget *mnemonic_widget;
174
175       mnemonic_widget = gtk_label_get_mnemonic_widget (GTK_LABEL (widget));
176
177       if (mnemonic_widget)
178         {
179           AtkObject *accessible_array[1];
180           AtkRelation* relation;
181
182           if (!gtk_widget_get_can_focus (mnemonic_widget))
183             {
184             /*
185              * Handle the case where a GtkFileChooserButton is specified
186              * as the mnemonic widget. use the combobox which is a child of the
187              * GtkFileChooserButton as the mnemonic widget. See bug #359843.
188              */
189              if (GTK_IS_BOX (mnemonic_widget))
190                {
191                   GList *list, *tmpl;
192
193                   list = gtk_container_get_children (GTK_CONTAINER (mnemonic_widget));
194                   if (g_list_length (list) == 2)
195                     {
196                       tmpl = g_list_last (list);
197                       if (GTK_IS_COMBO_BOX(tmpl->data))
198                         {
199                           mnemonic_widget = GTK_WIDGET(tmpl->data);
200                         }
201                     }
202                   g_list_free (list);
203                 }
204             }
205           accessible_array[0] = gtk_widget_get_accessible (mnemonic_widget);
206           relation = atk_relation_new (accessible_array, 1,
207                                        ATK_RELATION_LABEL_FOR);
208           atk_relation_set_add (relation_set, relation);
209           /*
210            * Unref the relation so that it is not leaked.
211            */
212           g_object_unref (relation);
213         }
214     }
215   return relation_set;
216 }
217
218 static const gchar*
219 gtk_label_accessible_get_name (AtkObject *accessible)
220 {
221   const gchar *name;
222
223   g_return_val_if_fail (GTK_IS_LABEL_ACCESSIBLE (accessible), NULL);
224
225   name = ATK_OBJECT_CLASS (gtk_label_accessible_parent_class)->get_name (accessible);
226   if (name != NULL)
227     return name;
228   else
229     {
230       /*
231        * Get the text on the label
232        */
233       GtkWidget *widget;
234
235       widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible));
236       if (widget == NULL)
237         return NULL;
238
239       g_return_val_if_fail (GTK_IS_LABEL (widget), NULL);
240
241       return gtk_label_get_text (GTK_LABEL (widget));
242     }
243 }
244
245 static void
246 gtk_label_accessible_class_init (GtkLabelAccessibleClass *klass)
247 {
248   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
249   AtkObjectClass *class = ATK_OBJECT_CLASS (klass);
250   GtkWidgetAccessibleClass *widget_class = (GtkWidgetAccessibleClass*)klass;
251
252   gobject_class->finalize = gtk_label_accessible_finalize;
253
254   widget_class->notify_gtk = gtk_label_accessible_notify_gtk;
255
256   class->get_name = gtk_label_accessible_get_name;
257   class->ref_state_set = gtk_label_accessible_ref_state_set;
258   class->ref_relation_set = gtk_label_accessible_ref_relation_set;
259   class->initialize = gtk_label_accessible_initialize;
260 }
261
262 /* atktext.h */
263
264 static gchar*
265 gtk_label_accessible_get_text (AtkText *atk_text,
266                                gint     start_pos,
267                                gint     end_pos)
268 {
269   GtkWidget *widget;
270   const gchar *text;
271
272   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (atk_text));
273   if (widget == NULL)
274     return NULL;
275
276   text = gtk_label_get_text (GTK_LABEL (widget));
277
278   if (text)
279     return g_utf8_substring (text, start_pos, end_pos > -1 ? end_pos : g_utf8_strlen (text, -1));
280
281   return NULL;
282 }
283
284 static gchar *
285 gtk_label_accessible_get_text_before_offset (AtkText         *text,
286                                              gint             offset,
287                                              AtkTextBoundary  boundary_type,
288                                              gint            *start_offset,
289                                              gint            *end_offset)
290 {
291   GtkWidget *widget;
292
293   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
294   if (widget == NULL)
295     return NULL;
296
297   return _gtk_pango_get_text_before (gtk_label_get_layout (GTK_LABEL (widget)),
298                                      boundary_type, offset,
299                                      start_offset, end_offset);
300 }
301
302 static gchar*
303 gtk_label_accessible_get_text_at_offset (AtkText         *text,
304                                          gint             offset,
305                                          AtkTextBoundary  boundary_type,
306                                          gint            *start_offset,
307                                          gint            *end_offset)
308 {
309   GtkWidget *widget;
310
311   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
312   if (widget == NULL)
313     return NULL;
314
315   return _gtk_pango_get_text_at (gtk_label_get_layout (GTK_LABEL (widget)),
316                                  boundary_type, offset,
317                                  start_offset, end_offset);
318 }
319
320 static gchar*
321 gtk_label_accessible_get_text_after_offset (AtkText         *text,
322                                             gint             offset,
323                                             AtkTextBoundary  boundary_type,
324                                             gint            *start_offset,
325                                             gint            *end_offset)
326 {
327   GtkWidget *widget;
328
329   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
330   if (widget == NULL)
331     return NULL;
332
333   return _gtk_pango_get_text_after (gtk_label_get_layout (GTK_LABEL (widget)),
334                                     boundary_type, offset,
335                                     start_offset, end_offset);
336 }
337
338 static gint
339 gtk_label_accessible_get_character_count (AtkText *atk_text)
340 {
341   GtkWidget *widget;
342   const gchar *text;
343
344   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (atk_text));
345   if (widget == NULL)
346     return 0;
347
348   text = gtk_label_get_text (GTK_LABEL (widget));
349
350   if (text)
351     return g_utf8_strlen (text, -1);
352
353   return 0;
354 }
355
356 static gint
357 gtk_label_accessible_get_caret_offset (AtkText *text)
358 {
359   GtkWidget *widget;
360
361   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
362   if (widget == NULL)
363     return 0;
364
365   return _gtk_label_get_cursor_position (GTK_LABEL (widget));
366 }
367
368 static gboolean
369 gtk_label_accessible_set_caret_offset (AtkText *text,
370                                        gint     offset)
371 {
372   GtkWidget *widget;
373   GtkLabel *label;
374
375   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
376   if (widget == NULL)
377     return FALSE;
378
379   label = GTK_LABEL (widget);
380
381   if (!gtk_label_get_selectable (label))
382     return FALSE;
383
384   gtk_label_select_region (label, offset, offset);
385
386   return TRUE;
387 }
388
389 static gint
390 gtk_label_accessible_get_n_selections (AtkText *text)
391 {
392   GtkWidget *widget;
393
394   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
395   if (widget == NULL)
396     return 0;
397
398   if (gtk_label_get_selection_bounds (GTK_LABEL (widget), NULL, NULL))
399     return 1;
400
401   return 0;
402 }
403
404 static gchar *
405 gtk_label_accessible_get_selection (AtkText *text,
406                                     gint     selection_num,
407                                     gint    *start_pos,
408                                     gint    *end_pos)
409 {
410   GtkWidget *widget;
411   GtkLabel  *label;
412
413   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
414   if (widget == NULL)
415     return NULL;
416
417   if (selection_num != 0)
418     return NULL;
419
420   label = GTK_LABEL (widget);
421
422   if (gtk_label_get_selection_bounds (label, start_pos, end_pos))
423     {
424       const gchar *text;
425
426       text = gtk_label_get_text (label);
427
428       if (text)
429         return g_utf8_substring (text, *start_pos, *end_pos);
430     }
431
432   return NULL;
433 }
434
435 static gboolean
436 gtk_label_accessible_add_selection (AtkText *text,
437                                     gint     start_pos,
438                                     gint     end_pos)
439 {
440   GtkWidget *widget;
441   GtkLabel  *label;
442   gint start, end;
443
444   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
445   if (widget == NULL)
446     return FALSE;
447
448   label = GTK_LABEL (widget);
449
450   if (!gtk_label_get_selectable (label))
451     return FALSE;
452
453   if (!gtk_label_get_selection_bounds (label, &start, &end))
454     {
455       gtk_label_select_region (label, start_pos, end_pos);
456       return TRUE;
457     }
458   else
459     return FALSE;
460 }
461
462 static gboolean
463 gtk_label_accessible_remove_selection (AtkText *text,
464                                        gint     selection_num)
465 {
466   GtkWidget *widget;
467   GtkLabel  *label;
468   gint start, end;
469
470   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
471   if (widget == NULL)
472     return FALSE;
473
474   if (selection_num != 0)
475      return FALSE;
476
477   label = GTK_LABEL (widget);
478
479   if (!gtk_label_get_selectable (label))
480      return FALSE;
481
482   if (gtk_label_get_selection_bounds (label, &start, &end))
483     {
484       gtk_label_select_region (label, end, end);
485       return TRUE;
486     }
487   else
488     return FALSE;
489 }
490
491 static gboolean
492 gtk_label_accessible_set_selection (AtkText *text,
493                                     gint     selection_num,
494                                     gint     start_pos,
495                                     gint     end_pos)
496 {
497   GtkWidget *widget;
498   GtkLabel  *label;
499   gint start, end;
500
501   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
502   if (widget == NULL)
503     return FALSE;
504
505   if (selection_num != 0)
506     return FALSE;
507
508   label = GTK_LABEL (widget);
509
510   if (!gtk_label_get_selectable (label))
511     return FALSE;
512
513   if (gtk_label_get_selection_bounds (label, &start, &end))
514     {
515       gtk_label_select_region (label, start_pos, end_pos);
516       return TRUE;
517     }
518   else
519     return FALSE;
520 }
521
522 static void
523 gtk_label_accessible_get_character_extents (AtkText      *text,
524                                             gint          offset,
525                                             gint         *x,
526                                             gint         *y,
527                                             gint         *width,
528                                             gint         *height,
529                                             AtkCoordType  coords)
530 {
531   GtkWidget *widget;
532   GtkLabel *label;
533   PangoRectangle char_rect;
534   const gchar *label_text;
535   gint index, x_layout, y_layout;
536   GdkWindow *window;
537   gint x_window, y_window;
538
539   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
540   if (widget == NULL)
541     return;
542
543   label = GTK_LABEL (widget);
544
545   gtk_label_get_layout_offsets (label, &x_layout, &y_layout);
546   label_text = gtk_label_get_text (label);
547   index = g_utf8_offset_to_pointer (label_text, offset) - label_text;
548   pango_layout_index_to_pos (gtk_label_get_layout (label), index, &char_rect);
549   pango_extents_to_pixels (&char_rect, NULL);
550
551   window = gtk_widget_get_window (widget);
552   gdk_window_get_origin (window, &x_window, &y_window);
553
554   *x = x_window + x_layout + char_rect.x;
555   *y = x_window + y_layout + char_rect.y;
556   *width = char_rect.width;
557   *height = char_rect.height;
558
559   if (coords == ATK_XY_WINDOW)
560     {
561       window = gdk_window_get_toplevel (window);
562       gdk_window_get_origin (window, &x_window, &y_window);
563
564       *x -= x_window;
565       *y -= y_window;
566     }
567 }
568
569 static gint
570 gtk_label_accessible_get_offset_at_point (AtkText      *atk_text,
571                                           gint          x,
572                                           gint          y,
573                                           AtkCoordType  coords)
574 {
575   GtkWidget *widget;
576   GtkLabel *label;
577   const gchar *text;
578   gint index, x_layout, y_layout;
579   gint x_window, y_window;
580   gint x_local, y_local;
581   GdkWindow *window;
582
583   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (atk_text));
584   if (widget == NULL)
585     return -1;
586
587   label = GTK_LABEL (widget);
588
589   gtk_label_get_layout_offsets (label, &x_layout, &y_layout);
590
591   window = gtk_widget_get_window (widget);
592   gdk_window_get_origin (window, &x_window, &y_window);
593
594   x_local = x - x_layout - x_window;
595   y_local = y - y_layout - y_window;
596
597   if (coords == ATK_XY_WINDOW)
598     {
599       window = gdk_window_get_toplevel (window);
600       gdk_window_get_origin (window, &x_window, &y_window);
601
602       x_local += x_window;
603       y_local += y_window;
604     }
605
606   if (!pango_layout_xy_to_index (gtk_label_get_layout (label),
607                                  x_local * PANGO_SCALE,
608                                  y_local * PANGO_SCALE,
609                                  &index, NULL))
610     {
611       if (x_local < 0 || y_local < 0)
612         index = 0;
613       else
614         index = -1;
615     }
616
617   if (index != -1)
618     {
619       text = gtk_label_get_text (label);
620       return g_utf8_pointer_to_offset (text, text + index);
621     }
622
623   return -1;
624 }
625
626 static AtkAttributeSet *
627 add_attribute (AtkAttributeSet  *attributes,
628                AtkTextAttribute  attr,
629                const gchar      *value)
630 {
631   AtkAttribute *at;
632
633   at = g_new (AtkAttribute, 1);
634   at->name = g_strdup (atk_text_attribute_get_name (attr));
635   at->value = g_strdup (value);
636
637   return g_slist_prepend (attributes, at);
638 }
639
640 static AtkAttributeSet*
641 gtk_label_accessible_get_run_attributes (AtkText *text,
642                                          gint     offset,
643                                          gint    *start_offset,
644                                          gint    *end_offset)
645 {
646   GtkWidget *widget;
647   AtkAttributeSet *attributes;
648
649   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
650   if (widget == NULL)
651     return NULL;
652
653   attributes = NULL;
654   attributes = add_attribute (attributes, ATK_TEXT_ATTR_DIRECTION,
655                    atk_text_attribute_get_value (ATK_TEXT_ATTR_DIRECTION,
656                                                  gtk_widget_get_direction (widget)));
657   attributes = _gtk_pango_get_run_attributes (attributes,
658                                               gtk_label_get_layout (GTK_LABEL (widget)),
659                                               offset,
660                                               start_offset,
661                                               end_offset);
662
663   return attributes;
664 }
665
666 static AtkAttributeSet *
667 gtk_label_accessible_get_default_attributes (AtkText *text)
668 {
669   GtkWidget *widget;
670   AtkAttributeSet *attributes;
671
672   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
673   if (widget == NULL)
674     return NULL;
675
676   attributes = NULL;
677   attributes = add_attribute (attributes, ATK_TEXT_ATTR_DIRECTION,
678                    atk_text_attribute_get_value (ATK_TEXT_ATTR_DIRECTION,
679                                                  gtk_widget_get_direction (widget)));
680   attributes = _gtk_pango_get_default_attributes (attributes,
681                                                   gtk_label_get_layout (GTK_LABEL (widget)));
682   attributes = _gtk_style_context_get_attributes (attributes,
683                                                   gtk_widget_get_style_context (widget),
684                                                   gtk_widget_get_state_flags (widget));
685
686   return attributes;
687 }
688
689 static gunichar
690 gtk_label_accessible_get_character_at_offset (AtkText *atk_text,
691                                               gint     offset)
692 {
693   GtkWidget *widget;
694   const gchar *text;
695   gchar *index;
696
697   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (atk_text));
698   if (widget == NULL)
699     return '\0';
700
701   text = gtk_label_get_text (GTK_LABEL (widget));
702   if (offset >= g_utf8_strlen (text, -1))
703     return '\0';
704
705   index = g_utf8_offset_to_pointer (text, offset);
706
707   return g_utf8_get_char (index);
708 }
709
710 static void
711 atk_text_interface_init (AtkTextIface *iface)
712 {
713   iface->get_text = gtk_label_accessible_get_text;
714   iface->get_character_at_offset = gtk_label_accessible_get_character_at_offset;
715   iface->get_text_before_offset = gtk_label_accessible_get_text_before_offset;
716   iface->get_text_at_offset = gtk_label_accessible_get_text_at_offset;
717   iface->get_text_after_offset = gtk_label_accessible_get_text_after_offset;
718   iface->get_character_count = gtk_label_accessible_get_character_count;
719   iface->get_caret_offset = gtk_label_accessible_get_caret_offset;
720   iface->set_caret_offset = gtk_label_accessible_set_caret_offset;
721   iface->get_n_selections = gtk_label_accessible_get_n_selections;
722   iface->get_selection = gtk_label_accessible_get_selection;
723   iface->add_selection = gtk_label_accessible_add_selection;
724   iface->remove_selection = gtk_label_accessible_remove_selection;
725   iface->set_selection = gtk_label_accessible_set_selection;
726   iface->get_character_extents = gtk_label_accessible_get_character_extents;
727   iface->get_offset_at_point = gtk_label_accessible_get_offset_at_point;
728   iface->get_run_attributes = gtk_label_accessible_get_run_attributes;
729   iface->get_default_attributes = gtk_label_accessible_get_default_attributes;
730 }
731