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 #undef GTK_DISABLE_DEPRECATED
28 #include <libgail-util/gailmisc.h>
30 static void gail_item_class_init (GailItemClass *klass);
31 static void gail_item_init (GailItem *item);
32 static G_CONST_RETURN gchar* gail_item_get_name (AtkObject *obj);
33 static gint gail_item_get_n_children (AtkObject *obj);
34 static AtkObject* gail_item_ref_child (AtkObject *obj,
36 static void gail_item_real_initialize (AtkObject *obj,
38 static void gail_item_label_map_gtk (GtkWidget *widget,
40 static void gail_item_finalize (GObject *object);
41 static void gail_item_init_textutil (GailItem *item,
43 static void gail_item_notify_label_gtk(GObject *obj,
48 static void atk_text_interface_init (AtkTextIface *iface);
50 static gchar* gail_item_get_text (AtkText *text,
53 static gunichar gail_item_get_character_at_offset(AtkText *text,
55 static gchar* gail_item_get_text_before_offset (AtkText *text,
57 AtkTextBoundary boundary_type,
60 static gchar* gail_item_get_text_at_offset (AtkText *text,
62 AtkTextBoundary boundary_type,
65 static gchar* gail_item_get_text_after_offset (AtkText *text,
67 AtkTextBoundary boundary_type,
70 static gint gail_item_get_character_count (AtkText *text);
71 static void gail_item_get_character_extents (AtkText *text,
78 static gint gail_item_get_offset_at_point (AtkText *text,
82 static AtkAttributeSet* gail_item_get_run_attributes
87 static AtkAttributeSet* gail_item_get_default_attributes
89 static GtkWidget* get_label_from_container (GtkWidget *container);
91 G_DEFINE_TYPE_WITH_CODE (GailItem, gail_item, GAIL_TYPE_CONTAINER,
92 G_IMPLEMENT_INTERFACE (ATK_TYPE_TEXT, atk_text_interface_init))
95 gail_item_class_init (GailItemClass *klass)
97 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
98 AtkObjectClass *class = ATK_OBJECT_CLASS (klass);
99 GailContainerClass *container_class;
101 container_class = (GailContainerClass *)klass;
103 gobject_class->finalize = gail_item_finalize;
105 class->get_name = gail_item_get_name;
106 class->get_n_children = gail_item_get_n_children;
107 class->ref_child = gail_item_ref_child;
108 class->initialize = gail_item_real_initialize;
110 * As we report the item as having no children we are not interested
111 * in add and remove signals
113 container_class->add_gtk = NULL;
114 container_class->remove_gtk = NULL;
118 gail_item_init (GailItem *item)
123 gail_item_real_initialize (AtkObject *obj,
126 GailItem *item = GAIL_ITEM (obj);
129 ATK_OBJECT_CLASS (gail_item_parent_class)->initialize (obj, data);
131 item->textutil = NULL;
134 label = get_label_from_container (GTK_WIDGET (data));
135 if (GTK_IS_LABEL (label))
137 if (GTK_WIDGET_MAPPED (label))
138 gail_item_init_textutil (item, label);
140 g_signal_connect (label,
142 G_CALLBACK (gail_item_label_map_gtk),
146 obj->role = ATK_ROLE_LIST_ITEM;
150 gail_item_label_map_gtk (GtkWidget *widget,
155 item = GAIL_ITEM (data);
156 gail_item_init_textutil (item, widget);
160 gail_item_init_textutil (GailItem *item,
163 const gchar *label_text;
165 if (item->textutil == NULL)
167 item->textutil = gail_text_util_new ();
168 g_signal_connect (label,
170 (GCallback) gail_item_notify_label_gtk,
173 label_text = gtk_label_get_text (GTK_LABEL (label));
174 gail_text_util_text_setup (item->textutil, label_text);
178 gail_item_finalize (GObject *object)
180 GailItem *item = GAIL_ITEM (object);
184 g_object_unref (item->textutil);
191 G_OBJECT_CLASS (gail_item_parent_class)->finalize (object);
194 static G_CONST_RETURN gchar*
195 gail_item_get_name (AtkObject *obj)
197 G_CONST_RETURN gchar* name;
199 g_return_val_if_fail (GAIL_IS_ITEM (obj), NULL);
201 name = ATK_OBJECT_CLASS (gail_item_parent_class)->get_name (obj);
205 * Get the label child
210 widget = GTK_ACCESSIBLE (obj)->widget;
217 label = get_label_from_container (widget);
218 if (GTK_IS_LABEL (label))
219 return gtk_label_get_text (GTK_LABEL(label));
221 * If we have a menu item in a menu attached to a GtkOptionMenu
222 * the label of the selected item is detached from the menu item
224 else if (GTK_IS_MENU_ITEM (widget))
229 AtkObject *parent_obj;
232 parent = gtk_widget_get_parent (widget);
233 if (GTK_IS_MENU (parent))
235 attach = gtk_menu_get_attach_widget (GTK_MENU (parent));
237 if (GTK_IS_OPTION_MENU (attach))
239 label = get_label_from_container (attach);
240 if (GTK_IS_LABEL (label))
241 return gtk_label_get_text (GTK_LABEL(label));
243 list = gtk_container_get_children (GTK_CONTAINER (parent));
244 index = g_list_index (list, widget);
246 if (index < 0 || index > g_list_length (list))
253 parent_obj = atk_object_get_parent (gtk_widget_get_accessible (parent));
254 if (GTK_IS_ACCESSIBLE (parent_obj))
256 parent = GTK_ACCESSIBLE (parent_obj)->widget;
257 if (GTK_IS_COMBO_BOX (parent))
264 model = gtk_combo_box_get_model (GTK_COMBO_BOX (parent));
265 item = GAIL_ITEM (obj);
266 if (gtk_tree_model_iter_nth_child (model, &iter, NULL, index))
268 n_columns = gtk_tree_model_get_n_columns (model);
269 for (i = 0; i < n_columns; i++)
271 GValue value = { 0, };
273 gtk_tree_model_get_value (model, &iter, i, &value);
274 if (G_VALUE_HOLDS_STRING (&value))
277 item->text = (gchar *) g_value_dup_string (&value);
278 g_value_unset (&value);
282 g_value_unset (&value);
295 * We report that this object has no children
299 gail_item_get_n_children (AtkObject* obj)
305 gail_item_ref_child (AtkObject *obj,
312 gail_item_notify_label_gtk (GObject *obj,
316 AtkObject* atk_obj = ATK_OBJECT (data);
320 if (strcmp (pspec->name, "label") == 0)
322 const gchar* label_text;
324 label = GTK_LABEL (obj);
326 label_text = gtk_label_get_text (label);
328 gail_item = GAIL_ITEM (atk_obj);
329 gail_text_util_text_setup (gail_item->textutil, label_text);
331 if (atk_obj->name == NULL)
334 * The label has changed so notify a change in accessible-name
336 g_object_notify (G_OBJECT (atk_obj), "accessible-name");
339 * The label is the only property which can be changed
341 g_signal_emit_by_name (atk_obj, "visible_data_changed");
348 atk_text_interface_init (AtkTextIface *iface)
350 iface->get_text = gail_item_get_text;
351 iface->get_character_at_offset = gail_item_get_character_at_offset;
352 iface->get_text_before_offset = gail_item_get_text_before_offset;
353 iface->get_text_at_offset = gail_item_get_text_at_offset;
354 iface->get_text_after_offset = gail_item_get_text_after_offset;
355 iface->get_character_count = gail_item_get_character_count;
356 iface->get_character_extents = gail_item_get_character_extents;
357 iface->get_offset_at_point = gail_item_get_offset_at_point;
358 iface->get_run_attributes = gail_item_get_run_attributes;
359 iface->get_default_attributes = gail_item_get_default_attributes;
363 gail_item_get_text (AtkText *text,
370 const gchar *label_text;
372 widget = GTK_ACCESSIBLE (text)->widget;
374 /* State is defunct */
377 label = get_label_from_container (widget);
379 if (!GTK_IS_LABEL (label))
382 item = GAIL_ITEM (text);
384 gail_item_init_textutil (item, label);
386 label_text = gtk_label_get_text (GTK_LABEL (label));
388 if (label_text == NULL)
392 return gail_text_util_get_substring (item->textutil,
398 gail_item_get_text_before_offset (AtkText *text,
400 AtkTextBoundary boundary_type,
408 widget = GTK_ACCESSIBLE (text)->widget;
411 /* State is defunct */
415 label = get_label_from_container (widget);
417 if (!GTK_IS_LABEL(label))
420 item = GAIL_ITEM (text);
422 gail_item_init_textutil (item, label);
424 return gail_text_util_get_text (item->textutil,
425 gtk_label_get_layout (GTK_LABEL (label)), GAIL_BEFORE_OFFSET,
426 boundary_type, offset, start_offset, end_offset);
430 gail_item_get_text_at_offset (AtkText *text,
432 AtkTextBoundary boundary_type,
440 widget = GTK_ACCESSIBLE (text)->widget;
443 /* State is defunct */
447 label = get_label_from_container (widget);
449 if (!GTK_IS_LABEL(label))
452 item = GAIL_ITEM (text);
454 gail_item_init_textutil (item, label);
456 return gail_text_util_get_text (item->textutil,
457 gtk_label_get_layout (GTK_LABEL (label)), GAIL_AT_OFFSET,
458 boundary_type, offset, start_offset, end_offset);
462 gail_item_get_text_after_offset (AtkText *text,
464 AtkTextBoundary boundary_type,
472 widget = GTK_ACCESSIBLE (text)->widget;
476 /* State is defunct */
481 label = get_label_from_container (widget);
483 if (!GTK_IS_LABEL(label))
486 item = GAIL_ITEM (text);
488 gail_item_init_textutil (item, label);
490 return gail_text_util_get_text (item->textutil,
491 gtk_label_get_layout (GTK_LABEL (label)), GAIL_AFTER_OFFSET,
492 boundary_type, offset, start_offset, end_offset);
496 gail_item_get_character_count (AtkText *text)
501 widget = GTK_ACCESSIBLE (text)->widget;
503 /* State is defunct */
506 label = get_label_from_container (widget);
508 if (!GTK_IS_LABEL(label))
511 return g_utf8_strlen (gtk_label_get_text (GTK_LABEL (label)), -1);
515 gail_item_get_character_extents (AtkText *text,
525 PangoRectangle char_rect;
526 gint index, x_layout, y_layout;
527 const gchar *label_text;
529 widget = GTK_ACCESSIBLE (text)->widget;
532 /* State is defunct */
535 label = get_label_from_container (widget);
537 if (!GTK_IS_LABEL(label))
540 gtk_label_get_layout_offsets (GTK_LABEL (label), &x_layout, &y_layout);
541 label_text = gtk_label_get_text (GTK_LABEL (label));
542 index = g_utf8_offset_to_pointer (label_text, offset) - label_text;
543 pango_layout_index_to_pos (gtk_label_get_layout (GTK_LABEL (label)), index, &char_rect);
545 gail_misc_get_extents_from_pango_rectangle (label, &char_rect,
546 x_layout, y_layout, x, y, width, height, coords);
550 gail_item_get_offset_at_point (AtkText *text,
557 gint index, x_layout, y_layout;
558 const gchar *label_text;
560 widget = GTK_ACCESSIBLE (text)->widget;
562 /* State is defunct */
565 label = get_label_from_container (widget);
567 if (!GTK_IS_LABEL(label))
570 gtk_label_get_layout_offsets (GTK_LABEL (label), &x_layout, &y_layout);
572 index = gail_misc_get_index_at_point_in_layout (label,
573 gtk_label_get_layout (GTK_LABEL (label)),
574 x_layout, y_layout, x, y, coords);
575 label_text = gtk_label_get_text (GTK_LABEL (label));
578 if (coords == ATK_XY_WINDOW || coords == ATK_XY_SCREEN)
579 return g_utf8_strlen (label_text, -1);
584 return g_utf8_pointer_to_offset (label_text, label_text + index);
587 static AtkAttributeSet*
588 gail_item_get_run_attributes (AtkText *text,
595 AtkAttributeSet *at_set = NULL;
596 GtkJustification justify;
597 GtkTextDirection dir;
599 widget = GTK_ACCESSIBLE (text)->widget;
601 /* State is defunct */
604 label = get_label_from_container (widget);
606 if (!GTK_IS_LABEL(label))
609 /* Get values set for entire label, if any */
610 justify = gtk_label_get_justify (GTK_LABEL (label));
611 if (justify != GTK_JUSTIFY_CENTER)
613 at_set = gail_misc_add_attribute (at_set,
614 ATK_TEXT_ATTR_JUSTIFICATION,
615 g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_JUSTIFICATION, justify)));
617 dir = gtk_widget_get_direction (label);
618 if (dir == GTK_TEXT_DIR_RTL)
620 at_set = gail_misc_add_attribute (at_set,
621 ATK_TEXT_ATTR_DIRECTION,
622 g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_DIRECTION, dir)));
625 at_set = gail_misc_layout_get_run_attributes (at_set,
626 gtk_label_get_layout (GTK_LABEL (label)),
627 (gchar *) gtk_label_get_text (GTK_LABEL (label)),
634 static AtkAttributeSet*
635 gail_item_get_default_attributes (AtkText *text)
639 AtkAttributeSet *at_set = NULL;
641 widget = GTK_ACCESSIBLE (text)->widget;
643 /* State is defunct */
646 label = get_label_from_container (widget);
648 if (!GTK_IS_LABEL(label))
651 at_set = gail_misc_get_default_attributes (at_set,
652 gtk_label_get_layout (GTK_LABEL (label)),
658 gail_item_get_character_at_offset (AtkText *text,
666 widget = GTK_ACCESSIBLE (text)->widget;
668 /* State is defunct */
671 label = get_label_from_container (widget);
673 if (!GTK_IS_LABEL(label))
675 string = gtk_label_get_text (GTK_LABEL (label));
676 if (offset >= g_utf8_strlen (string, -1))
678 index = g_utf8_offset_to_pointer (string, offset);
680 return g_utf8_get_char (index);
684 get_label_from_container (GtkWidget *container)
687 GList *children, *tmp_list;
689 if (!GTK_IS_CONTAINER (container))
692 children = gtk_container_get_children (GTK_CONTAINER (container));
695 for (tmp_list = children; tmp_list != NULL; tmp_list = tmp_list->next)
697 if (GTK_IS_LABEL (tmp_list->data))
699 label = tmp_list->data;
703 * Get label from menu item in desktop background preferences
704 * option menu. See bug #144084.
706 else if (GTK_IS_BOX (tmp_list->data))
708 label = get_label_from_container (GTK_WIDGET (tmp_list->data));
713 g_list_free (children);