]> Pileus Git - ~andy/gtk/blob - modules/other/gail/gailnotebookpage.c
Use G_DEFINE_TYPE[_WITH_CODE] instead of hand-coding the get_type functions. Bug...
[~andy/gtk] / modules / other / gail / gailnotebookpage.c
1 /* GAIL - The GNOME Accessibility Implementation Library
2  * Copyright 2001, 2002, 2003 Sun Microsystems Inc.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 #include <string.h>
21 #include <gtk/gtk.h>
22 #include "gailnotebookpage.h"
23 #include <libgail-util/gailmisc.h>
24 #include "gail-private-macros.h"
25
26 static void      gail_notebook_page_class_init      (GailNotebookPageClass     *klass);
27 static void                  gail_notebook_page_init           (GailNotebookPage *page);
28 static void                  gail_notebook_page_finalize       (GObject   *object);
29 static void                  gail_notebook_page_label_map_gtk  (GtkWidget *widget,
30                                                                 gpointer  data);
31
32 static G_CONST_RETURN gchar* gail_notebook_page_get_name       (AtkObject *accessible);
33 static AtkObject*            gail_notebook_page_get_parent     (AtkObject *accessible);
34 static gint                  gail_notebook_page_get_n_children (AtkObject *accessible);
35 static AtkObject*            gail_notebook_page_ref_child      (AtkObject *accessible,
36                                                                 gint      i); 
37 static gint                  gail_notebook_page_get_index_in_parent
38                                                                (AtkObject *accessible);
39 static AtkStateSet*          gail_notebook_page_ref_state_set  (AtkObject *accessible);
40
41 static gint                  gail_notebook_page_notify          (GObject   *obj,
42                                                                  GParamSpec *pspec,
43                                                                  gpointer   user_data);
44 static void                  gail_notebook_page_init_textutil   (GailNotebookPage      *notebook_page,
45                                                                  GtkWidget             *label);
46
47 static void                  atk_component_interface_init      (AtkComponentIface *iface);
48
49 static AtkObject*            gail_notebook_page_ref_accessible_at_point 
50                                                                (AtkComponent *component,
51                                                                 gint         x,
52                                                                 gint         y,
53                                                                 AtkCoordType coord_type);
54
55 static void                  gail_notebook_page_get_extents    (AtkComponent *component,
56                                                                 gint         *x,
57                                                                 gint         *y,
58                                                                 gint         *width,
59                                                                 gint         *height,
60                                                                 AtkCoordType coord_type);
61
62 static AtkObject*            _gail_notebook_page_get_tab_label (GailNotebookPage *page);
63
64 /* atktext.h */ 
65 static void       atk_text_interface_init          (AtkTextIface        *iface);
66
67 static gchar*     gail_notebook_page_get_text      (AtkText           *text,
68                                                     gint              start_pos,
69                                                     gint              end_pos);
70 static gunichar   gail_notebook_page_get_character_at_offset
71                                                    (AtkText           *text,
72                                                     gint              offset);
73 static gchar*     gail_notebook_page_get_text_before_offset
74                                                    (AtkText           *text,
75                                                     gint              offset,
76                                                     AtkTextBoundary   boundary_type,
77                                                     gint              *start_offset,
78                                                     gint              *end_offset);
79 static gchar*     gail_notebook_page_get_text_at_offset
80                                                    (AtkText           *text,
81                                                     gint              offset,
82                                                     AtkTextBoundary   boundary_type,
83                                                     gint              *start_offset,
84                                                     gint              *end_offset);
85 static gchar*     gail_notebook_page_get_text_after_offset
86                                                    (AtkText           *text,
87                                                     gint              offset,
88                                                     AtkTextBoundary   boundary_type,
89                                                     gint              *start_offset,
90                                                     gint              *end_offset);
91 static gint       gail_notebook_page_get_character_count (AtkText             *text);
92 static void       gail_notebook_page_get_character_extents
93                                                    (AtkText           *text,
94                                                     gint              offset,
95                                                     gint              *x,
96                                                     gint              *y,
97                                                     gint              *width,
98                                                     gint              *height,
99                                                     AtkCoordType      coords);
100 static gint      gail_notebook_page_get_offset_at_point
101                                                    (AtkText           *text,
102                                                     gint              x,
103                                                     gint              y,
104                                                     AtkCoordType      coords);
105 static AtkAttributeSet* gail_notebook_page_get_run_attributes 
106                                                    (AtkText           *text,
107                                                     gint              offset,
108                                                     gint              *start_offset,
109                                                     gint              *end_offset);
110 static AtkAttributeSet* gail_notebook_page_get_default_attributes
111                                                    (AtkText           *text);
112 static GtkWidget* get_label_from_notebook_page     (GailNotebookPage  *page);
113 static GtkWidget* find_label_child (GtkContainer *container);
114
115 /* FIXME: not GAIL_TYPE_OBJECT? */
116 G_DEFINE_TYPE_WITH_CODE (GailNotebookPage, gail_notebook_page, ATK_TYPE_OBJECT,
117                          G_IMPLEMENT_INTERFACE (ATK_TYPE_COMPONENT, atk_component_interface_init)
118                          G_IMPLEMENT_INTERFACE (ATK_TYPE_TEXT, atk_text_interface_init))
119
120 static void
121 gail_notebook_page_class_init (GailNotebookPageClass *klass)
122 {
123   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
124   AtkObjectClass *class = ATK_OBJECT_CLASS (klass);
125   
126   class->get_name = gail_notebook_page_get_name;
127   class->get_parent = gail_notebook_page_get_parent;
128   class->get_n_children = gail_notebook_page_get_n_children;
129   class->ref_child = gail_notebook_page_ref_child;
130   class->ref_state_set = gail_notebook_page_ref_state_set;
131   class->get_index_in_parent = gail_notebook_page_get_index_in_parent;
132
133   gobject_class->finalize = gail_notebook_page_finalize;
134 }
135
136 static void
137 gail_notebook_page_init (GailNotebookPage *page)
138 {
139 }
140
141 static gint
142 notify_child_added (gpointer data)
143 {
144   GailNotebookPage *page;
145   AtkObject *atk_object, *atk_parent;
146
147   g_return_val_if_fail (GAIL_IS_NOTEBOOK_PAGE (data), FALSE);
148   page = GAIL_NOTEBOOK_PAGE (data);
149   atk_object = ATK_OBJECT (data);
150
151   page->notify_child_added_id = 0;
152
153   /* The widget page->notebook may be deleted before this handler is called */
154   if (page->notebook != NULL)
155     {
156       atk_parent = gtk_widget_get_accessible (GTK_WIDGET (page->notebook));
157       atk_object_set_parent (atk_object, atk_parent);
158       g_signal_emit_by_name (atk_parent, "children_changed::add", page->index, atk_object, NULL);
159     }
160   
161   return FALSE;
162 }
163
164 AtkObject*
165 gail_notebook_page_new (GtkNotebook *notebook, 
166                         gint        pagenum)
167 {
168   GObject *object;
169   AtkObject *atk_object;
170   GailNotebookPage *page;
171   GtkWidget *child;
172   GtkWidget *label;
173   GList *list;
174   
175   g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), NULL);
176
177   child = gtk_notebook_get_nth_page (notebook, pagenum);
178
179   if (!child)
180     return NULL;
181
182   object = g_object_new (GAIL_TYPE_NOTEBOOK_PAGE, NULL);
183   g_return_val_if_fail (object != NULL, NULL);
184
185   page = GAIL_NOTEBOOK_PAGE (object);
186   page->notebook = notebook;
187   g_object_add_weak_pointer (G_OBJECT (page->notebook), (gpointer *)&page->notebook);
188   page->index = pagenum;
189   list = g_list_nth (notebook->children, pagenum);
190   page->page = list->data;
191   page->textutil = NULL;
192   
193   atk_object = ATK_OBJECT (page);
194   atk_object->role = ATK_ROLE_PAGE_TAB;
195   atk_object->layer = ATK_LAYER_WIDGET;
196
197   page->notify_child_added_id = gdk_threads_add_idle (notify_child_added, atk_object);
198   /*
199    * We get notified of changes to the label
200    */
201   label = get_label_from_notebook_page (page);
202   if (GTK_IS_LABEL (label))
203     {
204       if (GTK_WIDGET_MAPPED (label))
205         gail_notebook_page_init_textutil (page, label);
206       else
207         g_signal_connect (label,
208                           "map",
209                           G_CALLBACK (gail_notebook_page_label_map_gtk),
210                           page);
211     }
212
213   return atk_object;
214 }
215
216 static void
217 gail_notebook_page_label_map_gtk (GtkWidget *widget,
218                                   gpointer data)
219 {
220   GailNotebookPage *page;
221
222   page = GAIL_NOTEBOOK_PAGE (data);
223   gail_notebook_page_init_textutil (page, widget);
224 }
225
226 static void
227 gail_notebook_page_init_textutil (GailNotebookPage *page,
228                                   GtkWidget        *label)
229 {
230   const gchar *label_text;
231
232   if (page->textutil == NULL)
233     {
234       page->textutil = gail_text_util_new ();
235       g_signal_connect (label,
236                         "notify",
237                         (GCallback) gail_notebook_page_notify,
238                         page);     
239     }
240   label_text = gtk_label_get_text (GTK_LABEL (label));
241   gail_text_util_text_setup (page->textutil, label_text);
242 }
243
244 static gint
245 gail_notebook_page_notify (GObject    *obj, 
246                            GParamSpec *pspec,
247                            gpointer   user_data)
248 {
249   AtkObject *atk_obj = ATK_OBJECT (user_data);
250   GtkLabel *label;
251   GailNotebookPage *page;
252
253   if (strcmp (pspec->name, "label") == 0)
254     {
255       const gchar* label_text;
256
257       label = GTK_LABEL (obj);
258
259       label_text = gtk_label_get_text (label);
260
261       page = GAIL_NOTEBOOK_PAGE (atk_obj);
262       gail_text_util_text_setup (page->textutil, label_text);
263
264       if (atk_obj->name == NULL)
265       {
266         /*
267          * The label has changed so notify a change in accessible-name
268          */
269         g_object_notify (G_OBJECT (atk_obj), "accessible-name");
270       }
271       /*
272        * The label is the only property which can be changed
273        */
274       g_signal_emit_by_name (atk_obj, "visible_data_changed");
275     }
276   return 1;
277 }
278
279 static void
280 gail_notebook_page_finalize (GObject *object)
281 {
282   GailNotebookPage *page = GAIL_NOTEBOOK_PAGE (object);
283
284   if (page->notebook)
285     g_object_remove_weak_pointer (G_OBJECT (page->notebook), (gpointer *)&page->notebook);
286
287   if (page->textutil)
288     g_object_unref (page->textutil);
289
290   if (page->notify_child_added_id)
291     g_source_remove (page->notify_child_added_id);
292
293   G_OBJECT_CLASS (gail_notebook_page_parent_class)->finalize (object);
294 }
295
296 static G_CONST_RETURN gchar*
297 gail_notebook_page_get_name (AtkObject *accessible)
298 {
299   g_return_val_if_fail (GAIL_IS_NOTEBOOK_PAGE (accessible), NULL);
300   
301   if (accessible->name != NULL)
302     return accessible->name;
303   else
304     {
305       GtkWidget *label;
306
307       label = get_label_from_notebook_page (GAIL_NOTEBOOK_PAGE (accessible));
308       if (GTK_IS_LABEL (label))
309         return gtk_label_get_text (GTK_LABEL (label));
310       else
311         return NULL;
312     }
313 }
314
315 static AtkObject*
316 gail_notebook_page_get_parent (AtkObject *accessible)
317 {
318   GailNotebookPage *page;
319   
320   g_return_val_if_fail (GAIL_IS_NOTEBOOK_PAGE (accessible), NULL);
321   
322   page = GAIL_NOTEBOOK_PAGE (accessible);
323
324   if (!page->notebook)
325     return NULL;
326
327   return gtk_widget_get_accessible (GTK_WIDGET (page->notebook));
328 }
329
330 static gint
331 gail_notebook_page_get_n_children (AtkObject *accessible)
332 {
333   /* Notebook page has only one child */
334   g_return_val_if_fail (GAIL_IS_NOTEBOOK_PAGE (accessible), 0);
335
336   return 1;
337 }
338
339 static AtkObject*
340 gail_notebook_page_ref_child (AtkObject *accessible,
341                               gint i)
342 {
343   GtkWidget *child;
344   AtkObject *child_obj;
345   GailNotebookPage *page = NULL;
346    
347   g_return_val_if_fail (GAIL_IS_NOTEBOOK_PAGE (accessible), NULL);
348   if (i != 0)
349     return NULL;
350    
351   page = GAIL_NOTEBOOK_PAGE (accessible);
352   if (!page->notebook)
353     return NULL;
354
355   child = gtk_notebook_get_nth_page (page->notebook, page->index);
356   gail_return_val_if_fail (GTK_IS_WIDGET (child), NULL);
357    
358   child_obj = gtk_widget_get_accessible (child);
359   g_object_ref (child_obj);
360   return child_obj;
361 }
362
363 static gint
364 gail_notebook_page_get_index_in_parent (AtkObject *accessible)
365 {
366   GailNotebookPage *page;
367
368   g_return_val_if_fail (GAIL_IS_NOTEBOOK_PAGE (accessible), -1);
369   page = GAIL_NOTEBOOK_PAGE (accessible);
370
371   return page->index;
372 }
373
374 static AtkStateSet*
375 gail_notebook_page_ref_state_set (AtkObject *accessible)
376 {
377   AtkStateSet *state_set, *label_state_set, *merged_state_set;
378   AtkObject *atk_label;
379
380   g_return_val_if_fail (GAIL_NOTEBOOK_PAGE (accessible), NULL);
381
382   state_set = ATK_OBJECT_CLASS (gail_notebook_page_parent_class)->ref_state_set (accessible);
383
384   atk_label = _gail_notebook_page_get_tab_label (GAIL_NOTEBOOK_PAGE (accessible));
385   if (atk_label)
386     {
387       label_state_set = atk_object_ref_state_set (atk_label);
388       merged_state_set = atk_state_set_or_sets (state_set, label_state_set);
389       g_object_unref (label_state_set);
390       g_object_unref (state_set);
391     }
392   else
393     {
394       AtkObject *child;
395
396       child = atk_object_ref_accessible_child (accessible, 0);
397       gail_return_val_if_fail (child, state_set);
398
399       merged_state_set = state_set;
400       state_set = atk_object_ref_state_set (child);
401       if (atk_state_set_contains_state (state_set, ATK_STATE_VISIBLE))
402         {
403           atk_state_set_add_state (merged_state_set, ATK_STATE_VISIBLE);
404           if (atk_state_set_contains_state (state_set, ATK_STATE_ENABLED))
405               atk_state_set_add_state (merged_state_set, ATK_STATE_ENABLED);
406           if (atk_state_set_contains_state (state_set, ATK_STATE_SHOWING))
407               atk_state_set_add_state (merged_state_set, ATK_STATE_SHOWING);
408
409         } 
410       g_object_unref (state_set);
411       g_object_unref (child);
412     }
413   return merged_state_set;
414 }
415
416
417 static void
418 atk_component_interface_init (AtkComponentIface *iface)
419 {
420   /*
421    * We use the default implementations for contains, get_position, get_size
422    */
423   iface->ref_accessible_at_point = gail_notebook_page_ref_accessible_at_point;
424   iface->get_extents = gail_notebook_page_get_extents;
425 }
426
427 static AtkObject*
428 gail_notebook_page_ref_accessible_at_point (AtkComponent *component,
429                                             gint         x,
430                                             gint         y,
431                                             AtkCoordType coord_type)
432 {
433   /*
434    * There is only one child so we return it.
435    */
436   AtkObject* child;
437
438   g_return_val_if_fail (ATK_IS_OBJECT (component), NULL);
439
440   child = atk_object_ref_accessible_child (ATK_OBJECT (component), 0);
441   return child;
442 }
443
444 static void
445 gail_notebook_page_get_extents (AtkComponent *component,
446                                 gint         *x,
447                                 gint         *y,
448                                 gint         *width,
449                                 gint         *height,
450                                 AtkCoordType coord_type)
451 {
452   AtkObject *atk_label;
453
454   g_return_if_fail (GAIL_IS_NOTEBOOK_PAGE (component));
455
456   atk_label = _gail_notebook_page_get_tab_label (GAIL_NOTEBOOK_PAGE (component));
457
458   if (!atk_label)
459     {
460       AtkObject *child;
461
462       *width = 0;
463       *height = 0;
464
465       child = atk_object_ref_accessible_child (ATK_OBJECT (component), 0);
466       gail_return_if_fail (child);
467
468       atk_component_get_position (ATK_COMPONENT (child), x, y, coord_type);
469       g_object_unref (child);
470     }
471   else
472     {
473       atk_component_get_extents (ATK_COMPONENT (atk_label), 
474                                  x, y, width, height, coord_type);
475     }
476   return; 
477 }
478
479 static AtkObject*
480 _gail_notebook_page_get_tab_label (GailNotebookPage *page)
481 {
482   GtkWidget *label;
483
484   label = get_label_from_notebook_page (page);
485   if (label)
486     return gtk_widget_get_accessible (label);
487   else
488     return NULL;
489 }
490
491 /* atktext.h */
492
493 static void
494 atk_text_interface_init (AtkTextIface *iface)
495 {
496   iface->get_text = gail_notebook_page_get_text;
497   iface->get_character_at_offset = gail_notebook_page_get_character_at_offset;
498   iface->get_text_before_offset = gail_notebook_page_get_text_before_offset;
499   iface->get_text_at_offset = gail_notebook_page_get_text_at_offset;
500   iface->get_text_after_offset = gail_notebook_page_get_text_after_offset;
501   iface->get_character_count = gail_notebook_page_get_character_count;
502   iface->get_character_extents = gail_notebook_page_get_character_extents;
503   iface->get_offset_at_point = gail_notebook_page_get_offset_at_point;
504   iface->get_run_attributes = gail_notebook_page_get_run_attributes;
505   iface->get_default_attributes = gail_notebook_page_get_default_attributes;
506 }
507
508 static gchar*
509 gail_notebook_page_get_text (AtkText *text,
510                              gint    start_pos,
511                              gint    end_pos)
512 {
513   GtkWidget *label;
514   GailNotebookPage *notebook_page;
515   const gchar *label_text;
516
517   notebook_page = GAIL_NOTEBOOK_PAGE (text);
518   label = get_label_from_notebook_page (notebook_page);
519
520   if (!GTK_IS_LABEL (label))
521     return NULL;
522
523   if (!notebook_page->textutil) 
524     gail_notebook_page_init_textutil (notebook_page, label);
525
526   label_text = gtk_label_get_text (GTK_LABEL (label));
527
528   if (label_text == NULL)
529     return NULL;
530   else
531   {
532     return gail_text_util_get_substring (notebook_page->textutil, 
533                                          start_pos, end_pos);
534   }
535 }
536
537 static gchar*
538 gail_notebook_page_get_text_before_offset (AtkText         *text,
539                                            gint            offset,
540                                            AtkTextBoundary boundary_type,
541                                            gint            *start_offset,
542                                            gint            *end_offset)
543 {
544   GtkWidget *label;
545   GailNotebookPage *notebook_page;
546   
547   notebook_page = GAIL_NOTEBOOK_PAGE (text);
548   label = get_label_from_notebook_page (notebook_page);
549
550   if (!GTK_IS_LABEL(label))
551     return NULL;
552
553   if (!notebook_page->textutil)
554     gail_notebook_page_init_textutil (notebook_page, label);
555
556   return gail_text_util_get_text (notebook_page->textutil,
557                            gtk_label_get_layout (GTK_LABEL (label)), GAIL_BEFORE_OFFSET, 
558                            boundary_type, offset, start_offset, end_offset); 
559 }
560
561 static gchar*
562 gail_notebook_page_get_text_at_offset (AtkText         *text,
563                                        gint            offset,
564                                        AtkTextBoundary boundary_type,
565                                        gint            *start_offset,
566                                        gint            *end_offset)
567 {
568   GtkWidget *label;
569   GailNotebookPage *notebook_page;
570  
571   notebook_page = GAIL_NOTEBOOK_PAGE (text);
572   label = get_label_from_notebook_page (notebook_page);
573
574   if (!GTK_IS_LABEL(label))
575     return NULL;
576
577   if (!notebook_page->textutil)
578     gail_notebook_page_init_textutil (notebook_page, label);
579
580   return gail_text_util_get_text (notebook_page->textutil,
581                               gtk_label_get_layout (GTK_LABEL (label)), GAIL_AT_OFFSET, 
582                               boundary_type, offset, start_offset, end_offset);
583 }
584
585 static gchar*
586 gail_notebook_page_get_text_after_offset (AtkText         *text,
587                                           gint            offset,
588                                           AtkTextBoundary boundary_type,
589                                           gint            *start_offset,
590                                           gint            *end_offset)
591 {
592   GtkWidget *label;
593   GailNotebookPage *notebook_page;
594
595   notebook_page = GAIL_NOTEBOOK_PAGE (text);
596   label = get_label_from_notebook_page (notebook_page);
597
598   if (!GTK_IS_LABEL(label))
599     return NULL;
600
601   if (!notebook_page->textutil)
602     gail_notebook_page_init_textutil (notebook_page, label);
603
604   return gail_text_util_get_text (notebook_page->textutil,
605                            gtk_label_get_layout (GTK_LABEL (label)), GAIL_AFTER_OFFSET, 
606                            boundary_type, offset, start_offset, end_offset);
607 }
608
609 static gint
610 gail_notebook_page_get_character_count (AtkText *text)
611 {
612   GtkWidget *label;
613   GailNotebookPage *notebook_page;
614
615   notebook_page = GAIL_NOTEBOOK_PAGE (text);
616   label = get_label_from_notebook_page (notebook_page);
617
618   if (!GTK_IS_LABEL(label))
619     return 0;
620
621   return g_utf8_strlen (gtk_label_get_text (GTK_LABEL (label)), -1);
622 }
623
624 static void
625 gail_notebook_page_get_character_extents (AtkText      *text,
626                                           gint         offset,
627                                           gint         *x,
628                                           gint         *y,
629                                           gint         *width,
630                                           gint         *height,
631                                           AtkCoordType coords)
632 {
633   GtkWidget *label;
634   GailNotebookPage *notebook_page;
635   PangoRectangle char_rect;
636   gint index, x_layout, y_layout;
637   const gchar *label_text;
638  
639   notebook_page = GAIL_NOTEBOOK_PAGE (text);
640   label = get_label_from_notebook_page (notebook_page);
641
642   if (!GTK_IS_LABEL(label))
643     return;
644   
645   gtk_label_get_layout_offsets (GTK_LABEL (label), &x_layout, &y_layout);
646   label_text = gtk_label_get_text (GTK_LABEL (label));
647   index = g_utf8_offset_to_pointer (label_text, offset) - label_text;
648   pango_layout_index_to_pos (gtk_label_get_layout (GTK_LABEL (label)), index, &char_rect);
649   
650   gail_misc_get_extents_from_pango_rectangle (label, &char_rect, 
651                     x_layout, y_layout, x, y, width, height, coords);
652
653
654 static gint 
655 gail_notebook_page_get_offset_at_point (AtkText      *text,
656                                         gint         x,
657                                         gint         y,
658                                         AtkCoordType coords)
659
660   GtkWidget *label;
661   GailNotebookPage *notebook_page;
662   gint index, x_layout, y_layout;
663   const gchar *label_text;
664
665   notebook_page = GAIL_NOTEBOOK_PAGE (text);
666   label = get_label_from_notebook_page (notebook_page);
667
668   if (!GTK_IS_LABEL(label))
669     return -1;
670   
671   gtk_label_get_layout_offsets (GTK_LABEL (label), &x_layout, &y_layout);
672   
673   index = gail_misc_get_index_at_point_in_layout (label, 
674                                               gtk_label_get_layout (GTK_LABEL (label)), 
675                                               x_layout, y_layout, x, y, coords);
676   label_text = gtk_label_get_text (GTK_LABEL (label));
677   if (index == -1)
678     {
679       if (coords == ATK_XY_WINDOW || coords == ATK_XY_SCREEN)
680         return g_utf8_strlen (label_text, -1);
681
682       return index;  
683     }
684   else
685     return g_utf8_pointer_to_offset (label_text, label_text + index);  
686 }
687
688 static AtkAttributeSet*
689 gail_notebook_page_get_run_attributes (AtkText *text,
690                                        gint    offset,
691                                        gint    *start_offset,
692                                        gint    *end_offset)
693 {
694   GtkWidget *label;
695   GailNotebookPage *notebook_page;
696   AtkAttributeSet *at_set = NULL;
697   GtkJustification justify;
698   GtkTextDirection dir;
699
700   notebook_page = GAIL_NOTEBOOK_PAGE (text);
701   label = get_label_from_notebook_page (notebook_page);
702
703   if (!GTK_IS_LABEL(label))
704     return NULL;
705   
706   /* Get values set for entire label, if any */
707   justify = gtk_label_get_justify (GTK_LABEL (label));
708   if (justify != GTK_JUSTIFY_CENTER)
709     {
710       at_set = gail_misc_add_attribute (at_set, 
711                                         ATK_TEXT_ATTR_JUSTIFICATION,
712      g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_JUSTIFICATION, justify)));
713     }
714   dir = gtk_widget_get_direction (label);
715   if (dir == GTK_TEXT_DIR_RTL)
716     {
717       at_set = gail_misc_add_attribute (at_set, 
718                                         ATK_TEXT_ATTR_DIRECTION,
719      g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_DIRECTION, dir)));
720     }
721
722   at_set = gail_misc_layout_get_run_attributes (at_set,
723                                                 gtk_label_get_layout (GTK_LABEL (label)),
724                                                 (gchar *) gtk_label_get_text (GTK_LABEL (label)),
725                                                 offset,
726                                                 start_offset,
727                                                 end_offset);
728   return at_set;
729 }
730
731 static AtkAttributeSet*
732 gail_notebook_page_get_default_attributes (AtkText *text)
733 {
734   GtkWidget *label;
735   GailNotebookPage *notebook_page;
736   AtkAttributeSet *at_set = NULL;
737
738   notebook_page = GAIL_NOTEBOOK_PAGE (text);
739   label = get_label_from_notebook_page (notebook_page);
740
741   if (!GTK_IS_LABEL(label))
742     return NULL;
743
744   at_set = gail_misc_get_default_attributes (at_set,
745                                              gtk_label_get_layout (GTK_LABEL (label)),
746                                              label);
747   return at_set;
748 }
749
750 static gunichar 
751 gail_notebook_page_get_character_at_offset (AtkText *text,
752                                             gint    offset)
753 {
754   GtkWidget *label;
755   GailNotebookPage *notebook_page;
756   const gchar *string;
757   gchar *index;
758
759   notebook_page = GAIL_NOTEBOOK_PAGE (text);
760   label = get_label_from_notebook_page (notebook_page);
761
762   if (!GTK_IS_LABEL(label))
763     return '\0';
764   string = gtk_label_get_text (GTK_LABEL (label));
765   if (offset >= g_utf8_strlen (string, -1))
766     return '\0';
767   index = g_utf8_offset_to_pointer (string, offset);
768
769   return g_utf8_get_char (index);
770 }
771
772 static GtkWidget*
773 get_label_from_notebook_page (GailNotebookPage *page)
774 {
775   GtkWidget *child;
776   GtkNotebook *notebook;
777
778   notebook = page->notebook;
779   if (!notebook)
780     return NULL;
781
782   if (!gtk_notebook_get_show_tabs (notebook))
783     return NULL;
784
785   child = gtk_notebook_get_nth_page (notebook, page->index);
786   if (child == NULL) return NULL;
787   g_return_val_if_fail (GTK_IS_WIDGET (child), NULL);
788
789   child = gtk_notebook_get_tab_label (notebook, child);
790
791   if (GTK_IS_LABEL (child))
792     return child;
793
794   if (GTK_IS_CONTAINER (child))
795     child = find_label_child (GTK_CONTAINER (child));
796
797   return child;
798 }
799
800 static GtkWidget*
801 find_label_child (GtkContainer *container)
802 {
803   GList *children, *tmp_list;
804   GtkWidget *child;
805
806   children = gtk_container_get_children (container);
807
808   child = NULL;
809   for (tmp_list = children; tmp_list != NULL; tmp_list = tmp_list->next)
810     {
811       if (GTK_IS_LABEL (tmp_list->data))
812         {
813           child = GTK_WIDGET (tmp_list->data);
814           break;
815         }
816       else if (GTK_IS_CONTAINER (tmp_list->data))
817         {
818           child = find_label_child (GTK_CONTAINER (tmp_list->data));
819           if (child)
820             break;
821         }
822     }
823   g_list_free (children);
824   return child;
825 }