]> Pileus Git - ~andy/gtk/blob - gtk/a11y/gtklabelaccessible.c
GtkLabelAccessible: remove broken workarounds
[~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 "gtklabelaccessible.h"
26 #include <libgail-util/gailmisc.h>
27
28 static void       gtk_label_accessible_class_init            (GtkLabelAccessibleClass    *klass);
29 static void       gtk_label_accessible_init                  (GtkLabelAccessible         *label);
30 static void       gtk_label_accessible_real_initialize     (AtkObject         *obj,
31                                                     gpointer          data);
32 static void       gtk_label_accessible_real_notify_gtk     (GObject           *obj,
33                                                     GParamSpec        *pspec);
34 static void       gtk_label_accessible_init_text_util        (GtkLabelAccessible         *gail_label,
35                                                     GtkWidget         *widget);
36 static void       gtk_label_accessible_finalize              (GObject           *object);
37
38 static void       atk_text_interface_init          (AtkTextIface      *iface);
39
40 /* atkobject.h */
41
42 static const gchar* gtk_label_accessible_get_name         (AtkObject         *accessible);
43 static AtkStateSet*          gtk_label_accessible_ref_state_set  (AtkObject         *accessible);
44 static AtkRelationSet*       gtk_label_accessible_ref_relation_set (AtkObject         *accessible);
45
46 /* atktext.h */
47
48 static gchar*     gtk_label_accessible_get_text            (AtkText           *text,
49                                                     gint              start_pos,
50                                                     gint              end_pos);
51 static gunichar   gtk_label_accessible_get_character_at_offset(AtkText        *text,
52                                                     gint              offset);
53 static gchar*     gtk_label_accessible_get_text_before_offset(AtkText         *text,
54                                                     gint              offset,
55                                                     AtkTextBoundary   boundary_type,
56                                                     gint              *start_offset,
57                                                     gint              *end_offset);
58 static gchar*     gtk_label_accessible_get_text_at_offset    (AtkText         *text,
59                                                     gint              offset,
60                                                     AtkTextBoundary   boundary_type,
61                                                     gint              *start_offset,
62                                                     gint              *end_offset);
63 static gchar*     gtk_label_accessible_get_text_after_offset    (AtkText              *text,
64                                                     gint              offset,
65                                                     AtkTextBoundary   boundary_type,
66                                                     gint              *start_offset,
67                                                     gint              *end_offset);
68 static gint       gtk_label_accessible_get_character_count   (AtkText         *text);
69 static gint       gtk_label_accessible_get_caret_offset    (AtkText           *text);
70 static gboolean   gtk_label_accessible_set_caret_offset    (AtkText           *text,
71                                                     gint              offset);
72 static gint       gtk_label_accessible_get_n_selections    (AtkText           *text);
73 static gchar*     gtk_label_accessible_get_selection       (AtkText           *text,
74                                                     gint              selection_num,
75                                                     gint              *start_offset,
76                                                     gint              *end_offset);
77 static gboolean   gtk_label_accessible_add_selection       (AtkText           *text,
78                                                     gint              start_offset,
79                                                     gint              end_offset);
80 static gboolean   gtk_label_accessible_remove_selection    (AtkText           *text,
81                                                     gint              selection_num);
82 static gboolean   gtk_label_accessible_set_selection       (AtkText           *text,
83                                                     gint              selection_num,
84                                                     gint              start_offset,
85                                                     gint              end_offset);
86 static void gtk_label_accessible_get_character_extents       (AtkText         *text,
87                                                     gint              offset,
88                                                     gint              *x,
89                                                     gint              *y,
90                                                     gint              *width,
91                                                     gint              *height,
92                                                     AtkCoordType      coords);
93 static gint gtk_label_accessible_get_offset_at_point         (AtkText           *text,
94                                                     gint              x,
95                                                     gint              y,
96                                                     AtkCoordType      coords);
97 static AtkAttributeSet* gtk_label_accessible_get_run_attributes 
98                                                    (AtkText           *text,
99                                                     gint              offset,
100                                                     gint              *start_offset,
101                                                     gint              *end_offset);
102 static AtkAttributeSet* gtk_label_accessible_get_default_attributes
103                                                    (AtkText           *text);
104
105 G_DEFINE_TYPE_WITH_CODE (GtkLabelAccessible, gtk_label_accessible, GAIL_TYPE_WIDGET,
106                          G_IMPLEMENT_INTERFACE (ATK_TYPE_TEXT, atk_text_interface_init))
107
108 static void
109 gtk_label_accessible_class_init (GtkLabelAccessibleClass *klass)
110 {
111   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
112   AtkObjectClass  *class = ATK_OBJECT_CLASS (klass);
113   GailWidgetClass *widget_class;
114
115   gobject_class->finalize = gtk_label_accessible_finalize;
116
117   widget_class = (GailWidgetClass*)klass;
118   widget_class->notify_gtk = gtk_label_accessible_real_notify_gtk;
119
120   class->get_name = gtk_label_accessible_get_name;
121   class->ref_state_set = gtk_label_accessible_ref_state_set;
122   class->ref_relation_set = gtk_label_accessible_ref_relation_set;
123   class->initialize = gtk_label_accessible_real_initialize;
124 }
125
126 static void
127 gtk_label_accessible_init (GtkLabelAccessible *label)
128 {
129 }
130
131 static void
132 gtk_label_accessible_real_initialize (AtkObject *obj,
133                             gpointer  data)
134 {
135   GtkWidget  *widget;
136   GtkLabelAccessible *accessible;
137
138   ATK_OBJECT_CLASS (gtk_label_accessible_parent_class)->initialize (obj, data);
139   
140   accessible = GTK_LABEL_ACCESSIBLE (obj);
141
142   accessible->cursor_position = 0;
143   accessible->selection_bound = 0;
144   accessible->textutil = NULL;
145   accessible->label_length = 0;
146   
147   widget = GTK_WIDGET (data);
148
149   gtk_label_accessible_init_text_util (accessible, widget);
150
151   /*
152    * Check whether ancestor of GtkLabel is a GtkButton and if so
153    * set accessible parent for GtkLabelAccessible
154    */
155   while (widget != NULL)
156     {
157       widget = gtk_widget_get_parent (widget);
158       if (GTK_IS_BUTTON (widget))
159         {
160           atk_object_set_parent (obj, gtk_widget_get_accessible (widget));
161           break;
162         }
163     }
164
165   if (GTK_IS_ACCEL_LABEL (widget))
166     obj->role = ATK_ROLE_ACCEL_LABEL;
167   else
168     obj->role = ATK_ROLE_LABEL;
169 }
170
171 static void
172 gtk_label_accessible_init_text_util (GtkLabelAccessible *accessible,
173                            GtkWidget *widget)
174 {
175   GtkLabel *label;
176   const gchar *label_text;
177
178   if (accessible->textutil == NULL)
179     accessible->textutil = gail_text_util_new ();
180
181   label = GTK_LABEL (widget);
182   label_text = gtk_label_get_text (label);
183   gail_text_util_text_setup (accessible->textutil, label_text);
184   
185   if (label_text == NULL)
186     accessible->label_length = 0;
187   else
188     accessible->label_length = g_utf8_strlen (label_text, -1);
189 }
190
191 static void
192 notify_name_change (AtkObject *atk_obj)
193 {
194   GtkLabel *label;
195   GtkLabelAccessible *accessible;
196   GtkWidget *widget;
197   GObject *gail_obj;
198
199   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (atk_obj));
200   if (widget == NULL)
201     /*
202      * State is defunct
203      */
204     return;
205
206   gail_obj = G_OBJECT (atk_obj);
207   label = GTK_LABEL (widget);
208   accessible = GTK_LABEL_ACCESSIBLE (atk_obj);
209
210   if (accessible->textutil == NULL)
211     return;
212
213   /*
214    * Check whether the label has actually changed before emitting
215    * notification.
216    */
217   if (accessible->textutil->buffer)
218     {
219       GtkTextIter start, end;
220       const char *new_label;
221       char *old_label;
222       int same;
223
224       gtk_text_buffer_get_start_iter (accessible->textutil->buffer, &start);
225       gtk_text_buffer_get_end_iter (accessible->textutil->buffer, &end);
226       old_label = gtk_text_buffer_get_text (accessible->textutil->buffer, &start, &end, FALSE);
227       new_label = gtk_label_get_text (label);
228       same = strcmp (new_label, old_label);
229       g_free (old_label);
230       if (same == 0)
231         return;
232     }
233
234   /* Create a delete text and an insert text signal */
235
236   g_signal_emit_by_name (gail_obj, "text_changed::delete", 0,
237                          accessible->label_length);
238
239   gtk_label_accessible_init_text_util (accessible, widget);
240
241   g_signal_emit_by_name (gail_obj, "text_changed::insert", 0, 
242                          accessible->label_length);
243
244   if (atk_obj->name == NULL)
245     /*
246      * The label has changed so notify a change in accessible-name
247      */
248     g_object_notify (gail_obj, "accessible-name");
249
250   g_signal_emit_by_name (gail_obj, "visible_data_changed");
251 }
252
253 static void
254 gtk_label_accessible_real_notify_gtk (GObject           *obj,
255                                 GParamSpec        *pspec)
256 {
257   GtkWidget *widget = GTK_WIDGET (obj);
258   AtkObject* atk_obj = gtk_widget_get_accessible (widget);
259   GtkLabel *label;
260   GtkLabelAccessible *accessible;
261   GObject *gail_obj;
262
263   accessible = GTK_LABEL_ACCESSIBLE (atk_obj);
264
265   if (strcmp (pspec->name, "label") == 0 ||
266       strcmp (pspec->name, "use-underline") == 0 ||
267       strcmp (pspec->name, "use-markup") == 0)
268     {
269       notify_name_change (atk_obj);
270     }
271   else if (strcmp (pspec->name, "cursor-position") == 0)
272     {
273       gint start, end, tmp;
274       gboolean text_caret_moved = FALSE;
275       gboolean selection_changed = FALSE;
276
277       gail_obj = G_OBJECT (atk_obj);
278       label = GTK_LABEL (widget);
279
280       if (accessible->selection_bound != -1 && accessible->selection_bound < accessible->cursor_position)
281         {
282           tmp = accessible->selection_bound;
283           accessible->selection_bound = accessible->cursor_position;
284           accessible->cursor_position = tmp;
285         }
286
287       if (gtk_label_get_selection_bounds (label, &start, &end))
288         {
289           if (start != accessible->cursor_position ||
290               end != accessible->selection_bound)
291             {
292               if (end != accessible->selection_bound)
293                 {
294                   accessible->selection_bound = start;
295                   accessible->cursor_position = end;
296                 }
297               else
298                 {
299                   accessible->selection_bound = end;
300                   accessible->cursor_position = start;
301                 }
302               text_caret_moved = TRUE;
303               if (start != end)
304                 selection_changed = TRUE;
305             }
306         }
307       else
308         {
309           if (accessible->cursor_position != accessible->selection_bound)
310             selection_changed = TRUE;
311           if (gtk_label_get_selectable (label))
312             {
313               if (accessible->cursor_position != -1 && start != accessible->cursor_position)
314                 text_caret_moved = TRUE;
315               if (accessible->selection_bound != -1 && end != accessible
316 ->selection_bound)
317                 {
318                   text_caret_moved = TRUE;
319                   accessible->cursor_position = end;
320                   accessible->selection_bound = start;
321                 }
322               else
323                 {
324                   accessible->cursor_position = start;
325                   accessible->selection_bound = end;
326                 }
327             }
328           else
329             {
330               /* GtkLabel has become non selectable */
331
332               accessible->cursor_position = 0;
333               accessible->selection_bound = 0;
334               text_caret_moved = TRUE;
335             }
336
337         }
338         if (text_caret_moved)
339           g_signal_emit_by_name (gail_obj, "text_caret_moved",
340                                  accessible->cursor_position);
341         if (selection_changed)
342           g_signal_emit_by_name (gail_obj, "text_selection_changed");
343
344     }
345   else
346     GAIL_WIDGET_CLASS (gtk_label_accessible_parent_class)->notify_gtk (obj, pspec);
347 }
348
349 static void
350 gtk_label_accessible_finalize (GObject *object)
351 {
352   GtkLabelAccessible *accessible = GTK_LABEL_ACCESSIBLE (object);
353
354   if (accessible->textutil)
355     g_object_unref (accessible->textutil);
356   G_OBJECT_CLASS (gtk_label_accessible_parent_class)->finalize (object);
357 }
358
359
360 /* atkobject.h */
361
362 static AtkStateSet*
363 gtk_label_accessible_ref_state_set (AtkObject *accessible)
364 {
365   AtkStateSet *state_set;
366   GtkWidget *widget;
367
368   state_set = ATK_OBJECT_CLASS (gtk_label_accessible_parent_class)->ref_state_set (accessible);
369   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible));
370
371   if (widget == NULL)
372     return state_set;
373
374   atk_state_set_add_state (state_set, ATK_STATE_MULTI_LINE);
375
376   return state_set;
377 }
378
379 AtkRelationSet*
380 gtk_label_accessible_ref_relation_set (AtkObject *obj)
381 {
382   GtkWidget *widget;
383   AtkRelationSet *relation_set;
384
385   g_return_val_if_fail (GTK_IS_LABEL_ACCESSIBLE (obj), NULL);
386
387   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (obj));
388   if (widget == NULL)
389     /*
390      * State is defunct
391      */
392     return NULL;
393
394   relation_set = ATK_OBJECT_CLASS (gtk_label_accessible_parent_class)->ref_relation_set (obj);
395
396   if (!atk_relation_set_contains (relation_set, ATK_RELATION_LABEL_FOR))
397     {
398       /*
399        * Get the mnemonic widget
400        *
401        * The relation set is not updated if the mnemonic widget is changed
402        */
403       GtkWidget *mnemonic_widget = gtk_label_get_mnemonic_widget (GTK_LABEL (widget));
404
405       if (mnemonic_widget)
406         {
407           AtkObject *accessible_array[1];
408           AtkRelation* relation;
409
410           if (!gtk_widget_get_can_focus (mnemonic_widget))
411             {
412             /*
413              * Handle the case where a GtkFileChooserButton is specified as the 
414              * mnemonic widget. use the combobox which is a child of the
415              * GtkFileChooserButton as the mnemonic widget. See bug #359843.
416              */
417              if (GTK_IS_BOX (mnemonic_widget))
418                {
419                   GList *list, *tmpl;
420
421                   list = gtk_container_get_children (GTK_CONTAINER (mnemonic_widget));
422                   if (g_list_length (list) == 2)
423                     {
424                       tmpl = g_list_last (list);
425                       if (GTK_IS_COMBO_BOX(tmpl->data))
426                         {
427                           mnemonic_widget = GTK_WIDGET(tmpl->data);
428                         }
429                     }
430                   g_list_free (list);
431                 }
432             }
433           accessible_array[0] = gtk_widget_get_accessible (mnemonic_widget);
434           relation = atk_relation_new (accessible_array, 1,
435                                        ATK_RELATION_LABEL_FOR);
436           atk_relation_set_add (relation_set, relation);
437           /*
438            * Unref the relation so that it is not leaked.
439            */
440           g_object_unref (relation);
441         }
442     }
443   return relation_set;
444 }
445
446 static const gchar*
447 gtk_label_accessible_get_name (AtkObject *accessible)
448 {
449   const gchar *name;
450
451   g_return_val_if_fail (GTK_IS_LABEL_ACCESSIBLE (accessible), NULL);
452
453   name = ATK_OBJECT_CLASS (gtk_label_accessible_parent_class)->get_name (accessible);
454   if (name != NULL)
455     return name;
456   else
457     {
458       /*
459        * Get the text on the label
460        */
461       GtkWidget *widget;
462
463       widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible));
464       if (widget == NULL)
465         /*
466          * State is defunct
467          */
468         return NULL;
469
470       g_return_val_if_fail (GTK_IS_LABEL (widget), NULL);
471
472       return gtk_label_get_text (GTK_LABEL (widget));
473     }
474 }
475
476 /* atktext.h */
477
478 static void
479 atk_text_interface_init (AtkTextIface *iface)
480 {
481   iface->get_text = gtk_label_accessible_get_text;
482   iface->get_character_at_offset = gtk_label_accessible_get_character_at_offset;
483   iface->get_text_before_offset = gtk_label_accessible_get_text_before_offset;
484   iface->get_text_at_offset = gtk_label_accessible_get_text_at_offset;
485   iface->get_text_after_offset = gtk_label_accessible_get_text_after_offset;
486   iface->get_character_count = gtk_label_accessible_get_character_count;
487   iface->get_caret_offset = gtk_label_accessible_get_caret_offset;
488   iface->set_caret_offset = gtk_label_accessible_set_caret_offset;
489   iface->get_n_selections = gtk_label_accessible_get_n_selections;
490   iface->get_selection = gtk_label_accessible_get_selection;
491   iface->add_selection = gtk_label_accessible_add_selection;
492   iface->remove_selection = gtk_label_accessible_remove_selection;
493   iface->set_selection = gtk_label_accessible_set_selection;
494   iface->get_character_extents = gtk_label_accessible_get_character_extents;
495   iface->get_offset_at_point = gtk_label_accessible_get_offset_at_point;
496   iface->get_run_attributes = gtk_label_accessible_get_run_attributes;
497   iface->get_default_attributes = gtk_label_accessible_get_default_attributes;
498 }
499
500 static gchar*
501 gtk_label_accessible_get_text (AtkText *text,
502                      gint    start_pos,
503                      gint    end_pos)
504 {
505   GtkWidget *widget;
506   GtkLabel  *label;
507
508   const gchar *label_text;
509
510   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
511   if (widget == NULL)
512     /* State is defunct */
513     return NULL;
514
515   label = GTK_LABEL (widget);
516
517   label_text = gtk_label_get_text (label);
518  
519   if (label_text == NULL)
520     return NULL;
521   else
522   {
523     if (GTK_LABEL_ACCESSIBLE (text)->textutil == NULL) 
524       gtk_label_accessible_init_text_util (GTK_LABEL_ACCESSIBLE (text), widget);
525     return gail_text_util_get_substring (GTK_LABEL_ACCESSIBLE (text)->textutil, 
526                                          start_pos, end_pos);
527   }
528 }
529
530 static gchar*
531 gtk_label_accessible_get_text_before_offset (AtkText         *text,
532                                    gint            offset,
533                                    AtkTextBoundary boundary_type,
534                                    gint            *start_offset,
535                                    gint            *end_offset)
536 {
537   GtkWidget *widget;
538   GtkLabel *label;
539   
540   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
541   
542   if (widget == NULL)
543     /* State is defunct */
544     return NULL;
545   
546   /* Get label */
547   label = GTK_LABEL (widget);
548
549   return gail_text_util_get_text (GTK_LABEL_ACCESSIBLE (text)->textutil,
550                            gtk_label_get_layout (label), GAIL_BEFORE_OFFSET, 
551                            boundary_type, offset, start_offset, end_offset); 
552 }
553
554 static gchar*
555 gtk_label_accessible_get_text_at_offset (AtkText         *text,
556                                gint            offset,
557                                AtkTextBoundary boundary_type,
558                                gint            *start_offset,
559                                gint            *end_offset)
560 {
561   GtkWidget *widget;
562   GtkLabel *label;
563  
564   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
565   
566   if (widget == NULL)
567     /* State is defunct */
568     return NULL;
569   
570   /* Get label */
571   label = GTK_LABEL (widget);
572
573   return gail_text_util_get_text (GTK_LABEL_ACCESSIBLE (text)->textutil,
574                               gtk_label_get_layout (label), GAIL_AT_OFFSET, 
575                               boundary_type, offset, start_offset, end_offset);
576 }
577
578 static gchar*
579 gtk_label_accessible_get_text_after_offset (AtkText         *text,
580                                   gint            offset,
581                                   AtkTextBoundary boundary_type,
582                                   gint            *start_offset,
583                                   gint            *end_offset)
584 {
585   GtkWidget *widget;
586   GtkLabel *label;
587
588   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
589   
590   if (widget == NULL)
591   {
592     /* State is defunct */
593     return NULL;
594   }
595   
596   /* Get label */
597   label = GTK_LABEL (widget);
598
599   return gail_text_util_get_text (GTK_LABEL_ACCESSIBLE (text)->textutil,
600                            gtk_label_get_layout (label), GAIL_AFTER_OFFSET, 
601                            boundary_type, offset, start_offset, end_offset);
602 }
603
604 static gint
605 gtk_label_accessible_get_character_count (AtkText *text)
606 {
607   GtkWidget *widget;
608   GtkLabel  *label;
609
610   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
611   if (widget == NULL)
612     /* State is defunct */
613     return 0;
614
615   label = GTK_LABEL (widget);
616   return g_utf8_strlen (gtk_label_get_text (label), -1);
617 }
618
619 static gint
620 gtk_label_accessible_get_caret_offset (AtkText *text)
621 {
622    return GTK_LABEL_ACCESSIBLE (text)->cursor_position;
623 }
624
625 static gboolean
626 gtk_label_accessible_set_caret_offset (AtkText *text, 
627                              gint    offset)
628 {
629   GtkWidget *widget;
630   GtkLabel  *label;
631
632   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
633   if (widget == NULL)
634     /* State is defunct */
635     return 0;
636
637   label = GTK_LABEL (widget);
638
639   if (gtk_label_get_selectable (label) &&
640       offset >= 0 &&
641       offset <= g_utf8_strlen (gtk_label_get_text (label), -1))
642     {
643       gtk_label_select_region (label, offset, offset);
644       return TRUE;
645     }
646   else
647     return FALSE;
648 }
649
650 static gint
651 gtk_label_accessible_get_n_selections (AtkText *text)
652 {
653   GtkWidget *widget;
654   GtkLabel  *label;
655   gint start, end;
656
657   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
658   if (widget == NULL)
659     /* State is defunct */
660     return 0;
661
662   label = GTK_LABEL (widget);
663
664   if (!gtk_label_get_selectable (label))
665      return 0;
666
667   if (gtk_label_get_selection_bounds (label, &start, &end))
668      return 1;
669   else 
670      return 0;
671 }
672
673 static gchar*
674 gtk_label_accessible_get_selection (AtkText *text,
675                           gint    selection_num,
676                           gint    *start_pos,
677                           gint    *end_pos)
678 {
679   GtkWidget *widget;
680   GtkLabel  *label;
681
682   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
683   if (widget == NULL)
684     /* State is defunct */
685     return NULL;
686
687   label = GTK_LABEL (widget);
688
689  /* Only let the user get the selection if one is set, and if the
690   * selection_num is 0.
691   */
692   if (!gtk_label_get_selectable( label) || selection_num != 0)
693      return NULL;
694
695   if (gtk_label_get_selection_bounds (label, start_pos, end_pos))
696     {
697       const gchar* label_text = gtk_label_get_text (label);
698     
699       if (label_text == NULL)
700         return 0;
701       else
702         return gail_text_util_get_substring (GTK_LABEL_ACCESSIBLE (text)->textutil, 
703                                              *start_pos, *end_pos);
704     }
705   else 
706     return NULL;
707 }
708
709 static gboolean
710 gtk_label_accessible_add_selection (AtkText *text,
711                           gint    start_pos,
712                           gint    end_pos)
713 {
714   GtkWidget *widget;
715   GtkLabel  *label;
716   gint start, end;
717
718   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
719   if (widget == NULL)
720     /* State is defunct */
721     return FALSE;
722
723   label = GTK_LABEL (widget);
724
725   if (!gtk_label_get_selectable (label))
726      return FALSE;
727
728   if (! gtk_label_get_selection_bounds (label, &start, &end))
729     {
730       gtk_label_select_region (label, start_pos, end_pos);
731       return TRUE;
732     }
733   else
734     return FALSE;
735 }
736
737 static gboolean
738 gtk_label_accessible_remove_selection (AtkText *text,
739                              gint    selection_num)
740 {
741   GtkWidget *widget;
742   GtkLabel  *label;
743   gint start, end;
744
745   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
746   if (widget == NULL)
747     /* State is defunct */
748     return FALSE;
749
750   if (selection_num != 0)
751      return FALSE;
752
753   label = GTK_LABEL (widget);
754
755   if (!gtk_label_get_selectable (label))
756      return FALSE;
757
758   if (gtk_label_get_selection_bounds (label, &start, &end))
759     {
760       gtk_label_select_region (label, 0, 0);
761       return TRUE;
762     }
763   else
764     return FALSE;
765 }
766
767 static gboolean
768 gtk_label_accessible_set_selection (AtkText *text,
769                           gint    selection_num,
770                           gint    start_pos,
771                           gint    end_pos)
772 {
773   GtkWidget *widget;
774   GtkLabel  *label;
775   gint start, end;
776
777   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
778   if (widget == NULL)
779     /* State is defunct */
780     return FALSE;
781
782   if (selection_num != 0)
783      return FALSE;
784
785   label = GTK_LABEL (widget);
786
787   if (!gtk_label_get_selectable (label))
788      return FALSE;
789
790   if (gtk_label_get_selection_bounds (label, &start, &end))
791     {
792       gtk_label_select_region (label, start_pos, end_pos);
793       return TRUE;
794     }
795   else
796     return FALSE;
797 }
798
799 static void
800 gtk_label_accessible_get_character_extents (AtkText      *text,
801                                   gint         offset,
802                                   gint         *x,
803                                   gint         *y,
804                                   gint         *width,
805                                   gint         *height,
806                                   AtkCoordType coords)
807 {
808   GtkWidget *widget;
809   GtkLabel *label;
810   PangoRectangle char_rect;
811   const gchar *label_text;
812   gint index, x_layout, y_layout;
813  
814   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
815
816   if (widget == NULL)
817     /* State is defunct */
818     return;
819
820   label = GTK_LABEL (widget);
821   
822   gtk_label_get_layout_offsets (label, &x_layout, &y_layout);
823   label_text = gtk_label_get_text (label);
824   index = g_utf8_offset_to_pointer (label_text, offset) - label_text;
825   pango_layout_index_to_pos (gtk_label_get_layout (label), index, &char_rect);
826   
827   gail_misc_get_extents_from_pango_rectangle (widget, &char_rect, 
828                     x_layout, y_layout, x, y, width, height, coords);
829
830
831 static gint 
832 gtk_label_accessible_get_offset_at_point (AtkText      *text,
833                                 gint         x,
834                                 gint         y,
835                                 AtkCoordType coords)
836
837   GtkWidget *widget;
838   GtkLabel *label;
839   const gchar *label_text;
840   gint index, x_layout, y_layout;
841
842   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
843   if (widget == NULL)
844     /* State is defunct */
845     return -1;
846   label = GTK_LABEL (widget);
847   
848   gtk_label_get_layout_offsets (label, &x_layout, &y_layout);
849   
850   index = gail_misc_get_index_at_point_in_layout (widget, 
851                                               gtk_label_get_layout (label), 
852                                               x_layout, y_layout, x, y, coords);
853   label_text = gtk_label_get_text (label);
854   if (index == -1)
855     {
856       if (coords == ATK_XY_WINDOW || coords == ATK_XY_SCREEN)
857         return g_utf8_strlen (label_text, -1);
858
859       return index;  
860     }
861   else
862     return g_utf8_pointer_to_offset (label_text, label_text + index);
863 }
864
865 static AtkAttributeSet*
866 gtk_label_accessible_get_run_attributes (AtkText        *text,
867                                gint           offset,
868                                gint           *start_offset,
869                                gint           *end_offset)
870 {
871   GtkWidget *widget;
872   GtkLabel *label;
873   AtkAttributeSet *at_set = NULL;
874   GtkJustification justify;
875   GtkTextDirection dir;
876
877   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
878   if (widget == NULL)
879     /* State is defunct */
880     return NULL;
881
882   label = GTK_LABEL (widget);
883   
884   /* Get values set for entire label, if any */
885   justify = gtk_label_get_justify (label);
886   if (justify != GTK_JUSTIFY_CENTER)
887     {
888       at_set = gail_misc_add_attribute (at_set, 
889                                         ATK_TEXT_ATTR_JUSTIFICATION,
890      g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_JUSTIFICATION, justify)));
891     }
892   dir = gtk_widget_get_direction (widget);
893   if (dir == GTK_TEXT_DIR_RTL)
894     {
895       at_set = gail_misc_add_attribute (at_set, 
896                                         ATK_TEXT_ATTR_DIRECTION,
897      g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_DIRECTION, dir)));
898     }
899
900   at_set = gail_misc_layout_get_run_attributes (at_set,
901                                                 gtk_label_get_layout (label),
902                                                 gtk_label_get_text (label),
903                                                 offset,
904                                                 start_offset,
905                                                 end_offset);
906   return at_set;
907 }
908
909 static AtkAttributeSet*
910 gtk_label_accessible_get_default_attributes (AtkText        *text)
911 {
912   GtkWidget *widget;
913   GtkLabel *label;
914   AtkAttributeSet *at_set = NULL;
915
916   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
917   if (widget == NULL)
918     /* State is defunct */
919     return NULL;
920
921   label = GTK_LABEL (widget);
922   
923   at_set = gail_misc_get_default_attributes (at_set,
924                                              gtk_label_get_layout (label),
925                                              widget);
926   return at_set;
927 }
928
929 static gunichar 
930 gtk_label_accessible_get_character_at_offset (AtkText            *text,
931                                     gint                 offset)
932 {
933   GtkWidget *widget;
934   GtkLabel *label;
935   const gchar *string;
936   gchar *index;
937
938   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
939   if (widget == NULL)
940     /* State is defunct */
941     return '\0';
942
943   label = GTK_LABEL (widget);
944   string = gtk_label_get_text (label);
945   if (offset >= g_utf8_strlen (string, -1))
946     return '\0';
947   index = g_utf8_offset_to_pointer (string, offset);
948
949   return g_utf8_get_char (index);
950 }