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.
26 #include <libgail-util/gailmisc.h>
28 static void gail_item_class_init (GailItemClass *klass);
29 static void gail_item_init (GailItem *item);
30 static G_CONST_RETURN gchar* gail_item_get_name (AtkObject *obj);
31 static gint gail_item_get_n_children (AtkObject *obj);
32 static AtkObject* gail_item_ref_child (AtkObject *obj,
34 static void gail_item_real_initialize (AtkObject *obj,
36 static void gail_item_label_map_gtk (GtkWidget *widget,
38 static void gail_item_finalize (GObject *object);
39 static void gail_item_init_textutil (GailItem *item,
41 static void gail_item_notify_label_gtk(GObject *obj,
46 static void atk_text_interface_init (AtkTextIface *iface);
48 static gchar* gail_item_get_text (AtkText *text,
51 static gunichar gail_item_get_character_at_offset(AtkText *text,
53 static gchar* gail_item_get_text_before_offset (AtkText *text,
55 AtkTextBoundary boundary_type,
58 static gchar* gail_item_get_text_at_offset (AtkText *text,
60 AtkTextBoundary boundary_type,
63 static gchar* gail_item_get_text_after_offset (AtkText *text,
65 AtkTextBoundary boundary_type,
68 static gint gail_item_get_character_count (AtkText *text);
69 static void gail_item_get_character_extents (AtkText *text,
76 static gint gail_item_get_offset_at_point (AtkText *text,
80 static AtkAttributeSet* gail_item_get_run_attributes
85 static AtkAttributeSet* gail_item_get_default_attributes
87 static GtkWidget* get_label_from_container (GtkWidget *container);
89 G_DEFINE_TYPE_WITH_CODE (GailItem, gail_item, GAIL_TYPE_CONTAINER,
90 G_IMPLEMENT_INTERFACE (ATK_TYPE_TEXT, atk_text_interface_init))
93 gail_item_class_init (GailItemClass *klass)
95 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
96 AtkObjectClass *class = ATK_OBJECT_CLASS (klass);
97 GailContainerClass *container_class;
99 container_class = (GailContainerClass *)klass;
101 gobject_class->finalize = gail_item_finalize;
103 class->get_name = gail_item_get_name;
104 class->get_n_children = gail_item_get_n_children;
105 class->ref_child = gail_item_ref_child;
106 class->initialize = gail_item_real_initialize;
108 * As we report the item as having no children we are not interested
109 * in add and remove signals
111 container_class->add_gtk = NULL;
112 container_class->remove_gtk = NULL;
116 gail_item_init (GailItem *item)
121 gail_item_real_initialize (AtkObject *obj,
124 GailItem *item = GAIL_ITEM (obj);
127 ATK_OBJECT_CLASS (gail_item_parent_class)->initialize (obj, data);
129 item->textutil = NULL;
132 label = get_label_from_container (GTK_WIDGET (data));
133 if (GTK_IS_LABEL (label))
135 if (gtk_widget_get_mapped (label))
136 gail_item_init_textutil (item, label);
138 g_signal_connect (label,
140 G_CALLBACK (gail_item_label_map_gtk),
144 obj->role = ATK_ROLE_LIST_ITEM;
148 gail_item_label_map_gtk (GtkWidget *widget,
153 item = GAIL_ITEM (data);
154 gail_item_init_textutil (item, widget);
158 gail_item_init_textutil (GailItem *item,
161 const gchar *label_text;
163 if (item->textutil == NULL)
165 item->textutil = gail_text_util_new ();
166 g_signal_connect (label,
168 (GCallback) gail_item_notify_label_gtk,
171 label_text = gtk_label_get_text (GTK_LABEL (label));
172 gail_text_util_text_setup (item->textutil, label_text);
176 gail_item_finalize (GObject *object)
178 GailItem *item = GAIL_ITEM (object);
182 g_object_unref (item->textutil);
189 G_OBJECT_CLASS (gail_item_parent_class)->finalize (object);
192 static G_CONST_RETURN gchar*
193 gail_item_get_name (AtkObject *obj)
195 G_CONST_RETURN gchar* name;
197 g_return_val_if_fail (GAIL_IS_ITEM (obj), NULL);
199 name = ATK_OBJECT_CLASS (gail_item_parent_class)->get_name (obj);
203 * Get the label child
208 widget = GTK_ACCESSIBLE (obj)->widget;
215 label = get_label_from_container (widget);
216 if (GTK_IS_LABEL (label))
217 return gtk_label_get_text (GTK_LABEL(label));
219 * If we have a menu item in a menu attached to a GtkOptionMenu
220 * the label of the selected item is detached from the menu item
222 else if (GTK_IS_MENU_ITEM (widget))
227 AtkObject *parent_obj;
230 parent = gtk_widget_get_parent (widget);
231 if (GTK_IS_MENU (parent))
233 attach = gtk_menu_get_attach_widget (GTK_MENU (parent));
234 list = gtk_container_get_children (GTK_CONTAINER (parent));
235 index = g_list_index (list, widget);
237 if (index < 0 || index > g_list_length (list))
244 parent_obj = atk_object_get_parent (gtk_widget_get_accessible (parent));
245 if (GTK_IS_ACCESSIBLE (parent_obj))
247 parent = GTK_ACCESSIBLE (parent_obj)->widget;
248 if (GTK_IS_COMBO_BOX (parent))
255 model = gtk_combo_box_get_model (GTK_COMBO_BOX (parent));
256 item = GAIL_ITEM (obj);
257 if (gtk_tree_model_iter_nth_child (model, &iter, NULL, index))
259 n_columns = gtk_tree_model_get_n_columns (model);
260 for (i = 0; i < n_columns; i++)
262 GValue value = { 0, };
264 gtk_tree_model_get_value (model, &iter, i, &value);
265 if (G_VALUE_HOLDS_STRING (&value))
268 item->text = (gchar *) g_value_dup_string (&value);
269 g_value_unset (&value);
273 g_value_unset (&value);
286 * We report that this object has no children
290 gail_item_get_n_children (AtkObject* obj)
296 gail_item_ref_child (AtkObject *obj,
303 gail_item_notify_label_gtk (GObject *obj,
307 AtkObject* atk_obj = ATK_OBJECT (data);
311 if (strcmp (pspec->name, "label") == 0)
313 const gchar* label_text;
315 label = GTK_LABEL (obj);
317 label_text = gtk_label_get_text (label);
319 gail_item = GAIL_ITEM (atk_obj);
320 gail_text_util_text_setup (gail_item->textutil, label_text);
322 if (atk_obj->name == NULL)
325 * The label has changed so notify a change in accessible-name
327 g_object_notify (G_OBJECT (atk_obj), "accessible-name");
330 * The label is the only property which can be changed
332 g_signal_emit_by_name (atk_obj, "visible_data_changed");
339 atk_text_interface_init (AtkTextIface *iface)
341 iface->get_text = gail_item_get_text;
342 iface->get_character_at_offset = gail_item_get_character_at_offset;
343 iface->get_text_before_offset = gail_item_get_text_before_offset;
344 iface->get_text_at_offset = gail_item_get_text_at_offset;
345 iface->get_text_after_offset = gail_item_get_text_after_offset;
346 iface->get_character_count = gail_item_get_character_count;
347 iface->get_character_extents = gail_item_get_character_extents;
348 iface->get_offset_at_point = gail_item_get_offset_at_point;
349 iface->get_run_attributes = gail_item_get_run_attributes;
350 iface->get_default_attributes = gail_item_get_default_attributes;
354 gail_item_get_text (AtkText *text,
361 const gchar *label_text;
363 widget = GTK_ACCESSIBLE (text)->widget;
365 /* State is defunct */
368 label = get_label_from_container (widget);
370 if (!GTK_IS_LABEL (label))
373 item = GAIL_ITEM (text);
375 gail_item_init_textutil (item, label);
377 label_text = gtk_label_get_text (GTK_LABEL (label));
379 if (label_text == NULL)
383 return gail_text_util_get_substring (item->textutil,
389 gail_item_get_text_before_offset (AtkText *text,
391 AtkTextBoundary boundary_type,
399 widget = GTK_ACCESSIBLE (text)->widget;
402 /* State is defunct */
406 label = get_label_from_container (widget);
408 if (!GTK_IS_LABEL(label))
411 item = GAIL_ITEM (text);
413 gail_item_init_textutil (item, label);
415 return gail_text_util_get_text (item->textutil,
416 gtk_label_get_layout (GTK_LABEL (label)), GAIL_BEFORE_OFFSET,
417 boundary_type, offset, start_offset, end_offset);
421 gail_item_get_text_at_offset (AtkText *text,
423 AtkTextBoundary boundary_type,
431 widget = GTK_ACCESSIBLE (text)->widget;
434 /* State is defunct */
438 label = get_label_from_container (widget);
440 if (!GTK_IS_LABEL(label))
443 item = GAIL_ITEM (text);
445 gail_item_init_textutil (item, label);
447 return gail_text_util_get_text (item->textutil,
448 gtk_label_get_layout (GTK_LABEL (label)), GAIL_AT_OFFSET,
449 boundary_type, offset, start_offset, end_offset);
453 gail_item_get_text_after_offset (AtkText *text,
455 AtkTextBoundary boundary_type,
463 widget = GTK_ACCESSIBLE (text)->widget;
467 /* State is defunct */
472 label = get_label_from_container (widget);
474 if (!GTK_IS_LABEL(label))
477 item = GAIL_ITEM (text);
479 gail_item_init_textutil (item, label);
481 return gail_text_util_get_text (item->textutil,
482 gtk_label_get_layout (GTK_LABEL (label)), GAIL_AFTER_OFFSET,
483 boundary_type, offset, start_offset, end_offset);
487 gail_item_get_character_count (AtkText *text)
492 widget = GTK_ACCESSIBLE (text)->widget;
494 /* State is defunct */
497 label = get_label_from_container (widget);
499 if (!GTK_IS_LABEL(label))
502 return g_utf8_strlen (gtk_label_get_text (GTK_LABEL (label)), -1);
506 gail_item_get_character_extents (AtkText *text,
516 PangoRectangle char_rect;
517 gint index, x_layout, y_layout;
518 const gchar *label_text;
520 widget = GTK_ACCESSIBLE (text)->widget;
523 /* State is defunct */
526 label = get_label_from_container (widget);
528 if (!GTK_IS_LABEL(label))
531 gtk_label_get_layout_offsets (GTK_LABEL (label), &x_layout, &y_layout);
532 label_text = gtk_label_get_text (GTK_LABEL (label));
533 index = g_utf8_offset_to_pointer (label_text, offset) - label_text;
534 pango_layout_index_to_pos (gtk_label_get_layout (GTK_LABEL (label)), index, &char_rect);
536 gail_misc_get_extents_from_pango_rectangle (label, &char_rect,
537 x_layout, y_layout, x, y, width, height, coords);
541 gail_item_get_offset_at_point (AtkText *text,
548 gint index, x_layout, y_layout;
549 const gchar *label_text;
551 widget = GTK_ACCESSIBLE (text)->widget;
553 /* State is defunct */
556 label = get_label_from_container (widget);
558 if (!GTK_IS_LABEL(label))
561 gtk_label_get_layout_offsets (GTK_LABEL (label), &x_layout, &y_layout);
563 index = gail_misc_get_index_at_point_in_layout (label,
564 gtk_label_get_layout (GTK_LABEL (label)),
565 x_layout, y_layout, x, y, coords);
566 label_text = gtk_label_get_text (GTK_LABEL (label));
569 if (coords == ATK_XY_WINDOW || coords == ATK_XY_SCREEN)
570 return g_utf8_strlen (label_text, -1);
575 return g_utf8_pointer_to_offset (label_text, label_text + index);
578 static AtkAttributeSet*
579 gail_item_get_run_attributes (AtkText *text,
586 AtkAttributeSet *at_set = NULL;
587 GtkJustification justify;
588 GtkTextDirection dir;
590 widget = GTK_ACCESSIBLE (text)->widget;
592 /* State is defunct */
595 label = get_label_from_container (widget);
597 if (!GTK_IS_LABEL(label))
600 /* Get values set for entire label, if any */
601 justify = gtk_label_get_justify (GTK_LABEL (label));
602 if (justify != GTK_JUSTIFY_CENTER)
604 at_set = gail_misc_add_attribute (at_set,
605 ATK_TEXT_ATTR_JUSTIFICATION,
606 g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_JUSTIFICATION, justify)));
608 dir = gtk_widget_get_direction (label);
609 if (dir == GTK_TEXT_DIR_RTL)
611 at_set = gail_misc_add_attribute (at_set,
612 ATK_TEXT_ATTR_DIRECTION,
613 g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_DIRECTION, dir)));
616 at_set = gail_misc_layout_get_run_attributes (at_set,
617 gtk_label_get_layout (GTK_LABEL (label)),
618 (gchar *) gtk_label_get_text (GTK_LABEL (label)),
625 static AtkAttributeSet*
626 gail_item_get_default_attributes (AtkText *text)
630 AtkAttributeSet *at_set = NULL;
632 widget = GTK_ACCESSIBLE (text)->widget;
634 /* State is defunct */
637 label = get_label_from_container (widget);
639 if (!GTK_IS_LABEL(label))
642 at_set = gail_misc_get_default_attributes (at_set,
643 gtk_label_get_layout (GTK_LABEL (label)),
649 gail_item_get_character_at_offset (AtkText *text,
657 widget = GTK_ACCESSIBLE (text)->widget;
659 /* State is defunct */
662 label = get_label_from_container (widget);
664 if (!GTK_IS_LABEL(label))
666 string = gtk_label_get_text (GTK_LABEL (label));
667 if (offset >= g_utf8_strlen (string, -1))
669 index = g_utf8_offset_to_pointer (string, offset);
671 return g_utf8_get_char (index);
675 get_label_from_container (GtkWidget *container)
678 GList *children, *tmp_list;
680 if (!GTK_IS_CONTAINER (container))
683 children = gtk_container_get_children (GTK_CONTAINER (container));
686 for (tmp_list = children; tmp_list != NULL; tmp_list = tmp_list->next)
688 if (GTK_IS_LABEL (tmp_list->data))
690 label = tmp_list->data;
694 * Get label from menu item in desktop background preferences
695 * option menu. See bug #144084.
697 else if (GTK_IS_BOX (tmp_list->data))
699 label = get_label_from_container (GTK_WIDGET (tmp_list->data));
704 g_list_free (children);