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.
24 #include "gailnotebookpage.h"
25 #include <libgail-util/gailmisc.h>
26 #include "gail-private-macros.h"
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,
34 static G_CONST_RETURN 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,
39 static gint gail_notebook_page_get_index_in_parent
40 (AtkObject *accessible);
41 static AtkStateSet* gail_notebook_page_ref_state_set (AtkObject *accessible);
43 static gint gail_notebook_page_notify (GObject *obj,
46 static void gail_notebook_page_init_textutil (GailNotebookPage *notebook_page,
49 static void atk_component_interface_init (AtkComponentIface *iface);
51 static AtkObject* gail_notebook_page_ref_accessible_at_point
52 (AtkComponent *component,
55 AtkCoordType coord_type);
57 static void gail_notebook_page_get_extents (AtkComponent *component,
62 AtkCoordType coord_type);
64 static AtkObject* _gail_notebook_page_get_tab_label (GailNotebookPage *page);
67 static void atk_text_interface_init (AtkTextIface *iface);
69 static gchar* gail_notebook_page_get_text (AtkText *text,
72 static gunichar gail_notebook_page_get_character_at_offset
75 static gchar* gail_notebook_page_get_text_before_offset
78 AtkTextBoundary boundary_type,
81 static gchar* gail_notebook_page_get_text_at_offset
84 AtkTextBoundary boundary_type,
87 static gchar* gail_notebook_page_get_text_after_offset
90 AtkTextBoundary boundary_type,
93 static gint gail_notebook_page_get_character_count (AtkText *text);
94 static void gail_notebook_page_get_character_extents
101 AtkCoordType coords);
102 static gint gail_notebook_page_get_offset_at_point
106 AtkCoordType coords);
107 static AtkAttributeSet* gail_notebook_page_get_run_attributes
112 static AtkAttributeSet* gail_notebook_page_get_default_attributes
114 static GtkWidget* get_label_from_notebook_page (GailNotebookPage *page);
115 static GtkWidget* find_label_child (GtkContainer *container);
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))
123 gail_notebook_page_class_init (GailNotebookPageClass *klass)
125 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
126 AtkObjectClass *class = ATK_OBJECT_CLASS (klass);
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;
135 gobject_class->finalize = gail_notebook_page_finalize;
139 gail_notebook_page_init (GailNotebookPage *page)
144 notify_child_added (gpointer data)
146 GailNotebookPage *page;
147 AtkObject *atk_object, *atk_parent;
149 g_return_val_if_fail (GAIL_IS_NOTEBOOK_PAGE (data), FALSE);
150 page = GAIL_NOTEBOOK_PAGE (data);
151 atk_object = ATK_OBJECT (data);
153 page->notify_child_added_id = 0;
155 /* The widget page->notebook may be deleted before this handler is called */
156 if (page->notebook != NULL)
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);
167 gail_notebook_page_new (GtkNotebook *notebook,
171 AtkObject *atk_object;
172 GailNotebookPage *page;
175 GtkWidget *widget_page;
177 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), NULL);
179 child = gtk_notebook_get_nth_page (notebook, pagenum);
184 object = g_object_new (GAIL_TYPE_NOTEBOOK_PAGE, NULL);
185 g_return_val_if_fail (object != NULL, NULL);
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;
195 atk_object = ATK_OBJECT (page);
196 atk_object->role = ATK_ROLE_PAGE_TAB;
197 atk_object->layer = ATK_LAYER_WIDGET;
199 page->notify_child_added_id = gdk_threads_add_idle (notify_child_added, atk_object);
201 * We get notified of changes to the label
203 label = get_label_from_notebook_page (page);
204 if (GTK_IS_LABEL (label))
206 if (gtk_widget_get_mapped (label))
207 gail_notebook_page_init_textutil (page, label);
209 g_signal_connect (label,
211 G_CALLBACK (gail_notebook_page_label_map_gtk),
219 gail_notebook_page_label_map_gtk (GtkWidget *widget,
222 GailNotebookPage *page;
224 page = GAIL_NOTEBOOK_PAGE (data);
225 gail_notebook_page_init_textutil (page, widget);
229 gail_notebook_page_init_textutil (GailNotebookPage *page,
232 const gchar *label_text;
234 if (page->textutil == NULL)
236 page->textutil = gail_text_util_new ();
237 g_signal_connect (label,
239 (GCallback) gail_notebook_page_notify,
242 label_text = gtk_label_get_text (GTK_LABEL (label));
243 gail_text_util_text_setup (page->textutil, label_text);
247 gail_notebook_page_notify (GObject *obj,
251 AtkObject *atk_obj = ATK_OBJECT (user_data);
253 GailNotebookPage *page;
255 if (strcmp (pspec->name, "label") == 0)
257 const gchar* label_text;
259 label = GTK_LABEL (obj);
261 label_text = gtk_label_get_text (label);
263 page = GAIL_NOTEBOOK_PAGE (atk_obj);
264 gail_text_util_text_setup (page->textutil, label_text);
266 if (atk_obj->name == NULL)
269 * The label has changed so notify a change in accessible-name
271 g_object_notify (G_OBJECT (atk_obj), "accessible-name");
274 * The label is the only property which can be changed
276 g_signal_emit_by_name (atk_obj, "visible_data_changed");
282 gail_notebook_page_finalize (GObject *object)
284 GailNotebookPage *page = GAIL_NOTEBOOK_PAGE (object);
287 g_object_remove_weak_pointer (G_OBJECT (page->notebook), (gpointer *)&page->notebook);
290 g_object_unref (page->textutil);
292 if (page->notify_child_added_id)
293 g_source_remove (page->notify_child_added_id);
295 G_OBJECT_CLASS (gail_notebook_page_parent_class)->finalize (object);
298 static G_CONST_RETURN gchar*
299 gail_notebook_page_get_name (AtkObject *accessible)
301 g_return_val_if_fail (GAIL_IS_NOTEBOOK_PAGE (accessible), NULL);
303 if (accessible->name != NULL)
304 return accessible->name;
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));
318 gail_notebook_page_get_parent (AtkObject *accessible)
320 GailNotebookPage *page;
322 g_return_val_if_fail (GAIL_IS_NOTEBOOK_PAGE (accessible), NULL);
324 page = GAIL_NOTEBOOK_PAGE (accessible);
329 return gtk_widget_get_accessible (GTK_WIDGET (page->notebook));
333 gail_notebook_page_get_n_children (AtkObject *accessible)
335 /* Notebook page has only one child */
336 g_return_val_if_fail (GAIL_IS_NOTEBOOK_PAGE (accessible), 0);
342 gail_notebook_page_ref_child (AtkObject *accessible,
346 AtkObject *child_obj;
347 GailNotebookPage *page = NULL;
349 g_return_val_if_fail (GAIL_IS_NOTEBOOK_PAGE (accessible), NULL);
353 page = GAIL_NOTEBOOK_PAGE (accessible);
357 child = gtk_notebook_get_nth_page (page->notebook, page->index);
358 gail_return_val_if_fail (GTK_IS_WIDGET (child), NULL);
360 child_obj = gtk_widget_get_accessible (child);
361 g_object_ref (child_obj);
366 gail_notebook_page_get_index_in_parent (AtkObject *accessible)
368 GailNotebookPage *page;
370 g_return_val_if_fail (GAIL_IS_NOTEBOOK_PAGE (accessible), -1);
371 page = GAIL_NOTEBOOK_PAGE (accessible);
377 gail_notebook_page_ref_state_set (AtkObject *accessible)
379 AtkStateSet *state_set, *label_state_set, *merged_state_set;
380 AtkObject *atk_label;
382 g_return_val_if_fail (GAIL_NOTEBOOK_PAGE (accessible), NULL);
384 state_set = ATK_OBJECT_CLASS (gail_notebook_page_parent_class)->ref_state_set (accessible);
386 atk_label = _gail_notebook_page_get_tab_label (GAIL_NOTEBOOK_PAGE (accessible));
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);
398 child = atk_object_ref_accessible_child (accessible, 0);
399 gail_return_val_if_fail (child, state_set);
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))
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);
412 g_object_unref (state_set);
413 g_object_unref (child);
415 return merged_state_set;
420 atk_component_interface_init (AtkComponentIface *iface)
423 * We use the default implementations for contains, get_position, get_size
425 iface->ref_accessible_at_point = gail_notebook_page_ref_accessible_at_point;
426 iface->get_extents = gail_notebook_page_get_extents;
430 gail_notebook_page_ref_accessible_at_point (AtkComponent *component,
433 AtkCoordType coord_type)
436 * There is only one child so we return it.
440 g_return_val_if_fail (ATK_IS_OBJECT (component), NULL);
442 child = atk_object_ref_accessible_child (ATK_OBJECT (component), 0);
447 gail_notebook_page_get_extents (AtkComponent *component,
452 AtkCoordType coord_type)
454 AtkObject *atk_label;
456 g_return_if_fail (GAIL_IS_NOTEBOOK_PAGE (component));
458 atk_label = _gail_notebook_page_get_tab_label (GAIL_NOTEBOOK_PAGE (component));
467 child = atk_object_ref_accessible_child (ATK_OBJECT (component), 0);
468 gail_return_if_fail (child);
470 atk_component_get_position (ATK_COMPONENT (child), x, y, coord_type);
471 g_object_unref (child);
475 atk_component_get_extents (ATK_COMPONENT (atk_label),
476 x, y, width, height, coord_type);
482 _gail_notebook_page_get_tab_label (GailNotebookPage *page)
486 label = get_label_from_notebook_page (page);
488 return gtk_widget_get_accessible (label);
496 atk_text_interface_init (AtkTextIface *iface)
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;
511 gail_notebook_page_get_text (AtkText *text,
516 GailNotebookPage *notebook_page;
517 const gchar *label_text;
519 notebook_page = GAIL_NOTEBOOK_PAGE (text);
520 label = get_label_from_notebook_page (notebook_page);
522 if (!GTK_IS_LABEL (label))
525 if (!notebook_page->textutil)
526 gail_notebook_page_init_textutil (notebook_page, label);
528 label_text = gtk_label_get_text (GTK_LABEL (label));
530 if (label_text == NULL)
534 return gail_text_util_get_substring (notebook_page->textutil,
540 gail_notebook_page_get_text_before_offset (AtkText *text,
542 AtkTextBoundary boundary_type,
547 GailNotebookPage *notebook_page;
549 notebook_page = GAIL_NOTEBOOK_PAGE (text);
550 label = get_label_from_notebook_page (notebook_page);
552 if (!GTK_IS_LABEL(label))
555 if (!notebook_page->textutil)
556 gail_notebook_page_init_textutil (notebook_page, label);
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);
564 gail_notebook_page_get_text_at_offset (AtkText *text,
566 AtkTextBoundary boundary_type,
571 GailNotebookPage *notebook_page;
573 notebook_page = GAIL_NOTEBOOK_PAGE (text);
574 label = get_label_from_notebook_page (notebook_page);
576 if (!GTK_IS_LABEL(label))
579 if (!notebook_page->textutil)
580 gail_notebook_page_init_textutil (notebook_page, label);
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);
588 gail_notebook_page_get_text_after_offset (AtkText *text,
590 AtkTextBoundary boundary_type,
595 GailNotebookPage *notebook_page;
597 notebook_page = GAIL_NOTEBOOK_PAGE (text);
598 label = get_label_from_notebook_page (notebook_page);
600 if (!GTK_IS_LABEL(label))
603 if (!notebook_page->textutil)
604 gail_notebook_page_init_textutil (notebook_page, label);
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);
612 gail_notebook_page_get_character_count (AtkText *text)
615 GailNotebookPage *notebook_page;
617 notebook_page = GAIL_NOTEBOOK_PAGE (text);
618 label = get_label_from_notebook_page (notebook_page);
620 if (!GTK_IS_LABEL(label))
623 return g_utf8_strlen (gtk_label_get_text (GTK_LABEL (label)), -1);
627 gail_notebook_page_get_character_extents (AtkText *text,
636 GailNotebookPage *notebook_page;
637 PangoRectangle char_rect;
638 gint index, x_layout, y_layout;
639 const gchar *label_text;
641 notebook_page = GAIL_NOTEBOOK_PAGE (text);
642 label = get_label_from_notebook_page (notebook_page);
644 if (!GTK_IS_LABEL(label))
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);
652 gail_misc_get_extents_from_pango_rectangle (label, &char_rect,
653 x_layout, y_layout, x, y, width, height, coords);
657 gail_notebook_page_get_offset_at_point (AtkText *text,
663 GailNotebookPage *notebook_page;
664 gint index, x_layout, y_layout;
665 const gchar *label_text;
667 notebook_page = GAIL_NOTEBOOK_PAGE (text);
668 label = get_label_from_notebook_page (notebook_page);
670 if (!GTK_IS_LABEL(label))
673 gtk_label_get_layout_offsets (GTK_LABEL (label), &x_layout, &y_layout);
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));
681 if (coords == ATK_XY_WINDOW || coords == ATK_XY_SCREEN)
682 return g_utf8_strlen (label_text, -1);
687 return g_utf8_pointer_to_offset (label_text, label_text + index);
690 static AtkAttributeSet*
691 gail_notebook_page_get_run_attributes (AtkText *text,
697 GailNotebookPage *notebook_page;
698 AtkAttributeSet *at_set = NULL;
699 GtkJustification justify;
700 GtkTextDirection dir;
702 notebook_page = GAIL_NOTEBOOK_PAGE (text);
703 label = get_label_from_notebook_page (notebook_page);
705 if (!GTK_IS_LABEL(label))
708 /* Get values set for entire label, if any */
709 justify = gtk_label_get_justify (GTK_LABEL (label));
710 if (justify != GTK_JUSTIFY_CENTER)
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)));
716 dir = gtk_widget_get_direction (label);
717 if (dir == GTK_TEXT_DIR_RTL)
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)));
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)),
733 static AtkAttributeSet*
734 gail_notebook_page_get_default_attributes (AtkText *text)
737 GailNotebookPage *notebook_page;
738 AtkAttributeSet *at_set = NULL;
740 notebook_page = GAIL_NOTEBOOK_PAGE (text);
741 label = get_label_from_notebook_page (notebook_page);
743 if (!GTK_IS_LABEL(label))
746 at_set = gail_misc_get_default_attributes (at_set,
747 gtk_label_get_layout (GTK_LABEL (label)),
753 gail_notebook_page_get_character_at_offset (AtkText *text,
757 GailNotebookPage *notebook_page;
761 notebook_page = GAIL_NOTEBOOK_PAGE (text);
762 label = get_label_from_notebook_page (notebook_page);
764 if (!GTK_IS_LABEL(label))
766 string = gtk_label_get_text (GTK_LABEL (label));
767 if (offset >= g_utf8_strlen (string, -1))
769 index = g_utf8_offset_to_pointer (string, offset);
771 return g_utf8_get_char (index);
775 get_label_from_notebook_page (GailNotebookPage *page)
778 GtkNotebook *notebook;
780 notebook = page->notebook;
784 if (!gtk_notebook_get_show_tabs (notebook))
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);
791 child = gtk_notebook_get_tab_label (notebook, child);
793 if (GTK_IS_LABEL (child))
796 if (GTK_IS_CONTAINER (child))
797 child = find_label_child (GTK_CONTAINER (child));
803 find_label_child (GtkContainer *container)
805 GList *children, *tmp_list;
808 children = gtk_container_get_children (container);
811 for (tmp_list = children; tmp_list != NULL; tmp_list = tmp_list->next)
813 if (GTK_IS_LABEL (tmp_list->data))
815 child = GTK_WIDGET (tmp_list->data);
818 else if (GTK_IS_CONTAINER (tmp_list->data))
820 child = find_label_child (GTK_CONTAINER (tmp_list->data));
825 g_list_free (children);