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 G_CONST_RETURN 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 G_CONST_RETURN 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 G_CONST_RETURN gchar* gail_expander_get_description
58 static G_CONST_RETURN gchar* gail_expander_get_keybinding
61 static G_CONST_RETURN 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;
148 static G_CONST_RETURN gchar*
149 gail_expander_get_name (AtkObject *obj)
151 G_CONST_RETURN gchar *name;
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 (obj)->widget;
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 (obj)->widget;
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;
214 g_return_val_if_fail (GAIL_IS_CONTAINER (obj), NULL);
215 g_return_val_if_fail ((i >= 0), NULL);
216 widget = GTK_ACCESSIBLE (obj)->widget;
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 count = g_list_length (children);
228 for (index = 0; index <= i; index++) {
229 tmp_list = g_list_nth (children, index);
230 if (label == GTK_WIDGET (tmp_list->data)) {
237 tmp_list = g_list_nth (children, i);
240 g_list_free (children);
243 accessible = gtk_widget_get_accessible (GTK_WIDGET (tmp_list->data));
245 g_list_free (children);
246 g_object_ref (accessible);
251 gail_expander_real_initialize (AtkObject *obj,
254 GailExpander *gail_expander = GAIL_EXPANDER (obj);
257 ATK_OBJECT_CLASS (gail_expander_parent_class)->initialize (obj, data);
259 expander = GTK_WIDGET (data);
260 if (gtk_widget_get_mapped (expander))
261 gail_expander_init_textutil (gail_expander, GTK_EXPANDER (expander));
263 g_signal_connect (expander,
265 G_CALLBACK (gail_expander_map_gtk),
268 obj->role = ATK_ROLE_TOGGLE_BUTTON;
272 gail_expander_map_gtk (GtkWidget *widget,
275 GailExpander *expander;
277 expander = GAIL_EXPANDER (data);
278 gail_expander_init_textutil (expander, GTK_EXPANDER (widget));
282 gail_expander_real_notify_gtk (GObject *obj,
286 GtkExpander *expander;
287 GailExpander *gail_expander;
289 expander = GTK_EXPANDER (obj);
290 atk_obj = gtk_widget_get_accessible (GTK_WIDGET (expander));
292 if (strcmp (pspec->name, "label") == 0)
294 const gchar* label_text;
297 label_text = gail_expander_get_full_text (expander);
299 gail_expander = GAIL_EXPANDER (atk_obj);
300 if (gail_expander->textutil)
301 gail_text_util_text_setup (gail_expander->textutil, label_text);
303 if (atk_obj->name == NULL)
306 * The label has changed so notify a change in accessible-name
308 g_object_notify (G_OBJECT (atk_obj), "accessible-name");
311 * The label is the only property which can be changed
313 g_signal_emit_by_name (atk_obj, "visible_data_changed");
315 else if (strcmp (pspec->name, "expanded") == 0)
317 atk_object_notify_state_change (atk_obj, ATK_STATE_CHECKED,
318 gtk_expander_get_expanded (expander));
319 atk_object_notify_state_change (atk_obj, ATK_STATE_EXPANDED,
320 gtk_expander_get_expanded (expander));
321 g_signal_emit_by_name (atk_obj, "visible_data_changed");
324 GAIL_WIDGET_CLASS (gail_expander_parent_class)->notify_gtk (obj, pspec);
327 static G_CONST_RETURN gchar*
328 gail_expander_get_full_text (GtkExpander *widget)
330 GtkWidget *label_widget;
332 label_widget = gtk_expander_get_label_widget (widget);
334 if (!GTK_IS_LABEL (label_widget))
337 return gtk_label_get_text (GTK_LABEL (label_widget));
341 gail_expander_init_textutil (GailExpander *expander,
344 const gchar *label_text;
346 expander->textutil = gail_text_util_new ();
347 label_text = gail_expander_get_full_text (widget);
348 gail_text_util_text_setup (expander->textutil, label_text);
352 atk_action_interface_init (AtkActionIface *iface)
354 iface->do_action = gail_expander_do_action;
355 iface->get_n_actions = gail_expander_get_n_actions;
356 iface->get_description = gail_expander_get_description;
357 iface->get_keybinding = gail_expander_get_keybinding;
358 iface->get_name = gail_expander_action_get_name;
359 iface->set_description = gail_expander_set_description;
363 gail_expander_do_action (AtkAction *action,
367 GailExpander *expander;
368 gboolean return_value = TRUE;
370 widget = GTK_ACCESSIBLE (action)->widget;
377 if (!gtk_widget_is_sensitive (widget) || !gtk_widget_get_visible (widget))
380 expander = GAIL_EXPANDER (action);
384 if (expander->action_idle_handler)
385 return_value = FALSE;
387 expander->action_idle_handler = gdk_threads_add_idle (idle_do_action, expander);
390 return_value = FALSE;
397 idle_do_action (gpointer data)
400 GailExpander *gail_expander;
402 gail_expander = GAIL_EXPANDER (data);
403 gail_expander->action_idle_handler = 0;
405 widget = GTK_ACCESSIBLE (gail_expander)->widget;
406 if (widget == NULL /* State is defunct */ ||
407 !gtk_widget_is_sensitive (widget) || !gtk_widget_get_visible (widget))
410 gtk_widget_activate (widget);
416 gail_expander_get_n_actions (AtkAction *action)
421 static G_CONST_RETURN gchar*
422 gail_expander_get_description (AtkAction *action,
425 GailExpander *expander;
426 G_CONST_RETURN gchar *return_value;
428 expander = GAIL_EXPANDER (action);
433 return_value = expander->activate_description;
442 static G_CONST_RETURN gchar*
443 gail_expander_get_keybinding (AtkAction *action,
446 GailExpander *expander;
447 gchar *return_value = NULL;
454 * We look for a mnemonic on the label
459 expander = GAIL_EXPANDER (action);
460 widget = GTK_ACCESSIBLE (expander)->widget;
467 g_return_val_if_fail (GTK_IS_EXPANDER (widget), NULL);
469 label = gtk_expander_get_label_widget (GTK_EXPANDER (widget));
470 if (GTK_IS_LABEL (label))
474 key_val = gtk_label_get_mnemonic_keyval (GTK_LABEL (label));
475 if (key_val != GDK_VoidSymbol)
476 return_value = gtk_accelerator_name (key_val, GDK_MOD1_MASK);
477 g_free (expander->activate_keybinding);
478 expander->activate_keybinding = return_value;
488 static G_CONST_RETURN gchar*
489 gail_expander_action_get_name (AtkAction *action,
492 G_CONST_RETURN gchar *return_value;
497 return_value = "activate";
507 gail_expander_set_description (AtkAction *action,
511 GailExpander *expander;
514 expander = GAIL_EXPANDER (action);
519 value = &expander->activate_description;
528 *value = g_strdup (desc);
536 gail_expander_ref_state_set (AtkObject *obj)
538 AtkStateSet *state_set;
540 GtkExpander *expander;
542 state_set = ATK_OBJECT_CLASS (gail_expander_parent_class)->ref_state_set (obj);
543 widget = GTK_ACCESSIBLE (obj)->widget;
548 expander = GTK_EXPANDER (widget);
550 atk_state_set_add_state (state_set, ATK_STATE_EXPANDABLE);
552 if (gtk_expander_get_expanded (expander)) {
553 atk_state_set_add_state (state_set, ATK_STATE_CHECKED);
554 atk_state_set_add_state (state_set, ATK_STATE_EXPANDED);
563 atk_text_interface_init (AtkTextIface *iface)
565 iface->get_text = gail_expander_get_text;
566 iface->get_character_at_offset = gail_expander_get_character_at_offset;
567 iface->get_text_before_offset = gail_expander_get_text_before_offset;
568 iface->get_text_at_offset = gail_expander_get_text_at_offset;
569 iface->get_text_after_offset = gail_expander_get_text_after_offset;
570 iface->get_character_count = gail_expander_get_character_count;
571 iface->get_character_extents = gail_expander_get_character_extents;
572 iface->get_offset_at_point = gail_expander_get_offset_at_point;
573 iface->get_run_attributes = gail_expander_get_run_attributes;
574 iface->get_default_attributes = gail_expander_get_default_attributes;
578 gail_expander_get_text (AtkText *text,
583 GailExpander *expander;
584 const gchar *label_text;
586 widget = GTK_ACCESSIBLE (text)->widget;
588 /* State is defunct */
591 expander = GAIL_EXPANDER (text);
592 if (!expander->textutil)
593 gail_expander_init_textutil (expander, GTK_EXPANDER (widget));
595 label_text = gail_expander_get_full_text (GTK_EXPANDER (widget));
597 if (label_text == NULL)
600 return gail_text_util_get_substring (expander->textutil,
605 gail_expander_get_text_before_offset (AtkText *text,
607 AtkTextBoundary boundary_type,
612 GailExpander *expander;
615 widget = GTK_ACCESSIBLE (text)->widget;
617 /* State is defunct */
620 expander = GAIL_EXPANDER (text);
621 if (!expander->textutil)
622 gail_expander_init_textutil (expander, GTK_EXPANDER (widget));
624 label = gtk_expander_get_label_widget (GTK_EXPANDER (widget));
625 if (!GTK_IS_LABEL(label))
627 return gail_text_util_get_text (expander->textutil,
628 gtk_label_get_layout (GTK_LABEL (label)),
630 boundary_type, offset, start_offset, end_offset);
634 gail_expander_get_text_at_offset (AtkText *text,
636 AtkTextBoundary boundary_type,
641 GailExpander *expander;
644 widget = GTK_ACCESSIBLE (text)->widget;
646 /* State is defunct */
649 expander = GAIL_EXPANDER (text);
650 if (!expander->textutil)
651 gail_expander_init_textutil (expander, GTK_EXPANDER (widget));
653 label = gtk_expander_get_label_widget (GTK_EXPANDER (widget));
654 if (!GTK_IS_LABEL(label))
656 return gail_text_util_get_text (expander->textutil,
657 gtk_label_get_layout (GTK_LABEL (label)),
659 boundary_type, offset, start_offset, end_offset);
663 gail_expander_get_text_after_offset (AtkText *text,
665 AtkTextBoundary boundary_type,
670 GailExpander *expander;
673 widget = GTK_ACCESSIBLE (text)->widget;
675 /* State is defunct */
678 expander = GAIL_EXPANDER (text);
679 if (!expander->textutil)
680 gail_expander_init_textutil (expander, GTK_EXPANDER (widget));
682 label = gtk_expander_get_label_widget (GTK_EXPANDER (widget));
683 if (!GTK_IS_LABEL(label))
685 return gail_text_util_get_text (expander->textutil,
686 gtk_label_get_layout (GTK_LABEL (label)),
688 boundary_type, offset, start_offset, end_offset);
692 gail_expander_get_character_count (AtkText *text)
697 widget = GTK_ACCESSIBLE (text)->widget;
699 /* State is defunct */
702 label = gtk_expander_get_label_widget (GTK_EXPANDER (widget));
703 if (!GTK_IS_LABEL(label))
706 return g_utf8_strlen (gtk_label_get_text (GTK_LABEL (label)), -1);
710 gail_expander_get_character_extents (AtkText *text,
720 PangoRectangle char_rect;
721 gint index, x_layout, y_layout;
722 const gchar *label_text;
724 widget = GTK_ACCESSIBLE (text)->widget;
727 /* State is defunct */
730 label = gtk_expander_get_label_widget (GTK_EXPANDER (widget));
731 if (!GTK_IS_LABEL(label))
734 gtk_label_get_layout_offsets (GTK_LABEL (label), &x_layout, &y_layout);
735 label_text = gtk_label_get_text (GTK_LABEL (label));
736 index = g_utf8_offset_to_pointer (label_text, offset) - label_text;
737 pango_layout_index_to_pos (gtk_label_get_layout (GTK_LABEL (label)), index, &char_rect);
739 gail_misc_get_extents_from_pango_rectangle (label, &char_rect,
740 x_layout, y_layout, x, y, width, height, coords);
744 gail_expander_get_offset_at_point (AtkText *text,
751 gint index, x_layout, y_layout;
752 const gchar *label_text;
754 widget = GTK_ACCESSIBLE (text)->widget;
756 /* State is defunct */
758 label = gtk_expander_get_label_widget (GTK_EXPANDER (widget));
760 if (!GTK_IS_LABEL(label))
763 gtk_label_get_layout_offsets (GTK_LABEL (label), &x_layout, &y_layout);
765 index = gail_misc_get_index_at_point_in_layout (label,
766 gtk_label_get_layout (GTK_LABEL (label)),
767 x_layout, y_layout, x, y, coords);
768 label_text = gtk_label_get_text (GTK_LABEL (label));
771 if (coords == ATK_XY_WINDOW || coords == ATK_XY_SCREEN)
772 return g_utf8_strlen (label_text, -1);
777 return g_utf8_pointer_to_offset (label_text, label_text + index);
780 static AtkAttributeSet*
781 gail_expander_get_run_attributes (AtkText *text,
788 AtkAttributeSet *at_set = NULL;
789 GtkJustification justify;
790 GtkTextDirection dir;
792 widget = GTK_ACCESSIBLE (text)->widget;
794 /* State is defunct */
797 label = gtk_expander_get_label_widget (GTK_EXPANDER (widget));
799 if (!GTK_IS_LABEL(label))
802 /* Get values set for entire label, if any */
803 justify = gtk_label_get_justify (GTK_LABEL (label));
804 if (justify != GTK_JUSTIFY_CENTER)
806 at_set = gail_misc_add_attribute (at_set,
807 ATK_TEXT_ATTR_JUSTIFICATION,
808 g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_JUSTIFICATION, justify)));
810 dir = gtk_widget_get_direction (label);
811 if (dir == GTK_TEXT_DIR_RTL)
813 at_set = gail_misc_add_attribute (at_set,
814 ATK_TEXT_ATTR_DIRECTION,
815 g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_DIRECTION, dir)));
818 at_set = gail_misc_layout_get_run_attributes (at_set,
819 gtk_label_get_layout (GTK_LABEL (label)),
820 (gchar *) gtk_label_get_text (GTK_LABEL (label)),
827 static AtkAttributeSet*
828 gail_expander_get_default_attributes (AtkText *text)
832 AtkAttributeSet *at_set = NULL;
834 widget = GTK_ACCESSIBLE (text)->widget;
836 /* State is defunct */
839 label = gtk_expander_get_label_widget (GTK_EXPANDER (widget));
841 if (!GTK_IS_LABEL(label))
844 at_set = gail_misc_get_default_attributes (at_set,
845 gtk_label_get_layout (GTK_LABEL (label)),
851 gail_expander_get_character_at_offset (AtkText *text,
859 widget = GTK_ACCESSIBLE (text)->widget;
861 /* State is defunct */
864 label = gtk_expander_get_label_widget (GTK_EXPANDER (widget));
866 if (!GTK_IS_LABEL(label))
868 string = gtk_label_get_text (GTK_LABEL (label));
869 if (offset >= g_utf8_strlen (string, -1))
871 index = g_utf8_offset_to_pointer (string, offset);
873 return g_utf8_get_char (index);
877 gail_expander_finalize (GObject *object)
879 GailExpander *expander = GAIL_EXPANDER (object);
881 g_free (expander->activate_description);
882 g_free (expander->activate_keybinding);
883 if (expander->action_idle_handler)
885 g_source_remove (expander->action_idle_handler);
886 expander->action_idle_handler = 0;
888 if (expander->textutil)
889 g_object_unref (expander->textutil);
891 G_OBJECT_CLASS (gail_expander_parent_class)->finalize (object);