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