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