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