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