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.
22 #include <gdk/gdkkeysyms.h>
23 #include "gailexpander.h"
24 #include <libgail-util/gailmisc.h>
26 static void gail_expander_class_init (GailExpanderClass *klass);
27 static void gail_expander_object_init (GailExpander *expander);
29 static G_CONST_RETURN gchar* gail_expander_get_name (AtkObject *obj);
30 static gint gail_expander_get_n_children (AtkObject *obj)
32 static AtkObject* gail_expander_ref_child (AtkObject *obj,
35 static AtkStateSet* gail_expander_ref_state_set (AtkObject *obj);
36 static void gail_expander_real_notify_gtk (GObject *obj,
38 static void gail_expander_map_gtk (GtkWidget *widget,
41 static void gail_expander_real_initialize (AtkObject *obj,
43 static void gail_expander_finalize (GObject *object);
44 static void gail_expander_init_textutil (GailExpander *expander,
47 static void atk_action_interface_init (AtkActionIface *iface);
48 static gboolean gail_expander_do_action (AtkAction *action,
50 static gboolean idle_do_action (gpointer data);
51 static gint gail_expander_get_n_actions(AtkAction *action);
52 static G_CONST_RETURN gchar* gail_expander_get_description
55 static G_CONST_RETURN gchar* gail_expander_get_keybinding
58 static G_CONST_RETURN gchar* gail_expander_action_get_name
61 static gboolean gail_expander_set_description
67 static void atk_text_interface_init (AtkTextIface *iface);
69 static gchar* gail_expander_get_text (AtkText *text,
72 static gunichar gail_expander_get_character_at_offset
75 static gchar* gail_expander_get_text_before_offset
78 AtkTextBoundary boundary_type,
81 static gchar* gail_expander_get_text_at_offset (AtkText *text,
83 AtkTextBoundary boundary_type,
86 static gchar* gail_expander_get_text_after_offset
89 AtkTextBoundary boundary_type,
92 static gint gail_expander_get_character_count(AtkText *text);
93 static void gail_expander_get_character_extents (AtkText *text,
100 static gint gail_expander_get_offset_at_point (AtkText *text,
103 AtkCoordType coords);
104 static AtkAttributeSet* gail_expander_get_run_attributes
109 static AtkAttributeSet* gail_expander_get_default_attributes
112 static GailContainer* parent_class = NULL;
115 gail_expander_get_type (void)
117 static GType type = 0;
121 static const GTypeInfo tinfo =
123 sizeof (GailExpanderClass),
124 (GBaseInitFunc) NULL, /* base init */
125 (GBaseFinalizeFunc) NULL, /* base finalize */
126 (GClassInitFunc) gail_expander_class_init, /* class init */
127 (GClassFinalizeFunc) NULL, /* class finalize */
128 NULL, /* class data */
129 sizeof (GailExpander), /* instance size */
130 0, /* nb preallocs */
131 (GInstanceInitFunc) gail_expander_object_init, /* instance init */
132 NULL /* value table */
135 static const GInterfaceInfo atk_action_info =
137 (GInterfaceInitFunc) atk_action_interface_init,
138 (GInterfaceFinalizeFunc) NULL,
142 static const GInterfaceInfo atk_text_info =
144 (GInterfaceInitFunc) atk_text_interface_init,
145 (GInterfaceFinalizeFunc) NULL,
149 type = g_type_register_static (GAIL_TYPE_CONTAINER,
150 "GailExpander", &tinfo, 0);
152 g_type_add_interface_static (type, ATK_TYPE_ACTION,
154 g_type_add_interface_static (type, ATK_TYPE_TEXT,
163 gail_expander_class_init (GailExpanderClass *klass)
165 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
166 AtkObjectClass *class = ATK_OBJECT_CLASS (klass);
167 GailWidgetClass *widget_class;
169 widget_class = (GailWidgetClass*)klass;
170 widget_class->notify_gtk = gail_expander_real_notify_gtk;
172 gobject_class->finalize = gail_expander_finalize;
174 parent_class = g_type_class_peek_parent (klass);
176 class->get_name = gail_expander_get_name;
177 class->get_n_children = gail_expander_get_n_children;
178 class->ref_child = gail_expander_ref_child;
179 class->ref_state_set = gail_expander_ref_state_set;
181 class->initialize = gail_expander_real_initialize;
185 gail_expander_object_init (GailExpander *expander)
187 expander->activate_description = NULL;
188 expander->activate_keybinding = NULL;
189 expander->action_idle_handler = 0;
190 expander->textutil = NULL;
194 gail_expander_new (GtkWidget *widget)
197 AtkObject *accessible;
199 g_return_val_if_fail (GTK_IS_EXPANDER (widget), NULL);
201 object = g_object_new (GAIL_TYPE_EXPANDER, NULL);
203 accessible = ATK_OBJECT (object);
204 atk_object_initialize (accessible, widget);
209 static G_CONST_RETURN gchar*
210 gail_expander_get_name (AtkObject *obj)
212 G_CONST_RETURN gchar *name;
213 g_return_val_if_fail (GAIL_IS_EXPANDER (obj), NULL);
215 name = ATK_OBJECT_CLASS (parent_class)->get_name (obj);
221 * Get the text on the label
225 widget = GTK_ACCESSIBLE (obj)->widget;
232 g_return_val_if_fail (GTK_IS_EXPANDER (widget), NULL);
234 return gtk_expander_get_label (GTK_EXPANDER (widget));
239 gail_expander_get_n_children (AtkObject* obj)
245 g_return_val_if_fail (GAIL_IS_CONTAINER (obj), count);
247 widget = GTK_ACCESSIBLE (obj)->widget;
251 children = gtk_container_get_children (GTK_CONTAINER(widget));
252 count = g_list_length (children);
253 g_list_free (children);
255 /* See if there is a label - if there is, reduce our count by 1
256 * since we don't want the label included with the children.
258 if (gtk_expander_get_label_widget (widget))
265 gail_expander_ref_child (AtkObject *obj,
268 GList *children, *tmp_list;
269 AtkObject *accessible;
275 g_return_val_if_fail (GAIL_IS_CONTAINER (obj), NULL);
276 g_return_val_if_fail ((i >= 0), NULL);
277 widget = GTK_ACCESSIBLE (obj)->widget;
281 children = gtk_container_get_children (GTK_CONTAINER (widget));
283 /* See if there is a label - if there is, we need to skip it
284 * since we don't want the label included with the children.
286 label = gtk_expander_get_label_widget (widget);
288 count = g_list_length (children);
289 for (index = 0; index <= i; index++) {
290 tmp_list = g_list_nth (children, index);
291 if (label == GTK_WIDGET (tmp_list->data)) {
298 tmp_list = g_list_nth (children, i);
301 g_list_free (children);
304 accessible = gtk_widget_get_accessible (GTK_WIDGET (tmp_list->data));
306 g_list_free (children);
307 g_object_ref (accessible);
312 gail_expander_real_initialize (AtkObject *obj,
315 GailExpander *gail_expander = GAIL_EXPANDER (obj);
318 ATK_OBJECT_CLASS (parent_class)->initialize (obj, data);
320 expander = GTK_WIDGET (data);
321 if (GTK_WIDGET_MAPPED (expander))
322 gail_expander_init_textutil (gail_expander, GTK_EXPANDER (expander));
324 g_signal_connect (expander,
326 G_CALLBACK (gail_expander_map_gtk),
329 obj->role = ATK_ROLE_TOGGLE_BUTTON;
333 gail_expander_map_gtk (GtkWidget *widget,
336 GailExpander *expander;
338 expander = GAIL_EXPANDER (data);
339 gail_expander_init_textutil (expander, GTK_EXPANDER (widget));
343 gail_expander_real_notify_gtk (GObject *obj,
347 GtkExpander *expander;
348 GailExpander *gail_expander;
350 expander = GTK_EXPANDER (obj);
351 atk_obj = gtk_widget_get_accessible (GTK_WIDGET (expander));
353 if (strcmp (pspec->name, "label") == 0)
355 const gchar* label_text;
358 label_text = gtk_expander_get_label (expander);
360 gail_expander = GAIL_EXPANDER (atk_obj);
361 if (gail_expander->textutil)
362 gail_text_util_text_setup (gail_expander->textutil, label_text);
364 if (atk_obj->name == NULL)
367 * The label has changed so notify a change in accessible-name
369 g_object_notify (G_OBJECT (atk_obj), "accessible-name");
372 * The label is the only property which can be changed
374 g_signal_emit_by_name (atk_obj, "visible_data_changed");
376 else if (strcmp (pspec->name, "expanded") == 0)
378 atk_object_notify_state_change (atk_obj, ATK_STATE_CHECKED,
379 gtk_expander_get_expanded (expander));
380 atk_object_notify_state_change (atk_obj, ATK_STATE_EXPANDED,
381 gtk_expander_get_expanded (expander));
382 g_signal_emit_by_name (atk_obj, "visible_data_changed");
385 GAIL_WIDGET_CLASS (parent_class)->notify_gtk (obj, pspec);
389 gail_expander_init_textutil (GailExpander *expander,
392 const gchar *label_text;
394 expander->textutil = gail_text_util_new ();
395 label_text = gtk_expander_get_label (widget);
396 gail_text_util_text_setup (expander->textutil, label_text);
400 atk_action_interface_init (AtkActionIface *iface)
402 g_return_if_fail (iface != NULL);
404 iface->do_action = gail_expander_do_action;
405 iface->get_n_actions = gail_expander_get_n_actions;
406 iface->get_description = gail_expander_get_description;
407 iface->get_keybinding = gail_expander_get_keybinding;
408 iface->get_name = gail_expander_action_get_name;
409 iface->set_description = gail_expander_set_description;
413 gail_expander_do_action (AtkAction *action,
417 GailExpander *expander;
418 gboolean return_value = TRUE;
420 widget = GTK_ACCESSIBLE (action)->widget;
427 if (!GTK_WIDGET_IS_SENSITIVE (widget) || !GTK_WIDGET_VISIBLE (widget))
430 expander = GAIL_EXPANDER (action);
434 if (expander->action_idle_handler)
435 return_value = FALSE;
437 expander->action_idle_handler = gdk_threads_add_idle (idle_do_action, expander);
440 return_value = FALSE;
447 idle_do_action (gpointer data)
450 GailExpander *gail_expander;
452 gail_expander = GAIL_EXPANDER (data);
453 gail_expander->action_idle_handler = 0;
455 widget = GTK_ACCESSIBLE (gail_expander)->widget;
456 if (widget == NULL /* State is defunct */ ||
457 !GTK_WIDGET_IS_SENSITIVE (widget) || !GTK_WIDGET_VISIBLE (widget))
460 gtk_widget_activate (widget);
466 gail_expander_get_n_actions (AtkAction *action)
471 static G_CONST_RETURN gchar*
472 gail_expander_get_description (AtkAction *action,
475 GailExpander *expander;
476 G_CONST_RETURN gchar *return_value;
478 expander = GAIL_EXPANDER (action);
483 return_value = expander->activate_description;
492 static G_CONST_RETURN gchar*
493 gail_expander_get_keybinding (AtkAction *action,
496 GailExpander *expander;
497 gchar *return_value = NULL;
504 * We look for a mnemonic on the label
509 expander = GAIL_EXPANDER (action);
510 widget = GTK_ACCESSIBLE (expander)->widget;
517 g_return_val_if_fail (GTK_IS_EXPANDER (widget), NULL);
519 label = gtk_expander_get_label_widget (GTK_EXPANDER (widget));
520 if (GTK_IS_LABEL (label))
524 key_val = gtk_label_get_mnemonic_keyval (GTK_LABEL (label));
525 if (key_val != GDK_VoidSymbol)
526 return_value = gtk_accelerator_name (key_val, GDK_MOD1_MASK);
527 g_free (expander->activate_keybinding);
528 expander->activate_keybinding = return_value;
538 static G_CONST_RETURN gchar*
539 gail_expander_action_get_name (AtkAction *action,
542 G_CONST_RETURN gchar *return_value;
547 return_value = "activate";
557 gail_expander_set_description (AtkAction *action,
561 GailExpander *expander;
564 expander = GAIL_EXPANDER (action);
569 value = &expander->activate_description;
578 *value = g_strdup (desc);
586 gail_expander_ref_state_set (AtkObject *obj)
588 AtkStateSet *state_set;
590 GtkExpander *expander;
592 state_set = ATK_OBJECT_CLASS (parent_class)->ref_state_set (obj);
593 widget = GTK_ACCESSIBLE (obj)->widget;
598 expander = GTK_EXPANDER (widget);
600 atk_state_set_add_state (state_set, ATK_STATE_EXPANDABLE);
602 if (gtk_expander_get_expanded (expander)) {
603 atk_state_set_add_state (state_set, ATK_STATE_CHECKED);
604 atk_state_set_add_state (state_set, ATK_STATE_EXPANDED);
613 atk_text_interface_init (AtkTextIface *iface)
615 g_return_if_fail (iface != NULL);
616 iface->get_text = gail_expander_get_text;
617 iface->get_character_at_offset = gail_expander_get_character_at_offset;
618 iface->get_text_before_offset = gail_expander_get_text_before_offset;
619 iface->get_text_at_offset = gail_expander_get_text_at_offset;
620 iface->get_text_after_offset = gail_expander_get_text_after_offset;
621 iface->get_character_count = gail_expander_get_character_count;
622 iface->get_character_extents = gail_expander_get_character_extents;
623 iface->get_offset_at_point = gail_expander_get_offset_at_point;
624 iface->get_run_attributes = gail_expander_get_run_attributes;
625 iface->get_default_attributes = gail_expander_get_default_attributes;
629 gail_expander_get_text (AtkText *text,
634 GailExpander *expander;
635 const gchar *label_text;
637 widget = GTK_ACCESSIBLE (text)->widget;
639 /* State is defunct */
642 expander = GAIL_EXPANDER (text);
643 if (!expander->textutil)
644 gail_expander_init_textutil (expander, GTK_EXPANDER (widget));
646 label_text = gtk_expander_get_label (GTK_EXPANDER (widget));
648 if (label_text == NULL)
651 return gail_text_util_get_substring (expander->textutil,
656 gail_expander_get_text_before_offset (AtkText *text,
658 AtkTextBoundary boundary_type,
663 GailExpander *expander;
666 widget = GTK_ACCESSIBLE (text)->widget;
668 /* State is defunct */
671 expander = GAIL_EXPANDER (text);
672 if (!expander->textutil)
673 gail_expander_init_textutil (expander, GTK_EXPANDER (widget));
675 label = gtk_expander_get_label_widget (GTK_EXPANDER (widget));
676 if (!GTK_IS_LABEL(label))
678 return gail_text_util_get_text (expander->textutil,
679 gtk_label_get_layout (GTK_LABEL (label)),
681 boundary_type, offset, start_offset, end_offset);
685 gail_expander_get_text_at_offset (AtkText *text,
687 AtkTextBoundary boundary_type,
692 GailExpander *expander;
695 widget = GTK_ACCESSIBLE (text)->widget;
697 /* State is defunct */
700 expander = GAIL_EXPANDER (text);
701 if (!expander->textutil)
702 gail_expander_init_textutil (expander, GTK_EXPANDER (widget));
704 label = gtk_expander_get_label_widget (GTK_EXPANDER (widget));
705 if (!GTK_IS_LABEL(label))
707 return gail_text_util_get_text (expander->textutil,
708 gtk_label_get_layout (GTK_LABEL (label)),
710 boundary_type, offset, start_offset, end_offset);
714 gail_expander_get_text_after_offset (AtkText *text,
716 AtkTextBoundary boundary_type,
721 GailExpander *expander;
724 widget = GTK_ACCESSIBLE (text)->widget;
726 /* State is defunct */
729 expander = GAIL_EXPANDER (text);
730 if (!expander->textutil)
731 gail_expander_init_textutil (expander, GTK_EXPANDER (widget));
733 label = gtk_expander_get_label_widget (GTK_EXPANDER (widget));
734 if (!GTK_IS_LABEL(label))
736 return gail_text_util_get_text (expander->textutil,
737 gtk_label_get_layout (GTK_LABEL (label)),
739 boundary_type, offset, start_offset, end_offset);
743 gail_expander_get_character_count (AtkText *text)
748 widget = GTK_ACCESSIBLE (text)->widget;
750 /* State is defunct */
753 label = gtk_expander_get_label_widget (GTK_EXPANDER (widget));
754 if (!GTK_IS_LABEL(label))
757 return g_utf8_strlen (gtk_label_get_text (GTK_LABEL (label)), -1);
761 gail_expander_get_character_extents (AtkText *text,
771 PangoRectangle char_rect;
772 gint index, x_layout, y_layout;
773 const gchar *label_text;
775 widget = GTK_ACCESSIBLE (text)->widget;
778 /* State is defunct */
781 label = gtk_expander_get_label_widget (GTK_EXPANDER (widget));
782 if (!GTK_IS_LABEL(label))
785 gtk_label_get_layout_offsets (GTK_LABEL (label), &x_layout, &y_layout);
786 label_text = gtk_label_get_text (GTK_LABEL (label));
787 index = g_utf8_offset_to_pointer (label_text, offset) - label_text;
788 pango_layout_index_to_pos (gtk_label_get_layout (GTK_LABEL (label)), index, &char_rect);
790 gail_misc_get_extents_from_pango_rectangle (label, &char_rect,
791 x_layout, y_layout, x, y, width, height, coords);
795 gail_expander_get_offset_at_point (AtkText *text,
802 gint index, x_layout, y_layout;
803 const gchar *label_text;
805 widget = GTK_ACCESSIBLE (text)->widget;
807 /* State is defunct */
809 label = gtk_expander_get_label_widget (GTK_EXPANDER (widget));
811 if (!GTK_IS_LABEL(label))
814 gtk_label_get_layout_offsets (GTK_LABEL (label), &x_layout, &y_layout);
816 index = gail_misc_get_index_at_point_in_layout (label,
817 gtk_label_get_layout (GTK_LABEL (label)),
818 x_layout, y_layout, x, y, coords);
819 label_text = gtk_label_get_text (GTK_LABEL (label));
822 if (coords == ATK_XY_WINDOW || coords == ATK_XY_SCREEN)
823 return g_utf8_strlen (label_text, -1);
828 return g_utf8_pointer_to_offset (label_text, label_text + index);
831 static AtkAttributeSet*
832 gail_expander_get_run_attributes (AtkText *text,
839 AtkAttributeSet *at_set = NULL;
840 GtkJustification justify;
841 GtkTextDirection dir;
843 widget = GTK_ACCESSIBLE (text)->widget;
845 /* State is defunct */
848 label = gtk_expander_get_label_widget (GTK_EXPANDER (widget));
850 if (!GTK_IS_LABEL(label))
853 /* Get values set for entire label, if any */
854 justify = gtk_label_get_justify (GTK_LABEL (label));
855 if (justify != GTK_JUSTIFY_CENTER)
857 at_set = gail_misc_add_attribute (at_set,
858 ATK_TEXT_ATTR_JUSTIFICATION,
859 g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_JUSTIFICATION, justify)));
861 dir = gtk_widget_get_direction (label);
862 if (dir == GTK_TEXT_DIR_RTL)
864 at_set = gail_misc_add_attribute (at_set,
865 ATK_TEXT_ATTR_DIRECTION,
866 g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_DIRECTION, dir)));
869 at_set = gail_misc_layout_get_run_attributes (at_set,
870 gtk_label_get_layout (GTK_LABEL (label)),
871 (gchar *) gtk_label_get_text (GTK_LABEL (label)),
878 static AtkAttributeSet*
879 gail_expander_get_default_attributes (AtkText *text)
883 AtkAttributeSet *at_set = NULL;
885 widget = GTK_ACCESSIBLE (text)->widget;
887 /* State is defunct */
890 label = gtk_expander_get_label_widget (GTK_EXPANDER (widget));
892 if (!GTK_IS_LABEL(label))
895 at_set = gail_misc_get_default_attributes (at_set,
896 gtk_label_get_layout (GTK_LABEL (label)),
902 gail_expander_get_character_at_offset (AtkText *text,
910 widget = GTK_ACCESSIBLE (text)->widget;
912 /* State is defunct */
915 label = gtk_expander_get_label_widget (GTK_EXPANDER (widget));
917 if (!GTK_IS_LABEL(label))
919 string = gtk_label_get_text (GTK_LABEL (label));
920 if (offset >= g_utf8_strlen (string, -1))
922 index = g_utf8_offset_to_pointer (string, offset);
924 return g_utf8_get_char (index);
928 gail_expander_finalize (GObject *object)
930 GailExpander *expander = GAIL_EXPANDER (object);
932 g_free (expander->activate_description);
933 g_free (expander->activate_keybinding);
934 if (expander->action_idle_handler)
936 g_source_remove (expander->action_idle_handler);
937 expander->action_idle_handler = 0;
939 if (expander->textutil)
940 g_object_unref (expander->textutil);
942 G_OBJECT_CLASS (parent_class)->finalize (object);