]> Pileus Git - ~andy/gtk/blob - gtk/a11y/gailexpander.c
gail: No need to include modules/other in CFLAGS anymore
[~andy/gtk] / gtk / a11y / gailexpander.c
1 /* GAIL - The GNOME Accessibility Implementation Library
2  * Copyright 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 <gdk/gdkkeysyms.h>
25 #include "gailexpander.h"
26 #include <libgail-util/gailmisc.h>
27
28 static void                  gail_expander_class_init       (GailExpanderClass *klass);
29 static void                  gail_expander_init             (GailExpander      *expander);
30
31 static const gchar* gail_expander_get_name         (AtkObject         *obj);
32 static gint                  gail_expander_get_n_children   (AtkObject         *obj)
33 ;
34 static AtkObject*            gail_expander_ref_child        (AtkObject         *obj,
35                                                              gint              i);
36
37 static AtkStateSet*          gail_expander_ref_state_set    (AtkObject         *obj);
38 static void                  gail_expander_real_notify_gtk  (GObject           *obj,
39                                                              GParamSpec        *pspec);
40 static void                  gail_expander_map_gtk          (GtkWidget         *widget,
41                                                              gpointer          data);
42
43 static void                  gail_expander_real_initialize  (AtkObject         *obj,
44                                                              gpointer          data);
45 static void                  gail_expander_finalize         (GObject           *object);
46 static void                  gail_expander_init_textutil    (GailExpander      *expander,
47                                                              GtkExpander       *widget);
48 static const gchar* gail_expander_get_full_text    (GtkExpander       *widget);
49
50 static void                  atk_action_interface_init  (AtkActionIface *iface);
51 static gboolean              gail_expander_do_action    (AtkAction      *action,
52                                                          gint           i);
53 static gboolean              idle_do_action             (gpointer       data);
54 static gint                  gail_expander_get_n_actions(AtkAction      *action);
55 static const gchar* gail_expander_get_description
56                                                         (AtkAction      *action,
57                                                          gint           i);
58 static const gchar* gail_expander_get_keybinding
59                                                         (AtkAction      *action,
60                                                          gint           i);
61 static const gchar* gail_expander_action_get_name
62                                                         (AtkAction      *action,
63                                                          gint           i);
64 static gboolean              gail_expander_set_description
65                                                         (AtkAction      *action,
66                                                          gint           i,
67                                                          const gchar    *desc);
68
69 /* atktext.h */ 
70 static void       atk_text_interface_init          (AtkTextIface        *iface);
71
72 static gchar*     gail_expander_get_text           (AtkText           *text,
73                                                     gint              start_pos,
74                                                     gint              end_pos);
75 static gunichar   gail_expander_get_character_at_offset
76                                                    (AtkText           *text,
77                                                     gint              offset);
78 static gchar*     gail_expander_get_text_before_offset
79                                                    (AtkText           *text,
80                                                     gint              offset,
81                                                     AtkTextBoundary   boundary_type,
82                                                     gint              *start_offset,
83                                                     gint              *end_offset);
84 static gchar*     gail_expander_get_text_at_offset (AtkText           *text,
85                                                     gint              offset,
86                                                     AtkTextBoundary   boundary_type,
87                                                     gint              *start_offset,
88                                                     gint              *end_offset);
89 static gchar*     gail_expander_get_text_after_offset
90                                                    (AtkText           *text,
91                                                     gint              offset,
92                                                     AtkTextBoundary   boundary_type,
93                                                     gint              *start_offset,
94                                                     gint              *end_offset);
95 static gint       gail_expander_get_character_count(AtkText           *text);
96 static void gail_expander_get_character_extents    (AtkText           *text,
97                                                     gint              offset,
98                                                     gint              *x,
99                                                     gint              *y,
100                                                     gint              *width,
101                                                     gint              *height,
102                                                     AtkCoordType      coords);
103 static gint gail_expander_get_offset_at_point      (AtkText           *text,
104                                                     gint              x,
105                                                     gint              y,
106                                                     AtkCoordType      coords);
107 static AtkAttributeSet* gail_expander_get_run_attributes 
108                                                    (AtkText           *text,
109                                                     gint              offset,
110                                                     gint              *start_offset,
111                                                     gint              *end_offset);
112 static AtkAttributeSet* gail_expander_get_default_attributes
113                                                    (AtkText           *text);
114
115 G_DEFINE_TYPE_WITH_CODE (GailExpander, gail_expander, GAIL_TYPE_CONTAINER,
116                          G_IMPLEMENT_INTERFACE (ATK_TYPE_ACTION, atk_action_interface_init)
117                          G_IMPLEMENT_INTERFACE (ATK_TYPE_TEXT, atk_text_interface_init))
118
119 static void
120 gail_expander_class_init (GailExpanderClass *klass)
121 {
122   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
123   AtkObjectClass *class = ATK_OBJECT_CLASS (klass);
124   GailWidgetClass *widget_class;
125
126   widget_class = (GailWidgetClass*)klass;
127   widget_class->notify_gtk = gail_expander_real_notify_gtk;
128
129   gobject_class->finalize = gail_expander_finalize;
130
131   class->get_name = gail_expander_get_name;
132   class->get_n_children = gail_expander_get_n_children;
133   class->ref_child = gail_expander_ref_child;
134   class->ref_state_set = gail_expander_ref_state_set;
135
136   class->initialize = gail_expander_real_initialize;
137 }
138
139 static void
140 gail_expander_init (GailExpander *expander)
141 {
142   expander->activate_description = NULL;
143   expander->activate_keybinding = NULL;
144   expander->action_idle_handler = 0;
145   expander->textutil = NULL;
146 }
147
148 static const gchar*
149 gail_expander_get_name (AtkObject *obj)
150 {
151   const gchar *name;
152   g_return_val_if_fail (GAIL_IS_EXPANDER (obj), NULL);
153
154   name = ATK_OBJECT_CLASS (gail_expander_parent_class)->get_name (obj);
155   if (name != NULL)
156     return name;
157   else
158     {
159       /*
160        * Get the text on the label
161        */
162       GtkWidget *widget;
163
164       widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (obj));
165       if (widget == NULL)
166         /*
167          * State is defunct
168          */
169         return NULL;
170
171       g_return_val_if_fail (GTK_IS_EXPANDER (widget), NULL);
172
173       return gail_expander_get_full_text (GTK_EXPANDER (widget));
174     }
175 }
176
177 static gint
178 gail_expander_get_n_children (AtkObject* obj)
179 {
180   GtkWidget *widget;
181   GList *children;
182   gint count = 0;
183
184   g_return_val_if_fail (GAIL_IS_CONTAINER (obj), count);
185
186   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (obj));
187   if (widget == NULL)
188     return 0;
189
190   children = gtk_container_get_children (GTK_CONTAINER(widget));
191   count = g_list_length (children);
192   g_list_free (children);
193
194   /* See if there is a label - if there is, reduce our count by 1
195    * since we don't want the label included with the children.
196    */
197   if (gtk_expander_get_label_widget (GTK_EXPANDER (widget)))
198     count -= 1;
199
200   return count; 
201 }
202
203 static AtkObject*
204 gail_expander_ref_child (AtkObject *obj,
205                          gint      i)
206 {
207   GList *children, *tmp_list;
208   AtkObject *accessible;
209   GtkWidget *widget;
210   GtkWidget *label;
211   gint index;
212
213   g_return_val_if_fail (GAIL_IS_CONTAINER (obj), NULL);
214   g_return_val_if_fail ((i >= 0), NULL);
215
216   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (obj));
217   if (widget == NULL)
218     return NULL;
219
220   children = gtk_container_get_children (GTK_CONTAINER (widget));
221
222   /* See if there is a label - if there is, we need to skip it
223    * since we don't want the label included with the children.
224    */
225   label = gtk_expander_get_label_widget (GTK_EXPANDER (widget));
226   if (label) {
227     for (index = 0; index <= i; index++) {
228       tmp_list = g_list_nth (children, index);
229       if (label == GTK_WIDGET (tmp_list->data)) {
230         i += 1;
231         break;
232       }
233     }
234   }
235
236   tmp_list = g_list_nth (children, i);
237   if (!tmp_list)
238     {
239       g_list_free (children);
240       return NULL;
241     }  
242   accessible = gtk_widget_get_accessible (GTK_WIDGET (tmp_list->data));
243
244   g_list_free (children);
245   g_object_ref (accessible);
246   return accessible; 
247 }
248
249 static void
250 gail_expander_real_initialize (AtkObject *obj,
251                                gpointer   data)
252 {
253   GailExpander *gail_expander = GAIL_EXPANDER (obj);
254   GtkWidget  *expander;
255
256   ATK_OBJECT_CLASS (gail_expander_parent_class)->initialize (obj, data);
257
258   expander = GTK_WIDGET (data);
259   if (gtk_widget_get_mapped (expander))
260     gail_expander_init_textutil (gail_expander, GTK_EXPANDER (expander));
261   else 
262     g_signal_connect (expander,
263                       "map",
264                       G_CALLBACK (gail_expander_map_gtk),
265                       gail_expander);
266  
267   obj->role = ATK_ROLE_TOGGLE_BUTTON;
268 }
269
270 static void
271 gail_expander_map_gtk (GtkWidget *widget,
272                        gpointer data)
273 {
274   GailExpander *expander; 
275
276   expander = GAIL_EXPANDER (data);
277   gail_expander_init_textutil (expander, GTK_EXPANDER (widget));
278 }
279
280 static void
281 gail_expander_real_notify_gtk (GObject    *obj,
282                                GParamSpec *pspec)
283 {
284   AtkObject* atk_obj;
285   GtkExpander *expander;
286   GailExpander *gail_expander;
287
288   expander = GTK_EXPANDER (obj);
289   atk_obj = gtk_widget_get_accessible (GTK_WIDGET (expander));
290 ;
291   if (strcmp (pspec->name, "label") == 0)
292     {
293       const gchar* label_text;
294
295
296       label_text = gail_expander_get_full_text (expander);
297
298       gail_expander = GAIL_EXPANDER (atk_obj);
299       if (gail_expander->textutil)
300         gail_text_util_text_setup (gail_expander->textutil, label_text);
301
302       if (atk_obj->name == NULL)
303       {
304         /*
305          * The label has changed so notify a change in accessible-name
306          */
307         g_object_notify (G_OBJECT (atk_obj), "accessible-name");
308       }
309       /*
310        * The label is the only property which can be changed
311        */
312       g_signal_emit_by_name (atk_obj, "visible_data_changed");
313     }
314   else if (strcmp (pspec->name, "expanded") == 0)
315     {
316       atk_object_notify_state_change (atk_obj, ATK_STATE_CHECKED, 
317                                       gtk_expander_get_expanded (expander));
318       atk_object_notify_state_change (atk_obj, ATK_STATE_EXPANDED, 
319                                       gtk_expander_get_expanded (expander));
320       g_signal_emit_by_name (atk_obj, "visible_data_changed");
321     }
322   else
323     GAIL_WIDGET_CLASS (gail_expander_parent_class)->notify_gtk (obj, pspec);
324 }
325
326 static const gchar*
327 gail_expander_get_full_text (GtkExpander *widget)
328 {
329   GtkWidget *label_widget;
330
331   label_widget = gtk_expander_get_label_widget (widget);
332
333   if (!GTK_IS_LABEL (label_widget))
334     return NULL;
335
336   return gtk_label_get_text (GTK_LABEL (label_widget));
337 }
338
339 static void
340 gail_expander_init_textutil (GailExpander *expander,
341                              GtkExpander  *widget)
342 {
343   const gchar *label_text;
344
345   expander->textutil = gail_text_util_new ();
346   label_text = gail_expander_get_full_text (widget);
347   gail_text_util_text_setup (expander->textutil, label_text);
348 }
349
350 static void
351 atk_action_interface_init (AtkActionIface *iface)
352 {
353   iface->do_action = gail_expander_do_action;
354   iface->get_n_actions = gail_expander_get_n_actions;
355   iface->get_description = gail_expander_get_description;
356   iface->get_keybinding = gail_expander_get_keybinding;
357   iface->get_name = gail_expander_action_get_name;
358   iface->set_description = gail_expander_set_description;
359 }
360
361 static gboolean
362 gail_expander_do_action (AtkAction *action,
363                          gint      i)
364 {
365   GtkWidget *widget;
366   GailExpander *expander;
367   gboolean return_value = TRUE;
368
369   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (action));
370   if (widget == NULL)
371     /*
372      * State is defunct
373      */
374     return FALSE;
375
376   if (!gtk_widget_is_sensitive (widget) || !gtk_widget_get_visible (widget))
377     return FALSE;
378
379   expander = GAIL_EXPANDER (action);
380   switch (i)
381     {
382     case 0:
383       if (expander->action_idle_handler)
384         return_value = FALSE;
385       else
386         expander->action_idle_handler = gdk_threads_add_idle (idle_do_action, expander);
387       break;
388     default:
389       return_value = FALSE;
390       break;
391     }
392   return return_value; 
393 }
394
395 static gboolean
396 idle_do_action (gpointer data)
397 {
398   GtkWidget *widget;
399   GailExpander *gail_expander;
400
401   gail_expander = GAIL_EXPANDER (data);
402   gail_expander->action_idle_handler = 0;
403
404   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (gail_expander));
405   if (widget == NULL /* State is defunct */ ||
406       !gtk_widget_is_sensitive (widget) || !gtk_widget_get_visible (widget))
407     return FALSE;
408
409   gtk_widget_activate (widget);
410
411   return FALSE;
412 }
413
414 static gint
415 gail_expander_get_n_actions (AtkAction *action)
416 {
417   return 1;
418 }
419
420 static const gchar*
421 gail_expander_get_description (AtkAction *action,
422                                gint      i)
423 {
424   GailExpander *expander;
425   const gchar *return_value;
426
427   expander = GAIL_EXPANDER (action);
428
429   switch (i)
430     {
431     case 0:
432       return_value = expander->activate_description;
433       break;
434     default:
435       return_value = NULL;
436       break;
437     }
438   return return_value; 
439 }
440
441 static const gchar*
442 gail_expander_get_keybinding (AtkAction *action,
443                               gint      i)
444 {
445   GailExpander *expander;
446   gchar *return_value = NULL;
447
448   switch (i)
449     {
450     case 0:
451       {
452         /*
453          * We look for a mnemonic on the label
454          */
455         GtkWidget *widget;
456         GtkWidget *label;
457
458         expander = GAIL_EXPANDER (action);
459         widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (expander));
460         if (widget == NULL)
461           /*
462            * State is defunct
463            */
464           return NULL;
465
466         g_return_val_if_fail (GTK_IS_EXPANDER (widget), NULL);
467
468         label = gtk_expander_get_label_widget (GTK_EXPANDER (widget));
469         if (GTK_IS_LABEL (label))
470           {
471             guint key_val; 
472
473             key_val = gtk_label_get_mnemonic_keyval (GTK_LABEL (label)); 
474             if (key_val != GDK_KEY_VoidSymbol)
475               return_value = gtk_accelerator_name (key_val, GDK_MOD1_MASK);
476             g_free (expander->activate_keybinding);
477             expander->activate_keybinding = return_value;
478           }
479         break;
480       }
481     default:
482       break;
483     }
484   return return_value; 
485 }
486
487 static const gchar*
488 gail_expander_action_get_name (AtkAction *action,
489                                gint      i)
490 {
491   const gchar *return_value;
492
493   switch (i)
494     {
495     case 0:
496       return_value = "activate";
497       break;
498     default:
499       return_value = NULL;
500       break;
501     }
502   return return_value; 
503 }
504
505 static gboolean
506 gail_expander_set_description (AtkAction      *action,
507                                gint           i,
508                                const gchar    *desc)
509 {
510   GailExpander *expander;
511   gchar **value;
512
513   expander = GAIL_EXPANDER (action);
514
515   switch (i)
516     {
517     case 0:
518       value = &expander->activate_description;
519       break;
520     default:
521       value = NULL;
522       break;
523     }
524   if (value)
525     {
526       g_free (*value);
527       *value = g_strdup (desc);
528       return TRUE;
529     }
530   else
531     return FALSE;
532 }
533
534 static AtkStateSet*
535 gail_expander_ref_state_set (AtkObject *obj)
536 {
537   AtkStateSet *state_set;
538   GtkWidget *widget;
539   GtkExpander *expander;
540
541   state_set = ATK_OBJECT_CLASS (gail_expander_parent_class)->ref_state_set (obj);
542   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (obj));
543
544   if (widget == NULL)
545     return state_set;
546
547   expander = GTK_EXPANDER (widget);
548
549   atk_state_set_add_state (state_set, ATK_STATE_EXPANDABLE);
550
551   if (gtk_expander_get_expanded (expander)) {
552     atk_state_set_add_state (state_set, ATK_STATE_CHECKED);
553     atk_state_set_add_state (state_set, ATK_STATE_EXPANDED);
554   }
555
556   return state_set;
557 }
558
559 /* atktext.h */
560
561 static void
562 atk_text_interface_init (AtkTextIface *iface)
563 {
564   iface->get_text = gail_expander_get_text;
565   iface->get_character_at_offset = gail_expander_get_character_at_offset;
566   iface->get_text_before_offset = gail_expander_get_text_before_offset;
567   iface->get_text_at_offset = gail_expander_get_text_at_offset;
568   iface->get_text_after_offset = gail_expander_get_text_after_offset;
569   iface->get_character_count = gail_expander_get_character_count;
570   iface->get_character_extents = gail_expander_get_character_extents;
571   iface->get_offset_at_point = gail_expander_get_offset_at_point;
572   iface->get_run_attributes = gail_expander_get_run_attributes;
573   iface->get_default_attributes = gail_expander_get_default_attributes;
574 }
575
576 static gchar*
577 gail_expander_get_text (AtkText *text,
578                         gint    start_pos,
579                         gint    end_pos)
580 {
581   GtkWidget *widget;
582   GailExpander *expander;
583   const gchar *label_text;
584
585   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
586   if (widget == NULL)
587     /* State is defunct */
588     return NULL;
589
590   expander = GAIL_EXPANDER (text);
591   if (!expander->textutil) 
592     gail_expander_init_textutil (expander, GTK_EXPANDER (widget));
593
594   label_text = gail_expander_get_full_text (GTK_EXPANDER (widget));
595
596   if (label_text == NULL)
597     return NULL;
598   else
599     return gail_text_util_get_substring (expander->textutil, 
600                                          start_pos, end_pos);
601 }
602
603 static gchar*
604 gail_expander_get_text_before_offset (AtkText         *text,
605                                       gint            offset,
606                                       AtkTextBoundary boundary_type,
607                                       gint            *start_offset,
608                                       gint            *end_offset)
609 {
610   GtkWidget *widget;
611   GailExpander *expander;
612   GtkWidget *label;
613
614   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
615   if (widget == NULL)
616     /* State is defunct */
617     return NULL;
618
619   expander = GAIL_EXPANDER (text);
620   if (!expander->textutil) 
621     gail_expander_init_textutil (expander, GTK_EXPANDER (widget));
622
623   label = gtk_expander_get_label_widget (GTK_EXPANDER (widget));
624   if (!GTK_IS_LABEL(label))
625     return NULL;
626   return gail_text_util_get_text (expander->textutil,
627                            gtk_label_get_layout (GTK_LABEL (label)),
628                            GAIL_BEFORE_OFFSET, 
629                            boundary_type, offset, start_offset, end_offset); 
630 }
631
632 static gchar*
633 gail_expander_get_text_at_offset (AtkText         *text,
634                                   gint            offset,
635                                   AtkTextBoundary boundary_type,
636                                   gint            *start_offset,
637                                   gint            *end_offset)
638 {
639   GtkWidget *widget;
640   GailExpander *expander;
641   GtkWidget *label;
642
643   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
644   if (widget == NULL)
645     /* State is defunct */
646     return NULL;
647
648   expander = GAIL_EXPANDER (text);
649   if (!expander->textutil) 
650     gail_expander_init_textutil (expander, GTK_EXPANDER (widget));
651
652   label = gtk_expander_get_label_widget (GTK_EXPANDER (widget));
653   if (!GTK_IS_LABEL(label))
654     return NULL;
655   return gail_text_util_get_text (expander->textutil,
656                            gtk_label_get_layout (GTK_LABEL (label)),
657                            GAIL_AT_OFFSET, 
658                            boundary_type, offset, start_offset, end_offset);
659 }
660
661 static gchar*
662 gail_expander_get_text_after_offset (AtkText         *text,
663                                      gint            offset,
664                                      AtkTextBoundary boundary_type,
665                                      gint            *start_offset,
666                                      gint            *end_offset)
667 {
668   GtkWidget *widget;
669   GailExpander *expander;
670   GtkWidget *label;
671
672   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
673   if (widget == NULL)
674     /* State is defunct */
675     return NULL;
676
677   expander = GAIL_EXPANDER (text);
678   if (!expander->textutil) 
679     gail_expander_init_textutil (expander, GTK_EXPANDER (widget));
680
681   label = gtk_expander_get_label_widget (GTK_EXPANDER (widget));
682   if (!GTK_IS_LABEL(label))
683     return NULL;
684   return gail_text_util_get_text (expander->textutil,
685                            gtk_label_get_layout (GTK_LABEL (label)),
686                            GAIL_AFTER_OFFSET, 
687                            boundary_type, offset, start_offset, end_offset);
688 }
689
690 static gint
691 gail_expander_get_character_count (AtkText *text)
692 {
693   GtkWidget *widget;
694   GtkWidget *label;
695
696   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
697   if (widget == NULL)
698     /* State is defunct */
699     return 0;
700
701   label = gtk_expander_get_label_widget (GTK_EXPANDER (widget));
702   if (!GTK_IS_LABEL(label))
703     return 0;
704
705   return g_utf8_strlen (gtk_label_get_text (GTK_LABEL (label)), -1);
706 }
707
708 static void
709 gail_expander_get_character_extents (AtkText      *text,
710                                      gint         offset,
711                                      gint         *x,
712                                      gint       *y,
713                                      gint       *width,
714                                      gint       *height,
715                                      AtkCoordType coords)
716 {
717   GtkWidget *widget;
718   GtkWidget *label;
719   PangoRectangle char_rect;
720   gint index, x_layout, y_layout;
721   const gchar *label_text;
722
723   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
724   if (widget == NULL)
725     /* State is defunct */
726     return;
727
728   label = gtk_expander_get_label_widget (GTK_EXPANDER (widget));
729   if (!GTK_IS_LABEL(label))
730     return;
731   
732   gtk_label_get_layout_offsets (GTK_LABEL (label), &x_layout, &y_layout);
733   label_text = gtk_label_get_text (GTK_LABEL (label));
734   index = g_utf8_offset_to_pointer (label_text, offset) - label_text;
735   pango_layout_index_to_pos (gtk_label_get_layout (GTK_LABEL (label)), index, &char_rect);
736   
737   gail_misc_get_extents_from_pango_rectangle (label, &char_rect, 
738                     x_layout, y_layout, x, y, width, height, coords);
739
740
741 static gint 
742 gail_expander_get_offset_at_point (AtkText      *text,
743                                    gint         x,
744                                    gint         y,
745                                    AtkCoordType coords)
746
747   GtkWidget *widget;
748   GtkWidget *label;
749   gint index, x_layout, y_layout;
750   const gchar *label_text;
751
752   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
753   if (widget == NULL)
754     /* State is defunct */
755     return -1;
756   label = gtk_expander_get_label_widget (GTK_EXPANDER (widget));
757
758   if (!GTK_IS_LABEL(label))
759     return -1;
760   
761   gtk_label_get_layout_offsets (GTK_LABEL (label), &x_layout, &y_layout);
762   
763   index = gail_misc_get_index_at_point_in_layout (label, 
764                                               gtk_label_get_layout (GTK_LABEL (label)), 
765                                               x_layout, y_layout, x, y, coords);
766   label_text = gtk_label_get_text (GTK_LABEL (label));
767   if (index == -1)
768     {
769       if (coords == ATK_XY_WINDOW || coords == ATK_XY_SCREEN)
770         return g_utf8_strlen (label_text, -1);
771
772       return index;  
773     }
774   else
775     return g_utf8_pointer_to_offset (label_text, label_text + index);  
776 }
777
778 static AtkAttributeSet*
779 gail_expander_get_run_attributes (AtkText *text,
780                                   gint    offset,
781                                   gint    *start_offset,
782                                   gint    *end_offset)
783 {
784   GtkWidget *widget;
785   GtkWidget *label;
786   AtkAttributeSet *at_set = NULL;
787   GtkJustification justify;
788   GtkTextDirection dir;
789
790   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
791   if (widget == NULL)
792     /* State is defunct */
793     return NULL;
794
795   label = gtk_expander_get_label_widget (GTK_EXPANDER (widget));
796
797   if (!GTK_IS_LABEL(label))
798     return NULL;
799   
800   /* Get values set for entire label, if any */
801   justify = gtk_label_get_justify (GTK_LABEL (label));
802   if (justify != GTK_JUSTIFY_CENTER)
803     {
804       at_set = gail_misc_add_attribute (at_set, 
805                                         ATK_TEXT_ATTR_JUSTIFICATION,
806      g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_JUSTIFICATION, justify)));
807     }
808   dir = gtk_widget_get_direction (label);
809   if (dir == GTK_TEXT_DIR_RTL)
810     {
811       at_set = gail_misc_add_attribute (at_set, 
812                                         ATK_TEXT_ATTR_DIRECTION,
813      g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_DIRECTION, dir)));
814     }
815
816   at_set = gail_misc_layout_get_run_attributes (at_set,
817                                                 gtk_label_get_layout (GTK_LABEL (label)),
818                                                 (gchar *) gtk_label_get_text (GTK_LABEL (label)),
819                                                 offset,
820                                                 start_offset,
821                                                 end_offset);
822   return at_set;
823 }
824
825 static AtkAttributeSet*
826 gail_expander_get_default_attributes (AtkText *text)
827 {
828   GtkWidget *widget;
829   GtkWidget *label;
830   AtkAttributeSet *at_set = NULL;
831
832   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
833   if (widget == NULL)
834     /* State is defunct */
835     return NULL;
836
837   label = gtk_expander_get_label_widget (GTK_EXPANDER (widget));
838
839   if (!GTK_IS_LABEL(label))
840     return NULL;
841
842   at_set = gail_misc_get_default_attributes (at_set,
843                                              gtk_label_get_layout (GTK_LABEL (label)),
844                                              widget);
845   return at_set;
846 }
847
848 static gunichar 
849 gail_expander_get_character_at_offset (AtkText *text,
850                                        gint    offset)
851 {
852   GtkWidget *widget;
853   GtkWidget *label;
854   const gchar *string;
855   gchar *index;
856
857   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
858   if (widget == NULL)
859     /* State is defunct */
860     return '\0';
861
862   label = gtk_expander_get_label_widget (GTK_EXPANDER (widget));
863
864   if (!GTK_IS_LABEL(label))
865     return '\0';
866   string = gtk_label_get_text (GTK_LABEL (label));
867   if (offset >= g_utf8_strlen (string, -1))
868     return '\0';
869   index = g_utf8_offset_to_pointer (string, offset);
870
871   return g_utf8_get_char (index);
872 }
873
874 static void
875 gail_expander_finalize (GObject *object)
876 {
877   GailExpander *expander = GAIL_EXPANDER (object);
878
879   g_free (expander->activate_description);
880   g_free (expander->activate_keybinding);
881   if (expander->action_idle_handler)
882     {
883       g_source_remove (expander->action_idle_handler);
884       expander->action_idle_handler = 0;
885     }
886   if (expander->textutil)
887     g_object_unref (expander->textutil);
888
889   G_OBJECT_CLASS (gail_expander_parent_class)->finalize (object);
890 }