]> Pileus Git - ~andy/gtk/blob - modules/other/gail/gailexpander.c
Deprecate widget flag: GTK_WIDGET_MAPPED
[~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 (obj)->widget;
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 (obj)->widget;
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   widget = GTK_ACCESSIBLE (obj)->widget;
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     count = g_list_length (children);
228     for (index = 0; index <= i; index++) {
229       tmp_list = g_list_nth (children, index);
230       if (label == GTK_WIDGET (tmp_list->data)) {
231         i += 1;
232         break;
233       }
234     }
235   }
236
237   tmp_list = g_list_nth (children, i);
238   if (!tmp_list)
239     {
240       g_list_free (children);
241       return NULL;
242     }  
243   accessible = gtk_widget_get_accessible (GTK_WIDGET (tmp_list->data));
244
245   g_list_free (children);
246   g_object_ref (accessible);
247   return accessible; 
248 }
249
250 static void
251 gail_expander_real_initialize (AtkObject *obj,
252                                gpointer   data)
253 {
254   GailExpander *gail_expander = GAIL_EXPANDER (obj);
255   GtkWidget  *expander;
256
257   ATK_OBJECT_CLASS (gail_expander_parent_class)->initialize (obj, data);
258
259   expander = GTK_WIDGET (data);
260   if (gtk_widget_get_mapped (expander))
261     gail_expander_init_textutil (gail_expander, GTK_EXPANDER (expander));
262   else 
263     g_signal_connect (expander,
264                       "map",
265                       G_CALLBACK (gail_expander_map_gtk),
266                       gail_expander);
267  
268   obj->role = ATK_ROLE_TOGGLE_BUTTON;
269 }
270
271 static void
272 gail_expander_map_gtk (GtkWidget *widget,
273                        gpointer data)
274 {
275   GailExpander *expander; 
276
277   expander = GAIL_EXPANDER (data);
278   gail_expander_init_textutil (expander, GTK_EXPANDER (widget));
279 }
280
281 static void
282 gail_expander_real_notify_gtk (GObject    *obj,
283                                GParamSpec *pspec)
284 {
285   AtkObject* atk_obj;
286   GtkExpander *expander;
287   GailExpander *gail_expander;
288
289   expander = GTK_EXPANDER (obj);
290   atk_obj = gtk_widget_get_accessible (GTK_WIDGET (expander));
291 ;
292   if (strcmp (pspec->name, "label") == 0)
293     {
294       const gchar* label_text;
295
296
297       label_text = gail_expander_get_full_text (expander);
298
299       gail_expander = GAIL_EXPANDER (atk_obj);
300       if (gail_expander->textutil)
301         gail_text_util_text_setup (gail_expander->textutil, label_text);
302
303       if (atk_obj->name == NULL)
304       {
305         /*
306          * The label has changed so notify a change in accessible-name
307          */
308         g_object_notify (G_OBJECT (atk_obj), "accessible-name");
309       }
310       /*
311        * The label is the only property which can be changed
312        */
313       g_signal_emit_by_name (atk_obj, "visible_data_changed");
314     }
315   else if (strcmp (pspec->name, "expanded") == 0)
316     {
317       atk_object_notify_state_change (atk_obj, ATK_STATE_CHECKED, 
318                                       gtk_expander_get_expanded (expander));
319       atk_object_notify_state_change (atk_obj, ATK_STATE_EXPANDED, 
320                                       gtk_expander_get_expanded (expander));
321       g_signal_emit_by_name (atk_obj, "visible_data_changed");
322     }
323   else
324     GAIL_WIDGET_CLASS (gail_expander_parent_class)->notify_gtk (obj, pspec);
325 }
326
327 static G_CONST_RETURN gchar*
328 gail_expander_get_full_text (GtkExpander *widget)
329 {
330   GtkWidget *label_widget;
331
332   label_widget = gtk_expander_get_label_widget (widget);
333
334   if (!GTK_IS_LABEL (label_widget))
335     return NULL;
336
337   return gtk_label_get_text (GTK_LABEL (label_widget));
338 }
339
340 static void
341 gail_expander_init_textutil (GailExpander *expander,
342                              GtkExpander  *widget)
343 {
344   const gchar *label_text;
345
346   expander->textutil = gail_text_util_new ();
347   label_text = gail_expander_get_full_text (widget);
348   gail_text_util_text_setup (expander->textutil, label_text);
349 }
350
351 static void
352 atk_action_interface_init (AtkActionIface *iface)
353 {
354   iface->do_action = gail_expander_do_action;
355   iface->get_n_actions = gail_expander_get_n_actions;
356   iface->get_description = gail_expander_get_description;
357   iface->get_keybinding = gail_expander_get_keybinding;
358   iface->get_name = gail_expander_action_get_name;
359   iface->set_description = gail_expander_set_description;
360 }
361
362 static gboolean
363 gail_expander_do_action (AtkAction *action,
364                          gint      i)
365 {
366   GtkWidget *widget;
367   GailExpander *expander;
368   gboolean return_value = TRUE;
369
370   widget = GTK_ACCESSIBLE (action)->widget;
371   if (widget == NULL)
372     /*
373      * State is defunct
374      */
375     return FALSE;
376
377   if (!gtk_widget_is_sensitive (widget) || !gtk_widget_get_visible (widget))
378     return FALSE;
379
380   expander = GAIL_EXPANDER (action);
381   switch (i)
382     {
383     case 0:
384       if (expander->action_idle_handler)
385         return_value = FALSE;
386       else
387         expander->action_idle_handler = gdk_threads_add_idle (idle_do_action, expander);
388       break;
389     default:
390       return_value = FALSE;
391       break;
392     }
393   return return_value; 
394 }
395
396 static gboolean
397 idle_do_action (gpointer data)
398 {
399   GtkWidget *widget;
400   GailExpander *gail_expander;
401
402   gail_expander = GAIL_EXPANDER (data);
403   gail_expander->action_idle_handler = 0;
404
405   widget = GTK_ACCESSIBLE (gail_expander)->widget;
406   if (widget == NULL /* State is defunct */ ||
407       !gtk_widget_is_sensitive (widget) || !gtk_widget_get_visible (widget))
408     return FALSE;
409
410   gtk_widget_activate (widget);
411
412   return FALSE;
413 }
414
415 static gint
416 gail_expander_get_n_actions (AtkAction *action)
417 {
418   return 1;
419 }
420
421 static G_CONST_RETURN gchar*
422 gail_expander_get_description (AtkAction *action,
423                                gint      i)
424 {
425   GailExpander *expander;
426   G_CONST_RETURN gchar *return_value;
427
428   expander = GAIL_EXPANDER (action);
429
430   switch (i)
431     {
432     case 0:
433       return_value = expander->activate_description;
434       break;
435     default:
436       return_value = NULL;
437       break;
438     }
439   return return_value; 
440 }
441
442 static G_CONST_RETURN gchar*
443 gail_expander_get_keybinding (AtkAction *action,
444                               gint      i)
445 {
446   GailExpander *expander;
447   gchar *return_value = NULL;
448
449   switch (i)
450     {
451     case 0:
452       {
453         /*
454          * We look for a mnemonic on the label
455          */
456         GtkWidget *widget;
457         GtkWidget *label;
458
459         expander = GAIL_EXPANDER (action);
460         widget = GTK_ACCESSIBLE (expander)->widget;
461         if (widget == NULL)
462           /*
463            * State is defunct
464            */
465           return NULL;
466
467         g_return_val_if_fail (GTK_IS_EXPANDER (widget), NULL);
468
469         label = gtk_expander_get_label_widget (GTK_EXPANDER (widget));
470         if (GTK_IS_LABEL (label))
471           {
472             guint key_val; 
473
474             key_val = gtk_label_get_mnemonic_keyval (GTK_LABEL (label)); 
475             if (key_val != GDK_VoidSymbol)
476               return_value = gtk_accelerator_name (key_val, GDK_MOD1_MASK);
477             g_free (expander->activate_keybinding);
478             expander->activate_keybinding = return_value;
479           }
480         break;
481       }
482     default:
483       break;
484     }
485   return return_value; 
486 }
487
488 static G_CONST_RETURN gchar*
489 gail_expander_action_get_name (AtkAction *action,
490                                gint      i)
491 {
492   G_CONST_RETURN gchar *return_value;
493
494   switch (i)
495     {
496     case 0:
497       return_value = "activate";
498       break;
499     default:
500       return_value = NULL;
501       break;
502     }
503   return return_value; 
504 }
505
506 static gboolean
507 gail_expander_set_description (AtkAction      *action,
508                                gint           i,
509                                const gchar    *desc)
510 {
511   GailExpander *expander;
512   gchar **value;
513
514   expander = GAIL_EXPANDER (action);
515
516   switch (i)
517     {
518     case 0:
519       value = &expander->activate_description;
520       break;
521     default:
522       value = NULL;
523       break;
524     }
525   if (value)
526     {
527       g_free (*value);
528       *value = g_strdup (desc);
529       return TRUE;
530     }
531   else
532     return FALSE;
533 }
534
535 static AtkStateSet*
536 gail_expander_ref_state_set (AtkObject *obj)
537 {
538   AtkStateSet *state_set;
539   GtkWidget *widget;
540   GtkExpander *expander;
541
542   state_set = ATK_OBJECT_CLASS (gail_expander_parent_class)->ref_state_set (obj);
543   widget = GTK_ACCESSIBLE (obj)->widget;
544
545   if (widget == NULL)
546     return state_set;
547
548   expander = GTK_EXPANDER (widget);
549
550   atk_state_set_add_state (state_set, ATK_STATE_EXPANDABLE);
551
552   if (gtk_expander_get_expanded (expander)) {
553     atk_state_set_add_state (state_set, ATK_STATE_CHECKED);
554     atk_state_set_add_state (state_set, ATK_STATE_EXPANDED);
555   }
556
557   return state_set;
558 }
559
560 /* atktext.h */
561
562 static void
563 atk_text_interface_init (AtkTextIface *iface)
564 {
565   iface->get_text = gail_expander_get_text;
566   iface->get_character_at_offset = gail_expander_get_character_at_offset;
567   iface->get_text_before_offset = gail_expander_get_text_before_offset;
568   iface->get_text_at_offset = gail_expander_get_text_at_offset;
569   iface->get_text_after_offset = gail_expander_get_text_after_offset;
570   iface->get_character_count = gail_expander_get_character_count;
571   iface->get_character_extents = gail_expander_get_character_extents;
572   iface->get_offset_at_point = gail_expander_get_offset_at_point;
573   iface->get_run_attributes = gail_expander_get_run_attributes;
574   iface->get_default_attributes = gail_expander_get_default_attributes;
575 }
576
577 static gchar*
578 gail_expander_get_text (AtkText *text,
579                         gint    start_pos,
580                         gint    end_pos)
581 {
582   GtkWidget *widget;
583   GailExpander *expander;
584   const gchar *label_text;
585
586   widget = GTK_ACCESSIBLE (text)->widget;
587   if (widget == NULL)
588     /* State is defunct */
589     return NULL;
590
591   expander = GAIL_EXPANDER (text);
592   if (!expander->textutil) 
593     gail_expander_init_textutil (expander, GTK_EXPANDER (widget));
594
595   label_text = gail_expander_get_full_text (GTK_EXPANDER (widget));
596
597   if (label_text == NULL)
598     return NULL;
599   else
600     return gail_text_util_get_substring (expander->textutil, 
601                                          start_pos, end_pos);
602 }
603
604 static gchar*
605 gail_expander_get_text_before_offset (AtkText         *text,
606                                       gint            offset,
607                                       AtkTextBoundary boundary_type,
608                                       gint            *start_offset,
609                                       gint            *end_offset)
610 {
611   GtkWidget *widget;
612   GailExpander *expander;
613   GtkWidget *label;
614
615   widget = GTK_ACCESSIBLE (text)->widget;
616   if (widget == NULL)
617     /* State is defunct */
618     return NULL;
619
620   expander = GAIL_EXPANDER (text);
621   if (!expander->textutil) 
622     gail_expander_init_textutil (expander, GTK_EXPANDER (widget));
623
624   label = gtk_expander_get_label_widget (GTK_EXPANDER (widget));
625   if (!GTK_IS_LABEL(label))
626     return NULL;
627   return gail_text_util_get_text (expander->textutil,
628                            gtk_label_get_layout (GTK_LABEL (label)),
629                            GAIL_BEFORE_OFFSET, 
630                            boundary_type, offset, start_offset, end_offset); 
631 }
632
633 static gchar*
634 gail_expander_get_text_at_offset (AtkText         *text,
635                                   gint            offset,
636                                   AtkTextBoundary boundary_type,
637                                   gint            *start_offset,
638                                   gint            *end_offset)
639 {
640   GtkWidget *widget;
641   GailExpander *expander;
642   GtkWidget *label;
643
644   widget = GTK_ACCESSIBLE (text)->widget;
645   if (widget == NULL)
646     /* State is defunct */
647     return NULL;
648
649   expander = GAIL_EXPANDER (text);
650   if (!expander->textutil) 
651     gail_expander_init_textutil (expander, GTK_EXPANDER (widget));
652
653   label = gtk_expander_get_label_widget (GTK_EXPANDER (widget));
654   if (!GTK_IS_LABEL(label))
655     return NULL;
656   return gail_text_util_get_text (expander->textutil,
657                            gtk_label_get_layout (GTK_LABEL (label)),
658                            GAIL_AT_OFFSET, 
659                            boundary_type, offset, start_offset, end_offset);
660 }
661
662 static gchar*
663 gail_expander_get_text_after_offset (AtkText         *text,
664                                      gint            offset,
665                                      AtkTextBoundary boundary_type,
666                                      gint            *start_offset,
667                                      gint            *end_offset)
668 {
669   GtkWidget *widget;
670   GailExpander *expander;
671   GtkWidget *label;
672
673   widget = GTK_ACCESSIBLE (text)->widget;
674   if (widget == NULL)
675     /* State is defunct */
676     return NULL;
677
678   expander = GAIL_EXPANDER (text);
679   if (!expander->textutil) 
680     gail_expander_init_textutil (expander, GTK_EXPANDER (widget));
681
682   label = gtk_expander_get_label_widget (GTK_EXPANDER (widget));
683   if (!GTK_IS_LABEL(label))
684     return NULL;
685   return gail_text_util_get_text (expander->textutil,
686                            gtk_label_get_layout (GTK_LABEL (label)),
687                            GAIL_AFTER_OFFSET, 
688                            boundary_type, offset, start_offset, end_offset);
689 }
690
691 static gint
692 gail_expander_get_character_count (AtkText *text)
693 {
694   GtkWidget *widget;
695   GtkWidget *label;
696
697   widget = GTK_ACCESSIBLE (text)->widget;
698   if (widget == NULL)
699     /* State is defunct */
700     return 0;
701
702   label = gtk_expander_get_label_widget (GTK_EXPANDER (widget));
703   if (!GTK_IS_LABEL(label))
704     return 0;
705
706   return g_utf8_strlen (gtk_label_get_text (GTK_LABEL (label)), -1);
707 }
708
709 static void
710 gail_expander_get_character_extents (AtkText      *text,
711                                      gint         offset,
712                                      gint         *x,
713                                      gint       *y,
714                                      gint       *width,
715                                      gint       *height,
716                                      AtkCoordType coords)
717 {
718   GtkWidget *widget;
719   GtkWidget *label;
720   PangoRectangle char_rect;
721   gint index, x_layout, y_layout;
722   const gchar *label_text;
723  
724   widget = GTK_ACCESSIBLE (text)->widget;
725
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 (text)->widget;
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 (text)->widget;
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 (text)->widget;
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 (text)->widget;
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 }