1 /* GAIL - The GNOME Accessibility Implementation Library
2 * Copyright 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 <gdk/gdkkeysyms.h>
25 #include "gailexpander.h"
26 #include <libgail-util/gailmisc.h>
28 static void gail_expander_class_init (GailExpanderClass *klass);
29 static void gail_expander_init (GailExpander *expander);
31 static const gchar* gail_expander_get_name (AtkObject *obj);
32 static gint gail_expander_get_n_children (AtkObject *obj)
34 static AtkObject* gail_expander_ref_child (AtkObject *obj,
37 static AtkStateSet* gail_expander_ref_state_set (AtkObject *obj);
38 static void gail_expander_real_notify_gtk (GObject *obj,
40 static void gail_expander_map_gtk (GtkWidget *widget,
43 static void gail_expander_real_initialize (AtkObject *obj,
45 static void gail_expander_finalize (GObject *object);
46 static void gail_expander_init_textutil (GailExpander *expander,
48 static const gchar* gail_expander_get_full_text (GtkExpander *widget);
50 static void atk_action_interface_init (AtkActionIface *iface);
51 static gboolean gail_expander_do_action (AtkAction *action,
53 static gboolean idle_do_action (gpointer data);
54 static gint gail_expander_get_n_actions(AtkAction *action);
55 static const gchar* gail_expander_get_description
58 static const gchar* gail_expander_get_keybinding
61 static const gchar* gail_expander_action_get_name
64 static gboolean gail_expander_set_description
70 static void atk_text_interface_init (AtkTextIface *iface);
72 static gchar* gail_expander_get_text (AtkText *text,
75 static gunichar gail_expander_get_character_at_offset
78 static gchar* gail_expander_get_text_before_offset
81 AtkTextBoundary boundary_type,
84 static gchar* gail_expander_get_text_at_offset (AtkText *text,
86 AtkTextBoundary boundary_type,
89 static gchar* gail_expander_get_text_after_offset
92 AtkTextBoundary boundary_type,
95 static gint gail_expander_get_character_count(AtkText *text);
96 static void gail_expander_get_character_extents (AtkText *text,
102 AtkCoordType coords);
103 static gint gail_expander_get_offset_at_point (AtkText *text,
106 AtkCoordType coords);
107 static AtkAttributeSet* gail_expander_get_run_attributes
112 static AtkAttributeSet* gail_expander_get_default_attributes
115 G_DEFINE_TYPE_WITH_CODE (GailExpander, gail_expander, GAIL_TYPE_CONTAINER,
116 G_IMPLEMENT_INTERFACE (ATK_TYPE_ACTION, atk_action_interface_init)
117 G_IMPLEMENT_INTERFACE (ATK_TYPE_TEXT, atk_text_interface_init))
120 gail_expander_class_init (GailExpanderClass *klass)
122 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
123 AtkObjectClass *class = ATK_OBJECT_CLASS (klass);
124 GailWidgetClass *widget_class;
126 widget_class = (GailWidgetClass*)klass;
127 widget_class->notify_gtk = gail_expander_real_notify_gtk;
129 gobject_class->finalize = gail_expander_finalize;
131 class->get_name = gail_expander_get_name;
132 class->get_n_children = gail_expander_get_n_children;
133 class->ref_child = gail_expander_ref_child;
134 class->ref_state_set = gail_expander_ref_state_set;
136 class->initialize = gail_expander_real_initialize;
140 gail_expander_init (GailExpander *expander)
142 expander->activate_description = NULL;
143 expander->activate_keybinding = NULL;
144 expander->action_idle_handler = 0;
145 expander->textutil = NULL;
149 gail_expander_get_name (AtkObject *obj)
152 g_return_val_if_fail (GAIL_IS_EXPANDER (obj), NULL);
154 name = ATK_OBJECT_CLASS (gail_expander_parent_class)->get_name (obj);
160 * Get the text on the label
164 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (obj));
171 g_return_val_if_fail (GTK_IS_EXPANDER (widget), NULL);
173 return gail_expander_get_full_text (GTK_EXPANDER (widget));
178 gail_expander_get_n_children (AtkObject* obj)
184 g_return_val_if_fail (GAIL_IS_CONTAINER (obj), count);
186 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (obj));
190 children = gtk_container_get_children (GTK_CONTAINER(widget));
191 count = g_list_length (children);
192 g_list_free (children);
194 /* See if there is a label - if there is, reduce our count by 1
195 * since we don't want the label included with the children.
197 if (gtk_expander_get_label_widget (GTK_EXPANDER (widget)))
204 gail_expander_ref_child (AtkObject *obj,
207 GList *children, *tmp_list;
208 AtkObject *accessible;
213 g_return_val_if_fail (GAIL_IS_CONTAINER (obj), NULL);
214 g_return_val_if_fail ((i >= 0), NULL);
216 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (obj));
220 children = gtk_container_get_children (GTK_CONTAINER (widget));
222 /* See if there is a label - if there is, we need to skip it
223 * since we don't want the label included with the children.
225 label = gtk_expander_get_label_widget (GTK_EXPANDER (widget));
227 for (index = 0; index <= i; index++) {
228 tmp_list = g_list_nth (children, index);
229 if (label == GTK_WIDGET (tmp_list->data)) {
236 tmp_list = g_list_nth (children, i);
239 g_list_free (children);
242 accessible = gtk_widget_get_accessible (GTK_WIDGET (tmp_list->data));
244 g_list_free (children);
245 g_object_ref (accessible);
250 gail_expander_real_initialize (AtkObject *obj,
253 GailExpander *gail_expander = GAIL_EXPANDER (obj);
256 ATK_OBJECT_CLASS (gail_expander_parent_class)->initialize (obj, data);
258 expander = GTK_WIDGET (data);
259 if (gtk_widget_get_mapped (expander))
260 gail_expander_init_textutil (gail_expander, GTK_EXPANDER (expander));
262 g_signal_connect (expander,
264 G_CALLBACK (gail_expander_map_gtk),
267 obj->role = ATK_ROLE_TOGGLE_BUTTON;
271 gail_expander_map_gtk (GtkWidget *widget,
274 GailExpander *expander;
276 expander = GAIL_EXPANDER (data);
277 gail_expander_init_textutil (expander, GTK_EXPANDER (widget));
281 gail_expander_real_notify_gtk (GObject *obj,
285 GtkExpander *expander;
286 GailExpander *gail_expander;
288 expander = GTK_EXPANDER (obj);
289 atk_obj = gtk_widget_get_accessible (GTK_WIDGET (expander));
291 if (strcmp (pspec->name, "label") == 0)
293 const gchar* label_text;
296 label_text = gail_expander_get_full_text (expander);
298 gail_expander = GAIL_EXPANDER (atk_obj);
299 if (gail_expander->textutil)
300 gail_text_util_text_setup (gail_expander->textutil, label_text);
302 if (atk_obj->name == NULL)
305 * The label has changed so notify a change in accessible-name
307 g_object_notify (G_OBJECT (atk_obj), "accessible-name");
310 * The label is the only property which can be changed
312 g_signal_emit_by_name (atk_obj, "visible_data_changed");
314 else if (strcmp (pspec->name, "expanded") == 0)
316 atk_object_notify_state_change (atk_obj, ATK_STATE_CHECKED,
317 gtk_expander_get_expanded (expander));
318 atk_object_notify_state_change (atk_obj, ATK_STATE_EXPANDED,
319 gtk_expander_get_expanded (expander));
320 g_signal_emit_by_name (atk_obj, "visible_data_changed");
323 GAIL_WIDGET_CLASS (gail_expander_parent_class)->notify_gtk (obj, pspec);
327 gail_expander_get_full_text (GtkExpander *widget)
329 GtkWidget *label_widget;
331 label_widget = gtk_expander_get_label_widget (widget);
333 if (!GTK_IS_LABEL (label_widget))
336 return gtk_label_get_text (GTK_LABEL (label_widget));
340 gail_expander_init_textutil (GailExpander *expander,
343 const gchar *label_text;
345 expander->textutil = gail_text_util_new ();
346 label_text = gail_expander_get_full_text (widget);
347 gail_text_util_text_setup (expander->textutil, label_text);
351 atk_action_interface_init (AtkActionIface *iface)
353 iface->do_action = gail_expander_do_action;
354 iface->get_n_actions = gail_expander_get_n_actions;
355 iface->get_description = gail_expander_get_description;
356 iface->get_keybinding = gail_expander_get_keybinding;
357 iface->get_name = gail_expander_action_get_name;
358 iface->set_description = gail_expander_set_description;
362 gail_expander_do_action (AtkAction *action,
366 GailExpander *expander;
367 gboolean return_value = TRUE;
369 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (action));
376 if (!gtk_widget_is_sensitive (widget) || !gtk_widget_get_visible (widget))
379 expander = GAIL_EXPANDER (action);
383 if (expander->action_idle_handler)
384 return_value = FALSE;
386 expander->action_idle_handler = gdk_threads_add_idle (idle_do_action, expander);
389 return_value = FALSE;
396 idle_do_action (gpointer data)
399 GailExpander *gail_expander;
401 gail_expander = GAIL_EXPANDER (data);
402 gail_expander->action_idle_handler = 0;
404 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (gail_expander));
405 if (widget == NULL /* State is defunct */ ||
406 !gtk_widget_is_sensitive (widget) || !gtk_widget_get_visible (widget))
409 gtk_widget_activate (widget);
415 gail_expander_get_n_actions (AtkAction *action)
421 gail_expander_get_description (AtkAction *action,
424 GailExpander *expander;
425 const gchar *return_value;
427 expander = GAIL_EXPANDER (action);
432 return_value = expander->activate_description;
442 gail_expander_get_keybinding (AtkAction *action,
445 GailExpander *expander;
446 gchar *return_value = NULL;
453 * We look for a mnemonic on the label
458 expander = GAIL_EXPANDER (action);
459 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (expander));
466 g_return_val_if_fail (GTK_IS_EXPANDER (widget), NULL);
468 label = gtk_expander_get_label_widget (GTK_EXPANDER (widget));
469 if (GTK_IS_LABEL (label))
473 key_val = gtk_label_get_mnemonic_keyval (GTK_LABEL (label));
474 if (key_val != GDK_KEY_VoidSymbol)
475 return_value = gtk_accelerator_name (key_val, GDK_MOD1_MASK);
476 g_free (expander->activate_keybinding);
477 expander->activate_keybinding = return_value;
488 gail_expander_action_get_name (AtkAction *action,
491 const gchar *return_value;
496 return_value = "activate";
506 gail_expander_set_description (AtkAction *action,
510 GailExpander *expander;
513 expander = GAIL_EXPANDER (action);
518 value = &expander->activate_description;
527 *value = g_strdup (desc);
535 gail_expander_ref_state_set (AtkObject *obj)
537 AtkStateSet *state_set;
539 GtkExpander *expander;
541 state_set = ATK_OBJECT_CLASS (gail_expander_parent_class)->ref_state_set (obj);
542 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (obj));
547 expander = GTK_EXPANDER (widget);
549 atk_state_set_add_state (state_set, ATK_STATE_EXPANDABLE);
551 if (gtk_expander_get_expanded (expander)) {
552 atk_state_set_add_state (state_set, ATK_STATE_CHECKED);
553 atk_state_set_add_state (state_set, ATK_STATE_EXPANDED);
562 atk_text_interface_init (AtkTextIface *iface)
564 iface->get_text = gail_expander_get_text;
565 iface->get_character_at_offset = gail_expander_get_character_at_offset;
566 iface->get_text_before_offset = gail_expander_get_text_before_offset;
567 iface->get_text_at_offset = gail_expander_get_text_at_offset;
568 iface->get_text_after_offset = gail_expander_get_text_after_offset;
569 iface->get_character_count = gail_expander_get_character_count;
570 iface->get_character_extents = gail_expander_get_character_extents;
571 iface->get_offset_at_point = gail_expander_get_offset_at_point;
572 iface->get_run_attributes = gail_expander_get_run_attributes;
573 iface->get_default_attributes = gail_expander_get_default_attributes;
577 gail_expander_get_text (AtkText *text,
582 GailExpander *expander;
583 const gchar *label_text;
585 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
587 /* State is defunct */
590 expander = GAIL_EXPANDER (text);
591 if (!expander->textutil)
592 gail_expander_init_textutil (expander, GTK_EXPANDER (widget));
594 label_text = gail_expander_get_full_text (GTK_EXPANDER (widget));
596 if (label_text == NULL)
599 return gail_text_util_get_substring (expander->textutil,
604 gail_expander_get_text_before_offset (AtkText *text,
606 AtkTextBoundary boundary_type,
611 GailExpander *expander;
614 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
616 /* State is defunct */
619 expander = GAIL_EXPANDER (text);
620 if (!expander->textutil)
621 gail_expander_init_textutil (expander, GTK_EXPANDER (widget));
623 label = gtk_expander_get_label_widget (GTK_EXPANDER (widget));
624 if (!GTK_IS_LABEL(label))
626 return gail_text_util_get_text (expander->textutil,
627 gtk_label_get_layout (GTK_LABEL (label)),
629 boundary_type, offset, start_offset, end_offset);
633 gail_expander_get_text_at_offset (AtkText *text,
635 AtkTextBoundary boundary_type,
640 GailExpander *expander;
643 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
645 /* State is defunct */
648 expander = GAIL_EXPANDER (text);
649 if (!expander->textutil)
650 gail_expander_init_textutil (expander, GTK_EXPANDER (widget));
652 label = gtk_expander_get_label_widget (GTK_EXPANDER (widget));
653 if (!GTK_IS_LABEL(label))
655 return gail_text_util_get_text (expander->textutil,
656 gtk_label_get_layout (GTK_LABEL (label)),
658 boundary_type, offset, start_offset, end_offset);
662 gail_expander_get_text_after_offset (AtkText *text,
664 AtkTextBoundary boundary_type,
669 GailExpander *expander;
672 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
674 /* State is defunct */
677 expander = GAIL_EXPANDER (text);
678 if (!expander->textutil)
679 gail_expander_init_textutil (expander, GTK_EXPANDER (widget));
681 label = gtk_expander_get_label_widget (GTK_EXPANDER (widget));
682 if (!GTK_IS_LABEL(label))
684 return gail_text_util_get_text (expander->textutil,
685 gtk_label_get_layout (GTK_LABEL (label)),
687 boundary_type, offset, start_offset, end_offset);
691 gail_expander_get_character_count (AtkText *text)
696 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
698 /* State is defunct */
701 label = gtk_expander_get_label_widget (GTK_EXPANDER (widget));
702 if (!GTK_IS_LABEL(label))
705 return g_utf8_strlen (gtk_label_get_text (GTK_LABEL (label)), -1);
709 gail_expander_get_character_extents (AtkText *text,
719 PangoRectangle char_rect;
720 gint index, x_layout, y_layout;
721 const gchar *label_text;
723 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
725 /* State is defunct */
728 label = gtk_expander_get_label_widget (GTK_EXPANDER (widget));
729 if (!GTK_IS_LABEL(label))
732 gtk_label_get_layout_offsets (GTK_LABEL (label), &x_layout, &y_layout);
733 label_text = gtk_label_get_text (GTK_LABEL (label));
734 index = g_utf8_offset_to_pointer (label_text, offset) - label_text;
735 pango_layout_index_to_pos (gtk_label_get_layout (GTK_LABEL (label)), index, &char_rect);
737 gail_misc_get_extents_from_pango_rectangle (label, &char_rect,
738 x_layout, y_layout, x, y, width, height, coords);
742 gail_expander_get_offset_at_point (AtkText *text,
749 gint index, x_layout, y_layout;
750 const gchar *label_text;
752 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
754 /* State is defunct */
756 label = gtk_expander_get_label_widget (GTK_EXPANDER (widget));
758 if (!GTK_IS_LABEL(label))
761 gtk_label_get_layout_offsets (GTK_LABEL (label), &x_layout, &y_layout);
763 index = gail_misc_get_index_at_point_in_layout (label,
764 gtk_label_get_layout (GTK_LABEL (label)),
765 x_layout, y_layout, x, y, coords);
766 label_text = gtk_label_get_text (GTK_LABEL (label));
769 if (coords == ATK_XY_WINDOW || coords == ATK_XY_SCREEN)
770 return g_utf8_strlen (label_text, -1);
775 return g_utf8_pointer_to_offset (label_text, label_text + index);
778 static AtkAttributeSet*
779 gail_expander_get_run_attributes (AtkText *text,
786 AtkAttributeSet *at_set = NULL;
787 GtkJustification justify;
788 GtkTextDirection dir;
790 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
792 /* State is defunct */
795 label = gtk_expander_get_label_widget (GTK_EXPANDER (widget));
797 if (!GTK_IS_LABEL(label))
800 /* Get values set for entire label, if any */
801 justify = gtk_label_get_justify (GTK_LABEL (label));
802 if (justify != GTK_JUSTIFY_CENTER)
804 at_set = gail_misc_add_attribute (at_set,
805 ATK_TEXT_ATTR_JUSTIFICATION,
806 g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_JUSTIFICATION, justify)));
808 dir = gtk_widget_get_direction (label);
809 if (dir == GTK_TEXT_DIR_RTL)
811 at_set = gail_misc_add_attribute (at_set,
812 ATK_TEXT_ATTR_DIRECTION,
813 g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_DIRECTION, dir)));
816 at_set = gail_misc_layout_get_run_attributes (at_set,
817 gtk_label_get_layout (GTK_LABEL (label)),
818 (gchar *) gtk_label_get_text (GTK_LABEL (label)),
825 static AtkAttributeSet*
826 gail_expander_get_default_attributes (AtkText *text)
830 AtkAttributeSet *at_set = NULL;
832 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
834 /* State is defunct */
837 label = gtk_expander_get_label_widget (GTK_EXPANDER (widget));
839 if (!GTK_IS_LABEL(label))
842 at_set = gail_misc_get_default_attributes (at_set,
843 gtk_label_get_layout (GTK_LABEL (label)),
849 gail_expander_get_character_at_offset (AtkText *text,
857 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
859 /* State is defunct */
862 label = gtk_expander_get_label_widget (GTK_EXPANDER (widget));
864 if (!GTK_IS_LABEL(label))
866 string = gtk_label_get_text (GTK_LABEL (label));
867 if (offset >= g_utf8_strlen (string, -1))
869 index = g_utf8_offset_to_pointer (string, offset);
871 return g_utf8_get_char (index);
875 gail_expander_finalize (GObject *object)
877 GailExpander *expander = GAIL_EXPANDER (object);
879 g_free (expander->activate_description);
880 g_free (expander->activate_keybinding);
881 if (expander->action_idle_handler)
883 g_source_remove (expander->action_idle_handler);
884 expander->action_idle_handler = 0;
886 if (expander->textutil)
887 g_object_unref (expander->textutil);
889 G_OBJECT_CLASS (gail_expander_parent_class)->finalize (object);