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