1 /*  This file is part of the GtkHTML library.
2  *
3  *  Copyright 2002 Ximian, Inc.
4  *
5  *  Author: Radek Doulik
6  *
7  *
8  *  This library is free software; you can redistribute it and/or
9  *  modify it under the terms of the GNU Library General Public
10  *  License as published by the Free Software Foundation; either
11  *  version 2 of the License, or (at your option) any later version.
12  *
13  *  This library is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  *  Library General Public License for more details.
17  *
18  *  You should have received a copy of the GNU Library General Public License
19  *  along with this library; see the file COPYING.LIB.  If not, write to
20  *  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21  *  Boston, MA 02110-1301, USA.
22  */
23 
24 #include <config.h>
25 
26 #include "gtkhtml.h"
27 #include "htmlengine.h"
28 #include "htmlobject.h"
29 
30 #include "object.h"
31 #include "paragraph.h"
32 #include "utils.h"
33 #include "text.h"
34 #include <glib/gi18n-lib.h>
35 
36 static void gtk_html_a11y_class_init (GtkHTMLA11YClass *klass);
37 static void gtk_html_a11y_init       (GtkHTMLA11Y *a11y);
38 static void gtk_html_a11y_initialize (AtkObject *obj,
39                                       gpointer data);
40 
41 static GtkContainerAccessibleClass *parent_class = NULL;
42 
43 static gint
get_n_actions(AtkAction * action)44 get_n_actions (AtkAction *action)
45 {
46 	return 1;
47 }
48 
49 static const gchar *
get_description(AtkAction * action,gint i)50 get_description (AtkAction *action,
51                  gint i)
52 {
53 	if (i == 0)
54 		return _("grab focus");
55 
56 	return NULL;
57 }
58 
59 static const gchar *
action_get_name(AtkAction * action,gint i)60 action_get_name (AtkAction *action,
61                  gint i)
62 {
63 	if (i == 0)
64 		return _("grab focus");
65 
66 	return NULL;
67 }
68 
69 static gboolean
do_action(AtkAction * action,gint i)70 do_action (AtkAction *action,
71            gint i)
72 {
73 	GtkWidget *widget;
74 	gboolean return_value = TRUE;
75 
76 	widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (action));
77 
78 	if (widget == NULL) {
79 	/*
80 	* State is defunct
81 	*/
82 	return FALSE;
83 	}
84 
85 	if (!gtk_widget_get_sensitive (widget) || !gtk_widget_get_visible (widget))
86 		return FALSE;
87 
88 	switch (i) {
89 	case 0:
90 		gtk_widget_grab_focus (widget);
91 	default:
92 		return_value = FALSE;
93 		break;
94 	}
95 	return return_value;
96 }
97 
98 static void
atk_action_interface_init(AtkActionIface * iface)99 atk_action_interface_init (AtkActionIface *iface)
100 {
101 	g_return_if_fail (iface != NULL);
102 
103 	iface->do_action = do_action;
104 	iface->get_n_actions = get_n_actions;
105 	iface->get_description = get_description;
106 	iface->get_name = action_get_name;
107 }
108 
109 GType
gtk_html_a11y_get_type(void)110 gtk_html_a11y_get_type (void)
111 {
112 	static GType type = 0;
113 
114 	if (!type) {
115 		static GTypeInfo tinfo = {
116 			sizeof (GtkHTMLA11YClass),
117 			NULL,                                            /* base init */
118 			NULL,                                            /* base finalize */
119 			(GClassInitFunc) gtk_html_a11y_class_init,       /* class init */
120 			NULL,                                            /* class finalize */
121 			NULL,                                            /* class data */
122 			sizeof (GtkHTMLA11Y),                            /* instance size */
123 			0,                                               /* nb preallocs */
124 			(GInstanceInitFunc) gtk_html_a11y_init,          /* instance init */
125 			NULL                                             /* value table */
126 		};
127 
128 		static const GInterfaceInfo atk_action_info = {
129 			(GInterfaceInitFunc) atk_action_interface_init,
130 			(GInterfaceFinalizeFunc) NULL,
131 			NULL
132 		};
133 
134 		type = g_type_register_static (GTK_TYPE_CONTAINER_ACCESSIBLE, "GtkHTMLA11Y", &tinfo, 0);
135 
136 		g_type_add_interface_static (type, ATK_TYPE_ACTION, &atk_action_info);
137 	}
138 
139 	return type;
140 }
141 
142 static void
gtk_html_a11y_finalize(GObject * obj)143 gtk_html_a11y_finalize (GObject *obj)
144 {
145 	G_OBJECT_CLASS (parent_class)->finalize (obj);
146 }
147 
148 static gint
gtk_html_a11y_get_n_children(AtkObject * accessible)149 gtk_html_a11y_get_n_children (AtkObject *accessible)
150 {
151 	HTMLObject *clue;
152 	gint n_children = 0;
153 	AtkStateSet *ss;
154 
155 	if (GTK_HTML_A11Y_GTKHTML (accessible)->engine->parsing)
156 		return 0;
157 
158 	ss = atk_object_ref_state_set (accessible);
159 	if (atk_state_set_contains_state (ss, ATK_STATE_DEFUNCT)) {
160 		g_object_unref (ss);
161 		return 0;
162 	}
163 	g_object_unref (ss);
164 
165 	clue = GTK_HTML_A11Y_GTKHTML (accessible)->engine->clue;
166 	if (clue) {
167 		AtkObject *atk_clue = html_utils_get_accessible (clue, NULL);
168 		if (atk_clue) {
169 			AtkStateSet *ss_clue = atk_object_ref_state_set (atk_clue);
170 			if (atk_state_set_contains_state (ss_clue, ATK_STATE_DEFUNCT)) {
171 				g_object_unref (ss_clue);
172 				return 0;
173 			}
174 			g_object_unref (ss_clue);
175 		}
176 
177 		n_children = html_object_get_n_children (GTK_HTML_A11Y_GTKHTML (accessible)->engine->clue);
178 	}
179 
180 	/* printf ("gtk_html_a11y_get_n_children resolves to %d\n", n_children); */
181 
182 	return n_children;
183 }
184 
185 static AtkObject *
gtk_html_a11y_ref_child(AtkObject * accessible,gint index)186 gtk_html_a11y_ref_child (AtkObject *accessible,
187                          gint index)
188 {
189 	HTMLObject *child;
190 	AtkObject *accessible_child = NULL;
191 	AtkStateSet *ss;
192 
193 	if (GTK_HTML_A11Y_GTKHTML (accessible)->engine->parsing)
194 		return NULL;
195 
196 	ss = atk_object_ref_state_set (accessible);
197 	if (atk_state_set_contains_state (ss, ATK_STATE_DEFUNCT)) {
198 		g_object_unref (ss);
199 		return NULL;
200 	}
201 	g_object_unref (ss);
202 
203 	if (GTK_HTML_A11Y_GTKHTML (accessible)->engine->clue) {
204 		AtkObject *atk_clue = html_utils_get_accessible (GTK_HTML_A11Y_GTKHTML (accessible)->engine->clue, NULL);
205 		if (atk_clue) {
206 			AtkStateSet *ss_clue = atk_object_ref_state_set (atk_clue);
207 			if (atk_state_set_contains_state (ss_clue, ATK_STATE_DEFUNCT)) {
208 				g_object_unref (ss_clue);
209 				return NULL;
210 			}
211 			g_object_unref (ss_clue);
212 		}
213 
214 		child = html_object_get_child (GTK_HTML_A11Y_GTKHTML (accessible)->engine->clue, index);
215 		if (child) {
216 			accessible_child = html_utils_get_accessible (child, accessible);
217 			if (accessible_child)
218 				g_object_ref (accessible_child);
219 		}
220 	}
221 
222 	/* printf ("gtk_html_a11y_ref_child %d resolves to %p\n", index, accessible_child); */
223 
224 	return accessible_child;
225 }
226 
227 static const gchar *
gtk_html_a11y_get_name(AtkObject * obj)228 gtk_html_a11y_get_name (AtkObject *obj)
229 {
230 	if (obj->name != NULL) {
231 		return obj->name;
232 	}
233 
234 	return _("Panel containing HTML");
235 }
236 
237 static void
gtk_html_a11y_class_init(GtkHTMLA11YClass * klass)238 gtk_html_a11y_class_init (GtkHTMLA11YClass *klass)
239 {
240 	GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
241 	AtkObjectClass *atk_class = ATK_OBJECT_CLASS (klass);
242 
243 	parent_class = g_type_class_peek_parent (klass);
244 
245 	atk_class->initialize = gtk_html_a11y_initialize;
246 	atk_class->get_n_children = gtk_html_a11y_get_n_children;
247 	atk_class->ref_child = gtk_html_a11y_ref_child;
248 	atk_class->get_name = gtk_html_a11y_get_name;
249 
250 	gobject_class->finalize = gtk_html_a11y_finalize;
251 }
252 
253 static void
gtk_html_a11y_init(GtkHTMLA11Y * a11y)254 gtk_html_a11y_init (GtkHTMLA11Y *a11y)
255 {
256 }
257 
258 static AtkObject *
gtk_html_a11y_get_focus_object(GtkWidget * widget)259 gtk_html_a11y_get_focus_object (GtkWidget *widget)
260 {
261 	GtkHTML * html;
262 	HTMLObject * htmlobj = NULL;
263 	AtkObject *obj = NULL;
264 	gint offset;
265 
266 	html = GTK_HTML (widget);
267 
268 	g_return_val_if_fail (html && html->engine, NULL);
269 
270 	if (!html->engine->caret_mode && !gtk_html_get_editable (html))
271 		htmlobj = html_engine_get_focus_object (html->engine, &offset);
272 	else if (html->engine && html->engine->cursor)
273 		htmlobj = html->engine->cursor->object;
274 
275 	if (htmlobj)
276 		obj = html_utils_get_accessible (htmlobj, NULL);
277 
278 	return obj;
279 }
280 
281 static AtkObject * gtk_html_a11y_focus_object = NULL;
282 
283 static void
gtk_html_a11y_grab_focus_cb(GtkWidget * widget)284 gtk_html_a11y_grab_focus_cb (GtkWidget *widget)
285 {
286 	AtkObject *focus_object, *obj, *clue;
287 
288 	focus_object = gtk_html_a11y_get_focus_object (widget);
289 	if (focus_object == NULL)
290 		return;
291 
292 	obj = gtk_widget_get_accessible (widget);
293 
294 	clue = html_utils_get_accessible (GTK_HTML (widget)->engine->clue, obj);
295 	atk_object_set_parent (clue, obj);
296 
297 	gtk_html_a11y_focus_object = focus_object;
298 	atk_focus_tracker_notify (focus_object);
299 }
300 
301 static void
gtk_html_a11y_cursor_changed_cb(GtkWidget * widget)302 gtk_html_a11y_cursor_changed_cb (GtkWidget *widget)
303 {
304 	AtkObject *focus_object;
305 
306 	focus_object = gtk_html_a11y_get_focus_object (widget);
307 	g_return_if_fail (focus_object != NULL);
308 
309 	if (gtk_html_a11y_focus_object != focus_object) {
310 		gtk_html_a11y_focus_object = focus_object;
311 		atk_focus_tracker_notify (focus_object);
312 	} else {
313 		if (G_IS_HTML_A11Y_TEXT (focus_object)) {
314 			gint offset;
315 
316 			offset = (GTK_HTML (widget))->engine->cursor->offset;
317 			g_signal_emit_by_name (focus_object, "text_caret_moved",offset);
318 		}
319 	}
320 }
321 
322 static void
gtk_html_a11y_insert_object_cb(GtkWidget * widget,gint pos,gint len)323 gtk_html_a11y_insert_object_cb (GtkWidget *widget,
324                                 gint pos,
325                                 gint len)
326 {
327 	AtkObject * a11y;
328 
329 	HTMLCursor *cursor = GTK_HTML (widget)->engine->cursor;
330 
331 	a11y = gtk_html_a11y_get_focus_object (widget);
332 	g_return_if_fail (a11y != NULL);
333 
334 	if (gtk_html_a11y_focus_object != a11y) {
335 		gtk_html_a11y_focus_object = a11y;
336 		atk_focus_tracker_notify (a11y);
337 	}
338 
339 	if (G_IS_HTML_A11Y_TEXT (a11y)) {
340 		g_signal_emit_by_name (a11y, "text_changed::insert", cursor->offset - len, len);
341 
342 	}
343 }
344 
345 static void
gtk_html_a11y_delete_object_cb(GtkWidget * widget,gint pos,gint len)346 gtk_html_a11y_delete_object_cb (GtkWidget *widget,
347                                 gint pos,
348                                 gint len)
349 {
350 	AtkObject * a11y;
351 
352 	a11y = gtk_html_a11y_get_focus_object (widget);
353 	g_return_if_fail (a11y != NULL);
354 
355 	if (gtk_html_a11y_focus_object != a11y) {
356 		gtk_html_a11y_focus_object = a11y;
357 		atk_focus_tracker_notify (a11y);
358 	}
359 
360 	if (G_IS_HTML_A11Y_TEXT (a11y)) {
361 		g_signal_emit_by_name (a11y, "text_changed::delete", pos, len);
362 	}
363 }
364 
365 static void
gtk_html_a11y_initialize(AtkObject * obj,gpointer data)366 gtk_html_a11y_initialize (AtkObject *obj,
367                           gpointer data)
368 {
369 	GtkWidget *widget;
370 	GtkHTML *html;
371 	AtkObject *accessible;
372 	AtkObject *focus_object = NULL;
373 
374 	/* printf ("gtk_html_a11y_initialize\n"); */
375 
376 	if (ATK_OBJECT_CLASS (parent_class)->initialize)
377 		ATK_OBJECT_CLASS (parent_class)->initialize (obj, data);
378 
379 	g_object_set_data (G_OBJECT (obj), GTK_HTML_ID, data);
380 
381 	obj->role = ATK_ROLE_PANEL;
382 
383 	widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (obj));
384 	accessible = ATK_OBJECT (obj);
385 
386 	g_signal_connect (widget, "grab_focus",
387 			G_CALLBACK (gtk_html_a11y_grab_focus_cb),
388 			NULL);
389 	g_signal_connect (widget, "cursor_changed",
390 			G_CALLBACK (gtk_html_a11y_cursor_changed_cb),
391 			NULL);
392 	g_signal_connect_after (widget, "object_inserted",
393 			G_CALLBACK (gtk_html_a11y_insert_object_cb),
394 			NULL);
395 	g_signal_connect_after (widget, "object_delete",
396 			G_CALLBACK (gtk_html_a11y_delete_object_cb),
397 			NULL);
398 
399 	html = GTK_HTML (widget);
400 
401 	if (html->engine != NULL && html->engine->clue != NULL) {
402 		html_utils_get_accessible (html->engine->clue, accessible);
403 		focus_object = gtk_html_a11y_get_focus_object (widget);
404 	}
405 
406 	if (focus_object && gtk_html_a11y_focus_object != focus_object) {
407 		gtk_html_a11y_focus_object = focus_object;
408 		atk_focus_tracker_notify (focus_object);
409 	}
410 }
411