1 /* GAIL - The GNOME Accessibility Implementation Library
2 * Copyright 2001 Sun Microsystems Inc.
3 *
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.
8 *
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.
13 *
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.
18 */
19
20 #include "config.h"
21
22 #include <string.h>
23 #include <stdlib.h>
24 #include <gtk/gtk.h>
25 #include "gailcontainercell.h"
26 #include "gailcell.h"
27 #include "gailcellparent.h"
28
29 static void gail_cell_class_init (GailCellClass *klass);
30 static void gail_cell_destroyed (GtkWidget *widget,
31 GailCell *cell);
32
33 static void gail_cell_init (GailCell *cell);
34 static void gail_cell_object_finalize (GObject *cell);
35 static AtkStateSet* gail_cell_ref_state_set (AtkObject *obj);
36 static gint gail_cell_get_index_in_parent (AtkObject *obj);
37
38 /* AtkAction */
39
40 static void atk_action_interface_init
41 (AtkActionIface *iface);
42 static ActionInfo * _gail_cell_get_action_info (GailCell *cell,
43 gint index);
44 static void _gail_cell_destroy_action_info
45 (gpointer data,
46 gpointer user_data);
47
48 static gint gail_cell_action_get_n_actions
49 (AtkAction *action);
50 static const gchar *
51 gail_cell_action_get_name (AtkAction *action,
52 gint index);
53 static const gchar *
54 gail_cell_action_get_description
55 (AtkAction *action,
56 gint index);
57 static gboolean gail_cell_action_set_description
58 (AtkAction *action,
59 gint index,
60 const gchar *desc);
61 static const gchar *
62 gail_cell_action_get_keybinding
63 (AtkAction *action,
64 gint index);
65 static gboolean gail_cell_action_do_action (AtkAction *action,
66 gint index);
67 static gboolean idle_do_action (gpointer data);
68
69 static void atk_component_interface_init (AtkComponentIface *iface);
70 static void gail_cell_get_extents (AtkComponent *component,
71 gint *x,
72 gint *y,
73 gint *width,
74 gint *height,
75 AtkCoordType coord_type);
76 static gboolean gail_cell_grab_focus (AtkComponent *component);
77
G_DEFINE_TYPE_WITH_CODE(GailCell,gail_cell,ATK_TYPE_OBJECT,G_IMPLEMENT_INTERFACE (ATK_TYPE_ACTION,atk_action_interface_init)G_IMPLEMENT_INTERFACE (ATK_TYPE_COMPONENT,atk_component_interface_init))78 G_DEFINE_TYPE_WITH_CODE (GailCell, gail_cell, ATK_TYPE_OBJECT,
79 G_IMPLEMENT_INTERFACE (ATK_TYPE_ACTION, atk_action_interface_init)
80 G_IMPLEMENT_INTERFACE (ATK_TYPE_COMPONENT, atk_component_interface_init))
81
82 static void
83 gail_cell_class_init (GailCellClass *klass)
84 {
85 AtkObjectClass *class = ATK_OBJECT_CLASS (klass);
86 GObjectClass *g_object_class = G_OBJECT_CLASS (klass);
87
88 g_object_class->finalize = gail_cell_object_finalize;
89
90 class->get_index_in_parent = gail_cell_get_index_in_parent;
91 class->ref_state_set = gail_cell_ref_state_set;
92 }
93
94 void
gail_cell_initialise(GailCell * cell,GtkWidget * widget,AtkObject * parent,gint index)95 gail_cell_initialise (GailCell *cell,
96 GtkWidget *widget,
97 AtkObject *parent,
98 gint index)
99 {
100 g_return_if_fail (GAIL_IS_CELL (cell));
101 g_return_if_fail (GTK_IS_WIDGET (widget));
102
103 cell->widget = widget;
104 atk_object_set_parent (ATK_OBJECT (cell), parent);
105 cell->index = index;
106
107 g_signal_connect_object (G_OBJECT (widget),
108 "destroy",
109 G_CALLBACK (gail_cell_destroyed ),
110 cell, 0);
111 }
112
113 static void
gail_cell_destroyed(GtkWidget * widget,GailCell * cell)114 gail_cell_destroyed (GtkWidget *widget,
115 GailCell *cell)
116 {
117 /*
118 * This is the signal handler for the "destroy" signal for the
119 * GtkWidget. We set the pointer location to NULL;
120 */
121 cell->widget = NULL;
122 }
123
124 static void
gail_cell_init(GailCell * cell)125 gail_cell_init (GailCell *cell)
126 {
127 cell->state_set = atk_state_set_new ();
128 cell->widget = NULL;
129 cell->action_list = NULL;
130 cell->index = 0;
131 atk_state_set_add_state (cell->state_set, ATK_STATE_TRANSIENT);
132 atk_state_set_add_state (cell->state_set, ATK_STATE_ENABLED);
133 atk_state_set_add_state (cell->state_set, ATK_STATE_SENSITIVE);
134 atk_state_set_add_state (cell->state_set, ATK_STATE_SELECTABLE);
135 cell->refresh_index = NULL;
136 }
137
138 static void
gail_cell_object_finalize(GObject * obj)139 gail_cell_object_finalize (GObject *obj)
140 {
141 GailCell *cell = GAIL_CELL (obj);
142 AtkRelationSet *relation_set;
143 AtkRelation *relation;
144 GPtrArray *target;
145 gpointer target_object;
146 gint i;
147
148 if (cell->state_set)
149 g_object_unref (cell->state_set);
150 if (cell->action_list)
151 {
152 g_list_foreach (cell->action_list, _gail_cell_destroy_action_info, NULL);
153 g_list_free (cell->action_list);
154 }
155 if (cell->action_idle_handler)
156 {
157 g_source_remove (cell->action_idle_handler);
158 cell->action_idle_handler = 0;
159 }
160 relation_set = atk_object_ref_relation_set (ATK_OBJECT (obj));
161 if (ATK_IS_RELATION_SET (relation_set))
162 {
163 relation = atk_relation_set_get_relation_by_type (relation_set,
164 ATK_RELATION_NODE_CHILD_OF);
165 if (relation)
166 {
167 target = atk_relation_get_target (relation);
168 for (i = 0; i < target->len; i++)
169 {
170 target_object = g_ptr_array_index (target, i);
171 if (GAIL_IS_CELL (target_object))
172 {
173 g_object_unref (target_object);
174 }
175 }
176 }
177 g_object_unref (relation_set);
178 }
179 G_OBJECT_CLASS (gail_cell_parent_class)->finalize (obj);
180 }
181
182 static AtkStateSet *
gail_cell_ref_state_set(AtkObject * obj)183 gail_cell_ref_state_set (AtkObject *obj)
184 {
185 GailCell *cell = GAIL_CELL (obj);
186 g_assert (cell->state_set);
187
188 g_object_ref(cell->state_set);
189 return cell->state_set;
190 }
191
192 gboolean
gail_cell_add_state(GailCell * cell,AtkStateType state_type,gboolean emit_signal)193 gail_cell_add_state (GailCell *cell,
194 AtkStateType state_type,
195 gboolean emit_signal)
196 {
197 if (!atk_state_set_contains_state (cell->state_set, state_type))
198 {
199 gboolean rc;
200 AtkObject *parent;
201
202 rc = atk_state_set_add_state (cell->state_set, state_type);
203 /*
204 * The signal should only be generated if the value changed,
205 * not when the cell is set up. So states that are set
206 * initially should pass FALSE as the emit_signal argument.
207 */
208
209 if (emit_signal)
210 {
211 atk_object_notify_state_change (ATK_OBJECT (cell), state_type, TRUE);
212 /* If state_type is ATK_STATE_VISIBLE, additional notification */
213 if (state_type == ATK_STATE_VISIBLE)
214 g_signal_emit_by_name (cell, "visible_data_changed");
215 }
216
217 /*
218 * If the parent is a flyweight container cell, propagate the state
219 * change to it also
220 */
221
222 parent = atk_object_get_parent (ATK_OBJECT (cell));
223 if (GAIL_IS_CONTAINER_CELL (parent))
224 gail_cell_add_state (GAIL_CELL (parent), state_type, emit_signal);
225 return rc;
226 }
227 else
228 return FALSE;
229 }
230
231 gboolean
gail_cell_remove_state(GailCell * cell,AtkStateType state_type,gboolean emit_signal)232 gail_cell_remove_state (GailCell *cell,
233 AtkStateType state_type,
234 gboolean emit_signal)
235 {
236 if (atk_state_set_contains_state (cell->state_set, state_type))
237 {
238 gboolean rc;
239 AtkObject *parent;
240
241 parent = atk_object_get_parent (ATK_OBJECT (cell));
242
243 rc = atk_state_set_remove_state (cell->state_set, state_type);
244 /*
245 * The signal should only be generated if the value changed,
246 * not when the cell is set up. So states that are set
247 * initially should pass FALSE as the emit_signal argument.
248 */
249
250 if (emit_signal)
251 {
252 atk_object_notify_state_change (ATK_OBJECT (cell), state_type, FALSE);
253 /* If state_type is ATK_STATE_VISIBLE, additional notification */
254 if (state_type == ATK_STATE_VISIBLE)
255 g_signal_emit_by_name (cell, "visible_data_changed");
256 }
257
258 /*
259 * If the parent is a flyweight container cell, propagate the state
260 * change to it also
261 */
262
263 if (GAIL_IS_CONTAINER_CELL (parent))
264 gail_cell_remove_state (GAIL_CELL (parent), state_type, emit_signal);
265 return rc;
266 }
267 else
268 return FALSE;
269 }
270
271 static gint
gail_cell_get_index_in_parent(AtkObject * obj)272 gail_cell_get_index_in_parent (AtkObject *obj)
273 {
274 GailCell *cell;
275
276 g_assert (GAIL_IS_CELL (obj));
277
278 cell = GAIL_CELL (obj);
279 if (atk_state_set_contains_state (cell->state_set, ATK_STATE_STALE))
280 if (cell->refresh_index)
281 {
282 cell->refresh_index (cell);
283 atk_state_set_remove_state (cell->state_set, ATK_STATE_STALE);
284 }
285 return cell->index;
286 }
287
288 static void
atk_action_interface_init(AtkActionIface * iface)289 atk_action_interface_init (AtkActionIface *iface)
290 {
291 iface->get_n_actions = gail_cell_action_get_n_actions;
292 iface->do_action = gail_cell_action_do_action;
293 iface->get_name = gail_cell_action_get_name;
294 iface->get_description = gail_cell_action_get_description;
295 iface->set_description = gail_cell_action_set_description;
296 iface->get_keybinding = gail_cell_action_get_keybinding;
297 }
298
299 /*
300 * Deprecated: 2.22: The action interface is added for all cells now.
301 */
302 void
gail_cell_type_add_action_interface(GType type)303 gail_cell_type_add_action_interface (GType type)
304 {
305 }
306
307 gboolean
gail_cell_add_action(GailCell * cell,const gchar * action_name,const gchar * action_description,const gchar * action_keybinding,ACTION_FUNC action_func)308 gail_cell_add_action (GailCell *cell,
309 const gchar *action_name,
310 const gchar *action_description,
311 const gchar *action_keybinding,
312 ACTION_FUNC action_func)
313 {
314 ActionInfo *info;
315 g_return_val_if_fail (GAIL_IS_CELL (cell), FALSE);
316 info = g_new (ActionInfo, 1);
317
318 if (action_name != NULL)
319 info->name = g_strdup (action_name);
320 else
321 info->name = NULL;
322 if (action_description != NULL)
323 info->description = g_strdup (action_description);
324 else
325 info->description = NULL;
326 if (action_keybinding != NULL)
327 info->keybinding = g_strdup (action_keybinding);
328 else
329 info->keybinding = NULL;
330 info->do_action_func = action_func;
331
332 cell->action_list = g_list_append (cell->action_list, (gpointer) info);
333 return TRUE;
334 }
335
336 gboolean
gail_cell_remove_action(GailCell * cell,gint action_index)337 gail_cell_remove_action (GailCell *cell,
338 gint action_index)
339 {
340 GList *list_node;
341
342 g_return_val_if_fail (GAIL_IS_CELL (cell), FALSE);
343 list_node = g_list_nth (cell->action_list, action_index);
344 if (!list_node)
345 return FALSE;
346 _gail_cell_destroy_action_info (list_node->data, NULL);
347 cell->action_list = g_list_remove_link (cell->action_list, list_node);
348 return TRUE;
349 }
350
351
352 gboolean
gail_cell_remove_action_by_name(GailCell * cell,const gchar * action_name)353 gail_cell_remove_action_by_name (GailCell *cell,
354 const gchar *action_name)
355 {
356 GList *list_node;
357 gboolean action_found= FALSE;
358
359 g_return_val_if_fail (GAIL_IS_CELL (cell), FALSE);
360 for (list_node = cell->action_list; list_node && !action_found;
361 list_node = list_node->next)
362 {
363 if (!strcmp (((ActionInfo *)(list_node->data))->name, action_name))
364 {
365 action_found = TRUE;
366 break;
367 }
368 }
369 if (!action_found)
370 return FALSE;
371 _gail_cell_destroy_action_info (list_node->data, NULL);
372 cell->action_list = g_list_remove_link (cell->action_list, list_node);
373 return TRUE;
374 }
375
376 static ActionInfo *
_gail_cell_get_action_info(GailCell * cell,gint index)377 _gail_cell_get_action_info (GailCell *cell,
378 gint index)
379 {
380 GList *list_node;
381
382 g_return_val_if_fail (GAIL_IS_CELL (cell), NULL);
383 if (cell->action_list == NULL)
384 return NULL;
385 list_node = g_list_nth (cell->action_list, index);
386 if (!list_node)
387 return NULL;
388 return (ActionInfo *) (list_node->data);
389 }
390
391
392 static void
_gail_cell_destroy_action_info(gpointer action_info,gpointer user_data)393 _gail_cell_destroy_action_info (gpointer action_info,
394 gpointer user_data)
395 {
396 ActionInfo *info = (ActionInfo *)action_info;
397 g_assert (info != NULL);
398 g_free (info->name);
399 g_free (info->description);
400 g_free (info->keybinding);
401 g_free (info);
402 }
403 static gint
gail_cell_action_get_n_actions(AtkAction * action)404 gail_cell_action_get_n_actions (AtkAction *action)
405 {
406 GailCell *cell = GAIL_CELL(action);
407 if (cell->action_list != NULL)
408 return g_list_length (cell->action_list);
409 else
410 return 0;
411 }
412
413 static const gchar *
gail_cell_action_get_name(AtkAction * action,gint index)414 gail_cell_action_get_name (AtkAction *action,
415 gint index)
416 {
417 GailCell *cell = GAIL_CELL(action);
418 ActionInfo *info = _gail_cell_get_action_info (cell, index);
419
420 if (info == NULL)
421 return NULL;
422 return info->name;
423 }
424
425 static const gchar *
gail_cell_action_get_description(AtkAction * action,gint index)426 gail_cell_action_get_description (AtkAction *action,
427 gint index)
428 {
429 GailCell *cell = GAIL_CELL(action);
430 ActionInfo *info = _gail_cell_get_action_info (cell, index);
431
432 if (info == NULL)
433 return NULL;
434 return info->description;
435 }
436
437 static gboolean
gail_cell_action_set_description(AtkAction * action,gint index,const gchar * desc)438 gail_cell_action_set_description (AtkAction *action,
439 gint index,
440 const gchar *desc)
441 {
442 GailCell *cell = GAIL_CELL(action);
443 ActionInfo *info = _gail_cell_get_action_info (cell, index);
444
445 if (info == NULL)
446 return FALSE;
447 g_free (info->description);
448 info->description = g_strdup (desc);
449 return TRUE;
450 }
451
452 static const gchar *
gail_cell_action_get_keybinding(AtkAction * action,gint index)453 gail_cell_action_get_keybinding (AtkAction *action,
454 gint index)
455 {
456 GailCell *cell = GAIL_CELL(action);
457 ActionInfo *info = _gail_cell_get_action_info (cell, index);
458 if (info == NULL)
459 return NULL;
460 return info->keybinding;
461 }
462
463 static gboolean
gail_cell_action_do_action(AtkAction * action,gint index)464 gail_cell_action_do_action (AtkAction *action,
465 gint index)
466 {
467 GailCell *cell = GAIL_CELL(action);
468 ActionInfo *info = _gail_cell_get_action_info (cell, index);
469 if (info == NULL)
470 return FALSE;
471 if (info->do_action_func == NULL)
472 return FALSE;
473 if (cell->action_idle_handler)
474 return FALSE;
475 cell->action_func = info->do_action_func;
476 cell->action_idle_handler = gdk_threads_add_idle (idle_do_action, cell);
477 return TRUE;
478 }
479
480 static gboolean
idle_do_action(gpointer data)481 idle_do_action (gpointer data)
482 {
483 GailCell *cell;
484
485 cell = GAIL_CELL (data);
486 cell->action_idle_handler = 0;
487 cell->action_func (cell);
488
489 return FALSE;
490 }
491
492 static void
atk_component_interface_init(AtkComponentIface * iface)493 atk_component_interface_init (AtkComponentIface *iface)
494 {
495 iface->get_extents = gail_cell_get_extents;
496 iface->grab_focus = gail_cell_grab_focus;
497 }
498
499 static void
gail_cell_get_extents(AtkComponent * component,gint * x,gint * y,gint * width,gint * height,AtkCoordType coord_type)500 gail_cell_get_extents (AtkComponent *component,
501 gint *x,
502 gint *y,
503 gint *width,
504 gint *height,
505 AtkCoordType coord_type)
506 {
507 GailCell *gailcell;
508 AtkObject *cell_parent;
509
510 g_assert (GAIL_IS_CELL (component));
511
512 gailcell = GAIL_CELL (component);
513
514 cell_parent = gtk_widget_get_accessible (gailcell->widget);
515
516 gail_cell_parent_get_cell_extents (GAIL_CELL_PARENT (cell_parent),
517 gailcell, x, y, width, height, coord_type);
518 }
519
520 static gboolean
gail_cell_grab_focus(AtkComponent * component)521 gail_cell_grab_focus (AtkComponent *component)
522 {
523 GailCell *gailcell;
524 AtkObject *cell_parent;
525
526 g_assert (GAIL_IS_CELL (component));
527
528 gailcell = GAIL_CELL (component);
529
530 cell_parent = gtk_widget_get_accessible (gailcell->widget);
531
532 return gail_cell_parent_grab_focus (GAIL_CELL_PARENT (cell_parent),
533 gailcell);
534 }
535