1 /* GAIL - The GNOME Accessibility Implementation Library
2 * Copyright 2001, 2002, 2003 Sun Microsystems Inc.
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.
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.
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.
22 #include "gailnotebookpage.h"
23 #include <libgail-util/gailmisc.h>
24 #include "gail-private-macros.h"
26 static void gail_notebook_page_class_init (GailNotebookPageClass *klass);
28 static void gail_notebook_page_finalize (GObject *object);
29 static void gail_notebook_page_label_map_gtk (GtkWidget *widget,
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,
37 static gint gail_notebook_page_get_index_in_parent
38 (AtkObject *accessible);
39 static AtkStateSet* gail_notebook_page_ref_state_set (AtkObject *accessible);
41 static gint gail_notebook_page_notify (GObject *obj,
44 static void gail_notebook_page_init_textutil (GailNotebookPage *notebook_page,
47 static void atk_component_interface_init (AtkComponentIface *iface);
49 static AtkObject* gail_notebook_page_ref_accessible_at_point
50 (AtkComponent *component,
53 AtkCoordType coord_type);
55 static void gail_notebook_page_get_extents (AtkComponent *component,
60 AtkCoordType coord_type);
62 static AtkObject* _gail_notebook_page_get_tab_label (GailNotebookPage *page);
65 static void atk_text_interface_init (AtkTextIface *iface);
67 static gchar* gail_notebook_page_get_text (AtkText *text,
70 static gunichar gail_notebook_page_get_character_at_offset
73 static gchar* gail_notebook_page_get_text_before_offset
76 AtkTextBoundary boundary_type,
79 static gchar* gail_notebook_page_get_text_at_offset
82 AtkTextBoundary boundary_type,
85 static gchar* gail_notebook_page_get_text_after_offset
88 AtkTextBoundary boundary_type,
91 static gint gail_notebook_page_get_character_count (AtkText *text);
92 static void gail_notebook_page_get_character_extents
100 static gint gail_notebook_page_get_offset_at_point
104 AtkCoordType coords);
105 static AtkAttributeSet* gail_notebook_page_get_run_attributes
110 static AtkAttributeSet* gail_notebook_page_get_default_attributes
112 static GtkWidget* get_label_from_notebook_page (GailNotebookPage *page);
113 static GtkWidget* find_label_child (GtkContainer *container);
115 static gpointer parent_class = NULL;
118 gail_notebook_page_get_type (void)
120 static GType type = 0;
124 static const GTypeInfo tinfo =
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 */
138 static const GInterfaceInfo atk_component_info =
140 (GInterfaceInitFunc) atk_component_interface_init,
141 (GInterfaceFinalizeFunc) NULL,
145 static const GInterfaceInfo atk_text_info =
147 (GInterfaceInitFunc) atk_text_interface_init,
148 (GInterfaceFinalizeFunc) NULL,
152 type = g_type_register_static (ATK_TYPE_OBJECT,
153 "GailNotebookPage", &tinfo, 0);
155 g_type_add_interface_static (type, ATK_TYPE_COMPONENT,
156 &atk_component_info);
157 g_type_add_interface_static (type, ATK_TYPE_TEXT,
164 gail_notebook_page_class_init (GailNotebookPageClass *klass)
166 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
167 AtkObjectClass *class = ATK_OBJECT_CLASS (klass);
169 parent_class = g_type_class_peek_parent (klass);
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;
178 gobject_class->finalize = gail_notebook_page_finalize;
182 notify_child_added (gpointer data)
184 GailNotebookPage *page;
185 AtkObject *atk_object, *atk_parent;
187 g_return_val_if_fail (GAIL_IS_NOTEBOOK_PAGE (data), FALSE);
188 page = GAIL_NOTEBOOK_PAGE (data);
189 atk_object = ATK_OBJECT (data);
191 page->notify_child_added_id = 0;
193 /* The widget page->notebook may be deleted before this handler is called */
194 if (page->notebook != NULL)
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);
205 gail_notebook_page_new (GtkNotebook *notebook,
209 AtkObject *atk_object;
210 GailNotebookPage *page;
215 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), NULL);
217 child = gtk_notebook_get_nth_page (notebook, pagenum);
222 object = g_object_new (GAIL_TYPE_NOTEBOOK_PAGE, NULL);
223 g_return_val_if_fail (object != NULL, NULL);
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;
233 atk_object = ATK_OBJECT (page);
234 atk_object->role = ATK_ROLE_PAGE_TAB;
235 atk_object->layer = ATK_LAYER_WIDGET;
237 page->notify_child_added_id = gdk_threads_add_idle (notify_child_added, atk_object);
239 * We get notified of changes to the label
241 label = get_label_from_notebook_page (page);
242 if (GTK_IS_LABEL (label))
244 if (GTK_WIDGET_MAPPED (label))
245 gail_notebook_page_init_textutil (page, label);
247 g_signal_connect (label,
249 G_CALLBACK (gail_notebook_page_label_map_gtk),
257 gail_notebook_page_label_map_gtk (GtkWidget *widget,
260 GailNotebookPage *page;
262 page = GAIL_NOTEBOOK_PAGE (data);
263 gail_notebook_page_init_textutil (page, widget);
267 gail_notebook_page_init_textutil (GailNotebookPage *page,
270 const gchar *label_text;
272 if (page->textutil == NULL)
274 page->textutil = gail_text_util_new ();
275 g_signal_connect (label,
277 (GCallback) gail_notebook_page_notify,
280 label_text = gtk_label_get_text (GTK_LABEL (label));
281 gail_text_util_text_setup (page->textutil, label_text);
285 gail_notebook_page_notify (GObject *obj,
289 AtkObject *atk_obj = ATK_OBJECT (user_data);
291 GailNotebookPage *page;
293 if (strcmp (pspec->name, "label") == 0)
295 const gchar* label_text;
297 label = GTK_LABEL (obj);
299 label_text = gtk_label_get_text (label);
301 page = GAIL_NOTEBOOK_PAGE (atk_obj);
302 gail_text_util_text_setup (page->textutil, label_text);
304 if (atk_obj->name == NULL)
307 * The label has changed so notify a change in accessible-name
309 g_object_notify (G_OBJECT (atk_obj), "accessible-name");
312 * The label is the only property which can be changed
314 g_signal_emit_by_name (atk_obj, "visible_data_changed");
320 gail_notebook_page_finalize (GObject *object)
322 GailNotebookPage *page = GAIL_NOTEBOOK_PAGE (object);
325 g_object_remove_weak_pointer (G_OBJECT (page->notebook), (gpointer *)&page->notebook);
328 g_object_unref (page->textutil);
330 if (page->notify_child_added_id)
331 g_source_remove (page->notify_child_added_id);
333 G_OBJECT_CLASS (parent_class)->finalize (object);
336 static G_CONST_RETURN gchar*
337 gail_notebook_page_get_name (AtkObject *accessible)
339 g_return_val_if_fail (GAIL_IS_NOTEBOOK_PAGE (accessible), NULL);
341 if (accessible->name != NULL)
342 return accessible->name;
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));
356 gail_notebook_page_get_parent (AtkObject *accessible)
358 GailNotebookPage *page;
360 g_return_val_if_fail (GAIL_IS_NOTEBOOK_PAGE (accessible), NULL);
362 page = GAIL_NOTEBOOK_PAGE (accessible);
367 return gtk_widget_get_accessible (GTK_WIDGET (page->notebook));
371 gail_notebook_page_get_n_children (AtkObject *accessible)
373 /* Notebook page has only one child */
374 g_return_val_if_fail (GAIL_IS_NOTEBOOK_PAGE (accessible), 0);
380 gail_notebook_page_ref_child (AtkObject *accessible,
384 AtkObject *child_obj;
385 GailNotebookPage *page = NULL;
387 g_return_val_if_fail (GAIL_IS_NOTEBOOK_PAGE (accessible), NULL);
391 page = GAIL_NOTEBOOK_PAGE (accessible);
395 child = gtk_notebook_get_nth_page (page->notebook, page->index);
396 gail_return_val_if_fail (GTK_IS_WIDGET (child), NULL);
398 child_obj = gtk_widget_get_accessible (child);
399 g_object_ref (child_obj);
404 gail_notebook_page_get_index_in_parent (AtkObject *accessible)
406 GailNotebookPage *page;
408 g_return_val_if_fail (GAIL_IS_NOTEBOOK_PAGE (accessible), -1);
409 page = GAIL_NOTEBOOK_PAGE (accessible);
415 gail_notebook_page_ref_state_set (AtkObject *accessible)
417 AtkStateSet *state_set, *label_state_set, *merged_state_set;
418 AtkObject *atk_label;
420 g_return_val_if_fail (GAIL_NOTEBOOK_PAGE (accessible), NULL);
422 state_set = ATK_OBJECT_CLASS (parent_class)->ref_state_set (accessible);
424 atk_label = _gail_notebook_page_get_tab_label (GAIL_NOTEBOOK_PAGE (accessible));
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);
436 child = atk_object_ref_accessible_child (accessible, 0);
437 gail_return_val_if_fail (child, state_set);
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))
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);
450 g_object_unref (state_set);
451 g_object_unref (child);
453 return merged_state_set;
458 atk_component_interface_init (AtkComponentIface *iface)
460 g_return_if_fail (iface != NULL);
463 * We use the default implementations for contains, get_position, get_size
465 iface->ref_accessible_at_point = gail_notebook_page_ref_accessible_at_point;
466 iface->get_extents = gail_notebook_page_get_extents;
470 gail_notebook_page_ref_accessible_at_point (AtkComponent *component,
473 AtkCoordType coord_type)
476 * There is only one child so we return it.
480 g_return_val_if_fail (ATK_IS_OBJECT (component), NULL);
482 child = atk_object_ref_accessible_child (ATK_OBJECT (component), 0);
487 gail_notebook_page_get_extents (AtkComponent *component,
492 AtkCoordType coord_type)
494 AtkObject *atk_label;
496 g_return_if_fail (GAIL_IS_NOTEBOOK_PAGE (component));
498 atk_label = _gail_notebook_page_get_tab_label (GAIL_NOTEBOOK_PAGE (component));
507 child = atk_object_ref_accessible_child (ATK_OBJECT (component), 0);
508 gail_return_if_fail (child);
510 atk_component_get_position (ATK_COMPONENT (child), x, y, coord_type);
511 g_object_unref (child);
515 atk_component_get_extents (ATK_COMPONENT (atk_label),
516 x, y, width, height, coord_type);
522 _gail_notebook_page_get_tab_label (GailNotebookPage *page)
526 label = get_label_from_notebook_page (page);
528 return gtk_widget_get_accessible (label);
536 atk_text_interface_init (AtkTextIface *iface)
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;
552 gail_notebook_page_get_text (AtkText *text,
557 GailNotebookPage *notebook_page;
558 const gchar *label_text;
560 notebook_page = GAIL_NOTEBOOK_PAGE (text);
561 label = get_label_from_notebook_page (notebook_page);
563 if (!GTK_IS_LABEL (label))
566 if (!notebook_page->textutil)
567 gail_notebook_page_init_textutil (notebook_page, label);
569 label_text = gtk_label_get_text (GTK_LABEL (label));
571 if (label_text == NULL)
575 return gail_text_util_get_substring (notebook_page->textutil,
581 gail_notebook_page_get_text_before_offset (AtkText *text,
583 AtkTextBoundary boundary_type,
588 GailNotebookPage *notebook_page;
590 notebook_page = GAIL_NOTEBOOK_PAGE (text);
591 label = get_label_from_notebook_page (notebook_page);
593 if (!GTK_IS_LABEL(label))
596 if (!notebook_page->textutil)
597 gail_notebook_page_init_textutil (notebook_page, label);
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);
605 gail_notebook_page_get_text_at_offset (AtkText *text,
607 AtkTextBoundary boundary_type,
612 GailNotebookPage *notebook_page;
614 notebook_page = GAIL_NOTEBOOK_PAGE (text);
615 label = get_label_from_notebook_page (notebook_page);
617 if (!GTK_IS_LABEL(label))
620 if (!notebook_page->textutil)
621 gail_notebook_page_init_textutil (notebook_page, label);
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);
629 gail_notebook_page_get_text_after_offset (AtkText *text,
631 AtkTextBoundary boundary_type,
636 GailNotebookPage *notebook_page;
638 notebook_page = GAIL_NOTEBOOK_PAGE (text);
639 label = get_label_from_notebook_page (notebook_page);
641 if (!GTK_IS_LABEL(label))
644 if (!notebook_page->textutil)
645 gail_notebook_page_init_textutil (notebook_page, label);
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);
653 gail_notebook_page_get_character_count (AtkText *text)
656 GailNotebookPage *notebook_page;
658 notebook_page = GAIL_NOTEBOOK_PAGE (text);
659 label = get_label_from_notebook_page (notebook_page);
661 if (!GTK_IS_LABEL(label))
664 return g_utf8_strlen (gtk_label_get_text (GTK_LABEL (label)), -1);
668 gail_notebook_page_get_character_extents (AtkText *text,
677 GailNotebookPage *notebook_page;
678 PangoRectangle char_rect;
679 gint index, x_layout, y_layout;
680 const gchar *label_text;
682 notebook_page = GAIL_NOTEBOOK_PAGE (text);
683 label = get_label_from_notebook_page (notebook_page);
685 if (!GTK_IS_LABEL(label))
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);
693 gail_misc_get_extents_from_pango_rectangle (label, &char_rect,
694 x_layout, y_layout, x, y, width, height, coords);
698 gail_notebook_page_get_offset_at_point (AtkText *text,
704 GailNotebookPage *notebook_page;
705 gint index, x_layout, y_layout;
706 const gchar *label_text;
708 notebook_page = GAIL_NOTEBOOK_PAGE (text);
709 label = get_label_from_notebook_page (notebook_page);
711 if (!GTK_IS_LABEL(label))
714 gtk_label_get_layout_offsets (GTK_LABEL (label), &x_layout, &y_layout);
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));
722 if (coords == ATK_XY_WINDOW || coords == ATK_XY_SCREEN)
723 return g_utf8_strlen (label_text, -1);
728 return g_utf8_pointer_to_offset (label_text, label_text + index);
731 static AtkAttributeSet*
732 gail_notebook_page_get_run_attributes (AtkText *text,
738 GailNotebookPage *notebook_page;
739 AtkAttributeSet *at_set = NULL;
740 GtkJustification justify;
741 GtkTextDirection dir;
743 notebook_page = GAIL_NOTEBOOK_PAGE (text);
744 label = get_label_from_notebook_page (notebook_page);
746 if (!GTK_IS_LABEL(label))
749 /* Get values set for entire label, if any */
750 justify = gtk_label_get_justify (GTK_LABEL (label));
751 if (justify != GTK_JUSTIFY_CENTER)
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)));
757 dir = gtk_widget_get_direction (label);
758 if (dir == GTK_TEXT_DIR_RTL)
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)));
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)),
774 static AtkAttributeSet*
775 gail_notebook_page_get_default_attributes (AtkText *text)
778 GailNotebookPage *notebook_page;
779 AtkAttributeSet *at_set = NULL;
781 notebook_page = GAIL_NOTEBOOK_PAGE (text);
782 label = get_label_from_notebook_page (notebook_page);
784 if (!GTK_IS_LABEL(label))
787 at_set = gail_misc_get_default_attributes (at_set,
788 gtk_label_get_layout (GTK_LABEL (label)),
794 gail_notebook_page_get_character_at_offset (AtkText *text,
798 GailNotebookPage *notebook_page;
802 notebook_page = GAIL_NOTEBOOK_PAGE (text);
803 label = get_label_from_notebook_page (notebook_page);
805 if (!GTK_IS_LABEL(label))
807 string = gtk_label_get_text (GTK_LABEL (label));
808 if (offset >= g_utf8_strlen (string, -1))
810 index = g_utf8_offset_to_pointer (string, offset);
812 return g_utf8_get_char (index);
816 get_label_from_notebook_page (GailNotebookPage *page)
819 GtkNotebook *notebook;
821 notebook = page->notebook;
825 if (!gtk_notebook_get_show_tabs (notebook))
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);
832 child = gtk_notebook_get_tab_label (notebook, child);
834 if (GTK_IS_LABEL (child))
837 if (GTK_IS_CONTAINER (child))
838 child = find_label_child (GTK_CONTAINER (child));
844 find_label_child (GtkContainer *container)
846 GList *children, *tmp_list;
849 children = gtk_container_get_children (container);
852 for (tmp_list = children; tmp_list != NULL; tmp_list = tmp_list->next)
854 if (GTK_IS_LABEL (tmp_list->data))
856 child = GTK_WIDGET (tmp_list->data);
859 else if (GTK_IS_CONTAINER (tmp_list->data))
861 child = find_label_child (GTK_CONTAINER (tmp_list->data));
866 g_list_free (children);