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.
25 #include <libgail-util/gailmisc.h>
27 static void gail_item_class_init (GailItemClass *klass);
28 static void gail_item_init (GailItem *item);
29 static G_CONST_RETURN gchar* gail_item_get_name (AtkObject *obj);
30 static gint gail_item_get_n_children (AtkObject *obj);
31 static AtkObject* gail_item_ref_child (AtkObject *obj,
33 static void gail_item_real_initialize (AtkObject *obj,
35 static void gail_item_label_map_gtk (GtkWidget *widget,
37 static void gail_item_finalize (GObject *object);
38 static void gail_item_init_textutil (GailItem *item,
40 static void gail_item_notify_label_gtk(GObject *obj,
45 static void atk_text_interface_init (AtkTextIface *iface);
47 static gchar* gail_item_get_text (AtkText *text,
50 static gunichar gail_item_get_character_at_offset(AtkText *text,
52 static gchar* gail_item_get_text_before_offset (AtkText *text,
54 AtkTextBoundary boundary_type,
57 static gchar* gail_item_get_text_at_offset (AtkText *text,
59 AtkTextBoundary boundary_type,
62 static gchar* gail_item_get_text_after_offset (AtkText *text,
64 AtkTextBoundary boundary_type,
67 static gint gail_item_get_character_count (AtkText *text);
68 static void gail_item_get_character_extents (AtkText *text,
75 static gint gail_item_get_offset_at_point (AtkText *text,
79 static AtkAttributeSet* gail_item_get_run_attributes
84 static AtkAttributeSet* gail_item_get_default_attributes
86 static GtkWidget* get_label_from_container (GtkWidget *container);
88 G_DEFINE_TYPE_WITH_CODE (GailItem, gail_item, GAIL_TYPE_CONTAINER,
89 G_IMPLEMENT_INTERFACE (ATK_TYPE_TEXT, atk_text_interface_init))
92 gail_item_class_init (GailItemClass *klass)
94 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
95 AtkObjectClass *class = ATK_OBJECT_CLASS (klass);
96 GailContainerClass *container_class;
98 container_class = (GailContainerClass *)klass;
100 gobject_class->finalize = gail_item_finalize;
102 class->get_name = gail_item_get_name;
103 class->get_n_children = gail_item_get_n_children;
104 class->ref_child = gail_item_ref_child;
105 class->initialize = gail_item_real_initialize;
107 * As we report the item as having no children we are not interested
108 * in add and remove signals
110 container_class->add_gtk = NULL;
111 container_class->remove_gtk = NULL;
115 gail_item_init (GailItem *item)
120 gail_item_new (GtkWidget *widget)
123 AtkObject *accessible;
125 g_return_val_if_fail (GTK_IS_ITEM (widget), NULL);
127 object = g_object_new (GAIL_TYPE_ITEM, NULL);
129 accessible = ATK_OBJECT (object);
130 atk_object_initialize (accessible, widget);
136 gail_item_real_initialize (AtkObject *obj,
139 GailItem *item = GAIL_ITEM (obj);
142 ATK_OBJECT_CLASS (gail_item_parent_class)->initialize (obj, data);
144 item->textutil = NULL;
147 label = get_label_from_container (GTK_WIDGET (data));
148 if (GTK_IS_LABEL (label))
150 if (GTK_WIDGET_MAPPED (label))
151 gail_item_init_textutil (item, label);
153 g_signal_connect (label,
155 G_CALLBACK (gail_item_label_map_gtk),
159 obj->role = ATK_ROLE_LIST_ITEM;
163 gail_item_label_map_gtk (GtkWidget *widget,
168 item = GAIL_ITEM (data);
169 gail_item_init_textutil (item, widget);
173 gail_item_init_textutil (GailItem *item,
176 const gchar *label_text;
178 if (item->textutil == NULL)
180 item->textutil = gail_text_util_new ();
181 g_signal_connect (label,
183 (GCallback) gail_item_notify_label_gtk,
186 label_text = gtk_label_get_text (GTK_LABEL (label));
187 gail_text_util_text_setup (item->textutil, label_text);
191 gail_item_finalize (GObject *object)
193 GailItem *item = GAIL_ITEM (object);
197 g_object_unref (item->textutil);
204 G_OBJECT_CLASS (gail_item_parent_class)->finalize (object);
207 static G_CONST_RETURN gchar*
208 gail_item_get_name (AtkObject *obj)
210 G_CONST_RETURN gchar* name;
212 g_return_val_if_fail (GAIL_IS_ITEM (obj), NULL);
214 name = ATK_OBJECT_CLASS (gail_item_parent_class)->get_name (obj);
218 * Get the label child
223 widget = GTK_ACCESSIBLE (obj)->widget;
230 label = get_label_from_container (widget);
231 if (GTK_IS_LABEL (label))
232 return gtk_label_get_text (GTK_LABEL(label));
234 * If we have a menu item in a menu attached to a GtkOptionMenu
235 * the label of the selected item is detached from the menu item
237 else if (GTK_IS_MENU_ITEM (widget))
242 AtkObject *parent_obj;
245 parent = gtk_widget_get_parent (widget);
246 if (GTK_IS_MENU (parent))
248 attach = gtk_menu_get_attach_widget (GTK_MENU (parent));
250 if (GTK_IS_OPTION_MENU (attach))
252 label = get_label_from_container (attach);
253 if (GTK_IS_LABEL (label))
254 return gtk_label_get_text (GTK_LABEL(label));
256 list = gtk_container_get_children (GTK_CONTAINER (parent));
257 index = g_list_index (list, widget);
259 if (index < 0 || index > g_list_length (list))
266 parent_obj = atk_object_get_parent (gtk_widget_get_accessible (parent));
267 if (GTK_IS_ACCESSIBLE (parent_obj))
269 parent = GTK_ACCESSIBLE (parent_obj)->widget;
270 if (GTK_IS_COMBO_BOX (parent))
277 model = gtk_combo_box_get_model (GTK_COMBO_BOX (parent));
278 item = GAIL_ITEM (obj);
279 if (gtk_tree_model_iter_nth_child (model, &iter, NULL, index))
281 n_columns = gtk_tree_model_get_n_columns (model);
282 for (i = 0; i < n_columns; i++)
284 GValue value = { 0, };
286 gtk_tree_model_get_value (model, &iter, i, &value);
287 if (G_VALUE_HOLDS_STRING (&value))
290 item->text = (gchar *) g_value_dup_string (&value);
291 g_value_unset (&value);
306 * We report that this object has no children
310 gail_item_get_n_children (AtkObject* obj)
316 gail_item_ref_child (AtkObject *obj,
323 gail_item_notify_label_gtk (GObject *obj,
327 AtkObject* atk_obj = ATK_OBJECT (data);
331 if (strcmp (pspec->name, "label") == 0)
333 const gchar* label_text;
335 label = GTK_LABEL (obj);
337 label_text = gtk_label_get_text (label);
339 gail_item = GAIL_ITEM (atk_obj);
340 gail_text_util_text_setup (gail_item->textutil, label_text);
342 if (atk_obj->name == NULL)
345 * The label has changed so notify a change in accessible-name
347 g_object_notify (G_OBJECT (atk_obj), "accessible-name");
350 * The label is the only property which can be changed
352 g_signal_emit_by_name (atk_obj, "visible_data_changed");
359 atk_text_interface_init (AtkTextIface *iface)
361 iface->get_text = gail_item_get_text;
362 iface->get_character_at_offset = gail_item_get_character_at_offset;
363 iface->get_text_before_offset = gail_item_get_text_before_offset;
364 iface->get_text_at_offset = gail_item_get_text_at_offset;
365 iface->get_text_after_offset = gail_item_get_text_after_offset;
366 iface->get_character_count = gail_item_get_character_count;
367 iface->get_character_extents = gail_item_get_character_extents;
368 iface->get_offset_at_point = gail_item_get_offset_at_point;
369 iface->get_run_attributes = gail_item_get_run_attributes;
370 iface->get_default_attributes = gail_item_get_default_attributes;
374 gail_item_get_text (AtkText *text,
381 const gchar *label_text;
383 widget = GTK_ACCESSIBLE (text)->widget;
385 /* State is defunct */
388 label = get_label_from_container (widget);
390 if (!GTK_IS_LABEL (label))
393 item = GAIL_ITEM (text);
395 gail_item_init_textutil (item, label);
397 label_text = gtk_label_get_text (GTK_LABEL (label));
399 if (label_text == NULL)
403 return gail_text_util_get_substring (item->textutil,
409 gail_item_get_text_before_offset (AtkText *text,
411 AtkTextBoundary boundary_type,
419 widget = GTK_ACCESSIBLE (text)->widget;
422 /* State is defunct */
426 label = get_label_from_container (widget);
428 if (!GTK_IS_LABEL(label))
431 item = GAIL_ITEM (text);
433 gail_item_init_textutil (item, label);
435 return gail_text_util_get_text (item->textutil,
436 gtk_label_get_layout (GTK_LABEL (label)), GAIL_BEFORE_OFFSET,
437 boundary_type, offset, start_offset, end_offset);
441 gail_item_get_text_at_offset (AtkText *text,
443 AtkTextBoundary boundary_type,
451 widget = GTK_ACCESSIBLE (text)->widget;
454 /* State is defunct */
458 label = get_label_from_container (widget);
460 if (!GTK_IS_LABEL(label))
463 item = GAIL_ITEM (text);
465 gail_item_init_textutil (item, label);
467 return gail_text_util_get_text (item->textutil,
468 gtk_label_get_layout (GTK_LABEL (label)), GAIL_AT_OFFSET,
469 boundary_type, offset, start_offset, end_offset);
473 gail_item_get_text_after_offset (AtkText *text,
475 AtkTextBoundary boundary_type,
483 widget = GTK_ACCESSIBLE (text)->widget;
487 /* State is defunct */
492 label = get_label_from_container (widget);
494 if (!GTK_IS_LABEL(label))
497 item = GAIL_ITEM (text);
499 gail_item_init_textutil (item, label);
501 return gail_text_util_get_text (item->textutil,
502 gtk_label_get_layout (GTK_LABEL (label)), GAIL_AFTER_OFFSET,
503 boundary_type, offset, start_offset, end_offset);
507 gail_item_get_character_count (AtkText *text)
512 widget = GTK_ACCESSIBLE (text)->widget;
514 /* State is defunct */
517 label = get_label_from_container (widget);
519 if (!GTK_IS_LABEL(label))
522 return g_utf8_strlen (gtk_label_get_text (GTK_LABEL (label)), -1);
526 gail_item_get_character_extents (AtkText *text,
536 PangoRectangle char_rect;
537 gint index, x_layout, y_layout;
538 const gchar *label_text;
540 widget = GTK_ACCESSIBLE (text)->widget;
543 /* State is defunct */
546 label = get_label_from_container (widget);
548 if (!GTK_IS_LABEL(label))
551 gtk_label_get_layout_offsets (GTK_LABEL (label), &x_layout, &y_layout);
552 label_text = gtk_label_get_text (GTK_LABEL (label));
553 index = g_utf8_offset_to_pointer (label_text, offset) - label_text;
554 pango_layout_index_to_pos (gtk_label_get_layout (GTK_LABEL (label)), index, &char_rect);
556 gail_misc_get_extents_from_pango_rectangle (label, &char_rect,
557 x_layout, y_layout, x, y, width, height, coords);
561 gail_item_get_offset_at_point (AtkText *text,
568 gint index, x_layout, y_layout;
569 const gchar *label_text;
571 widget = GTK_ACCESSIBLE (text)->widget;
573 /* State is defunct */
576 label = get_label_from_container (widget);
578 if (!GTK_IS_LABEL(label))
581 gtk_label_get_layout_offsets (GTK_LABEL (label), &x_layout, &y_layout);
583 index = gail_misc_get_index_at_point_in_layout (label,
584 gtk_label_get_layout (GTK_LABEL (label)),
585 x_layout, y_layout, x, y, coords);
586 label_text = gtk_label_get_text (GTK_LABEL (label));
589 if (coords == ATK_XY_WINDOW || coords == ATK_XY_SCREEN)
590 return g_utf8_strlen (label_text, -1);
595 return g_utf8_pointer_to_offset (label_text, label_text + index);
598 static AtkAttributeSet*
599 gail_item_get_run_attributes (AtkText *text,
606 AtkAttributeSet *at_set = NULL;
607 GtkJustification justify;
608 GtkTextDirection dir;
610 widget = GTK_ACCESSIBLE (text)->widget;
612 /* State is defunct */
615 label = get_label_from_container (widget);
617 if (!GTK_IS_LABEL(label))
620 /* Get values set for entire label, if any */
621 justify = gtk_label_get_justify (GTK_LABEL (label));
622 if (justify != GTK_JUSTIFY_CENTER)
624 at_set = gail_misc_add_attribute (at_set,
625 ATK_TEXT_ATTR_JUSTIFICATION,
626 g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_JUSTIFICATION, justify)));
628 dir = gtk_widget_get_direction (label);
629 if (dir == GTK_TEXT_DIR_RTL)
631 at_set = gail_misc_add_attribute (at_set,
632 ATK_TEXT_ATTR_DIRECTION,
633 g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_DIRECTION, dir)));
636 at_set = gail_misc_layout_get_run_attributes (at_set,
637 gtk_label_get_layout (GTK_LABEL (label)),
638 (gchar *) gtk_label_get_text (GTK_LABEL (label)),
645 static AtkAttributeSet*
646 gail_item_get_default_attributes (AtkText *text)
650 AtkAttributeSet *at_set = NULL;
652 widget = GTK_ACCESSIBLE (text)->widget;
654 /* State is defunct */
657 label = get_label_from_container (widget);
659 if (!GTK_IS_LABEL(label))
662 at_set = gail_misc_get_default_attributes (at_set,
663 gtk_label_get_layout (GTK_LABEL (label)),
669 gail_item_get_character_at_offset (AtkText *text,
677 widget = GTK_ACCESSIBLE (text)->widget;
679 /* State is defunct */
682 label = get_label_from_container (widget);
684 if (!GTK_IS_LABEL(label))
686 string = gtk_label_get_text (GTK_LABEL (label));
687 if (offset >= g_utf8_strlen (string, -1))
689 index = g_utf8_offset_to_pointer (string, offset);
691 return g_utf8_get_char (index);
695 get_label_from_container (GtkWidget *container)
698 GList *children, *tmp_list;
700 if (!GTK_IS_CONTAINER (container))
703 children = gtk_container_get_children (GTK_CONTAINER (container));
706 for (tmp_list = children; tmp_list != NULL; tmp_list = tmp_list->next)
708 if (GTK_IS_LABEL (tmp_list->data))
710 label = tmp_list->data;
714 * Get label from menu item in desktop background preferences
715 * option menu. See bug #144084.
717 else if (GTK_IS_BOX (tmp_list->data))
719 label = get_label_from_container (GTK_WIDGET (tmp_list->data));
724 g_list_free (children);