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