1 /* GAIL - The GNOME Accessibility Enabling 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
27 #include "gaillabel.h"
28 #include "gailwindow.h"
29 #include <libgail-util/gailmisc.h>
31 static void gail_label_class_init (GailLabelClass *klass);
32 static void gail_label_init (GailLabel *label);
33 static void gail_label_real_initialize (AtkObject *obj,
35 static void gail_label_real_notify_gtk (GObject *obj,
37 static void gail_label_map_gtk (GtkWidget *widget,
39 static void gail_label_init_text_util (GailLabel *gail_label,
41 static void gail_label_finalize (GObject *object);
43 static void atk_text_interface_init (AtkTextIface *iface);
47 static G_CONST_RETURN gchar* gail_label_get_name (AtkObject *accessible);
48 static AtkStateSet* gail_label_ref_state_set (AtkObject *accessible);
49 static AtkRelationSet* gail_label_ref_relation_set (AtkObject *accessible);
53 static gchar* gail_label_get_text (AtkText *text,
56 static gunichar gail_label_get_character_at_offset(AtkText *text,
58 static gchar* gail_label_get_text_before_offset(AtkText *text,
60 AtkTextBoundary boundary_type,
63 static gchar* gail_label_get_text_at_offset (AtkText *text,
65 AtkTextBoundary boundary_type,
68 static gchar* gail_label_get_text_after_offset (AtkText *text,
70 AtkTextBoundary boundary_type,
73 static gint gail_label_get_character_count (AtkText *text);
74 static gint gail_label_get_caret_offset (AtkText *text);
75 static gboolean gail_label_set_caret_offset (AtkText *text,
77 static gint gail_label_get_n_selections (AtkText *text);
78 static gchar* gail_label_get_selection (AtkText *text,
82 static gboolean gail_label_add_selection (AtkText *text,
85 static gboolean gail_label_remove_selection (AtkText *text,
87 static gboolean gail_label_set_selection (AtkText *text,
91 static void gail_label_get_character_extents (AtkText *text,
98 static gint gail_label_get_offset_at_point (AtkText *text,
101 AtkCoordType coords);
102 static AtkAttributeSet* gail_label_get_run_attributes
107 static AtkAttributeSet* gail_label_get_default_attributes
110 G_DEFINE_TYPE_WITH_CODE (GailLabel, gail_label, GAIL_TYPE_WIDGET,
111 G_IMPLEMENT_INTERFACE (ATK_TYPE_TEXT, atk_text_interface_init))
114 gail_label_class_init (GailLabelClass *klass)
116 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
117 AtkObjectClass *class = ATK_OBJECT_CLASS (klass);
118 GailWidgetClass *widget_class;
120 gobject_class->finalize = gail_label_finalize;
122 widget_class = (GailWidgetClass*)klass;
123 widget_class->notify_gtk = gail_label_real_notify_gtk;
125 class->get_name = gail_label_get_name;
126 class->ref_state_set = gail_label_ref_state_set;
127 class->ref_relation_set = gail_label_ref_relation_set;
128 class->initialize = gail_label_real_initialize;
132 gail_label_init (GailLabel *label)
137 gail_label_real_initialize (AtkObject *obj,
141 GailLabel *gail_label;
143 ATK_OBJECT_CLASS (gail_label_parent_class)->initialize (obj, data);
145 gail_label = GAIL_LABEL (obj);
147 gail_label->window_create_handler = 0;
148 gail_label->has_top_level = FALSE;
149 gail_label->cursor_position = 0;
150 gail_label->selection_bound = 0;
151 gail_label->textutil = NULL;
152 gail_label->label_length = 0;
154 widget = GTK_WIDGET (data);
156 if (gtk_widget_get_mapped (widget))
157 gail_label_init_text_util (gail_label, widget);
159 g_signal_connect (widget,
161 G_CALLBACK (gail_label_map_gtk),
165 * Check whether ancestor of GtkLabel is a GtkButton and if so
166 * set accessible parent for GailLabel
168 while (widget != NULL)
170 widget = gtk_widget_get_parent (widget);
171 if (GTK_IS_BUTTON (widget))
173 atk_object_set_parent (obj, gtk_widget_get_accessible (widget));
178 if (GTK_IS_ACCEL_LABEL (widget))
179 obj->role = ATK_ROLE_ACCEL_LABEL;
181 obj->role = ATK_ROLE_LABEL;
185 gail_label_map_gtk (GtkWidget *widget,
188 GailLabel *gail_label;
190 gail_label = GAIL_LABEL (data);
191 gail_label_init_text_util (gail_label, widget);
195 gail_label_init_text_util (GailLabel *gail_label,
199 const gchar *label_text;
201 if (gail_label->textutil == NULL)
202 gail_label->textutil = gail_text_util_new ();
204 label = GTK_LABEL (widget);
205 label_text = gtk_label_get_text (label);
206 gail_text_util_text_setup (gail_label->textutil, label_text);
208 if (label_text == NULL)
209 gail_label->label_length = 0;
211 gail_label->label_length = g_utf8_strlen (label_text, -1);
215 notify_name_change (AtkObject *atk_obj)
218 GailLabel *gail_label;
222 widget = GTK_ACCESSIBLE (atk_obj)->widget;
229 gail_obj = G_OBJECT (atk_obj);
230 label = GTK_LABEL (widget);
231 gail_label = GAIL_LABEL (atk_obj);
233 if (gail_label->textutil == NULL)
237 * Check whether the label has actually changed before emitting
240 if (gail_label->textutil->buffer)
242 GtkTextIter start, end;
243 const char *new_label;
247 gtk_text_buffer_get_start_iter (gail_label->textutil->buffer, &start);
248 gtk_text_buffer_get_end_iter (gail_label->textutil->buffer, &end);
249 old_label = gtk_text_buffer_get_text (gail_label->textutil->buffer, &start, &end, FALSE);
250 new_label = gtk_label_get_text (label);
251 same = strcmp (new_label, old_label);
257 /* Create a delete text and an insert text signal */
259 g_signal_emit_by_name (gail_obj, "text_changed::delete", 0,
260 gail_label->label_length);
262 gail_label_init_text_util (gail_label, widget);
264 g_signal_emit_by_name (gail_obj, "text_changed::insert", 0,
265 gail_label->label_length);
267 if (atk_obj->name == NULL)
269 * The label has changed so notify a change in accessible-name
271 g_object_notify (gail_obj, "accessible-name");
273 g_signal_emit_by_name (gail_obj, "visible_data_changed");
277 window_created (GObject *obj,
280 g_return_if_fail (GAIL_LABEL (data));
282 notify_name_change (ATK_OBJECT (data));
286 gail_label_real_notify_gtk (GObject *obj,
289 GtkWidget *widget = GTK_WIDGET (obj);
290 AtkObject* atk_obj = gtk_widget_get_accessible (widget);
292 GailLabel *gail_label;
294 AtkObject *top_level;
297 gail_label = GAIL_LABEL (atk_obj);
299 if (strcmp (pspec->name, "label") == 0)
302 * We may get a label change for a label which is not attached to an
303 * application. We wait until the toplevel window is created before
304 * emitting the notification.
306 * This happens when [Ctrl+]Alt+Tab is pressed in metacity
308 if (!gail_label->has_top_level)
314 top_level = temp_obj;
315 temp_obj = atk_object_get_parent (top_level);
317 if (atk_object_get_role (top_level) != ATK_ROLE_APPLICATION)
319 if (gail_label->window_create_handler == 0 &&
320 GAIL_IS_WINDOW (top_level))
321 gail_label->window_create_handler = g_signal_connect_after (top_level, "create", G_CALLBACK (window_created), atk_obj);
324 gail_label->has_top_level = TRUE;
326 if (gail_label->has_top_level)
327 notify_name_change (atk_obj);
329 else if (strcmp (pspec->name, "cursor-position") == 0)
331 gint start, end, tmp;
332 gboolean text_caret_moved = FALSE;
333 gboolean selection_changed = FALSE;
335 gail_obj = G_OBJECT (atk_obj);
336 label = GTK_LABEL (widget);
338 if (gail_label->selection_bound != -1 && gail_label->selection_bound < gail_label->cursor_position)
340 tmp = gail_label->selection_bound;
341 gail_label->selection_bound = gail_label->cursor_position;
342 gail_label->cursor_position = tmp;
345 if (gtk_label_get_selection_bounds (label, &start, &end))
347 if (start != gail_label->cursor_position ||
348 end != gail_label->selection_bound)
350 if (end != gail_label->selection_bound)
352 gail_label->selection_bound = start;
353 gail_label->cursor_position = end;
357 gail_label->selection_bound = end;
358 gail_label->cursor_position = start;
360 text_caret_moved = TRUE;
362 selection_changed = TRUE;
367 if (gail_label->cursor_position != gail_label->selection_bound)
368 selection_changed = TRUE;
369 if (gtk_label_get_selectable (label))
371 if (gail_label->cursor_position != -1 && start != gail_label->cursor_position)
372 text_caret_moved = TRUE;
373 if (gail_label->selection_bound != -1 && end != gail_label->selection_bound)
375 text_caret_moved = TRUE;
376 gail_label->cursor_position = end;
377 gail_label->selection_bound = start;
381 gail_label->cursor_position = start;
382 gail_label->selection_bound = end;
387 /* GtkLabel has become non selectable */
389 gail_label->cursor_position = 0;
390 gail_label->selection_bound = 0;
391 text_caret_moved = TRUE;
395 if (text_caret_moved)
396 g_signal_emit_by_name (gail_obj, "text_caret_moved",
397 gail_label->cursor_position);
398 if (selection_changed)
399 g_signal_emit_by_name (gail_obj, "text_selection_changed");
403 GAIL_WIDGET_CLASS (gail_label_parent_class)->notify_gtk (obj, pspec);
407 gail_label_finalize (GObject *object)
409 GailLabel *label = GAIL_LABEL (object);
412 g_object_unref (label->textutil);
413 G_OBJECT_CLASS (gail_label_parent_class)->finalize (object);
420 gail_label_ref_state_set (AtkObject *accessible)
422 AtkStateSet *state_set;
425 state_set = ATK_OBJECT_CLASS (gail_label_parent_class)->ref_state_set (accessible);
426 widget = GTK_ACCESSIBLE (accessible)->widget;
431 atk_state_set_add_state (state_set, ATK_STATE_MULTI_LINE);
437 gail_label_ref_relation_set (AtkObject *obj)
440 AtkRelationSet *relation_set;
442 g_return_val_if_fail (GAIL_IS_LABEL (obj), NULL);
444 widget = GTK_ACCESSIBLE (obj)->widget;
451 relation_set = ATK_OBJECT_CLASS (gail_label_parent_class)->ref_relation_set (obj);
453 if (!atk_relation_set_contains (relation_set, ATK_RELATION_LABEL_FOR))
456 * Get the mnemonic widget
458 * The relation set is not updated if the mnemonic widget is changed
460 GtkWidget *mnemonic_widget = gtk_label_get_mnemonic_widget (GTK_LABEL (widget));
464 AtkObject *accessible_array[1];
465 AtkRelation* relation;
467 if (!gtk_widget_get_can_focus (mnemonic_widget))
470 * Handle the case where a GtkFileChooserButton is specified as the
471 * mnemonic widget. use the combobox which is a child of the
472 * GtkFileChooserButton as the mnemonic widget. See bug #359843.
474 if (GTK_IS_BOX (mnemonic_widget))
478 list = gtk_container_get_children (GTK_CONTAINER (mnemonic_widget));
479 if (g_list_length (list) == 2)
481 tmpl = g_list_last (list);
482 if (GTK_IS_COMBO_BOX(tmpl->data))
484 mnemonic_widget = GTK_WIDGET(tmpl->data);
490 * Handle the case where a GnomeIconEntry is specified as the
491 * mnemonic widget. use the button which is a grandchild of the
492 * GnomeIconEntry as the mnemonic widget. See bug #133967.
494 else if (GTK_IS_BOX (mnemonic_widget))
498 list = gtk_container_get_children (GTK_CONTAINER (mnemonic_widget));
499 if (g_list_length (list) == 1)
501 if (GTK_IS_ALIGNMENT (list->data))
503 GtkWidget *temp_widget;
505 temp_widget = GTK_BIN (list->data)->child;
506 if (GTK_IS_BUTTON (temp_widget))
507 mnemonic_widget = temp_widget;
509 else if (GTK_IS_HBOX (list->data))
511 GtkWidget *temp_widget;
513 temp_widget = GTK_WIDGET (list->data);
515 list = gtk_container_get_children (GTK_CONTAINER (temp_widget));
521 accessible_array[0] = gtk_widget_get_accessible (mnemonic_widget);
522 relation = atk_relation_new (accessible_array, 1,
523 ATK_RELATION_LABEL_FOR);
524 atk_relation_set_add (relation_set, relation);
526 * Unref the relation so that it is not leaked.
528 g_object_unref (relation);
534 static G_CONST_RETURN gchar*
535 gail_label_get_name (AtkObject *accessible)
537 G_CONST_RETURN gchar *name;
539 g_return_val_if_fail (GAIL_IS_LABEL (accessible), NULL);
541 name = ATK_OBJECT_CLASS (gail_label_parent_class)->get_name (accessible);
547 * Get the text on the label
551 widget = GTK_ACCESSIBLE (accessible)->widget;
558 g_return_val_if_fail (GTK_IS_LABEL (widget), NULL);
560 return gtk_label_get_text (GTK_LABEL (widget));
567 atk_text_interface_init (AtkTextIface *iface)
569 iface->get_text = gail_label_get_text;
570 iface->get_character_at_offset = gail_label_get_character_at_offset;
571 iface->get_text_before_offset = gail_label_get_text_before_offset;
572 iface->get_text_at_offset = gail_label_get_text_at_offset;
573 iface->get_text_after_offset = gail_label_get_text_after_offset;
574 iface->get_character_count = gail_label_get_character_count;
575 iface->get_caret_offset = gail_label_get_caret_offset;
576 iface->set_caret_offset = gail_label_set_caret_offset;
577 iface->get_n_selections = gail_label_get_n_selections;
578 iface->get_selection = gail_label_get_selection;
579 iface->add_selection = gail_label_add_selection;
580 iface->remove_selection = gail_label_remove_selection;
581 iface->set_selection = gail_label_set_selection;
582 iface->get_character_extents = gail_label_get_character_extents;
583 iface->get_offset_at_point = gail_label_get_offset_at_point;
584 iface->get_run_attributes = gail_label_get_run_attributes;
585 iface->get_default_attributes = gail_label_get_default_attributes;
589 gail_label_get_text (AtkText *text,
596 const gchar *label_text;
598 widget = GTK_ACCESSIBLE (text)->widget;
600 /* State is defunct */
603 label = GTK_LABEL (widget);
605 label_text = gtk_label_get_text (label);
607 if (label_text == NULL)
611 if (GAIL_LABEL (text)->textutil == NULL)
612 gail_label_init_text_util (GAIL_LABEL (text), widget);
613 return gail_text_util_get_substring (GAIL_LABEL(text)->textutil,
619 gail_label_get_text_before_offset (AtkText *text,
621 AtkTextBoundary boundary_type,
628 widget = GTK_ACCESSIBLE (text)->widget;
631 /* State is defunct */
635 label = GTK_LABEL (widget);
637 return gail_text_util_get_text (GAIL_LABEL (text)->textutil,
638 gtk_label_get_layout (label), GAIL_BEFORE_OFFSET,
639 boundary_type, offset, start_offset, end_offset);
643 gail_label_get_text_at_offset (AtkText *text,
645 AtkTextBoundary boundary_type,
652 widget = GTK_ACCESSIBLE (text)->widget;
655 /* State is defunct */
659 label = GTK_LABEL (widget);
661 return gail_text_util_get_text (GAIL_LABEL (text)->textutil,
662 gtk_label_get_layout (label), GAIL_AT_OFFSET,
663 boundary_type, offset, start_offset, end_offset);
667 gail_label_get_text_after_offset (AtkText *text,
669 AtkTextBoundary boundary_type,
676 widget = GTK_ACCESSIBLE (text)->widget;
680 /* State is defunct */
685 label = GTK_LABEL (widget);
687 return gail_text_util_get_text (GAIL_LABEL (text)->textutil,
688 gtk_label_get_layout (label), GAIL_AFTER_OFFSET,
689 boundary_type, offset, start_offset, end_offset);
693 gail_label_get_character_count (AtkText *text)
698 widget = GTK_ACCESSIBLE (text)->widget;
700 /* State is defunct */
703 label = GTK_LABEL (widget);
704 return g_utf8_strlen (gtk_label_get_text (label), -1);
708 gail_label_get_caret_offset (AtkText *text)
710 return GAIL_LABEL (text)->cursor_position;
714 gail_label_set_caret_offset (AtkText *text,
720 widget = GTK_ACCESSIBLE (text)->widget;
722 /* State is defunct */
725 label = GTK_LABEL (widget);
727 if (gtk_label_get_selectable (label) &&
729 offset <= g_utf8_strlen (gtk_label_get_text (label), -1))
731 gtk_label_select_region (label, offset, offset);
739 gail_label_get_n_selections (AtkText *text)
745 widget = GTK_ACCESSIBLE (text)->widget;
747 /* State is defunct */
750 label = GTK_LABEL (widget);
752 if (!gtk_label_get_selectable (label))
755 if (gtk_label_get_selection_bounds (label, &start, &end))
762 gail_label_get_selection (AtkText *text,
770 widget = GTK_ACCESSIBLE (text)->widget;
772 /* State is defunct */
775 label = GTK_LABEL (widget);
777 /* Only let the user get the selection if one is set, and if the
778 * selection_num is 0.
780 if (!gtk_label_get_selectable( label) || selection_num != 0)
783 if (gtk_label_get_selection_bounds (label, start_pos, end_pos))
785 const gchar* label_text = gtk_label_get_text (label);
787 if (label_text == NULL)
790 return gail_text_util_get_substring (GAIL_LABEL (text)->textutil,
791 *start_pos, *end_pos);
798 gail_label_add_selection (AtkText *text,
806 widget = GTK_ACCESSIBLE (text)->widget;
808 /* State is defunct */
811 label = GTK_LABEL (widget);
813 if (!gtk_label_get_selectable (label))
816 if (! gtk_label_get_selection_bounds (label, &start, &end))
818 gtk_label_select_region (label, start_pos, end_pos);
826 gail_label_remove_selection (AtkText *text,
833 widget = GTK_ACCESSIBLE (text)->widget;
835 /* State is defunct */
838 if (selection_num != 0)
841 label = GTK_LABEL (widget);
843 if (!gtk_label_get_selectable (label))
846 if (gtk_label_get_selection_bounds (label, &start, &end))
848 gtk_label_select_region (label, 0, 0);
856 gail_label_set_selection (AtkText *text,
865 widget = GTK_ACCESSIBLE (text)->widget;
867 /* State is defunct */
870 if (selection_num != 0)
873 label = GTK_LABEL (widget);
875 if (!gtk_label_get_selectable (label))
878 if (gtk_label_get_selection_bounds (label, &start, &end))
880 gtk_label_select_region (label, start_pos, end_pos);
888 gail_label_get_character_extents (AtkText *text,
898 PangoRectangle char_rect;
899 const gchar *label_text;
900 gint index, x_layout, y_layout;
902 widget = GTK_ACCESSIBLE (text)->widget;
905 /* State is defunct */
908 label = GTK_LABEL (widget);
910 gtk_label_get_layout_offsets (label, &x_layout, &y_layout);
911 label_text = gtk_label_get_text (label);
912 index = g_utf8_offset_to_pointer (label_text, offset) - label_text;
913 pango_layout_index_to_pos (gtk_label_get_layout (label), index, &char_rect);
915 gail_misc_get_extents_from_pango_rectangle (widget, &char_rect,
916 x_layout, y_layout, x, y, width, height, coords);
920 gail_label_get_offset_at_point (AtkText *text,
927 const gchar *label_text;
928 gint index, x_layout, y_layout;
930 widget = GTK_ACCESSIBLE (text)->widget;
932 /* State is defunct */
934 label = GTK_LABEL (widget);
936 gtk_label_get_layout_offsets (label, &x_layout, &y_layout);
938 index = gail_misc_get_index_at_point_in_layout (widget,
939 gtk_label_get_layout (label),
940 x_layout, y_layout, x, y, coords);
941 label_text = gtk_label_get_text (label);
944 if (coords == ATK_XY_WINDOW || coords == ATK_XY_SCREEN)
945 return g_utf8_strlen (label_text, -1);
950 return g_utf8_pointer_to_offset (label_text, label_text + index);
953 static AtkAttributeSet*
954 gail_label_get_run_attributes (AtkText *text,
961 AtkAttributeSet *at_set = NULL;
962 GtkJustification justify;
963 GtkTextDirection dir;
965 widget = GTK_ACCESSIBLE (text)->widget;
967 /* State is defunct */
970 label = GTK_LABEL (widget);
972 /* Get values set for entire label, if any */
973 justify = gtk_label_get_justify (label);
974 if (justify != GTK_JUSTIFY_CENTER)
976 at_set = gail_misc_add_attribute (at_set,
977 ATK_TEXT_ATTR_JUSTIFICATION,
978 g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_JUSTIFICATION, justify)));
980 dir = gtk_widget_get_direction (widget);
981 if (dir == GTK_TEXT_DIR_RTL)
983 at_set = gail_misc_add_attribute (at_set,
984 ATK_TEXT_ATTR_DIRECTION,
985 g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_DIRECTION, dir)));
988 at_set = gail_misc_layout_get_run_attributes (at_set,
989 gtk_label_get_layout (label),
990 gtk_label_get_text (label),
997 static AtkAttributeSet*
998 gail_label_get_default_attributes (AtkText *text)
1002 AtkAttributeSet *at_set = NULL;
1004 widget = GTK_ACCESSIBLE (text)->widget;
1006 /* State is defunct */
1009 label = GTK_LABEL (widget);
1011 at_set = gail_misc_get_default_attributes (at_set,
1012 gtk_label_get_layout (label),
1018 gail_label_get_character_at_offset (AtkText *text,
1023 const gchar *string;
1026 widget = GTK_ACCESSIBLE (text)->widget;
1028 /* State is defunct */
1031 label = GTK_LABEL (widget);
1032 string = gtk_label_get_text (label);
1033 if (offset >= g_utf8_strlen (string, -1))
1035 index = g_utf8_offset_to_pointer (string, offset);
1037 return g_utf8_get_char (index);