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