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,
49 static void atk_action_interface_init (AtkActionIface *iface);
50 static gboolean gail_expander_do_action (AtkAction *action,
52 static gboolean idle_do_action (gpointer data);
53 static gint gail_expander_get_n_actions(AtkAction *action);
54 static G_CONST_RETURN gchar* gail_expander_get_description
57 static G_CONST_RETURN gchar* gail_expander_get_keybinding
60 static G_CONST_RETURN gchar* gail_expander_action_get_name
63 static gboolean gail_expander_set_description
69 static void atk_text_interface_init (AtkTextIface *iface);
71 static gchar* gail_expander_get_text (AtkText *text,
74 static gunichar gail_expander_get_character_at_offset
77 static gchar* gail_expander_get_text_before_offset
80 AtkTextBoundary boundary_type,
83 static gchar* gail_expander_get_text_at_offset (AtkText *text,
85 AtkTextBoundary boundary_type,
88 static gchar* gail_expander_get_text_after_offset
91 AtkTextBoundary boundary_type,
94 static gint gail_expander_get_character_count(AtkText *text);
95 static void gail_expander_get_character_extents (AtkText *text,
101 AtkCoordType coords);
102 static gint gail_expander_get_offset_at_point (AtkText *text,
105 AtkCoordType coords);
106 static AtkAttributeSet* gail_expander_get_run_attributes
111 static AtkAttributeSet* gail_expander_get_default_attributes
114 G_DEFINE_TYPE_WITH_CODE (GailExpander, gail_expander, GAIL_TYPE_CONTAINER,
115 G_IMPLEMENT_INTERFACE (ATK_TYPE_ACTION, atk_action_interface_init)
116 G_IMPLEMENT_INTERFACE (ATK_TYPE_TEXT, atk_text_interface_init))
119 gail_expander_class_init (GailExpanderClass *klass)
121 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
122 AtkObjectClass *class = ATK_OBJECT_CLASS (klass);
123 GailWidgetClass *widget_class;
125 widget_class = (GailWidgetClass*)klass;
126 widget_class->notify_gtk = gail_expander_real_notify_gtk;
128 gobject_class->finalize = gail_expander_finalize;
130 class->get_name = gail_expander_get_name;
131 class->get_n_children = gail_expander_get_n_children;
132 class->ref_child = gail_expander_ref_child;
133 class->ref_state_set = gail_expander_ref_state_set;
135 class->initialize = gail_expander_real_initialize;
139 gail_expander_init (GailExpander *expander)
141 expander->activate_description = NULL;
142 expander->activate_keybinding = NULL;
143 expander->action_idle_handler = 0;
144 expander->textutil = NULL;
148 gail_expander_new (GtkWidget *widget)
151 AtkObject *accessible;
153 g_return_val_if_fail (GTK_IS_EXPANDER (widget), NULL);
155 object = g_object_new (GAIL_TYPE_EXPANDER, NULL);
157 accessible = ATK_OBJECT (object);
158 atk_object_initialize (accessible, widget);
163 static G_CONST_RETURN gchar*
164 gail_expander_get_name (AtkObject *obj)
166 G_CONST_RETURN gchar *name;
167 g_return_val_if_fail (GAIL_IS_EXPANDER (obj), NULL);
169 name = ATK_OBJECT_CLASS (gail_expander_parent_class)->get_name (obj);
175 * Get the text on the label
179 widget = GTK_ACCESSIBLE (obj)->widget;
186 g_return_val_if_fail (GTK_IS_EXPANDER (widget), NULL);
188 return gtk_expander_get_label (GTK_EXPANDER (widget));
193 gail_expander_get_n_children (AtkObject* obj)
199 g_return_val_if_fail (GAIL_IS_CONTAINER (obj), count);
201 widget = GTK_ACCESSIBLE (obj)->widget;
205 children = gtk_container_get_children (GTK_CONTAINER(widget));
206 count = g_list_length (children);
207 g_list_free (children);
209 /* See if there is a label - if there is, reduce our count by 1
210 * since we don't want the label included with the children.
212 if (gtk_expander_get_label_widget (widget))
219 gail_expander_ref_child (AtkObject *obj,
222 GList *children, *tmp_list;
223 AtkObject *accessible;
229 g_return_val_if_fail (GAIL_IS_CONTAINER (obj), NULL);
230 g_return_val_if_fail ((i >= 0), NULL);
231 widget = GTK_ACCESSIBLE (obj)->widget;
235 children = gtk_container_get_children (GTK_CONTAINER (widget));
237 /* See if there is a label - if there is, we need to skip it
238 * since we don't want the label included with the children.
240 label = gtk_expander_get_label_widget (widget);
242 count = g_list_length (children);
243 for (index = 0; index <= i; index++) {
244 tmp_list = g_list_nth (children, index);
245 if (label == GTK_WIDGET (tmp_list->data)) {
252 tmp_list = g_list_nth (children, i);
255 g_list_free (children);
258 accessible = gtk_widget_get_accessible (GTK_WIDGET (tmp_list->data));
260 g_list_free (children);
261 g_object_ref (accessible);
266 gail_expander_real_initialize (AtkObject *obj,
269 GailExpander *gail_expander = GAIL_EXPANDER (obj);
272 ATK_OBJECT_CLASS (gail_expander_parent_class)->initialize (obj, data);
274 expander = GTK_WIDGET (data);
275 if (GTK_WIDGET_MAPPED (expander))
276 gail_expander_init_textutil (gail_expander, GTK_EXPANDER (expander));
278 g_signal_connect (expander,
280 G_CALLBACK (gail_expander_map_gtk),
283 obj->role = ATK_ROLE_TOGGLE_BUTTON;
287 gail_expander_map_gtk (GtkWidget *widget,
290 GailExpander *expander;
292 expander = GAIL_EXPANDER (data);
293 gail_expander_init_textutil (expander, GTK_EXPANDER (widget));
297 gail_expander_real_notify_gtk (GObject *obj,
301 GtkExpander *expander;
302 GailExpander *gail_expander;
304 expander = GTK_EXPANDER (obj);
305 atk_obj = gtk_widget_get_accessible (GTK_WIDGET (expander));
307 if (strcmp (pspec->name, "label") == 0)
309 const gchar* label_text;
312 label_text = gtk_expander_get_label (expander);
314 gail_expander = GAIL_EXPANDER (atk_obj);
315 if (gail_expander->textutil)
316 gail_text_util_text_setup (gail_expander->textutil, label_text);
318 if (atk_obj->name == NULL)
321 * The label has changed so notify a change in accessible-name
323 g_object_notify (G_OBJECT (atk_obj), "accessible-name");
326 * The label is the only property which can be changed
328 g_signal_emit_by_name (atk_obj, "visible_data_changed");
330 else if (strcmp (pspec->name, "expanded") == 0)
332 atk_object_notify_state_change (atk_obj, ATK_STATE_CHECKED,
333 gtk_expander_get_expanded (expander));
334 atk_object_notify_state_change (atk_obj, ATK_STATE_EXPANDED,
335 gtk_expander_get_expanded (expander));
336 g_signal_emit_by_name (atk_obj, "visible_data_changed");
339 GAIL_WIDGET_CLASS (gail_expander_parent_class)->notify_gtk (obj, pspec);
343 gail_expander_init_textutil (GailExpander *expander,
346 const gchar *label_text;
348 expander->textutil = gail_text_util_new ();
349 label_text = gtk_expander_get_label (widget);
350 gail_text_util_text_setup (expander->textutil, label_text);
354 atk_action_interface_init (AtkActionIface *iface)
356 iface->do_action = gail_expander_do_action;
357 iface->get_n_actions = gail_expander_get_n_actions;
358 iface->get_description = gail_expander_get_description;
359 iface->get_keybinding = gail_expander_get_keybinding;
360 iface->get_name = gail_expander_action_get_name;
361 iface->set_description = gail_expander_set_description;
365 gail_expander_do_action (AtkAction *action,
369 GailExpander *expander;
370 gboolean return_value = TRUE;
372 widget = GTK_ACCESSIBLE (action)->widget;
379 if (!GTK_WIDGET_IS_SENSITIVE (widget) || !GTK_WIDGET_VISIBLE (widget))
382 expander = GAIL_EXPANDER (action);
386 if (expander->action_idle_handler)
387 return_value = FALSE;
389 expander->action_idle_handler = gdk_threads_add_idle (idle_do_action, expander);
392 return_value = FALSE;
399 idle_do_action (gpointer data)
402 GailExpander *gail_expander;
404 gail_expander = GAIL_EXPANDER (data);
405 gail_expander->action_idle_handler = 0;
407 widget = GTK_ACCESSIBLE (gail_expander)->widget;
408 if (widget == NULL /* State is defunct */ ||
409 !GTK_WIDGET_IS_SENSITIVE (widget) || !GTK_WIDGET_VISIBLE (widget))
412 gtk_widget_activate (widget);
418 gail_expander_get_n_actions (AtkAction *action)
423 static G_CONST_RETURN gchar*
424 gail_expander_get_description (AtkAction *action,
427 GailExpander *expander;
428 G_CONST_RETURN gchar *return_value;
430 expander = GAIL_EXPANDER (action);
435 return_value = expander->activate_description;
444 static G_CONST_RETURN gchar*
445 gail_expander_get_keybinding (AtkAction *action,
448 GailExpander *expander;
449 gchar *return_value = NULL;
456 * We look for a mnemonic on the label
461 expander = GAIL_EXPANDER (action);
462 widget = GTK_ACCESSIBLE (expander)->widget;
469 g_return_val_if_fail (GTK_IS_EXPANDER (widget), NULL);
471 label = gtk_expander_get_label_widget (GTK_EXPANDER (widget));
472 if (GTK_IS_LABEL (label))
476 key_val = gtk_label_get_mnemonic_keyval (GTK_LABEL (label));
477 if (key_val != GDK_VoidSymbol)
478 return_value = gtk_accelerator_name (key_val, GDK_MOD1_MASK);
479 g_free (expander->activate_keybinding);
480 expander->activate_keybinding = return_value;
490 static G_CONST_RETURN gchar*
491 gail_expander_action_get_name (AtkAction *action,
494 G_CONST_RETURN gchar *return_value;
499 return_value = "activate";
509 gail_expander_set_description (AtkAction *action,
513 GailExpander *expander;
516 expander = GAIL_EXPANDER (action);
521 value = &expander->activate_description;
530 *value = g_strdup (desc);
538 gail_expander_ref_state_set (AtkObject *obj)
540 AtkStateSet *state_set;
542 GtkExpander *expander;
544 state_set = ATK_OBJECT_CLASS (gail_expander_parent_class)->ref_state_set (obj);
545 widget = GTK_ACCESSIBLE (obj)->widget;
550 expander = GTK_EXPANDER (widget);
552 atk_state_set_add_state (state_set, ATK_STATE_EXPANDABLE);
554 if (gtk_expander_get_expanded (expander)) {
555 atk_state_set_add_state (state_set, ATK_STATE_CHECKED);
556 atk_state_set_add_state (state_set, ATK_STATE_EXPANDED);
565 atk_text_interface_init (AtkTextIface *iface)
567 iface->get_text = gail_expander_get_text;
568 iface->get_character_at_offset = gail_expander_get_character_at_offset;
569 iface->get_text_before_offset = gail_expander_get_text_before_offset;
570 iface->get_text_at_offset = gail_expander_get_text_at_offset;
571 iface->get_text_after_offset = gail_expander_get_text_after_offset;
572 iface->get_character_count = gail_expander_get_character_count;
573 iface->get_character_extents = gail_expander_get_character_extents;
574 iface->get_offset_at_point = gail_expander_get_offset_at_point;
575 iface->get_run_attributes = gail_expander_get_run_attributes;
576 iface->get_default_attributes = gail_expander_get_default_attributes;
580 gail_expander_get_text (AtkText *text,
585 GailExpander *expander;
586 const gchar *label_text;
588 widget = GTK_ACCESSIBLE (text)->widget;
590 /* State is defunct */
593 expander = GAIL_EXPANDER (text);
594 if (!expander->textutil)
595 gail_expander_init_textutil (expander, GTK_EXPANDER (widget));
597 label_text = gtk_expander_get_label (GTK_EXPANDER (widget));
599 if (label_text == NULL)
602 return gail_text_util_get_substring (expander->textutil,
607 gail_expander_get_text_before_offset (AtkText *text,
609 AtkTextBoundary boundary_type,
614 GailExpander *expander;
617 widget = GTK_ACCESSIBLE (text)->widget;
619 /* State is defunct */
622 expander = GAIL_EXPANDER (text);
623 if (!expander->textutil)
624 gail_expander_init_textutil (expander, GTK_EXPANDER (widget));
626 label = gtk_expander_get_label_widget (GTK_EXPANDER (widget));
627 if (!GTK_IS_LABEL(label))
629 return gail_text_util_get_text (expander->textutil,
630 gtk_label_get_layout (GTK_LABEL (label)),
632 boundary_type, offset, start_offset, end_offset);
636 gail_expander_get_text_at_offset (AtkText *text,
638 AtkTextBoundary boundary_type,
643 GailExpander *expander;
646 widget = GTK_ACCESSIBLE (text)->widget;
648 /* State is defunct */
651 expander = GAIL_EXPANDER (text);
652 if (!expander->textutil)
653 gail_expander_init_textutil (expander, GTK_EXPANDER (widget));
655 label = gtk_expander_get_label_widget (GTK_EXPANDER (widget));
656 if (!GTK_IS_LABEL(label))
658 return gail_text_util_get_text (expander->textutil,
659 gtk_label_get_layout (GTK_LABEL (label)),
661 boundary_type, offset, start_offset, end_offset);
665 gail_expander_get_text_after_offset (AtkText *text,
667 AtkTextBoundary boundary_type,
672 GailExpander *expander;
675 widget = GTK_ACCESSIBLE (text)->widget;
677 /* State is defunct */
680 expander = GAIL_EXPANDER (text);
681 if (!expander->textutil)
682 gail_expander_init_textutil (expander, GTK_EXPANDER (widget));
684 label = gtk_expander_get_label_widget (GTK_EXPANDER (widget));
685 if (!GTK_IS_LABEL(label))
687 return gail_text_util_get_text (expander->textutil,
688 gtk_label_get_layout (GTK_LABEL (label)),
690 boundary_type, offset, start_offset, end_offset);
694 gail_expander_get_character_count (AtkText *text)
699 widget = GTK_ACCESSIBLE (text)->widget;
701 /* State is defunct */
704 label = gtk_expander_get_label_widget (GTK_EXPANDER (widget));
705 if (!GTK_IS_LABEL(label))
708 return g_utf8_strlen (gtk_label_get_text (GTK_LABEL (label)), -1);
712 gail_expander_get_character_extents (AtkText *text,
722 PangoRectangle char_rect;
723 gint index, x_layout, y_layout;
724 const gchar *label_text;
726 widget = GTK_ACCESSIBLE (text)->widget;
729 /* State is defunct */
732 label = gtk_expander_get_label_widget (GTK_EXPANDER (widget));
733 if (!GTK_IS_LABEL(label))
736 gtk_label_get_layout_offsets (GTK_LABEL (label), &x_layout, &y_layout);
737 label_text = gtk_label_get_text (GTK_LABEL (label));
738 index = g_utf8_offset_to_pointer (label_text, offset) - label_text;
739 pango_layout_index_to_pos (gtk_label_get_layout (GTK_LABEL (label)), index, &char_rect);
741 gail_misc_get_extents_from_pango_rectangle (label, &char_rect,
742 x_layout, y_layout, x, y, width, height, coords);
746 gail_expander_get_offset_at_point (AtkText *text,
753 gint index, x_layout, y_layout;
754 const gchar *label_text;
756 widget = GTK_ACCESSIBLE (text)->widget;
758 /* State is defunct */
760 label = gtk_expander_get_label_widget (GTK_EXPANDER (widget));
762 if (!GTK_IS_LABEL(label))
765 gtk_label_get_layout_offsets (GTK_LABEL (label), &x_layout, &y_layout);
767 index = gail_misc_get_index_at_point_in_layout (label,
768 gtk_label_get_layout (GTK_LABEL (label)),
769 x_layout, y_layout, x, y, coords);
770 label_text = gtk_label_get_text (GTK_LABEL (label));
773 if (coords == ATK_XY_WINDOW || coords == ATK_XY_SCREEN)
774 return g_utf8_strlen (label_text, -1);
779 return g_utf8_pointer_to_offset (label_text, label_text + index);
782 static AtkAttributeSet*
783 gail_expander_get_run_attributes (AtkText *text,
790 AtkAttributeSet *at_set = NULL;
791 GtkJustification justify;
792 GtkTextDirection dir;
794 widget = GTK_ACCESSIBLE (text)->widget;
796 /* State is defunct */
799 label = gtk_expander_get_label_widget (GTK_EXPANDER (widget));
801 if (!GTK_IS_LABEL(label))
804 /* Get values set for entire label, if any */
805 justify = gtk_label_get_justify (GTK_LABEL (label));
806 if (justify != GTK_JUSTIFY_CENTER)
808 at_set = gail_misc_add_attribute (at_set,
809 ATK_TEXT_ATTR_JUSTIFICATION,
810 g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_JUSTIFICATION, justify)));
812 dir = gtk_widget_get_direction (label);
813 if (dir == GTK_TEXT_DIR_RTL)
815 at_set = gail_misc_add_attribute (at_set,
816 ATK_TEXT_ATTR_DIRECTION,
817 g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_DIRECTION, dir)));
820 at_set = gail_misc_layout_get_run_attributes (at_set,
821 gtk_label_get_layout (GTK_LABEL (label)),
822 (gchar *) gtk_label_get_text (GTK_LABEL (label)),
829 static AtkAttributeSet*
830 gail_expander_get_default_attributes (AtkText *text)
834 AtkAttributeSet *at_set = NULL;
836 widget = GTK_ACCESSIBLE (text)->widget;
838 /* State is defunct */
841 label = gtk_expander_get_label_widget (GTK_EXPANDER (widget));
843 if (!GTK_IS_LABEL(label))
846 at_set = gail_misc_get_default_attributes (at_set,
847 gtk_label_get_layout (GTK_LABEL (label)),
853 gail_expander_get_character_at_offset (AtkText *text,
861 widget = GTK_ACCESSIBLE (text)->widget;
863 /* State is defunct */
866 label = gtk_expander_get_label_widget (GTK_EXPANDER (widget));
868 if (!GTK_IS_LABEL(label))
870 string = gtk_label_get_text (GTK_LABEL (label));
871 if (offset >= g_utf8_strlen (string, -1))
873 index = g_utf8_offset_to_pointer (string, offset);
875 return g_utf8_get_char (index);
879 gail_expander_finalize (GObject *object)
881 GailExpander *expander = GAIL_EXPANDER (object);
883 g_free (expander->activate_description);
884 g_free (expander->activate_keybinding);
885 if (expander->action_idle_handler)
887 g_source_remove (expander->action_idle_handler);
888 expander->action_idle_handler = 0;
890 if (expander->textutil)
891 g_object_unref (expander->textutil);
893 G_OBJECT_CLASS (gail_expander_parent_class)->finalize (object);