]> Pileus Git - ~andy/gtk/blob - modules/other/gail/gaillabel.c
Remove some GTK_DISABLE_DEPRECATED and ENABLE_BROKEN guards
[~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 G_CONST_RETURN 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 (atk_obj)->widget;
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 (accessible)->widget;
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 (obj)->widget;
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              * Handle the case where a GnomeIconEntry is specified as the 
489              * mnemonic widget. use the button which is a grandchild of the
490              * GnomeIconEntry as the mnemonic widget. See bug #133967.
491              */
492               else if (GTK_IS_BOX (mnemonic_widget))
493                 {
494                   GList *list;
495
496                   list = gtk_container_get_children (GTK_CONTAINER (mnemonic_widget));
497                   if (g_list_length (list) == 1)
498                     {
499                       if (GTK_IS_ALIGNMENT (list->data))
500                         {
501                           GtkWidget *temp_widget;
502
503                           temp_widget = GTK_BIN (list->data)->child;
504                           if (GTK_IS_BUTTON (temp_widget))
505                             mnemonic_widget = temp_widget;
506                         }
507                       else if (GTK_IS_HBOX (list->data))
508                         {
509                           GtkWidget *temp_widget;
510
511                           temp_widget = GTK_WIDGET (list->data);
512                           g_list_free (list);
513                           list = gtk_container_get_children (GTK_CONTAINER (temp_widget));
514                         }
515                     }
516                   g_list_free (list);
517                 }
518             }
519           accessible_array[0] = gtk_widget_get_accessible (mnemonic_widget);
520           relation = atk_relation_new (accessible_array, 1,
521                                        ATK_RELATION_LABEL_FOR);
522           atk_relation_set_add (relation_set, relation);
523           /*
524            * Unref the relation so that it is not leaked.
525            */
526           g_object_unref (relation);
527         }
528     }
529   return relation_set;
530 }
531
532 static G_CONST_RETURN gchar*
533 gail_label_get_name (AtkObject *accessible)
534 {
535   G_CONST_RETURN gchar *name;
536
537   g_return_val_if_fail (GAIL_IS_LABEL (accessible), NULL);
538
539   name = ATK_OBJECT_CLASS (gail_label_parent_class)->get_name (accessible);
540   if (name != NULL)
541     return name;
542   else
543     {
544       /*
545        * Get the text on the label
546        */
547       GtkWidget *widget;
548
549       widget = GTK_ACCESSIBLE (accessible)->widget;
550       if (widget == NULL)
551         /*
552          * State is defunct
553          */
554         return NULL;
555
556       g_return_val_if_fail (GTK_IS_LABEL (widget), NULL);
557
558       return gtk_label_get_text (GTK_LABEL (widget));
559     }
560 }
561
562 /* atktext.h */
563
564 static void
565 atk_text_interface_init (AtkTextIface *iface)
566 {
567   iface->get_text = gail_label_get_text;
568   iface->get_character_at_offset = gail_label_get_character_at_offset;
569   iface->get_text_before_offset = gail_label_get_text_before_offset;
570   iface->get_text_at_offset = gail_label_get_text_at_offset;
571   iface->get_text_after_offset = gail_label_get_text_after_offset;
572   iface->get_character_count = gail_label_get_character_count;
573   iface->get_caret_offset = gail_label_get_caret_offset;
574   iface->set_caret_offset = gail_label_set_caret_offset;
575   iface->get_n_selections = gail_label_get_n_selections;
576   iface->get_selection = gail_label_get_selection;
577   iface->add_selection = gail_label_add_selection;
578   iface->remove_selection = gail_label_remove_selection;
579   iface->set_selection = gail_label_set_selection;
580   iface->get_character_extents = gail_label_get_character_extents;
581   iface->get_offset_at_point = gail_label_get_offset_at_point;
582   iface->get_run_attributes = gail_label_get_run_attributes;
583   iface->get_default_attributes = gail_label_get_default_attributes;
584 }
585
586 static gchar*
587 gail_label_get_text (AtkText *text,
588                      gint    start_pos,
589                      gint    end_pos)
590 {
591   GtkWidget *widget;
592   GtkLabel  *label;
593
594   const gchar *label_text;
595
596   widget = GTK_ACCESSIBLE (text)->widget;
597   if (widget == NULL)
598     /* State is defunct */
599     return NULL;
600
601   label = GTK_LABEL (widget);
602
603   label_text = gtk_label_get_text (label);
604  
605   if (label_text == NULL)
606     return NULL;
607   else
608   {
609     if (GAIL_LABEL (text)->textutil == NULL) 
610       gail_label_init_text_util (GAIL_LABEL (text), widget);
611     return gail_text_util_get_substring (GAIL_LABEL(text)->textutil, 
612                                          start_pos, end_pos);
613   }
614 }
615
616 static gchar*
617 gail_label_get_text_before_offset (AtkText         *text,
618                                    gint            offset,
619                                    AtkTextBoundary boundary_type,
620                                    gint            *start_offset,
621                                    gint            *end_offset)
622 {
623   GtkWidget *widget;
624   GtkLabel *label;
625   
626   widget = GTK_ACCESSIBLE (text)->widget;
627   
628   if (widget == NULL)
629     /* State is defunct */
630     return NULL;
631   
632   /* Get label */
633   label = GTK_LABEL (widget);
634
635   return gail_text_util_get_text (GAIL_LABEL (text)->textutil,
636                            gtk_label_get_layout (label), GAIL_BEFORE_OFFSET, 
637                            boundary_type, offset, start_offset, end_offset); 
638 }
639
640 static gchar*
641 gail_label_get_text_at_offset (AtkText         *text,
642                                gint            offset,
643                                AtkTextBoundary boundary_type,
644                                gint            *start_offset,
645                                gint            *end_offset)
646 {
647   GtkWidget *widget;
648   GtkLabel *label;
649  
650   widget = GTK_ACCESSIBLE (text)->widget;
651   
652   if (widget == NULL)
653     /* State is defunct */
654     return NULL;
655   
656   /* Get label */
657   label = GTK_LABEL (widget);
658
659   return gail_text_util_get_text (GAIL_LABEL (text)->textutil,
660                               gtk_label_get_layout (label), GAIL_AT_OFFSET, 
661                               boundary_type, offset, start_offset, end_offset);
662 }
663
664 static gchar*
665 gail_label_get_text_after_offset (AtkText         *text,
666                                   gint            offset,
667                                   AtkTextBoundary boundary_type,
668                                   gint            *start_offset,
669                                   gint            *end_offset)
670 {
671   GtkWidget *widget;
672   GtkLabel *label;
673
674   widget = GTK_ACCESSIBLE (text)->widget;
675   
676   if (widget == NULL)
677   {
678     /* State is defunct */
679     return NULL;
680   }
681   
682   /* Get label */
683   label = GTK_LABEL (widget);
684
685   return gail_text_util_get_text (GAIL_LABEL (text)->textutil,
686                            gtk_label_get_layout (label), GAIL_AFTER_OFFSET, 
687                            boundary_type, offset, start_offset, end_offset);
688 }
689
690 static gint
691 gail_label_get_character_count (AtkText *text)
692 {
693   GtkWidget *widget;
694   GtkLabel  *label;
695
696   widget = GTK_ACCESSIBLE (text)->widget;
697   if (widget == NULL)
698     /* State is defunct */
699     return 0;
700
701   label = GTK_LABEL (widget);
702   return g_utf8_strlen (gtk_label_get_text (label), -1);
703 }
704
705 static gint
706 gail_label_get_caret_offset (AtkText *text)
707 {
708    return GAIL_LABEL (text)->cursor_position;
709 }
710
711 static gboolean
712 gail_label_set_caret_offset (AtkText *text, 
713                              gint    offset)
714 {
715   GtkWidget *widget;
716   GtkLabel  *label;
717
718   widget = GTK_ACCESSIBLE (text)->widget;
719   if (widget == NULL)
720     /* State is defunct */
721     return 0;
722
723   label = GTK_LABEL (widget);
724
725   if (gtk_label_get_selectable (label) &&
726       offset >= 0 &&
727       offset <= g_utf8_strlen (gtk_label_get_text (label), -1))
728     {
729       gtk_label_select_region (label, offset, offset);
730       return TRUE;
731     }
732   else
733     return FALSE;
734 }
735
736 static gint
737 gail_label_get_n_selections (AtkText *text)
738 {
739   GtkWidget *widget;
740   GtkLabel  *label;
741   gint start, end;
742
743   widget = GTK_ACCESSIBLE (text)->widget;
744   if (widget == NULL)
745     /* State is defunct */
746     return 0;
747
748   label = GTK_LABEL (widget);
749
750   if (!gtk_label_get_selectable (label))
751      return 0;
752
753   if (gtk_label_get_selection_bounds (label, &start, &end))
754      return 1;
755   else 
756      return 0;
757 }
758
759 static gchar*
760 gail_label_get_selection (AtkText *text,
761                           gint    selection_num,
762                           gint    *start_pos,
763                           gint    *end_pos)
764 {
765   GtkWidget *widget;
766   GtkLabel  *label;
767
768   widget = GTK_ACCESSIBLE (text)->widget;
769   if (widget == NULL)
770     /* State is defunct */
771     return NULL;
772
773   label = GTK_LABEL (widget);
774
775  /* Only let the user get the selection if one is set, and if the
776   * selection_num is 0.
777   */
778   if (!gtk_label_get_selectable( label) || selection_num != 0)
779      return NULL;
780
781   if (gtk_label_get_selection_bounds (label, start_pos, end_pos))
782     {
783       const gchar* label_text = gtk_label_get_text (label);
784     
785       if (label_text == NULL)
786         return 0;
787       else
788         return gail_text_util_get_substring (GAIL_LABEL (text)->textutil, 
789                                              *start_pos, *end_pos);
790     }
791   else 
792     return NULL;
793 }
794
795 static gboolean
796 gail_label_add_selection (AtkText *text,
797                           gint    start_pos,
798                           gint    end_pos)
799 {
800   GtkWidget *widget;
801   GtkLabel  *label;
802   gint start, end;
803
804   widget = GTK_ACCESSIBLE (text)->widget;
805   if (widget == NULL)
806     /* State is defunct */
807     return FALSE;
808
809   label = GTK_LABEL (widget);
810
811   if (!gtk_label_get_selectable (label))
812      return FALSE;
813
814   if (! gtk_label_get_selection_bounds (label, &start, &end))
815     {
816       gtk_label_select_region (label, start_pos, end_pos);
817       return TRUE;
818     }
819   else
820     return FALSE;
821 }
822
823 static gboolean
824 gail_label_remove_selection (AtkText *text,
825                              gint    selection_num)
826 {
827   GtkWidget *widget;
828   GtkLabel  *label;
829   gint start, end;
830
831   widget = GTK_ACCESSIBLE (text)->widget;
832   if (widget == NULL)
833     /* State is defunct */
834     return FALSE;
835
836   if (selection_num != 0)
837      return FALSE;
838
839   label = GTK_LABEL (widget);
840
841   if (!gtk_label_get_selectable (label))
842      return FALSE;
843
844   if (gtk_label_get_selection_bounds (label, &start, &end))
845     {
846       gtk_label_select_region (label, 0, 0);
847       return TRUE;
848     }
849   else
850     return FALSE;
851 }
852
853 static gboolean
854 gail_label_set_selection (AtkText *text,
855                           gint    selection_num,
856                           gint    start_pos,
857                           gint    end_pos)
858 {
859   GtkWidget *widget;
860   GtkLabel  *label;
861   gint start, end;
862
863   widget = GTK_ACCESSIBLE (text)->widget;
864   if (widget == NULL)
865     /* State is defunct */
866     return FALSE;
867
868   if (selection_num != 0)
869      return FALSE;
870
871   label = GTK_LABEL (widget);
872
873   if (!gtk_label_get_selectable (label))
874      return FALSE;
875
876   if (gtk_label_get_selection_bounds (label, &start, &end))
877     {
878       gtk_label_select_region (label, start_pos, end_pos);
879       return TRUE;
880     }
881   else
882     return FALSE;
883 }
884
885 static void
886 gail_label_get_character_extents (AtkText      *text,
887                                   gint         offset,
888                                   gint         *x,
889                                   gint         *y,
890                                   gint         *width,
891                                   gint         *height,
892                                   AtkCoordType coords)
893 {
894   GtkWidget *widget;
895   GtkLabel *label;
896   PangoRectangle char_rect;
897   const gchar *label_text;
898   gint index, x_layout, y_layout;
899  
900   widget = GTK_ACCESSIBLE (text)->widget;
901
902   if (widget == NULL)
903     /* State is defunct */
904     return;
905
906   label = GTK_LABEL (widget);
907   
908   gtk_label_get_layout_offsets (label, &x_layout, &y_layout);
909   label_text = gtk_label_get_text (label);
910   index = g_utf8_offset_to_pointer (label_text, offset) - label_text;
911   pango_layout_index_to_pos (gtk_label_get_layout (label), index, &char_rect);
912   
913   gail_misc_get_extents_from_pango_rectangle (widget, &char_rect, 
914                     x_layout, y_layout, x, y, width, height, coords);
915
916
917 static gint 
918 gail_label_get_offset_at_point (AtkText      *text,
919                                 gint         x,
920                                 gint         y,
921                                 AtkCoordType coords)
922
923   GtkWidget *widget;
924   GtkLabel *label;
925   const gchar *label_text;
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   label_text = gtk_label_get_text (label);
940   if (index == -1)
941     {
942       if (coords == ATK_XY_WINDOW || coords == ATK_XY_SCREEN)
943         return g_utf8_strlen (label_text, -1);
944
945       return index;  
946     }
947   else
948     return g_utf8_pointer_to_offset (label_text, label_text + index);
949 }
950
951 static AtkAttributeSet*
952 gail_label_get_run_attributes (AtkText        *text,
953                                gint           offset,
954                                gint           *start_offset,
955                                gint           *end_offset)
956 {
957   GtkWidget *widget;
958   GtkLabel *label;
959   AtkAttributeSet *at_set = NULL;
960   GtkJustification justify;
961   GtkTextDirection dir;
962
963   widget = GTK_ACCESSIBLE (text)->widget;
964   if (widget == NULL)
965     /* State is defunct */
966     return NULL;
967
968   label = GTK_LABEL (widget);
969   
970   /* Get values set for entire label, if any */
971   justify = gtk_label_get_justify (label);
972   if (justify != GTK_JUSTIFY_CENTER)
973     {
974       at_set = gail_misc_add_attribute (at_set, 
975                                         ATK_TEXT_ATTR_JUSTIFICATION,
976      g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_JUSTIFICATION, justify)));
977     }
978   dir = gtk_widget_get_direction (widget);
979   if (dir == GTK_TEXT_DIR_RTL)
980     {
981       at_set = gail_misc_add_attribute (at_set, 
982                                         ATK_TEXT_ATTR_DIRECTION,
983      g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_DIRECTION, dir)));
984     }
985
986   at_set = gail_misc_layout_get_run_attributes (at_set,
987                                                 gtk_label_get_layout (label),
988                                                 gtk_label_get_text (label),
989                                                 offset,
990                                                 start_offset,
991                                                 end_offset);
992   return at_set;
993 }
994
995 static AtkAttributeSet*
996 gail_label_get_default_attributes (AtkText        *text)
997 {
998   GtkWidget *widget;
999   GtkLabel *label;
1000   AtkAttributeSet *at_set = NULL;
1001
1002   widget = GTK_ACCESSIBLE (text)->widget;
1003   if (widget == NULL)
1004     /* State is defunct */
1005     return NULL;
1006
1007   label = GTK_LABEL (widget);
1008   
1009   at_set = gail_misc_get_default_attributes (at_set,
1010                                              gtk_label_get_layout (label),
1011                                              widget);
1012   return at_set;
1013 }
1014
1015 static gunichar 
1016 gail_label_get_character_at_offset (AtkText              *text,
1017                                     gint                 offset)
1018 {
1019   GtkWidget *widget;
1020   GtkLabel *label;
1021   const gchar *string;
1022   gchar *index;
1023
1024   widget = GTK_ACCESSIBLE (text)->widget;
1025   if (widget == NULL)
1026     /* State is defunct */
1027     return '\0';
1028
1029   label = GTK_LABEL (widget);
1030   string = gtk_label_get_text (label);
1031   if (offset >= g_utf8_strlen (string, -1))
1032     return '\0';
1033   index = g_utf8_offset_to_pointer (string, offset);
1034
1035   return g_utf8_get_char (index);
1036 }