No issues found
1 /*
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU Lesser General Public
4 * License as published by the Free Software Foundation; either
5 * version 2 of the License, or (at your option) version 3.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10 * Lesser General Public License for more details.
11 *
12 * You should have received a copy of the GNU Lesser General Public
13 * License along with the program; if not, see <http://www.gnu.org/licenses/>
14 *
15 *
16 * Authors:
17 * Christopher James Lahey <clahey@ximian.com>
18 * Bolian Yin <bolian.yin@sun.com>
19 *
20 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
21 *
22 */
23
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27
28 #include <string.h>
29
30 #include <atk/atk.h>
31
32 #include "a11y/gal-a11y-util.h"
33 #include "table/e-table-click-to-add.h"
34 #include "table/e-table-subset.h"
35 #include "table/e-table.h"
36 #include "table/e-tree.h"
37 #include "misc/e-canvas.h"
38 #include "misc/e-selection-model.h"
39
40 #include "gal-a11y-e-table-item.h"
41 #include "gal-a11y-e-table-item-factory.h"
42 #include "gal-a11y-e-table-click-to-add.h"
43 #include "gal-a11y-e-cell-registry.h"
44 #include "gal-a11y-e-cell.h"
45 #include "gal-a11y-e-table-column-header.h"
46
47 static GObjectClass *parent_class;
48 static AtkComponentIface *component_parent_iface;
49 static GType parent_type;
50 static gint priv_offset;
51 static GQuark quark_accessible_object = 0;
52 #define GET_PRIVATE(object) \
53 ((GalA11yETableItemPrivate *) (((gchar *) object) + priv_offset))
54 #define PARENT_TYPE (parent_type)
55
56 struct _GalA11yETableItemPrivate {
57 ETableItem *item;
58 gint cols;
59 gint rows;
60 gint selection_change_id;
61 gint cursor_change_id;
62 ETableCol ** columns;
63 ESelectionModel *selection;
64 AtkStateSet *state_set;
65 GtkWidget *widget;
66 };
67
68 static gboolean gal_a11y_e_table_item_ref_selection (GalA11yETableItem *a11y,
69 ESelectionModel *selection);
70 static gboolean gal_a11y_e_table_item_unref_selection (GalA11yETableItem *a11y);
71
72 static AtkObject * eti_ref_at (AtkTable *table, gint row, gint column);
73
74 static void
75 free_columns (ETableCol **columns)
76 {
77 gint ii;
78
79 if (!columns)
80 return;
81
82 for (ii = 0; columns[ii]; ii++) {
83 g_object_unref (columns[ii]);
84 }
85
86 g_free (columns);
87 }
88
89 static void
90 item_finalized (gpointer user_data,
91 GObject *gone_item)
92 {
93 GalA11yETableItem *a11y;
94 GalA11yETableItemPrivate *priv;
95
96 a11y = GAL_A11Y_E_TABLE_ITEM (user_data);
97 priv = GET_PRIVATE (a11y);
98
99 priv->item = NULL;
100
101 atk_state_set_add_state (priv->state_set, ATK_STATE_DEFUNCT);
102 atk_object_notify_state_change (ATK_OBJECT (a11y), ATK_STATE_DEFUNCT, TRUE);
103
104 if (priv->selection)
105 gal_a11y_e_table_item_unref_selection (a11y);
106
107 g_object_unref (a11y);
108 }
109
110 static AtkStateSet *
111 eti_ref_state_set (AtkObject *accessible)
112 {
113 GalA11yETableItemPrivate *priv = GET_PRIVATE (accessible);
114
115 g_object_ref (priv->state_set);
116
117 return priv->state_set;
118 }
119
120 inline static gint
121 view_to_model_row (ETableItem *eti,
122 gint row)
123 {
124 if (eti->uses_source_model) {
125 ETableSubset *etss = E_TABLE_SUBSET (eti->table_model);
126 if (row >= 0 && row < etss->n_map) {
127 eti->row_guess = row;
128 return etss->map_table[row];
129 } else
130 return -1;
131 } else
132 return row;
133 }
134
135 inline static gint
136 view_to_model_col (ETableItem *eti,
137 gint col)
138 {
139 ETableCol *ecol = e_table_header_get_column (eti->header, col);
140 return ecol ? ecol->col_idx : -1;
141 }
142
143 inline static gint
144 model_to_view_row (ETableItem *eti,
145 gint row)
146 {
147 gint i;
148 if (row == -1)
149 return -1;
150 if (eti->uses_source_model) {
151 ETableSubset *etss = E_TABLE_SUBSET (eti->table_model);
152 if (eti->row_guess >= 0 && eti->row_guess < etss->n_map) {
153 if (etss->map_table[eti->row_guess] == row) {
154 return eti->row_guess;
155 }
156 }
157 for (i = 0; i < etss->n_map; i++) {
158 if (etss->map_table[i] == row)
159 return i;
160 }
161 return -1;
162 } else
163 return row;
164 }
165
166 inline static gint
167 model_to_view_col (ETableItem *eti,
168 gint col)
169 {
170 gint i;
171 if (col == -1)
172 return -1;
173 for (i = 0; i < eti->cols; i++) {
174 ETableCol *ecol = e_table_header_get_column (eti->header, i);
175 if (ecol->col_idx == col)
176 return i;
177 }
178 return -1;
179 }
180
181 inline static GObject *
182 eti_a11y_get_gobject (AtkObject *accessible)
183 {
184 return atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (accessible));
185 }
186
187 static void
188 eti_a11y_reset_focus_object (GalA11yETableItem *a11y,
189 ETableItem *item,
190 gboolean notify)
191 {
192 ESelectionModel * esm;
193 gint cursor_row, cursor_col, view_row, view_col;
194 AtkObject *cell, *old_cell;
195
196 esm = item->selection;
197 g_return_if_fail (esm);
198
199 cursor_row = e_selection_model_cursor_row (esm);
200 cursor_col = e_selection_model_cursor_col (esm);
201
202 view_row = model_to_view_row (item, cursor_row);
203 view_col = model_to_view_col (item, cursor_col);
204
205 if (view_row == -1)
206 view_row = 0;
207 if (view_col == -1)
208 view_col = 0;
209
210 old_cell = (AtkObject *) g_object_get_data (G_OBJECT (a11y), "gail-focus-object");
211 if (old_cell && GAL_A11Y_IS_E_CELL (old_cell))
212 gal_a11y_e_cell_remove_state (
213 GAL_A11Y_E_CELL (old_cell), ATK_STATE_FOCUSED, FALSE);
214 if (old_cell)
215 g_object_unref (old_cell);
216
217 cell = eti_ref_at (ATK_TABLE (a11y), view_row, view_col);
218
219 if (cell != NULL) {
220 g_object_set_data (G_OBJECT (a11y), "gail-focus-object", cell);
221 gal_a11y_e_cell_add_state (
222 GAL_A11Y_E_CELL (cell), ATK_STATE_FOCUSED, FALSE);
223 } else
224 g_object_set_data (G_OBJECT (a11y), "gail-focus-object", NULL);
225
226 if (notify && cell)
227 atk_focus_tracker_notify (cell);
228 }
229
230 static void
231 eti_dispose (GObject *object)
232 {
233 GalA11yETableItem *a11y = GAL_A11Y_E_TABLE_ITEM (object);
234 GalA11yETableItemPrivate *priv = GET_PRIVATE (a11y);
235
236 if (priv->columns) {
237 free_columns (priv->columns);
238 priv->columns = NULL;
239 }
240
241 if (priv->item) {
242 g_object_weak_unref (G_OBJECT (priv->item), item_finalized, a11y);
243 priv->item = NULL;
244 }
245
246 if (parent_class->dispose)
247 parent_class->dispose (object);
248 }
249
250 /* Static functions */
251 static gint
252 eti_get_n_children (AtkObject *accessible)
253 {
254 g_return_val_if_fail (GAL_A11Y_IS_E_TABLE_ITEM (accessible), 0);
255 if (!eti_a11y_get_gobject (accessible))
256 return 0;
257
258 return atk_table_get_n_columns (ATK_TABLE (accessible)) *
259 (atk_table_get_n_rows (ATK_TABLE (accessible)) + 1);
260 }
261
262 static AtkObject *
263 eti_ref_child (AtkObject *accessible,
264 gint index)
265 {
266 ETableItem *item;
267 gint col, row;
268
269 g_return_val_if_fail (GAL_A11Y_IS_E_TABLE_ITEM (accessible), NULL);
270 item = E_TABLE_ITEM (eti_a11y_get_gobject (accessible));
271 if (!item)
272 return NULL;
273
274 if (index < item->cols) {
275 ETableCol *ecol;
276 AtkObject *child;
277
278 ecol = e_table_header_get_column (item->header, index);
279 child = gal_a11y_e_table_column_header_new (ecol, item);
280 return child;
281 }
282 index -= item->cols;
283
284 col = index % item->cols;
285 row = index / item->cols;
286
287 return eti_ref_at (ATK_TABLE (accessible), row, col);
288 }
289
290 static void
291 eti_get_extents (AtkComponent *component,
292 gint *x,
293 gint *y,
294 gint *width,
295 gint *height,
296 AtkCoordType coord_type)
297 {
298 ETableItem *item;
299 AtkObject *parent;
300
301 item = E_TABLE_ITEM (eti_a11y_get_gobject (ATK_OBJECT (component)));
302 if (!item)
303 return;
304
305 parent = ATK_OBJECT (component)->accessible_parent;
306 if (parent && ATK_IS_COMPONENT (parent))
307 atk_component_get_extents (
308 ATK_COMPONENT (parent),
309 x, y,
310 width, height,
311 coord_type);
312
313 if (parent && GAL_A11Y_IS_E_TABLE_CLICK_TO_ADD (parent)) {
314 ETableClickToAdd *etcta = E_TABLE_CLICK_TO_ADD (
315 atk_gobject_accessible_get_object (
316 ATK_GOBJECT_ACCESSIBLE (parent)));
317 if (etcta) {
318 *width = etcta->width;
319 *height = etcta->height;
320 }
321 }
322 }
323
324 static AtkObject *
325 eti_ref_accessible_at_point (AtkComponent *component,
326 gint x,
327 gint y,
328 AtkCoordType coord_type)
329 {
330 gint row = -1;
331 gint col = -1;
332 gint x_origin, y_origin;
333 ETableItem *item;
334 GtkWidget *tableOrTree;
335
336 item = E_TABLE_ITEM (eti_a11y_get_gobject (ATK_OBJECT (component)));
337 if (!item)
338 return NULL;
339
340 atk_component_get_position (
341 component,
342 &x_origin,
343 &y_origin,
344 coord_type);
345 x -= x_origin;
346 y -= y_origin;
347
348 tableOrTree = gtk_widget_get_parent (GTK_WIDGET (item->parent.canvas));
349
350 if (E_IS_TREE (tableOrTree))
351 e_tree_get_cell_at (E_TREE (tableOrTree), x, y, &row, &col);
352 else
353 e_table_get_cell_at (E_TABLE (tableOrTree), x, y, &row, &col);
354
355 if (row != -1 && col != -1) {
356 return eti_ref_at (ATK_TABLE (component), row, col);
357 } else {
358 return NULL;
359 }
360 }
361
362 static void
363 cell_destroyed (gpointer data)
364 {
365 GalA11yECell * cell;
366
367 g_return_if_fail (GAL_A11Y_IS_E_CELL (data));
368 cell = GAL_A11Y_E_CELL (data);
369
370 g_return_if_fail (cell->item && G_IS_OBJECT (cell->item));
371
372 if (cell->item) {
373 g_object_unref (cell->item);
374 cell->item = NULL;
375 }
376
377 }
378
379 /* atk table */
380 static AtkObject *
381 eti_ref_at (AtkTable *table,
382 gint row,
383 gint column)
384 {
385 ETableItem *item;
386 AtkObject * ret;
387 GalA11yETableItemPrivate *priv = GET_PRIVATE (table);
388
389 if (atk_state_set_contains_state (priv->state_set, ATK_STATE_DEFUNCT))
390 return NULL;
391
392 item = E_TABLE_ITEM (eti_a11y_get_gobject (ATK_OBJECT (table)));
393 if (!item)
394 return NULL;
395
396 if (column >= 0 &&
397 column < item->cols &&
398 row >= 0 &&
399 row < item->rows &&
400 item->cell_views_realized) {
401 ECellView *cell_view = item->cell_views[column];
402 ETableCol *ecol = e_table_header_get_column (item->header, column);
403 ret = gal_a11y_e_cell_registry_get_object (
404 NULL,
405 item,
406 cell_view,
407 ATK_OBJECT (table),
408 ecol->col_idx,
409 column,
410 row);
411 if (ATK_IS_OBJECT (ret)) {
412 g_object_weak_ref (
413 G_OBJECT (ret),
414 (GWeakNotify) cell_destroyed,
415 ret);
416 /* if current cell is focused, add FOCUSED state */
417 if (e_selection_model_cursor_row (item->selection) ==
418 GAL_A11Y_E_CELL (ret)->row &&
419 e_selection_model_cursor_col (item->selection) ==
420 GAL_A11Y_E_CELL (ret)->model_col)
421 gal_a11y_e_cell_add_state (
422 GAL_A11Y_E_CELL (ret),
423 ATK_STATE_FOCUSED, FALSE);
424 } else
425 ret = NULL;
426
427 return ret;
428 }
429
430 return NULL;
431 }
432
433 static gint
434 eti_get_index_at (AtkTable *table,
435 gint row,
436 gint column)
437 {
438 ETableItem *item;
439
440 item = E_TABLE_ITEM (eti_a11y_get_gobject (ATK_OBJECT (table)));
441 if (!item)
442 return -1;
443
444 return column + (row + 1) * item->cols;
445 }
446
447 static gint
448 eti_get_column_at_index (AtkTable *table,
449 gint index)
450 {
451 ETableItem *item;
452
453 item = E_TABLE_ITEM (eti_a11y_get_gobject (ATK_OBJECT (table)));
454 if (!item)
455 return -1;
456
457 return index % item->cols;
458 }
459
460 static gint
461 eti_get_row_at_index (AtkTable *table,
462 gint index)
463 {
464 ETableItem *item;
465
466 item = E_TABLE_ITEM (eti_a11y_get_gobject (ATK_OBJECT (table)));
467 if (!item)
468 return -1;
469
470 return index / item->cols - 1;
471 }
472
473 static gint
474 eti_get_n_columns (AtkTable *table)
475 {
476 ETableItem *item;
477
478 item = E_TABLE_ITEM (eti_a11y_get_gobject (ATK_OBJECT (table)));
479 if (!item)
480 return -1;
481
482 return item->cols;
483 }
484
485 static gint
486 eti_get_n_rows (AtkTable *table)
487 {
488 ETableItem *item;
489
490 item = E_TABLE_ITEM (eti_a11y_get_gobject (ATK_OBJECT (table)));
491 if (!item)
492 return -1;
493
494 return item->rows;
495 }
496
497 static gint
498 eti_get_column_extent_at (AtkTable *table,
499 gint row,
500 gint column)
501 {
502 ETableItem *item;
503 gint width;
504
505 item = E_TABLE_ITEM (eti_a11y_get_gobject (ATK_OBJECT (table)));
506 if (!item)
507 return -1;
508
509 e_table_item_get_cell_geometry (
510 item,
511 &row,
512 &column,
513 NULL,
514 NULL,
515 &width,
516 NULL);
517
518 return width;
519 }
520
521 static gint
522 eti_get_row_extent_at (AtkTable *table,
523 gint row,
524 gint column)
525 {
526 ETableItem *item;
527 gint height;
528
529 item = E_TABLE_ITEM (eti_a11y_get_gobject (ATK_OBJECT (table)));
530 if (!item)
531 return -1;
532
533 e_table_item_get_cell_geometry (
534 item,
535 &row,
536 &column,
537 NULL,
538 NULL,
539 NULL,
540 &height);
541
542 return height;
543 }
544
545 static AtkObject *
546 eti_get_caption (AtkTable *table)
547 {
548 /* Unimplemented */
549 return NULL;
550 }
551
552 static const gchar *
553 eti_get_column_description (AtkTable *table,
554 gint column)
555 {
556 ETableItem *item;
557 ETableCol *ecol;
558
559 item = E_TABLE_ITEM (eti_a11y_get_gobject (ATK_OBJECT (table)));
560 if (!item)
561 return NULL;
562
563 ecol = e_table_header_get_column (item->header, column);
564
565 return ecol->text;
566 }
567
568 static AtkObject *
569 eti_get_column_header (AtkTable *table,
570 gint column)
571 {
572 ETableItem *item;
573 ETableCol *ecol;
574 AtkObject *atk_obj = NULL;
575
576 item = E_TABLE_ITEM (eti_a11y_get_gobject (ATK_OBJECT (table)));
577 if (!item)
578 return NULL;
579
580 ecol = e_table_header_get_column (item->header, column);
581 if (ecol) {
582 atk_obj = gal_a11y_e_table_column_header_new (ecol, item);
583 }
584
585 return atk_obj;
586 }
587
588 static const gchar *
589 eti_get_row_description (AtkTable *table,
590 gint row)
591 {
592 /* Unimplemented */
593 return NULL;
594 }
595
596 static AtkObject *
597 eti_get_row_header (AtkTable *table,
598 gint row)
599 {
600 /* Unimplemented */
601 return NULL;
602 }
603
604 static AtkObject *
605 eti_get_summary (AtkTable *table)
606 {
607 /* Unimplemented */
608 return NULL;
609 }
610
611 static gboolean
612 table_is_row_selected (AtkTable *table,
613 gint row)
614 {
615 ETableItem *item;
616 GalA11yETableItemPrivate *priv = GET_PRIVATE (table);
617
618 if (row < 0)
619 return FALSE;
620
621 if (atk_state_set_contains_state (priv->state_set, ATK_STATE_DEFUNCT))
622 return FALSE;
623
624 item = E_TABLE_ITEM (eti_a11y_get_gobject (ATK_OBJECT (table)));
625 if (!item)
626 return FALSE;
627
628 return e_selection_model_is_row_selected (
629 item->selection, view_to_model_row (item, row));
630 }
631
632 static gboolean
633 table_is_selected (AtkTable *table,
634 gint row,
635 gint column)
636 {
637 return table_is_row_selected (table, row);
638 }
639
640 static gint
641 table_get_selected_rows (AtkTable *table,
642 gint **rows_selected)
643 {
644 ETableItem *item;
645 gint n_selected, row, index_selected;
646 GalA11yETableItemPrivate *priv = GET_PRIVATE (table);
647
648 if (atk_state_set_contains_state (priv->state_set, ATK_STATE_DEFUNCT))
649 return 0;
650
651 item = E_TABLE_ITEM (eti_a11y_get_gobject (ATK_OBJECT (table)));
652 if (!item)
653 return 0;
654
655 n_selected = e_selection_model_selected_count (item->selection);
656 if (rows_selected) {
657 *rows_selected = (gint *) g_malloc (n_selected * sizeof (gint));
658
659 index_selected = 0;
660 for (row = 0; row < item->rows && index_selected < n_selected; ++row) {
661 if (atk_table_is_row_selected (table, row)) {
662 (*rows_selected)[index_selected] = row;
663 ++index_selected;
664 }
665 }
666 }
667 return n_selected;
668 }
669
670 static gboolean
671 table_add_row_selection (AtkTable *table,
672 gint row)
673 {
674 ETableItem *item;
675
676 item = E_TABLE_ITEM (eti_a11y_get_gobject (ATK_OBJECT (table)));
677 if (!item)
678 return FALSE;
679
680 if (table_is_row_selected (table, row))
681 return TRUE;
682 e_selection_model_toggle_single_row (
683 item->selection,
684 view_to_model_row (item, row));
685
686 return TRUE;
687 }
688
689 static gboolean
690 table_remove_row_selection (AtkTable *table,
691 gint row)
692 {
693 ETableItem *item;
694 GalA11yETableItemPrivate *priv = GET_PRIVATE (table);
695
696 if (atk_state_set_contains_state (priv->state_set, ATK_STATE_DEFUNCT))
697 return FALSE;
698
699 item = E_TABLE_ITEM (eti_a11y_get_gobject (ATK_OBJECT (table)));
700 if (!item)
701 return FALSE;
702
703 if (!atk_table_is_row_selected (table, row))
704 return TRUE;
705
706 e_selection_model_toggle_single_row (
707 item->selection, view_to_model_row (item, row));
708
709 return TRUE;
710 }
711
712 static void
713 eti_atk_table_iface_init (AtkTableIface *iface)
714 {
715 iface->ref_at = eti_ref_at;
716 iface->get_index_at = eti_get_index_at;
717 iface->get_column_at_index = eti_get_column_at_index;
718 iface->get_row_at_index = eti_get_row_at_index;
719 iface->get_n_columns = eti_get_n_columns;
720 iface->get_n_rows = eti_get_n_rows;
721 iface->get_column_extent_at = eti_get_column_extent_at;
722 iface->get_row_extent_at = eti_get_row_extent_at;
723 iface->get_caption = eti_get_caption;
724 iface->get_column_description = eti_get_column_description;
725 iface->get_column_header = eti_get_column_header;
726 iface->get_row_description = eti_get_row_description;
727 iface->get_row_header = eti_get_row_header;
728 iface->get_summary = eti_get_summary;
729
730 iface->is_row_selected = table_is_row_selected;
731 iface->is_selected = table_is_selected;
732 iface->get_selected_rows = table_get_selected_rows;
733 iface->add_row_selection = table_add_row_selection;
734 iface->remove_row_selection = table_remove_row_selection;
735 }
736
737 static void
738 eti_atk_component_iface_init (AtkComponentIface *iface)
739 {
740 component_parent_iface = g_type_interface_peek_parent (iface);
741
742 iface->ref_accessible_at_point = eti_ref_accessible_at_point;
743 iface->get_extents = eti_get_extents;
744 }
745
746 static void
747 eti_rows_inserted (ETableModel *model,
748 gint row,
749 gint count,
750 AtkObject *table_item)
751 {
752 gint n_cols,n_rows,i,j;
753 GalA11yETableItem * item_a11y;
754 gint old_nrows;
755
756 g_return_if_fail (table_item);
757 item_a11y = GAL_A11Y_E_TABLE_ITEM (table_item);
758
759 n_cols = atk_table_get_n_columns (ATK_TABLE (table_item));
760 n_rows = atk_table_get_n_rows (ATK_TABLE (table_item));
761
762 old_nrows = GET_PRIVATE (item_a11y)->rows;
763
764 g_return_if_fail (n_cols > 0 && n_rows > 0);
765 g_return_if_fail (old_nrows == n_rows - count);
766
767 GET_PRIVATE (table_item)->rows = n_rows;
768
769 g_signal_emit_by_name (
770 table_item, "row-inserted", row,
771 count, NULL);
772
773 for (i = row; i < (row + count); i++) {
774 for (j = 0; j < n_cols; j++) {
775 g_signal_emit_by_name (
776 table_item,
777 "children_changed::add",
778 (((i + 1) * n_cols) + j), NULL, NULL);
779 }
780 }
781
782 g_signal_emit_by_name (table_item, "visible-data-changed");
783 }
784
785 static void
786 eti_rows_deleted (ETableModel *model,
787 gint row,
788 gint count,
789 AtkObject *table_item)
790 {
791 gint i,j, n_rows, n_cols, old_nrows;
792 ETableItem *item = E_TABLE_ITEM (
793 atk_gobject_accessible_get_object (
794 ATK_GOBJECT_ACCESSIBLE (table_item)));
795
796 n_rows = atk_table_get_n_rows (ATK_TABLE (table_item));
797 n_cols = atk_table_get_n_columns (ATK_TABLE (table_item));
798
799 old_nrows = GET_PRIVATE (table_item)->rows;
800
801 g_return_if_fail (row + count <= old_nrows);
802 g_return_if_fail (old_nrows == n_rows + count);
803 GET_PRIVATE (table_item)->rows = n_rows;
804
805 g_signal_emit_by_name (
806 table_item, "row-deleted", row,
807 count, NULL);
808
809 for (i = row; i < (row + count); i++) {
810 for (j = 0; j < n_cols; j++) {
811 g_signal_emit_by_name (
812 table_item,
813 "children_changed::remove",
814 (((i + 1) * n_cols) + j), NULL, NULL);
815 }
816 }
817 g_signal_emit_by_name (table_item, "visible-data-changed");
818 eti_a11y_reset_focus_object ((GalA11yETableItem *) table_item, item, TRUE);
819 }
820
821 static void
822 eti_tree_model_node_changed_cb (ETreeModel *model,
823 ETreePath node,
824 ETableItem *eti)
825 {
826 AtkObject *atk_obj;
827 GalA11yETableItem *a11y;
828
829 g_return_if_fail (E_IS_TABLE_ITEM (eti));
830
831 atk_obj = atk_gobject_accessible_for_object (G_OBJECT (eti));
832 a11y = GAL_A11Y_E_TABLE_ITEM (atk_obj);
833
834 /* we can't figure out which rows are changed, so just send out a signal ... */
835 if (GET_PRIVATE (a11y)->rows > 0)
836 g_signal_emit_by_name (a11y, "visible-data-changed");
837 }
838
839 enum {
840 ETI_HEADER_UNCHANGED = 0,
841 ETI_HEADER_REORDERED,
842 ETI_HEADER_NEW_ADDED,
843 ETI_HEADER_REMOVED
844 };
845
846 /*
847 * 1. Check what actually happened: column reorder, remove or add
848 * 2. Update cache
849 * 3. Emit signals
850 */
851 static void
852 eti_header_structure_changed (ETableHeader *eth,
853 AtkObject *a11y)
854 {
855
856 gboolean reorder_found = FALSE, added_found = FALSE, removed_found = FALSE;
857 GalA11yETableItem * a11y_item;
858 ETableCol ** cols, **prev_cols;
859 GalA11yETableItemPrivate *priv;
860 gint *state = NULL, *prev_state = NULL, *reorder = NULL;
861 gint i,j,n_rows,n_cols, prev_n_cols;
862
863 a11y_item = GAL_A11Y_E_TABLE_ITEM (a11y);
864 priv = GET_PRIVATE (a11y_item);
865
866 /* Assume rows do not changed. */
867 n_rows = priv->rows;
868
869 prev_n_cols = priv->cols;
870 prev_cols = priv->columns;
871
872 cols = e_table_header_get_columns (eth);
873 n_cols = eth->col_count;
874
875 g_return_if_fail (cols && prev_cols && n_cols > 0);
876
877 /* Init to ETI_HEADER_UNCHANGED. */
878 state = g_malloc0 (sizeof (gint) * n_cols);
879 prev_state = g_malloc0 (sizeof (gint) * prev_n_cols);
880 reorder = g_malloc0 (sizeof (gint) * n_cols);
881
882 /* Compare with previously saved column headers. */
883 for (i = 0; i < n_cols && cols[i]; i++) {
884 for (j = 0; j < prev_n_cols && prev_cols[j]; j++) {
885 if (prev_cols[j] == cols[i] && i != j) {
886
887 reorder_found = TRUE;
888 state[i] = ETI_HEADER_REORDERED;
889 reorder[i] = j;
890
891 break;
892 } else if (prev_cols[j] == cols[i]) {
893 /* OK, this column is not changed. */
894 break;
895 }
896 }
897
898 /* cols[i] is new added column. */
899 if (j == prev_n_cols) {
900 added_found = TRUE;
901 state[i] = ETI_HEADER_NEW_ADDED;
902 }
903 }
904
905 /* Now try to find if there are removed columns. */
906 for (i = 0; i < prev_n_cols && prev_cols[i]; i++) {
907 for (j = 0; j < n_cols && cols[j]; j++)
908 if (prev_cols[j] == cols[i])
909 break;
910
911 /* Removed columns found. */
912 if (j == n_cols) {
913 removed_found = TRUE;
914 prev_state[j] = ETI_HEADER_REMOVED;
915 }
916 }
917
918 /* If nothing interesting just return. */
919 if (!reorder_found && !added_found && !removed_found)
920 return;
921
922 /* Emit signals */
923 if (reorder_found)
924 g_signal_emit_by_name (a11y_item, "column_reordered");
925
926 if (removed_found) {
927 for (i = 0; i < prev_n_cols; i++) {
928 if (prev_state[i] == ETI_HEADER_REMOVED) {
929 g_signal_emit_by_name (
930 a11y_item, "column-deleted", i, 1);
931 for (j = 0; j < n_rows; j++)
932 g_signal_emit_by_name (
933 a11y_item,
934 "children_changed::remove",
935 ((j + 1) * prev_n_cols + i),
936 NULL, NULL);
937 }
938 }
939 }
940
941 if (added_found) {
942 for (i = 0; i < n_cols; i++) {
943 if (state[i] == ETI_HEADER_NEW_ADDED) {
944 g_signal_emit_by_name (
945 a11y_item, "column-inserted", i, 1);
946 for (j = 0; j < n_rows; j++)
947 g_signal_emit_by_name (
948 a11y_item,
949 "children_changed::add",
950 ((j + 1) * n_cols + i),
951 NULL, NULL);
952 }
953 }
954 }
955
956 priv->cols = n_cols;
957
958 g_free (state);
959 g_free (reorder);
960 g_free (prev_state);
961
962 free_columns (priv->columns);
963 priv->columns = cols;
964 }
965
966 static void
967 eti_real_initialize (AtkObject *obj,
968 gpointer data)
969 {
970 ETableItem * eti;
971 ETableModel * model;
972
973 ATK_OBJECT_CLASS (parent_class)->initialize (obj, data);
974 eti = E_TABLE_ITEM (data);
975
976 model = eti->table_model;
977
978 g_signal_connect (
979 model, "model-rows-inserted",
980 G_CALLBACK (eti_rows_inserted), obj);
981 g_signal_connect (
982 model, "model-rows-deleted",
983 G_CALLBACK (eti_rows_deleted), obj);
984 g_signal_connect (
985 eti->header, "structure_change",
986 G_CALLBACK (eti_header_structure_changed), obj);
987
988 }
989
990 static void
991 eti_class_init (GalA11yETableItemClass *class)
992 {
993 AtkObjectClass *atk_object_class = ATK_OBJECT_CLASS (class);
994 GObjectClass *object_class = G_OBJECT_CLASS (class);
995
996 quark_accessible_object =
997 g_quark_from_static_string ("gtk-accessible-object");
998
999 parent_class = g_type_class_ref (PARENT_TYPE);
1000
1001 object_class->dispose = eti_dispose;
1002
1003 atk_object_class->get_n_children = eti_get_n_children;
1004 atk_object_class->ref_child = eti_ref_child;
1005 atk_object_class->initialize = eti_real_initialize;
1006 atk_object_class->ref_state_set = eti_ref_state_set;
1007 }
1008
1009 static void
1010 eti_init (GalA11yETableItem *a11y)
1011 {
1012 GalA11yETableItemPrivate *priv;
1013
1014 priv = GET_PRIVATE (a11y);
1015
1016 priv->selection_change_id = 0;
1017 priv->cursor_change_id = 0;
1018 priv->selection = NULL;
1019 }
1020
1021 /* atk selection */
1022
1023 static void atk_selection_interface_init (AtkSelectionIface *iface);
1024 static gboolean selection_add_selection (AtkSelection *selection,
1025 gint i);
1026 static gboolean selection_clear_selection (AtkSelection *selection);
1027 static AtkObject *
1028 selection_ref_selection (AtkSelection *selection,
1029 gint i);
1030 static gint selection_get_selection_count (AtkSelection *selection);
1031 static gboolean selection_is_child_selected (AtkSelection *selection,
1032 gint i);
1033
1034 /* callbacks */
1035 static void eti_a11y_selection_model_removed_cb (ETableItem *eti,
1036 ESelectionModel *selection,
1037 gpointer data);
1038 static void eti_a11y_selection_model_added_cb (ETableItem *eti,
1039 ESelectionModel *selection,
1040 gpointer data);
1041 static void eti_a11y_selection_changed_cb (ESelectionModel *selection,
1042 GalA11yETableItem *a11y);
1043 static void eti_a11y_cursor_changed_cb (ESelectionModel *selection,
1044 gint row, gint col,
1045 GalA11yETableItem *a11y);
1046
1047 /**
1048 * gal_a11y_e_table_item_get_type:
1049 * @void:
1050 *
1051 * Registers the &GalA11yETableItem class if necessary, and returns the type ID
1052 * associated to it.
1053 *
1054 * Return value: The type ID of the &GalA11yETableItem class.
1055 **/
1056 GType
1057 gal_a11y_e_table_item_get_type (void)
1058 {
1059 static GType type = 0;
1060
1061 if (!type) {
1062 AtkObjectFactory *factory;
1063
1064 GTypeInfo info = {
1065 sizeof (GalA11yETableItemClass),
1066 (GBaseInitFunc) NULL,
1067 (GBaseFinalizeFunc) NULL,
1068 (GClassInitFunc) eti_class_init,
1069 (GClassFinalizeFunc) NULL,
1070 NULL, /* class_data */
1071 sizeof (GalA11yETableItem),
1072 0,
1073 (GInstanceInitFunc) eti_init,
1074 NULL /* value_table_item */
1075 };
1076
1077 static const GInterfaceInfo atk_component_info = {
1078 (GInterfaceInitFunc) eti_atk_component_iface_init,
1079 (GInterfaceFinalizeFunc) NULL,
1080 NULL
1081 };
1082 static const GInterfaceInfo atk_table_info = {
1083 (GInterfaceInitFunc) eti_atk_table_iface_init,
1084 (GInterfaceFinalizeFunc) NULL,
1085 NULL
1086 };
1087
1088 static const GInterfaceInfo atk_selection_info = {
1089 (GInterfaceInitFunc) atk_selection_interface_init,
1090 (GInterfaceFinalizeFunc) NULL,
1091 NULL
1092 };
1093
1094 factory = atk_registry_get_factory (
1095 atk_get_default_registry (), GNOME_TYPE_CANVAS_ITEM);
1096 parent_type = atk_object_factory_get_accessible_type (factory);
1097
1098 type = gal_a11y_type_register_static_with_private (
1099 PARENT_TYPE, "GalA11yETableItem", &info, 0,
1100 sizeof (GalA11yETableItemPrivate), &priv_offset);
1101
1102 g_type_add_interface_static (type, ATK_TYPE_COMPONENT, &atk_component_info);
1103 g_type_add_interface_static (type, ATK_TYPE_TABLE, &atk_table_info);
1104 g_type_add_interface_static (type, ATK_TYPE_SELECTION, &atk_selection_info);
1105 }
1106
1107 return type;
1108 }
1109
1110 AtkObject *
1111 gal_a11y_e_table_item_new (ETableItem *item)
1112 {
1113 GalA11yETableItem *a11y;
1114 AtkObject *accessible;
1115 ESelectionModel * esm;
1116 AtkObject *parent;
1117 const gchar *name;
1118
1119 g_return_val_if_fail (item && item->cols >= 0 && item->rows >= 0, NULL);
1120 a11y = g_object_new (gal_a11y_e_table_item_get_type (), NULL);
1121
1122 atk_object_initialize (ATK_OBJECT (a11y), item);
1123
1124 GET_PRIVATE (a11y)->state_set = atk_state_set_new ();
1125
1126 atk_state_set_add_state (GET_PRIVATE (a11y)->state_set, ATK_STATE_TRANSIENT);
1127 atk_state_set_add_state (GET_PRIVATE (a11y)->state_set, ATK_STATE_ENABLED);
1128 atk_state_set_add_state (GET_PRIVATE (a11y)->state_set, ATK_STATE_SENSITIVE);
1129 atk_state_set_add_state (GET_PRIVATE (a11y)->state_set, ATK_STATE_SHOWING);
1130 atk_state_set_add_state (GET_PRIVATE (a11y)->state_set, ATK_STATE_VISIBLE);
1131
1132 accessible = ATK_OBJECT (a11y);
1133
1134 GET_PRIVATE (a11y)->item = item;
1135 /* Initialize cell data. */
1136 GET_PRIVATE (a11y)->cols = item->cols;
1137 GET_PRIVATE (a11y)->rows = item->rows;
1138
1139 GET_PRIVATE (a11y)->columns = e_table_header_get_columns (item->header);
1140 if (GET_PRIVATE (a11y)->columns == NULL)
1141 return NULL;
1142
1143 if (item) {
1144 g_signal_connect (
1145 item, "selection_model_removed",
1146 G_CALLBACK (eti_a11y_selection_model_removed_cb), NULL);
1147 g_signal_connect (
1148 item, "selection_model_added",
1149 G_CALLBACK (eti_a11y_selection_model_added_cb), NULL);
1150 if (item->selection)
1151 gal_a11y_e_table_item_ref_selection (
1152 a11y,
1153 item->selection);
1154
1155 /* find the TableItem's parent: table or tree */
1156 GET_PRIVATE (a11y)->widget = gtk_widget_get_parent (
1157 GTK_WIDGET (item->parent.canvas));
1158 parent = gtk_widget_get_accessible (GET_PRIVATE (a11y)->widget);
1159 name = atk_object_get_name (parent);
1160 if (name)
1161 atk_object_set_name (accessible, name);
1162 atk_object_set_parent (accessible, parent);
1163
1164 if (E_IS_TREE (GET_PRIVATE (a11y)->widget)) {
1165 ETreeModel *model;
1166 model = e_tree_get_model (E_TREE (GET_PRIVATE (a11y)->widget));
1167 g_signal_connect (
1168 model, "node_changed",
1169 G_CALLBACK (eti_tree_model_node_changed_cb), item);
1170 accessible->role = ATK_ROLE_TREE_TABLE;
1171 } else if (E_IS_TABLE (GET_PRIVATE (a11y)->widget)) {
1172 accessible->role = ATK_ROLE_TABLE;
1173 }
1174 }
1175
1176 if (item)
1177 g_object_weak_ref (G_OBJECT (item), item_finalized, g_object_ref (a11y));
1178
1179 esm = item->selection;
1180
1181 if (esm != NULL) {
1182 eti_a11y_reset_focus_object (a11y, item, FALSE);
1183 }
1184
1185 return ATK_OBJECT (a11y);
1186 }
1187
1188 static gboolean
1189 gal_a11y_e_table_item_ref_selection (GalA11yETableItem *a11y,
1190 ESelectionModel *selection)
1191 {
1192 GalA11yETableItemPrivate *priv;
1193
1194 g_return_val_if_fail (a11y && selection, FALSE);
1195
1196 priv = GET_PRIVATE (a11y);
1197 priv->selection_change_id = g_signal_connect (
1198 selection, "selection_changed",
1199 G_CALLBACK (eti_a11y_selection_changed_cb), a11y);
1200 priv->cursor_change_id = g_signal_connect (
1201 selection, "cursor_changed",
1202 G_CALLBACK (eti_a11y_cursor_changed_cb), a11y);
1203
1204 priv->selection = selection;
1205 g_object_ref (selection);
1206
1207 return TRUE;
1208 }
1209
1210 static gboolean
1211 gal_a11y_e_table_item_unref_selection (GalA11yETableItem *a11y)
1212 {
1213 GalA11yETableItemPrivate *priv;
1214
1215 g_return_val_if_fail (a11y, FALSE);
1216
1217 priv = GET_PRIVATE (a11y);
1218
1219 g_return_val_if_fail (priv->selection_change_id != 0, FALSE);
1220 g_return_val_if_fail (priv->cursor_change_id != 0, FALSE);
1221
1222 g_signal_handler_disconnect (
1223 priv->selection,
1224 priv->selection_change_id);
1225 g_signal_handler_disconnect (
1226 priv->selection,
1227 priv->cursor_change_id);
1228 priv->cursor_change_id = 0;
1229 priv->selection_change_id = 0;
1230
1231 g_object_unref (priv->selection);
1232 priv->selection = NULL;
1233
1234 return TRUE;
1235 }
1236
1237 /* callbacks */
1238
1239 static void
1240 eti_a11y_selection_model_removed_cb (ETableItem *eti,
1241 ESelectionModel *selection,
1242 gpointer data)
1243 {
1244 AtkObject *atk_obj;
1245 GalA11yETableItem *a11y;
1246
1247 g_return_if_fail (E_IS_TABLE_ITEM (eti));
1248 g_return_if_fail (E_IS_SELECTION_MODEL (selection));
1249
1250 atk_obj = atk_gobject_accessible_for_object (G_OBJECT (eti));
1251 a11y = GAL_A11Y_E_TABLE_ITEM (atk_obj);
1252
1253 if (selection == GET_PRIVATE (a11y)->selection)
1254 gal_a11y_e_table_item_unref_selection (a11y);
1255 }
1256
1257 static void
1258 eti_a11y_selection_model_added_cb (ETableItem *eti,
1259 ESelectionModel *selection,
1260 gpointer data)
1261 {
1262 AtkObject *atk_obj;
1263 GalA11yETableItem *a11y;
1264
1265 g_return_if_fail (E_IS_TABLE_ITEM (eti));
1266 g_return_if_fail (E_IS_SELECTION_MODEL (selection));
1267
1268 atk_obj = atk_gobject_accessible_for_object (G_OBJECT (eti));
1269 a11y = GAL_A11Y_E_TABLE_ITEM (atk_obj);
1270
1271 if (GET_PRIVATE (a11y)->selection)
1272 gal_a11y_e_table_item_unref_selection (a11y);
1273 gal_a11y_e_table_item_ref_selection (a11y, selection);
1274 }
1275
1276 static void
1277 eti_a11y_selection_changed_cb (ESelectionModel *selection,
1278 GalA11yETableItem *a11y)
1279 {
1280 GalA11yETableItemPrivate *priv = GET_PRIVATE (a11y);
1281
1282 if (atk_state_set_contains_state (priv->state_set, ATK_STATE_DEFUNCT))
1283 return;
1284
1285 g_return_if_fail (GAL_A11Y_IS_E_TABLE_ITEM (a11y));
1286
1287 g_signal_emit_by_name (a11y, "selection_changed");
1288 }
1289
1290 static void
1291 eti_a11y_cursor_changed_cb (ESelectionModel *selection,
1292 gint row,
1293 gint col,
1294 GalA11yETableItem *a11y)
1295 {
1296 ETableItem *item;
1297 GalA11yETableItemPrivate *priv = GET_PRIVATE (a11y);
1298
1299 g_return_if_fail (GAL_A11Y_IS_E_TABLE_ITEM (a11y));
1300
1301 if (atk_state_set_contains_state (priv->state_set, ATK_STATE_DEFUNCT))
1302 return;
1303
1304 item = E_TABLE_ITEM (eti_a11y_get_gobject (ATK_OBJECT (a11y)));
1305
1306 g_return_if_fail (item);
1307
1308 if (row == -1 && col == -1)
1309 return;
1310 eti_a11y_reset_focus_object (a11y, item, TRUE);
1311 }
1312
1313 /* atk selection */
1314
1315 static void atk_selection_interface_init (AtkSelectionIface *iface)
1316 {
1317 g_return_if_fail (iface != NULL);
1318 iface->add_selection = selection_add_selection;
1319 iface->clear_selection = selection_clear_selection;
1320 iface->ref_selection = selection_ref_selection;
1321 iface->get_selection_count = selection_get_selection_count;
1322 iface->is_child_selected = selection_is_child_selected;
1323 }
1324
1325 static gboolean
1326 selection_add_selection (AtkSelection *selection,
1327 gint index)
1328 {
1329 AtkTable *table;
1330 gint row, col, cursor_row, cursor_col, model_row, model_col;
1331 ETableItem *item;
1332
1333 item = E_TABLE_ITEM (eti_a11y_get_gobject (ATK_OBJECT (selection)));
1334 if (!item)
1335 return FALSE;
1336
1337 table = ATK_TABLE (selection);
1338
1339 row = atk_table_get_row_at_index (table, index);
1340 col = atk_table_get_column_at_index (table, index);
1341
1342 model_row = view_to_model_row (item, row);
1343 model_col = view_to_model_col (item, col);
1344
1345 cursor_row = e_selection_model_cursor_row (item->selection);
1346 cursor_col = e_selection_model_cursor_col (item->selection);
1347
1348 /* check whether is selected already */
1349 if (model_row == cursor_row && model_col == cursor_col)
1350 return TRUE;
1351
1352 if (model_row != cursor_row) {
1353 /* we need to make the item get focus */
1354 e_canvas_item_grab_focus (GNOME_CANVAS_ITEM (item), TRUE);
1355
1356 /* FIXME, currently we only support single row selection */
1357 atk_selection_clear_selection (selection);
1358 atk_table_add_row_selection (table, row);
1359 }
1360
1361 e_selection_model_change_cursor (
1362 item->selection,
1363 model_row,
1364 model_col);
1365 e_selection_model_cursor_changed (
1366 item->selection,
1367 model_row,
1368 model_col);
1369 e_selection_model_cursor_activated (
1370 item->selection,
1371 model_row,
1372 model_col);
1373 return TRUE;
1374 }
1375
1376 static gboolean
1377 selection_clear_selection (AtkSelection *selection)
1378 {
1379 ETableItem *item;
1380
1381 item = E_TABLE_ITEM (eti_a11y_get_gobject (ATK_OBJECT (selection)));
1382 if (!item)
1383 return FALSE;
1384
1385 e_selection_model_clear (item->selection);
1386 return TRUE;
1387 }
1388
1389 static AtkObject *
1390 selection_ref_selection (AtkSelection *selection,
1391 gint index)
1392 {
1393 AtkTable *table;
1394 gint row, col;
1395
1396 table = ATK_TABLE (selection);
1397 row = atk_table_get_row_at_index (table, index);
1398 col = atk_table_get_column_at_index (table, index);
1399 if (!atk_table_is_row_selected (table, row))
1400 return NULL;
1401
1402 return eti_ref_at (table, row, col);
1403 }
1404
1405 static gint
1406 selection_get_selection_count (AtkSelection *selection)
1407 {
1408 AtkTable *table;
1409 gint n_selected;
1410
1411 table = ATK_TABLE (selection);
1412 n_selected = atk_table_get_selected_rows (table, NULL);
1413 if (n_selected > 0)
1414 n_selected *= atk_table_get_n_columns (table);
1415 return n_selected;
1416 }
1417
1418 static gboolean
1419 selection_is_child_selected (AtkSelection *selection,
1420 gint i)
1421 {
1422 gint row;
1423
1424 row = atk_table_get_row_at_index (ATK_TABLE (selection), i);
1425 return atk_table_is_row_selected (ATK_TABLE (selection), row);
1426 }
1427
1428 void
1429 gal_a11y_e_table_item_init (void)
1430 {
1431 if (atk_get_root ())
1432 atk_registry_set_factory_type (
1433 atk_get_default_registry (),
1434 E_TYPE_TABLE_ITEM,
1435 gal_a11y_e_table_item_factory_get_type ());
1436 }