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);
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,
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 /* 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))
121 gail_notebook_page_class_init (GailNotebookPageClass *klass)
123 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
124 AtkObjectClass *class = ATK_OBJECT_CLASS (klass);
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;
133 gobject_class->finalize = gail_notebook_page_finalize;
137 gail_notebook_page_init (GailNotebookPage *page)
142 notify_child_added (gpointer data)
144 GailNotebookPage *page;
145 AtkObject *atk_object, *atk_parent;
147 g_return_val_if_fail (GAIL_IS_NOTEBOOK_PAGE (data), FALSE);
148 page = GAIL_NOTEBOOK_PAGE (data);
149 atk_object = ATK_OBJECT (data);
151 page->notify_child_added_id = 0;
153 /* The widget page->notebook may be deleted before this handler is called */
154 if (page->notebook != NULL)
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);
165 gail_notebook_page_new (GtkNotebook *notebook,
169 AtkObject *atk_object;
170 GailNotebookPage *page;
175 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), NULL);
177 child = gtk_notebook_get_nth_page (notebook, pagenum);
182 object = g_object_new (GAIL_TYPE_NOTEBOOK_PAGE, NULL);
183 g_return_val_if_fail (object != NULL, NULL);
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;
193 atk_object = ATK_OBJECT (page);
194 atk_object->role = ATK_ROLE_PAGE_TAB;
195 atk_object->layer = ATK_LAYER_WIDGET;
197 page->notify_child_added_id = gdk_threads_add_idle (notify_child_added, atk_object);
199 * We get notified of changes to the label
201 label = get_label_from_notebook_page (page);
202 if (GTK_IS_LABEL (label))
204 if (GTK_WIDGET_MAPPED (label))
205 gail_notebook_page_init_textutil (page, label);
207 g_signal_connect (label,
209 G_CALLBACK (gail_notebook_page_label_map_gtk),
217 gail_notebook_page_label_map_gtk (GtkWidget *widget,
220 GailNotebookPage *page;
222 page = GAIL_NOTEBOOK_PAGE (data);
223 gail_notebook_page_init_textutil (page, widget);
227 gail_notebook_page_init_textutil (GailNotebookPage *page,
230 const gchar *label_text;
232 if (page->textutil == NULL)
234 page->textutil = gail_text_util_new ();
235 g_signal_connect (label,
237 (GCallback) gail_notebook_page_notify,
240 label_text = gtk_label_get_text (GTK_LABEL (label));
241 gail_text_util_text_setup (page->textutil, label_text);
245 gail_notebook_page_notify (GObject *obj,
249 AtkObject *atk_obj = ATK_OBJECT (user_data);
251 GailNotebookPage *page;
253 if (strcmp (pspec->name, "label") == 0)
255 const gchar* label_text;
257 label = GTK_LABEL (obj);
259 label_text = gtk_label_get_text (label);
261 page = GAIL_NOTEBOOK_PAGE (atk_obj);
262 gail_text_util_text_setup (page->textutil, label_text);
264 if (atk_obj->name == NULL)
267 * The label has changed so notify a change in accessible-name
269 g_object_notify (G_OBJECT (atk_obj), "accessible-name");
272 * The label is the only property which can be changed
274 g_signal_emit_by_name (atk_obj, "visible_data_changed");
280 gail_notebook_page_finalize (GObject *object)
282 GailNotebookPage *page = GAIL_NOTEBOOK_PAGE (object);
285 g_object_remove_weak_pointer (G_OBJECT (page->notebook), (gpointer *)&page->notebook);
288 g_object_unref (page->textutil);
290 if (page->notify_child_added_id)
291 g_source_remove (page->notify_child_added_id);
293 G_OBJECT_CLASS (gail_notebook_page_parent_class)->finalize (object);
296 static G_CONST_RETURN gchar*
297 gail_notebook_page_get_name (AtkObject *accessible)
299 g_return_val_if_fail (GAIL_IS_NOTEBOOK_PAGE (accessible), NULL);
301 if (accessible->name != NULL)
302 return accessible->name;
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));
316 gail_notebook_page_get_parent (AtkObject *accessible)
318 GailNotebookPage *page;
320 g_return_val_if_fail (GAIL_IS_NOTEBOOK_PAGE (accessible), NULL);
322 page = GAIL_NOTEBOOK_PAGE (accessible);
327 return gtk_widget_get_accessible (GTK_WIDGET (page->notebook));
331 gail_notebook_page_get_n_children (AtkObject *accessible)
333 /* Notebook page has only one child */
334 g_return_val_if_fail (GAIL_IS_NOTEBOOK_PAGE (accessible), 0);
340 gail_notebook_page_ref_child (AtkObject *accessible,
344 AtkObject *child_obj;
345 GailNotebookPage *page = NULL;
347 g_return_val_if_fail (GAIL_IS_NOTEBOOK_PAGE (accessible), NULL);
351 page = GAIL_NOTEBOOK_PAGE (accessible);
355 child = gtk_notebook_get_nth_page (page->notebook, page->index);
356 gail_return_val_if_fail (GTK_IS_WIDGET (child), NULL);
358 child_obj = gtk_widget_get_accessible (child);
359 g_object_ref (child_obj);
364 gail_notebook_page_get_index_in_parent (AtkObject *accessible)
366 GailNotebookPage *page;
368 g_return_val_if_fail (GAIL_IS_NOTEBOOK_PAGE (accessible), -1);
369 page = GAIL_NOTEBOOK_PAGE (accessible);
375 gail_notebook_page_ref_state_set (AtkObject *accessible)
377 AtkStateSet *state_set, *label_state_set, *merged_state_set;
378 AtkObject *atk_label;
380 g_return_val_if_fail (GAIL_NOTEBOOK_PAGE (accessible), NULL);
382 state_set = ATK_OBJECT_CLASS (gail_notebook_page_parent_class)->ref_state_set (accessible);
384 atk_label = _gail_notebook_page_get_tab_label (GAIL_NOTEBOOK_PAGE (accessible));
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);
396 child = atk_object_ref_accessible_child (accessible, 0);
397 gail_return_val_if_fail (child, state_set);
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))
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);
410 g_object_unref (state_set);
411 g_object_unref (child);
413 return merged_state_set;
418 atk_component_interface_init (AtkComponentIface *iface)
421 * We use the default implementations for contains, get_position, get_size
423 iface->ref_accessible_at_point = gail_notebook_page_ref_accessible_at_point;
424 iface->get_extents = gail_notebook_page_get_extents;
428 gail_notebook_page_ref_accessible_at_point (AtkComponent *component,
431 AtkCoordType coord_type)
434 * There is only one child so we return it.
438 g_return_val_if_fail (ATK_IS_OBJECT (component), NULL);
440 child = atk_object_ref_accessible_child (ATK_OBJECT (component), 0);
445 gail_notebook_page_get_extents (AtkComponent *component,
450 AtkCoordType coord_type)
452 AtkObject *atk_label;
454 g_return_if_fail (GAIL_IS_NOTEBOOK_PAGE (component));
456 atk_label = _gail_notebook_page_get_tab_label (GAIL_NOTEBOOK_PAGE (component));
465 child = atk_object_ref_accessible_child (ATK_OBJECT (component), 0);
466 gail_return_if_fail (child);
468 atk_component_get_position (ATK_COMPONENT (child), x, y, coord_type);
469 g_object_unref (child);
473 atk_component_get_extents (ATK_COMPONENT (atk_label),
474 x, y, width, height, coord_type);
480 _gail_notebook_page_get_tab_label (GailNotebookPage *page)
484 label = get_label_from_notebook_page (page);
486 return gtk_widget_get_accessible (label);
494 atk_text_interface_init (AtkTextIface *iface)
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;
509 gail_notebook_page_get_text (AtkText *text,
514 GailNotebookPage *notebook_page;
515 const gchar *label_text;
517 notebook_page = GAIL_NOTEBOOK_PAGE (text);
518 label = get_label_from_notebook_page (notebook_page);
520 if (!GTK_IS_LABEL (label))
523 if (!notebook_page->textutil)
524 gail_notebook_page_init_textutil (notebook_page, label);
526 label_text = gtk_label_get_text (GTK_LABEL (label));
528 if (label_text == NULL)
532 return gail_text_util_get_substring (notebook_page->textutil,
538 gail_notebook_page_get_text_before_offset (AtkText *text,
540 AtkTextBoundary boundary_type,
545 GailNotebookPage *notebook_page;
547 notebook_page = GAIL_NOTEBOOK_PAGE (text);
548 label = get_label_from_notebook_page (notebook_page);
550 if (!GTK_IS_LABEL(label))
553 if (!notebook_page->textutil)
554 gail_notebook_page_init_textutil (notebook_page, label);
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);
562 gail_notebook_page_get_text_at_offset (AtkText *text,
564 AtkTextBoundary boundary_type,
569 GailNotebookPage *notebook_page;
571 notebook_page = GAIL_NOTEBOOK_PAGE (text);
572 label = get_label_from_notebook_page (notebook_page);
574 if (!GTK_IS_LABEL(label))
577 if (!notebook_page->textutil)
578 gail_notebook_page_init_textutil (notebook_page, label);
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);
586 gail_notebook_page_get_text_after_offset (AtkText *text,
588 AtkTextBoundary boundary_type,
593 GailNotebookPage *notebook_page;
595 notebook_page = GAIL_NOTEBOOK_PAGE (text);
596 label = get_label_from_notebook_page (notebook_page);
598 if (!GTK_IS_LABEL(label))
601 if (!notebook_page->textutil)
602 gail_notebook_page_init_textutil (notebook_page, label);
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);
610 gail_notebook_page_get_character_count (AtkText *text)
613 GailNotebookPage *notebook_page;
615 notebook_page = GAIL_NOTEBOOK_PAGE (text);
616 label = get_label_from_notebook_page (notebook_page);
618 if (!GTK_IS_LABEL(label))
621 return g_utf8_strlen (gtk_label_get_text (GTK_LABEL (label)), -1);
625 gail_notebook_page_get_character_extents (AtkText *text,
634 GailNotebookPage *notebook_page;
635 PangoRectangle char_rect;
636 gint index, x_layout, y_layout;
637 const gchar *label_text;
639 notebook_page = GAIL_NOTEBOOK_PAGE (text);
640 label = get_label_from_notebook_page (notebook_page);
642 if (!GTK_IS_LABEL(label))
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);
650 gail_misc_get_extents_from_pango_rectangle (label, &char_rect,
651 x_layout, y_layout, x, y, width, height, coords);
655 gail_notebook_page_get_offset_at_point (AtkText *text,
661 GailNotebookPage *notebook_page;
662 gint index, x_layout, y_layout;
663 const gchar *label_text;
665 notebook_page = GAIL_NOTEBOOK_PAGE (text);
666 label = get_label_from_notebook_page (notebook_page);
668 if (!GTK_IS_LABEL(label))
671 gtk_label_get_layout_offsets (GTK_LABEL (label), &x_layout, &y_layout);
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));
679 if (coords == ATK_XY_WINDOW || coords == ATK_XY_SCREEN)
680 return g_utf8_strlen (label_text, -1);
685 return g_utf8_pointer_to_offset (label_text, label_text + index);
688 static AtkAttributeSet*
689 gail_notebook_page_get_run_attributes (AtkText *text,
695 GailNotebookPage *notebook_page;
696 AtkAttributeSet *at_set = NULL;
697 GtkJustification justify;
698 GtkTextDirection dir;
700 notebook_page = GAIL_NOTEBOOK_PAGE (text);
701 label = get_label_from_notebook_page (notebook_page);
703 if (!GTK_IS_LABEL(label))
706 /* Get values set for entire label, if any */
707 justify = gtk_label_get_justify (GTK_LABEL (label));
708 if (justify != GTK_JUSTIFY_CENTER)
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)));
714 dir = gtk_widget_get_direction (label);
715 if (dir == GTK_TEXT_DIR_RTL)
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)));
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)),
731 static AtkAttributeSet*
732 gail_notebook_page_get_default_attributes (AtkText *text)
735 GailNotebookPage *notebook_page;
736 AtkAttributeSet *at_set = NULL;
738 notebook_page = GAIL_NOTEBOOK_PAGE (text);
739 label = get_label_from_notebook_page (notebook_page);
741 if (!GTK_IS_LABEL(label))
744 at_set = gail_misc_get_default_attributes (at_set,
745 gtk_label_get_layout (GTK_LABEL (label)),
751 gail_notebook_page_get_character_at_offset (AtkText *text,
755 GailNotebookPage *notebook_page;
759 notebook_page = GAIL_NOTEBOOK_PAGE (text);
760 label = get_label_from_notebook_page (notebook_page);
762 if (!GTK_IS_LABEL(label))
764 string = gtk_label_get_text (GTK_LABEL (label));
765 if (offset >= g_utf8_strlen (string, -1))
767 index = g_utf8_offset_to_pointer (string, offset);
769 return g_utf8_get_char (index);
773 get_label_from_notebook_page (GailNotebookPage *page)
776 GtkNotebook *notebook;
778 notebook = page->notebook;
782 if (!gtk_notebook_get_show_tabs (notebook))
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);
789 child = gtk_notebook_get_tab_label (notebook, child);
791 if (GTK_IS_LABEL (child))
794 if (GTK_IS_CONTAINER (child))
795 child = find_label_child (GTK_CONTAINER (child));
801 find_label_child (GtkContainer *container)
803 GList *children, *tmp_list;
806 children = gtk_container_get_children (container);
809 for (tmp_list = children; tmp_list != NULL; tmp_list = tmp_list->next)
811 if (GTK_IS_LABEL (tmp_list->data))
813 child = GTK_WIDGET (tmp_list->data);
816 else if (GTK_IS_CONTAINER (tmp_list->data))
818 child = find_label_child (GTK_CONTAINER (tmp_list->data));
823 g_list_free (children);