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_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;
214 g_return_val_if_fail (GAIL_IS_CONTAINER (obj), NULL);
215 g_return_val_if_fail ((i >= 0), NULL);
217 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (obj));
221 children = gtk_container_get_children (GTK_CONTAINER (widget));
223 /* See if there is a label - if there is, we need to skip it
224 * since we don't want the label included with the children.
226 label = gtk_expander_get_label_widget (GTK_EXPANDER (widget));
228 count = g_list_length (children);
229 for (index = 0; index <= i; index++) {
230 tmp_list = g_list_nth (children, index);
231 if (label == GTK_WIDGET (tmp_list->data)) {
238 tmp_list = g_list_nth (children, i);
241 g_list_free (children);
244 accessible = gtk_widget_get_accessible (GTK_WIDGET (tmp_list->data));
246 g_list_free (children);
247 g_object_ref (accessible);
252 gail_expander_real_initialize (AtkObject *obj,
255 GailExpander *gail_expander = GAIL_EXPANDER (obj);
258 ATK_OBJECT_CLASS (gail_expander_parent_class)->initialize (obj, data);
260 expander = GTK_WIDGET (data);
261 if (gtk_widget_get_mapped (expander))
262 gail_expander_init_textutil (gail_expander, GTK_EXPANDER (expander));
264 g_signal_connect (expander,
266 G_CALLBACK (gail_expander_map_gtk),
269 obj->role = ATK_ROLE_TOGGLE_BUTTON;
273 gail_expander_map_gtk (GtkWidget *widget,
276 GailExpander *expander;
278 expander = GAIL_EXPANDER (data);
279 gail_expander_init_textutil (expander, GTK_EXPANDER (widget));
283 gail_expander_real_notify_gtk (GObject *obj,
287 GtkExpander *expander;
288 GailExpander *gail_expander;
290 expander = GTK_EXPANDER (obj);
291 atk_obj = gtk_widget_get_accessible (GTK_WIDGET (expander));
293 if (strcmp (pspec->name, "label") == 0)
295 const gchar* label_text;
298 label_text = gail_expander_get_full_text (expander);
300 gail_expander = GAIL_EXPANDER (atk_obj);
301 if (gail_expander->textutil)
302 gail_text_util_text_setup (gail_expander->textutil, label_text);
304 if (atk_obj->name == NULL)
307 * The label has changed so notify a change in accessible-name
309 g_object_notify (G_OBJECT (atk_obj), "accessible-name");
312 * The label is the only property which can be changed
314 g_signal_emit_by_name (atk_obj, "visible_data_changed");
316 else if (strcmp (pspec->name, "expanded") == 0)
318 atk_object_notify_state_change (atk_obj, ATK_STATE_CHECKED,
319 gtk_expander_get_expanded (expander));
320 atk_object_notify_state_change (atk_obj, ATK_STATE_EXPANDED,
321 gtk_expander_get_expanded (expander));
322 g_signal_emit_by_name (atk_obj, "visible_data_changed");
325 GAIL_WIDGET_CLASS (gail_expander_parent_class)->notify_gtk (obj, pspec);
328 static G_CONST_RETURN gchar*
329 gail_expander_get_full_text (GtkExpander *widget)
331 GtkWidget *label_widget;
333 label_widget = gtk_expander_get_label_widget (widget);
335 if (!GTK_IS_LABEL (label_widget))
338 return gtk_label_get_text (GTK_LABEL (label_widget));
342 gail_expander_init_textutil (GailExpander *expander,
345 const gchar *label_text;
347 expander->textutil = gail_text_util_new ();
348 label_text = gail_expander_get_full_text (widget);
349 gail_text_util_text_setup (expander->textutil, label_text);
353 atk_action_interface_init (AtkActionIface *iface)
355 iface->do_action = gail_expander_do_action;
356 iface->get_n_actions = gail_expander_get_n_actions;
357 iface->get_description = gail_expander_get_description;
358 iface->get_keybinding = gail_expander_get_keybinding;
359 iface->get_name = gail_expander_action_get_name;
360 iface->set_description = gail_expander_set_description;
364 gail_expander_do_action (AtkAction *action,
368 GailExpander *expander;
369 gboolean return_value = TRUE;
371 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (action));
378 if (!gtk_widget_is_sensitive (widget) || !gtk_widget_get_visible (widget))
381 expander = GAIL_EXPANDER (action);
385 if (expander->action_idle_handler)
386 return_value = FALSE;
388 expander->action_idle_handler = gdk_threads_add_idle (idle_do_action, expander);
391 return_value = FALSE;
398 idle_do_action (gpointer data)
401 GailExpander *gail_expander;
403 gail_expander = GAIL_EXPANDER (data);
404 gail_expander->action_idle_handler = 0;
406 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (gail_expander));
407 if (widget == NULL /* State is defunct */ ||
408 !gtk_widget_is_sensitive (widget) || !gtk_widget_get_visible (widget))
411 gtk_widget_activate (widget);
417 gail_expander_get_n_actions (AtkAction *action)
422 static G_CONST_RETURN gchar*
423 gail_expander_get_description (AtkAction *action,
426 GailExpander *expander;
427 G_CONST_RETURN gchar *return_value;
429 expander = GAIL_EXPANDER (action);
434 return_value = expander->activate_description;
443 static G_CONST_RETURN gchar*
444 gail_expander_get_keybinding (AtkAction *action,
447 GailExpander *expander;
448 gchar *return_value = NULL;
455 * We look for a mnemonic on the label
460 expander = GAIL_EXPANDER (action);
461 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (expander));
468 g_return_val_if_fail (GTK_IS_EXPANDER (widget), NULL);
470 label = gtk_expander_get_label_widget (GTK_EXPANDER (widget));
471 if (GTK_IS_LABEL (label))
475 key_val = gtk_label_get_mnemonic_keyval (GTK_LABEL (label));
476 if (key_val != GDK_VoidSymbol)
477 return_value = gtk_accelerator_name (key_val, GDK_MOD1_MASK);
478 g_free (expander->activate_keybinding);
479 expander->activate_keybinding = return_value;
489 static G_CONST_RETURN gchar*
490 gail_expander_action_get_name (AtkAction *action,
493 G_CONST_RETURN gchar *return_value;
498 return_value = "activate";
508 gail_expander_set_description (AtkAction *action,
512 GailExpander *expander;
515 expander = GAIL_EXPANDER (action);
520 value = &expander->activate_description;
529 *value = g_strdup (desc);
537 gail_expander_ref_state_set (AtkObject *obj)
539 AtkStateSet *state_set;
541 GtkExpander *expander;
543 state_set = ATK_OBJECT_CLASS (gail_expander_parent_class)->ref_state_set (obj);
544 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (obj));
549 expander = GTK_EXPANDER (widget);
551 atk_state_set_add_state (state_set, ATK_STATE_EXPANDABLE);
553 if (gtk_expander_get_expanded (expander)) {
554 atk_state_set_add_state (state_set, ATK_STATE_CHECKED);
555 atk_state_set_add_state (state_set, ATK_STATE_EXPANDED);
564 atk_text_interface_init (AtkTextIface *iface)
566 iface->get_text = gail_expander_get_text;
567 iface->get_character_at_offset = gail_expander_get_character_at_offset;
568 iface->get_text_before_offset = gail_expander_get_text_before_offset;
569 iface->get_text_at_offset = gail_expander_get_text_at_offset;
570 iface->get_text_after_offset = gail_expander_get_text_after_offset;
571 iface->get_character_count = gail_expander_get_character_count;
572 iface->get_character_extents = gail_expander_get_character_extents;
573 iface->get_offset_at_point = gail_expander_get_offset_at_point;
574 iface->get_run_attributes = gail_expander_get_run_attributes;
575 iface->get_default_attributes = gail_expander_get_default_attributes;
579 gail_expander_get_text (AtkText *text,
584 GailExpander *expander;
585 const gchar *label_text;
587 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
589 /* State is defunct */
592 expander = GAIL_EXPANDER (text);
593 if (!expander->textutil)
594 gail_expander_init_textutil (expander, GTK_EXPANDER (widget));
596 label_text = gail_expander_get_full_text (GTK_EXPANDER (widget));
598 if (label_text == NULL)
601 return gail_text_util_get_substring (expander->textutil,
606 gail_expander_get_text_before_offset (AtkText *text,
608 AtkTextBoundary boundary_type,
613 GailExpander *expander;
616 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
618 /* State is defunct */
621 expander = GAIL_EXPANDER (text);
622 if (!expander->textutil)
623 gail_expander_init_textutil (expander, GTK_EXPANDER (widget));
625 label = gtk_expander_get_label_widget (GTK_EXPANDER (widget));
626 if (!GTK_IS_LABEL(label))
628 return gail_text_util_get_text (expander->textutil,
629 gtk_label_get_layout (GTK_LABEL (label)),
631 boundary_type, offset, start_offset, end_offset);
635 gail_expander_get_text_at_offset (AtkText *text,
637 AtkTextBoundary boundary_type,
642 GailExpander *expander;
645 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
647 /* State is defunct */
650 expander = GAIL_EXPANDER (text);
651 if (!expander->textutil)
652 gail_expander_init_textutil (expander, GTK_EXPANDER (widget));
654 label = gtk_expander_get_label_widget (GTK_EXPANDER (widget));
655 if (!GTK_IS_LABEL(label))
657 return gail_text_util_get_text (expander->textutil,
658 gtk_label_get_layout (GTK_LABEL (label)),
660 boundary_type, offset, start_offset, end_offset);
664 gail_expander_get_text_after_offset (AtkText *text,
666 AtkTextBoundary boundary_type,
671 GailExpander *expander;
674 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
676 /* State is defunct */
679 expander = GAIL_EXPANDER (text);
680 if (!expander->textutil)
681 gail_expander_init_textutil (expander, GTK_EXPANDER (widget));
683 label = gtk_expander_get_label_widget (GTK_EXPANDER (widget));
684 if (!GTK_IS_LABEL(label))
686 return gail_text_util_get_text (expander->textutil,
687 gtk_label_get_layout (GTK_LABEL (label)),
689 boundary_type, offset, start_offset, end_offset);
693 gail_expander_get_character_count (AtkText *text)
698 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
700 /* State is defunct */
703 label = gtk_expander_get_label_widget (GTK_EXPANDER (widget));
704 if (!GTK_IS_LABEL(label))
707 return g_utf8_strlen (gtk_label_get_text (GTK_LABEL (label)), -1);
711 gail_expander_get_character_extents (AtkText *text,
721 PangoRectangle char_rect;
722 gint index, x_layout, y_layout;
723 const gchar *label_text;
725 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
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_get_widget (GTK_ACCESSIBLE (text));
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_get_widget (GTK_ACCESSIBLE (text));
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_get_widget (GTK_ACCESSIBLE (text));
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_get_widget (GTK_ACCESSIBLE (text));
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);