1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2 /* eel-accessibility.h - Utility functions for accessibility
3 
4    Copyright (C) 2002 Anders Carlsson, Sun Microsystems, Inc.
5 
6    The Eel Library is free software; you can redistribute it and/or
7    modify it under the terms of the GNU Library General Public License as
8    published by the Free Software Foundation; either version 2 of the
9    License, or (at your option) any later version.
10 
11    The Eel Library is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14    Library General Public License for more details.
15 
16    You should have received a copy of the GNU Library General Public
17    License along with the Eel Library; see the file COPYING.LIB.  If not,
18    write to the Free Software Foundation, Inc., 51 Franklin Street - Suite 500,
19    Boston, MA 02110-1335, USA.
20 
21    Authors:
22 	Anders Carlsson <andersca@gnu.org>
23 	Michael Meeks   <michael@ximian.com>
24 */
25 #include <config.h>
26 #include <gtk/gtk.h>
27 #include <eel/eel-accessibility.h>
28 
29 void
eel_accessibility_set_up_label_widget_relation(GtkWidget * label,GtkWidget * widget)30 eel_accessibility_set_up_label_widget_relation (GtkWidget *label, GtkWidget *widget)
31 {
32 	AtkObject *atk_widget, *atk_label;
33 
34 	atk_label = gtk_widget_get_accessible (label);
35 	atk_widget = gtk_widget_get_accessible (widget);
36 
37 	/* Create the label -> widget relation */
38 	atk_object_add_relationship (atk_label, ATK_RELATION_LABEL_FOR, atk_widget);
39 
40 	/* Create the widget -> label relation */
41 	atk_object_add_relationship (atk_widget, ATK_RELATION_LABELLED_BY, atk_label);
42 }
43 
44 /*
45  * Hacks to make re-using gail somewhat easier.
46  */
47 
48 /**
49  * eel_accessibility_create_derived_type:
50  * @type_name: the name for the new accessible type eg. NemoIconCanvasItemAccessible
51  * @existing_gobject_with_proxy: the GType of an object that has a registered factory that
52  *      manufactures the type we want to inherit from. ie. to inherit from a GailCanvasItem
53  *      we need to pass GNOME_TYPE_CANVAS_ITEM - since GailCanvasItem is registered against
54  *      that type.
55  * @opt_gail_parent_class: the name of the Gail class to derive from eg. GailCanvasItem
56  * @class_init: the init function to run for this class
57  *
58  * This should be run to register the type, it can subsequently be run with
59  * the same name and will not re-register it, but simply return it.
60  *
61  * NB. to do instance init, you prolly want to override AtkObject::initialize
62  *
63  * Return value: the registered type, or 0 on failure.
64  **/
65 GType
eel_accessibility_create_derived_type(const char * type_name,GType existing_gobject_with_proxy,EelAccessibilityClassInitFn class_init)66 eel_accessibility_create_derived_type (const char *type_name,
67 				       GType existing_gobject_with_proxy,
68 				       EelAccessibilityClassInitFn class_init)
69 {
70 	GType type;
71 	GType parent_atk_type;
72 	GTypeInfo tinfo = { 0 };
73 	GTypeQuery query;
74 	AtkObjectFactory *factory;
75 
76 	if ((type = g_type_from_name (type_name))) {
77 		return type;
78 	}
79 
80 	factory = atk_registry_get_factory
81 		(atk_get_default_registry (),
82 		 existing_gobject_with_proxy);
83 	if (!factory) {
84 		return G_TYPE_INVALID;
85 	}
86 
87 	parent_atk_type = atk_object_factory_get_accessible_type (factory);
88 	if (!parent_atk_type) {
89 		return G_TYPE_INVALID;
90 	}
91 
92 	/*
93 	 * Figure out the size of the class and instance
94 	 * we are deriving from
95 	 */
96 	g_type_query (parent_atk_type, &query);
97 
98 	if (class_init) {
99 		tinfo.class_init = (GClassInitFunc) class_init;
100 	}
101 
102 	tinfo.class_size    = query.class_size;
103 	tinfo.instance_size = query.instance_size;
104 
105 	/* Register the type */
106 	type = g_type_register_static (
107 		parent_atk_type, type_name, &tinfo, 0);
108 
109 	return type;
110 }
111 
112 
113 static GQuark
get_quark_accessible(void)114 get_quark_accessible (void)
115 {
116 	static GQuark quark_accessible_object = 0;
117 
118 	if (!quark_accessible_object) {
119 		quark_accessible_object = g_quark_from_static_string
120 			("accessible-object");
121 	}
122 
123 	return quark_accessible_object;
124 }
125 
126 static GQuark
get_quark_gobject(void)127 get_quark_gobject (void)
128 {
129 	static GQuark quark_accessible_gobject = 0;
130 
131 	if (!quark_accessible_gobject) {
132 		quark_accessible_gobject = g_quark_from_static_string
133 			("object-for-accessible");
134 	}
135 
136 	return quark_accessible_gobject;
137 }
138 
139 /**
140  * eel_accessibility_get_atk_object:
141  * @object: a GObject of some sort
142  *
143  * gets an AtkObject associated with a GObject
144  *
145  * Return value: the associated accessible if one exists or NULL
146  **/
147 AtkObject *
eel_accessibility_get_atk_object(gpointer object)148 eel_accessibility_get_atk_object (gpointer object)
149 {
150 	return g_object_get_qdata (object, get_quark_accessible ());
151 }
152 
153 /**
154  * eel_accessibilty_for_object:
155  * @object: a GObject of some sort
156  *
157  * gets an AtkObject associated with a GObject and if it doesn't
158  * exist creates a suitable accessible object.
159  *
160  * Return value: an associated accessible.
161  **/
162 AtkObject *
eel_accessibility_for_object(gpointer object)163 eel_accessibility_for_object (gpointer object)
164 {
165 	if (GTK_IS_WIDGET (object))
166 		return gtk_widget_get_accessible (object);
167 
168 	return atk_gobject_accessible_for_object (object);
169 }
170 
171 /**
172  * eel_accessibility_get_gobject:
173  * @object: an AtkObject
174  *
175  * gets the GObject associated with the AtkObject, for which
176  * @object provides accessibility support.
177  *
178  * Return value: the accessible's associated GObject
179  **/
180 gpointer
eel_accessibility_get_gobject(AtkObject * object)181 eel_accessibility_get_gobject (AtkObject *object)
182 {
183 	return g_object_get_qdata (G_OBJECT (object), get_quark_gobject ());
184 }
185 
186 static void
eel_accessibility_destroy(gpointer data)187 eel_accessibility_destroy (gpointer data)
188 {
189 	g_object_set_qdata (G_OBJECT (data), get_quark_gobject (), NULL);
190 	atk_object_notify_state_change (ATK_OBJECT (data), ATK_STATE_DEFUNCT, TRUE);
191 	g_object_unref (data);
192 }
193 
194 /**
195  * eel_accessibility_set_atk_object_return:
196  * @object: a GObject
197  * @atk_object: it's AtkObject
198  *
199  * used to register and return a new accessible object for something
200  *
201  * Return value: @atk_object.
202  **/
203 AtkObject *
eel_accessibility_set_atk_object_return(gpointer object,AtkObject * atk_object)204 eel_accessibility_set_atk_object_return (gpointer   object,
205 					 AtkObject *atk_object)
206 {
207 	atk_object_initialize (atk_object, object);
208 
209 	if (!ATK_IS_GOBJECT_ACCESSIBLE (atk_object)) {
210 		g_object_set_qdata_full
211 			(object, get_quark_accessible (), atk_object, eel_accessibility_destroy);
212 		g_object_set_qdata
213 			(G_OBJECT (atk_object), get_quark_gobject (), object);
214 	}
215 
216 	return atk_object;
217 }
218 
219 static GailTextUtil *
get_simple_text(gpointer object)220 get_simple_text (gpointer object)
221 {
222 	GObject *gobject;
223 	EelAccessibleTextIface *aif;
224 
225 	if (GTK_IS_ACCESSIBLE (object)) {
226 		gobject = G_OBJECT (gtk_accessible_get_widget (GTK_ACCESSIBLE (object)));
227 	} else {
228 		gobject = eel_accessibility_get_gobject (object);
229 	}
230 
231 	if (!gobject) {
232 		return NULL;
233 	}
234 
235 	aif = EEL_ACCESSIBLE_TEXT_GET_IFACE (gobject);
236 	if (!aif) {
237 		g_warning ("No accessible text inferface on '%s'",
238 			   g_type_name_from_instance ((gpointer) gobject));
239 
240 	} else if (aif->get_text) {
241 		return aif->get_text (gobject);
242 	}
243 
244 	return NULL;
245 }
246 
247 char *
eel_accessibility_text_get_text(AtkText * text,gint start_pos,gint end_pos)248 eel_accessibility_text_get_text (AtkText *text,
249 				 gint     start_pos,
250 				 gint     end_pos)
251 {
252 	GailTextUtil *util = get_simple_text (text);
253 	g_return_val_if_fail (util != NULL, NULL);
254 
255 	return gail_text_util_get_substring (util, start_pos, end_pos);
256 }
257 
258 gunichar
eel_accessibility_text_get_character_at_offset(AtkText * text,gint offset)259 eel_accessibility_text_get_character_at_offset (AtkText *text,
260 						gint     offset)
261 {
262 	char *txt, *index;
263 	gint sucks1 = 0, sucks2 = -1;
264 	gunichar c;
265 	GailTextUtil *util = get_simple_text (text);
266 	g_return_val_if_fail (util != NULL, 0);
267 
268 	txt = gail_text_util_get_substring (util, sucks1, sucks2);
269 
270 	index = g_utf8_offset_to_pointer (txt, offset);
271 	c = g_utf8_get_char (index);
272 	g_free (txt);
273 
274 	return c;
275 }
276 
277 char *
eel_accessibility_text_get_text_before_offset(AtkText * text,gint offset,AtkTextBoundary boundary_type,gint * start_offset,gint * end_offset)278 eel_accessibility_text_get_text_before_offset (AtkText	      *text,
279 					       gint            offset,
280 					       AtkTextBoundary boundary_type,
281 					       gint           *start_offset,
282 					       gint           *end_offset)
283 {
284 	GailTextUtil *util = get_simple_text (text);
285 	g_return_val_if_fail (util != NULL, NULL);
286 
287 	return gail_text_util_get_text (
288 		util, NULL, GAIL_BEFORE_OFFSET,
289 		boundary_type, offset, start_offset, end_offset);
290 }
291 
292 char *
eel_accessibility_text_get_text_at_offset(AtkText * text,gint offset,AtkTextBoundary boundary_type,gint * start_offset,gint * end_offset)293 eel_accessibility_text_get_text_at_offset (AtkText        *text,
294 					   gint            offset,
295 					   AtkTextBoundary boundary_type,
296 					   gint           *start_offset,
297 					   gint           *end_offset)
298 {
299 	GailTextUtil *util = get_simple_text (text);
300 	g_return_val_if_fail (util != NULL, NULL);
301 
302 	return gail_text_util_get_text (
303 		util, NULL, GAIL_AT_OFFSET,
304 		boundary_type, offset, start_offset, end_offset);
305 }
306 
307 gchar*
eel_accessibility_text_get_text_after_offset(AtkText * text,gint offset,AtkTextBoundary boundary_type,gint * start_offset,gint * end_offset)308 eel_accessibility_text_get_text_after_offset  (AtkText	      *text,
309 					       gint            offset,
310 					       AtkTextBoundary boundary_type,
311 					       gint           *start_offset,
312 					       gint           *end_offset)
313 {
314 	GailTextUtil *util = get_simple_text (text);
315 	g_return_val_if_fail (util != NULL, NULL);
316 
317 	return gail_text_util_get_text (
318 		util, NULL, GAIL_AFTER_OFFSET,
319 		boundary_type, offset, start_offset, end_offset);
320 }
321 
322 gint
eel_accessibility_text_get_character_count(AtkText * text)323 eel_accessibility_text_get_character_count (AtkText *text)
324 {
325 	GailTextUtil *util = get_simple_text (text);
326 	g_return_val_if_fail (util != NULL, -1);
327 
328 	return gtk_text_buffer_get_char_count (util->buffer);
329 }
330 
331 GType
eel_accessible_text_get_type(void)332 eel_accessible_text_get_type (void)
333 {
334 	static GType type = 0;
335 
336 	if (!type) {
337 		const GTypeInfo tinfo = {
338 			sizeof (AtkTextIface),
339 			(GBaseInitFunc) NULL,
340 			(GBaseFinalizeFunc) NULL,
341 			(GClassInitFunc) NULL,
342 			(GClassFinalizeFunc) NULL
343 		};
344 
345 		type = g_type_register_static (
346 			G_TYPE_INTERFACE, "EelAccessibleText", &tinfo, 0);
347 	}
348 
349 	return type;
350 }
351