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