1 /*
2 * This program is free software; you can redistribute it and/or modify it
3 * under the terms of the GNU Lesser General Public License as published by
4 * the Free Software Foundation.
5 *
6 * This program is distributed in the hope that it will be useful, but
7 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
8 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
9 * for more details.
10 *
11 * You should have received a copy of the GNU Lesser General Public License
12 * along with this program; if not, see <http://www.gnu.org/licenses/>.
13 *
14 *
15 * Authors:
16 * Christopher James Lahey <clahey@ximian.com>
17 *
18 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
19 *
20 */
21
22 #include "evolution-config.h"
23
24 #include "gal-a11y-e-cell.h"
25
26 #include <string.h>
27
28 #include <gtk/gtk.h>
29 #include <glib/gi18n.h>
30
31 #include "e-table.h"
32 #include "e-tree.h"
33 #include "gal-a11y-e-cell-vbox.h"
34 #include "gal-a11y-e-table-item.h"
35 #include "gal-a11y-util.h"
36
37 static GObjectClass *parent_class;
38 #define PARENT_TYPE (atk_object_get_type ())
39
40 static void _gal_a11y_e_cell_destroy_action_info (gpointer action_info, gpointer user_data);
41
42 #if 0
43 static void
44 unref_item (gpointer user_data,
45 GObject *obj_loc)
46 {
47 GalA11yECell *a11y = GAL_A11Y_E_CELL (user_data);
48 a11y->item = NULL;
49 g_object_unref (a11y);
50 }
51
52 static void
53 unref_cell (gpointer user_data,
54 GObject *obj_loc)
55 {
56 GalA11yECell *a11y = GAL_A11Y_E_CELL (user_data);
57 a11y->cell_view = NULL;
58 g_object_unref (a11y);
59 }
60 #endif
61
62 static gboolean
is_valid(AtkObject * cell)63 is_valid (AtkObject *cell)
64 {
65 GalA11yECell *a11y = GAL_A11Y_E_CELL (cell);
66 GalA11yETableItem *a11yItem = GAL_A11Y_E_TABLE_ITEM (a11y->parent);
67 AtkStateSet *item_ss;
68 gboolean ret = TRUE;
69
70 item_ss = atk_object_ref_state_set (ATK_OBJECT (a11yItem));
71 if (atk_state_set_contains_state (item_ss, ATK_STATE_DEFUNCT))
72 ret = FALSE;
73
74 g_object_unref (item_ss);
75
76 if (ret && atk_state_set_contains_state (a11y->state_set, ATK_STATE_DEFUNCT))
77 ret = FALSE;
78
79 return ret;
80 }
81
82 static void
gal_a11y_e_cell_dispose(GObject * object)83 gal_a11y_e_cell_dispose (GObject *object)
84 {
85 GalA11yECell *a11y = GAL_A11Y_E_CELL (object);
86
87 #if 0
88 if (a11y->item)
89 g_object_unref (a11y->item); /*, unref_item, a11y); */
90 if (a11y->cell_view)
91 g_object_unref (a11y->cell_view); /*, unref_cell, a11y); */
92 if (a11y->parent)
93 g_object_unref (a11y->parent);
94 #endif
95
96 g_clear_object (&a11y->state_set);
97
98 if (a11y->action_list) {
99 g_list_foreach (a11y->action_list, _gal_a11y_e_cell_destroy_action_info, NULL);
100 g_list_free (a11y->action_list);
101 a11y->action_list = NULL;
102 }
103
104 if (parent_class->dispose)
105 parent_class->dispose (object);
106
107 }
108
109 /* Static functions */
110 static const gchar *
gal_a11y_e_cell_get_name(AtkObject * a11y)111 gal_a11y_e_cell_get_name (AtkObject *a11y)
112 {
113 GalA11yECell *cell = GAL_A11Y_E_CELL (a11y);
114 ETableCol *ecol;
115
116 if (a11y->name != NULL && strcmp (a11y->name, ""))
117 return a11y->name;
118
119 if (cell->item != NULL) {
120 ecol = e_table_header_get_column (cell->item->header, cell->view_col);
121 if (ecol != NULL)
122 return ecol->text;
123 }
124
125 return _("Table Cell");
126 }
127
128 static AtkStateSet *
gal_a11y_e_cell_ref_state_set(AtkObject * accessible)129 gal_a11y_e_cell_ref_state_set (AtkObject *accessible)
130 {
131 GalA11yECell *cell = GAL_A11Y_E_CELL (accessible);
132
133 g_return_val_if_fail (cell->state_set, NULL);
134
135 g_object_ref (cell->state_set);
136
137 return cell->state_set;
138 }
139
140 static AtkObject *
gal_a11y_e_cell_get_parent(AtkObject * accessible)141 gal_a11y_e_cell_get_parent (AtkObject *accessible)
142 {
143 GalA11yECell *a11y = GAL_A11Y_E_CELL (accessible);
144 return a11y->parent;
145 }
146
147 static gint
gal_a11y_e_cell_get_index_in_parent(AtkObject * accessible)148 gal_a11y_e_cell_get_index_in_parent (AtkObject *accessible)
149 {
150 GalA11yECell *a11y = GAL_A11Y_E_CELL (accessible);
151
152 if (!is_valid (accessible))
153 return -1;
154
155 return (a11y->row + 1) * a11y->item->cols + a11y->view_col;
156 }
157
158 /* Component IFace */
159 static void
gal_a11y_e_cell_get_extents(AtkComponent * component,gint * x,gint * y,gint * width,gint * height,AtkCoordType coord_type)160 gal_a11y_e_cell_get_extents (AtkComponent *component,
161 gint *x,
162 gint *y,
163 gint *width,
164 gint *height,
165 AtkCoordType coord_type)
166 {
167 GalA11yECell *a11y = GAL_A11Y_E_CELL (component);
168 GtkWidget *tableOrTree;
169 gint row;
170 gint col;
171 gint xval;
172 gint yval;
173
174 row = a11y->row;
175 col = a11y->view_col;
176
177 tableOrTree = gtk_widget_get_parent (GTK_WIDGET (a11y->item->parent.canvas));
178 if (E_IS_TREE (tableOrTree)) {
179 e_tree_get_cell_geometry (
180 E_TREE (tableOrTree),
181 row, col, &xval, &yval,
182 width, height);
183 } else {
184 e_table_get_cell_geometry (
185 E_TABLE (tableOrTree),
186 row, col, &xval, &yval,
187 width, height);
188 }
189
190 atk_component_get_extents (
191 ATK_COMPONENT (a11y->parent),
192 x, y, NULL, NULL, coord_type);
193 if (x && *x != G_MININT)
194 *x += xval;
195 if (y && *y != G_MININT)
196 *y += yval;
197 }
198
199 static gboolean
gal_a11y_e_cell_grab_focus(AtkComponent * component)200 gal_a11y_e_cell_grab_focus (AtkComponent *component)
201 {
202 GalA11yECell *a11y;
203 gint index;
204 GtkWidget *toplevel;
205 GalA11yETableItem *a11yTableItem;
206
207 a11y = GAL_A11Y_E_CELL (component);
208
209 /* for e_cell_vbox's children, we just grab the e_cell_vbox */
210 if (GAL_A11Y_IS_E_CELL_VBOX (a11y->parent)) {
211 return atk_component_grab_focus (ATK_COMPONENT (a11y->parent));
212 }
213
214 a11yTableItem = GAL_A11Y_E_TABLE_ITEM (a11y->parent);
215 index = atk_object_get_index_in_parent (ATK_OBJECT (a11y));
216
217 atk_selection_clear_selection (ATK_SELECTION (a11yTableItem));
218 atk_selection_add_selection (ATK_SELECTION (a11yTableItem), index);
219
220 gtk_widget_grab_focus (
221 GTK_WIDGET (GNOME_CANVAS_ITEM (a11y->item)->canvas));
222 toplevel = gtk_widget_get_toplevel (
223 GTK_WIDGET (GNOME_CANVAS_ITEM (a11y->item)->canvas));
224 if (toplevel && gtk_widget_is_toplevel (toplevel))
225 gtk_window_present (GTK_WINDOW (toplevel));
226
227 return TRUE;
228 }
229
230 /* Table IFace */
231
232 static void
gal_a11y_e_cell_atk_component_iface_init(AtkComponentIface * iface)233 gal_a11y_e_cell_atk_component_iface_init (AtkComponentIface *iface)
234 {
235 iface->get_extents = gal_a11y_e_cell_get_extents;
236 iface->grab_focus = gal_a11y_e_cell_grab_focus;
237 }
238
239 static void
gal_a11y_e_cell_class_init(GalA11yECellClass * class)240 gal_a11y_e_cell_class_init (GalA11yECellClass *class)
241 {
242 AtkObjectClass *atk_object_class = ATK_OBJECT_CLASS (class);
243 GObjectClass *object_class = G_OBJECT_CLASS (class);
244
245 parent_class = g_type_class_ref (PARENT_TYPE);
246
247 object_class->dispose = gal_a11y_e_cell_dispose;
248
249 atk_object_class->get_parent = gal_a11y_e_cell_get_parent;
250 atk_object_class->get_index_in_parent = gal_a11y_e_cell_get_index_in_parent;
251 atk_object_class->ref_state_set = gal_a11y_e_cell_ref_state_set;
252 atk_object_class->get_name = gal_a11y_e_cell_get_name;
253 }
254
255 static void
gal_a11y_e_cell_init(GalA11yECell * a11y)256 gal_a11y_e_cell_init (GalA11yECell *a11y)
257 {
258 a11y->item = NULL;
259 a11y->cell_view = NULL;
260 a11y->parent = NULL;
261 a11y->model_col = -1;
262 a11y->view_col = -1;
263 a11y->row = -1;
264
265 a11y->state_set = atk_state_set_new ();
266 atk_state_set_add_state (a11y->state_set, ATK_STATE_TRANSIENT);
267 atk_state_set_add_state (a11y->state_set, ATK_STATE_ENABLED);
268 atk_state_set_add_state (a11y->state_set, ATK_STATE_SENSITIVE);
269 atk_state_set_add_state (a11y->state_set, ATK_STATE_SELECTABLE);
270 atk_state_set_add_state (a11y->state_set, ATK_STATE_SHOWING);
271 atk_state_set_add_state (a11y->state_set, ATK_STATE_FOCUSABLE);
272 atk_state_set_add_state (a11y->state_set, ATK_STATE_VISIBLE);
273 }
274
275 static ActionInfo *
_gal_a11y_e_cell_get_action_info(GalA11yECell * cell,gint index)276 _gal_a11y_e_cell_get_action_info (GalA11yECell *cell,
277 gint index)
278 {
279 GList *list_node;
280
281 g_return_val_if_fail (GAL_A11Y_IS_E_CELL (cell), NULL);
282 if (cell->action_list == NULL)
283 return NULL;
284 list_node = g_list_nth (cell->action_list, index);
285 if (!list_node)
286 return NULL;
287 return (ActionInfo *) (list_node->data);
288 }
289
290 static void
_gal_a11y_e_cell_destroy_action_info(gpointer action_info,gpointer user_data)291 _gal_a11y_e_cell_destroy_action_info (gpointer action_info,
292 gpointer user_data)
293 {
294 ActionInfo *info = (ActionInfo *) action_info;
295
296 g_return_if_fail (info != NULL);
297 g_free (info->name);
298 g_free (info->description);
299 g_free (info->keybinding);
300 g_free (info);
301 }
302
303 gboolean
gal_a11y_e_cell_add_action(GalA11yECell * cell,const gchar * action_name,const gchar * action_description,const gchar * action_keybinding,ACTION_FUNC action_func)304 gal_a11y_e_cell_add_action (GalA11yECell *cell,
305 const gchar *action_name,
306 const gchar *action_description,
307 const gchar *action_keybinding,
308 ACTION_FUNC action_func)
309 {
310 ActionInfo *info;
311 g_return_val_if_fail (GAL_A11Y_IS_E_CELL (cell), FALSE);
312 info = g_new (ActionInfo, 1);
313
314 if (action_name != NULL)
315 info->name = g_strdup (action_name);
316 else
317 info->name = NULL;
318
319 if (action_description != NULL)
320 info->description = g_strdup (action_description);
321 else
322 info->description = NULL;
323 if (action_keybinding != NULL)
324 info->keybinding = g_strdup (action_keybinding);
325 else
326 info->keybinding = NULL;
327 info->do_action_func = action_func;
328
329 cell->action_list = g_list_append (cell->action_list, (gpointer) info);
330 return TRUE;
331 }
332
333 gboolean
gal_a11y_e_cell_remove_action(GalA11yECell * cell,gint action_index)334 gal_a11y_e_cell_remove_action (GalA11yECell *cell,
335 gint action_index)
336 {
337 GList *list_node;
338 gpointer data;
339
340 g_return_val_if_fail (GAL_A11Y_IS_E_CELL (cell), FALSE);
341 list_node = g_list_nth (cell->action_list, action_index);
342 if (!list_node)
343 return FALSE;
344
345 data = list_node->data;
346 g_return_val_if_fail (data != NULL, FALSE);
347
348 cell->action_list = g_list_remove (cell->action_list, data);
349 _gal_a11y_e_cell_destroy_action_info (data, NULL);
350
351 return TRUE;
352 }
353
354 gboolean
gal_a11y_e_cell_remove_action_by_name(GalA11yECell * cell,const gchar * action_name)355 gal_a11y_e_cell_remove_action_by_name (GalA11yECell *cell,
356 const gchar *action_name)
357 {
358 GList *list_node;
359 gpointer data;
360
361 g_return_val_if_fail (GAL_A11Y_IS_E_CELL (cell), FALSE);
362
363 for (list_node = cell->action_list; list_node; list_node = list_node->next) {
364 if (!g_ascii_strcasecmp (((ActionInfo *)(list_node->data))->name, action_name)) {
365 break;
366 }
367 }
368
369 if (!list_node) {
370 g_warn_if_reached ();
371 return FALSE;
372 }
373
374 data = list_node->data;
375 if (!data) {
376 g_warn_if_reached ();
377 return FALSE;
378 }
379
380 cell->action_list = g_list_remove (cell->action_list, data);
381 _gal_a11y_e_cell_destroy_action_info (data, NULL);
382
383 return TRUE;
384 }
385
386 static gint
gal_a11y_e_cell_action_get_n_actions(AtkAction * action)387 gal_a11y_e_cell_action_get_n_actions (AtkAction *action)
388 {
389 GalA11yECell *cell = GAL_A11Y_E_CELL (action);
390 if (cell->action_list != NULL)
391 return g_list_length (cell->action_list);
392 else
393 return 0;
394 }
395
396 static const gchar *
gal_a11y_e_cell_action_get_name(AtkAction * action,gint index)397 gal_a11y_e_cell_action_get_name (AtkAction *action,
398 gint index)
399 {
400 GalA11yECell *cell = GAL_A11Y_E_CELL (action);
401 ActionInfo *info = _gal_a11y_e_cell_get_action_info (cell, index);
402
403 if (info == NULL)
404 return NULL;
405 return info->name;
406 }
407
408 static const gchar *
gal_a11y_e_cell_action_get_description(AtkAction * action,gint index)409 gal_a11y_e_cell_action_get_description (AtkAction *action,
410 gint index)
411 {
412 GalA11yECell *cell = GAL_A11Y_E_CELL (action);
413 ActionInfo *info = _gal_a11y_e_cell_get_action_info (cell, index);
414
415 if (info == NULL)
416 return NULL;
417 return info->description;
418 }
419
420 static gboolean
gal_a11y_e_cell_action_set_description(AtkAction * action,gint index,const gchar * desc)421 gal_a11y_e_cell_action_set_description (AtkAction *action,
422 gint index,
423 const gchar *desc)
424 {
425 GalA11yECell *cell = GAL_A11Y_E_CELL (action);
426 ActionInfo *info = _gal_a11y_e_cell_get_action_info (cell, index);
427
428 if (info == NULL)
429 return FALSE;
430 g_free (info->description);
431 info->description = g_strdup (desc);
432 return TRUE;
433 }
434
435 static const gchar *
gal_a11y_e_cell_action_get_keybinding(AtkAction * action,gint index)436 gal_a11y_e_cell_action_get_keybinding (AtkAction *action,
437 gint index)
438 {
439 GalA11yECell *cell = GAL_A11Y_E_CELL (action);
440 ActionInfo *info = _gal_a11y_e_cell_get_action_info (cell, index);
441 if (info == NULL)
442 return NULL;
443
444 return info->keybinding;
445 }
446
447 static gboolean
idle_do_action(gpointer data)448 idle_do_action (gpointer data)
449 {
450 GalA11yECell *cell;
451
452 cell = GAL_A11Y_E_CELL (data);
453
454 if (!is_valid (ATK_OBJECT (cell)))
455 return FALSE;
456
457 cell->action_idle_handler = 0;
458 cell->action_func (cell);
459 g_object_unref (cell);
460
461 return FALSE;
462 }
463
464 static gboolean
gal_a11y_e_cell_action_do_action(AtkAction * action,gint index)465 gal_a11y_e_cell_action_do_action (AtkAction *action,
466 gint index)
467 {
468 GalA11yECell *cell = GAL_A11Y_E_CELL (action);
469 ActionInfo *info = _gal_a11y_e_cell_get_action_info (cell, index);
470
471 if (!is_valid (ATK_OBJECT (action)))
472 return FALSE;
473
474 if (info == NULL)
475 return FALSE;
476 g_return_val_if_fail (info->do_action_func, FALSE);
477 if (cell->action_idle_handler)
478 return FALSE;
479 cell->action_func = info->do_action_func;
480 g_object_ref (cell);
481 cell->action_idle_handler = g_idle_add (idle_do_action, cell);
482
483 return TRUE;
484 }
485
486 static void
gal_a11y_e_cell_atk_action_interface_init(AtkActionIface * iface)487 gal_a11y_e_cell_atk_action_interface_init (AtkActionIface *iface)
488 {
489 g_return_if_fail (iface != NULL);
490
491 iface->get_n_actions = gal_a11y_e_cell_action_get_n_actions;
492 iface->do_action = gal_a11y_e_cell_action_do_action;
493 iface->get_name = gal_a11y_e_cell_action_get_name;
494 iface->get_description = gal_a11y_e_cell_action_get_description;
495 iface->set_description = gal_a11y_e_cell_action_set_description;
496 iface->get_keybinding = gal_a11y_e_cell_action_get_keybinding;
497 }
498
499 void
gal_a11y_e_cell_type_add_action_interface(GType type)500 gal_a11y_e_cell_type_add_action_interface (GType type)
501 {
502 static const GInterfaceInfo atk_action_info =
503 {
504 (GInterfaceInitFunc) gal_a11y_e_cell_atk_action_interface_init,
505 (GInterfaceFinalizeFunc) NULL,
506 NULL
507 };
508
509 g_type_add_interface_static (
510 type, ATK_TYPE_ACTION,
511 &atk_action_info);
512 }
513
514 gboolean
gal_a11y_e_cell_add_state(GalA11yECell * cell,AtkStateType state_type,gboolean emit_signal)515 gal_a11y_e_cell_add_state (GalA11yECell *cell,
516 AtkStateType state_type,
517 gboolean emit_signal)
518 {
519 if (!atk_state_set_contains_state (cell->state_set, state_type)) {
520 gboolean rc;
521
522 rc = atk_state_set_add_state (cell->state_set, state_type);
523 /*
524 * The signal should only be generated if the value changed,
525 * not when the cell is set up. So states that are set
526 * initially should pass FALSE as the emit_signal argument.
527 */
528
529 if (emit_signal) {
530 atk_object_notify_state_change (ATK_OBJECT (cell), state_type, TRUE);
531 /* If state_type is ATK_STATE_VISIBLE, additional
532 * notification */
533 if (state_type == ATK_STATE_VISIBLE)
534 g_signal_emit_by_name (cell, "visible_data_changed");
535 }
536
537 return rc;
538 }
539 else
540 return FALSE;
541 }
542
543 gboolean
gal_a11y_e_cell_remove_state(GalA11yECell * cell,AtkStateType state_type,gboolean emit_signal)544 gal_a11y_e_cell_remove_state (GalA11yECell *cell,
545 AtkStateType state_type,
546 gboolean emit_signal)
547 {
548 if (atk_state_set_contains_state (cell->state_set, state_type)) {
549 gboolean rc;
550
551 rc = atk_state_set_remove_state (cell->state_set, state_type);
552 /*
553 * The signal should only be generated if the value changed,
554 * not when the cell is set up. So states that are set
555 * initially should pass FALSE as the emit_signal argument.
556 */
557
558 if (emit_signal) {
559 atk_object_notify_state_change (ATK_OBJECT (cell), state_type, FALSE);
560 /* If state_type is ATK_STATE_VISIBLE, additional notification */
561 if (state_type == ATK_STATE_VISIBLE)
562 g_signal_emit_by_name (cell, "visible_data_changed");
563 }
564
565 return rc;
566 }
567 else
568 return FALSE;
569 }
570
571 /**
572 * gal_a11y_e_cell_get_type:
573 * @void:
574 *
575 * Registers the &GalA11yECell class if necessary, and returns the type ID
576 * associated to it.
577 *
578 * Return value: The type ID of the &GalA11yECell class.
579 **/
580 GType
gal_a11y_e_cell_get_type(void)581 gal_a11y_e_cell_get_type (void)
582 {
583 static GType type = 0;
584
585 if (!type) {
586 GTypeInfo info = {
587 sizeof (GalA11yECellClass),
588 (GBaseInitFunc) NULL,
589 (GBaseFinalizeFunc) NULL,
590 (GClassInitFunc) gal_a11y_e_cell_class_init,
591 (GClassFinalizeFunc) NULL,
592 NULL, /* class_data */
593 sizeof (GalA11yECell),
594 0,
595 (GInstanceInitFunc) gal_a11y_e_cell_init,
596 NULL /* value_cell */
597 };
598
599 static const GInterfaceInfo atk_component_info = {
600 (GInterfaceInitFunc) gal_a11y_e_cell_atk_component_iface_init,
601 (GInterfaceFinalizeFunc) NULL,
602 NULL
603 };
604
605 type = g_type_register_static (PARENT_TYPE, "GalA11yECell", &info, 0);
606 g_type_add_interface_static (type, ATK_TYPE_COMPONENT, &atk_component_info);
607 }
608
609 return type;
610 }
611
612 AtkObject *
gal_a11y_e_cell_new(ETableItem * item,ECellView * cell_view,AtkObject * parent,gint model_col,gint view_col,gint row)613 gal_a11y_e_cell_new (ETableItem *item,
614 ECellView *cell_view,
615 AtkObject *parent,
616 gint model_col,
617 gint view_col,
618 gint row)
619 {
620 AtkObject *a11y;
621
622 a11y = g_object_new (gal_a11y_e_cell_get_type (), NULL);
623
624 gal_a11y_e_cell_construct (
625 a11y,
626 item,
627 cell_view,
628 parent,
629 model_col,
630 view_col,
631 row);
632 return a11y;
633 }
634
635 void
gal_a11y_e_cell_construct(AtkObject * object,ETableItem * item,ECellView * cell_view,AtkObject * parent,gint model_col,gint view_col,gint row)636 gal_a11y_e_cell_construct (AtkObject *object,
637 ETableItem *item,
638 ECellView *cell_view,
639 AtkObject *parent,
640 gint model_col,
641 gint view_col,
642 gint row)
643 {
644 GalA11yECell *a11y = GAL_A11Y_E_CELL (object);
645 a11y->item = item;
646 a11y->cell_view = cell_view;
647 a11y->parent = parent;
648 a11y->model_col = model_col;
649 a11y->view_col = view_col;
650 a11y->row = row;
651 ATK_OBJECT (a11y) ->role = ATK_ROLE_TABLE_CELL;
652
653 if (item)
654 g_object_ref (item);
655
656 #if 0
657 if (parent)
658 g_object_ref (parent);
659
660 if (cell_view)
661 g_object_ref (cell_view);
662
663 #endif
664 }
665