/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ /* * Authors: * Christopher James Lahey * Bolian Yin * * Copyright (C) 2002 Ximian, Inc. */ #include #include #include "gal-a11y-e-table-item.h" #include "gal-a11y-e-cell-registry.h" #include "gal-a11y-e-cell.h" #include "gal-a11y-util.h" #include #include #include #include #include #include #include #include #include #define CS_CLASS(a11y) (G_TYPE_INSTANCE_GET_CLASS ((a11y), C_TYPE_STREAM, GalA11yETableItemClass)) static GObjectClass *parent_class; static AtkComponentIface *component_parent_iface; static GType parent_type; static gint priv_offset; static GQuark quark_accessible_object = 0; #define GET_PRIVATE(object) ((GalA11yETableItemPrivate *) (((char *) object) + priv_offset)) #define PARENT_TYPE (parent_type) struct _GalA11yETableItemPrivate { AtkObject *parent; gint index_in_parent; gint cols; gint rows; gpointer *cell_data; int selection_change_id; int cursor_change_id; ETableCol ** columns; ESelectionModel *selection; GtkWidget *widget; }; static gboolean gal_a11y_e_table_item_ref_selection (GalA11yETableItem *a11y, ESelectionModel *selection); static gboolean gal_a11y_e_table_item_unref_selection (GalA11yETableItem *a11y); static gpointer *eti_reinit_data (AtkTable *table, ETableItem *item); #if 0 static void unref_accessible (gpointer user_data, GObject *obj_loc) { GalA11yETableItem *a11y = GAL_A11Y_E_TABLE_ITEM (user_data); GET_PRIVATE (a11y)->item = NULL; g_object_unref (a11y); } #endif inline static gint view_to_model_row(ETableItem *eti, int row) { if (eti->uses_source_model) { ETableSubset *etss = E_TABLE_SUBSET(eti->table_model); if (row >= 0 && row < etss->n_map) { eti->row_guess = row; return etss->map_table[row]; } else return -1; } else return row; } inline static gint view_to_model_col(ETableItem *eti, int col) { ETableCol *ecol = e_table_header_get_column (eti->header, col); return ecol ? ecol->col_idx : -1; } inline static gint model_to_view_row(ETableItem *eti, int row) { int i; if (row == -1) return -1; if (eti->uses_source_model) { ETableSubset *etss = E_TABLE_SUBSET(eti->table_model); if (eti->row_guess >= 0 && eti->row_guess < etss->n_map) { if (etss->map_table[eti->row_guess] == row) { return eti->row_guess; } } for (i = 0; i < etss->n_map; i++) { if (etss->map_table[i] == row) return i; } return -1; } else return row; } inline static gint model_to_view_col(ETableItem *eti, int col) { int i; if (col == -1) return -1; for (i = 0; i < eti->cols; i++) { ETableCol *ecol = e_table_header_get_column (eti->header, i); if (ecol->col_idx == col) return i; } return -1; } inline static GObject * eti_a11y_get_gobject (AtkObject *accessible) { return atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (accessible)); } static void eti_dispose (GObject *object) { GalA11yETableItem *a11y = GAL_A11Y_E_TABLE_ITEM (object); GalA11yETableItemPrivate *priv = GET_PRIVATE (a11y); priv->parent = NULL; if ( priv->cell_data != NULL ) { g_free(priv->cell_data); priv->cell_data = NULL; } if (priv->columns) { g_free(priv->columns); priv->columns = NULL; } if (parent_class->dispose) parent_class->dispose (object); if (priv->selection) gal_a11y_e_table_item_unref_selection (a11y); } /* Static functions */ static AtkObject * eti_get_parent (AtkObject *accessible) { GalA11yETableItem *a11y; g_return_val_if_fail (GAL_A11Y_IS_E_TABLE_ITEM (accessible), NULL); if (!eti_a11y_get_gobject (accessible)) /* defunct */ return NULL; a11y = GAL_A11Y_E_TABLE_ITEM (accessible); return GET_PRIVATE (a11y)->parent; } static gint eti_get_n_children (AtkObject *accessible) { g_return_val_if_fail (GAL_A11Y_IS_E_TABLE_ITEM (accessible), 0); if (!eti_a11y_get_gobject (accessible)) return 0; return atk_table_get_n_columns (ATK_TABLE (accessible)) * atk_table_get_n_rows (ATK_TABLE (accessible)); } static AtkObject* eti_ref_child (AtkObject *accessible, gint index) { ETableItem *item; gint col, row; g_return_val_if_fail (GAL_A11Y_IS_E_TABLE_ITEM (accessible), NULL); item = E_TABLE_ITEM (eti_a11y_get_gobject (accessible)); if (!item) return NULL; if (index < item->cols) { AtkObject *child; /* A column header is required */ child = atk_table_get_column_header (ATK_TABLE (accessible), index); if (child) g_object_ref (child); return child; } index -= item->cols; col = index % item->cols; row = index / item->cols; return atk_table_ref_at (ATK_TABLE (accessible), row, col); } static gint eti_get_index_in_parent (AtkObject *accessible) { GalA11yETableItem *a11y; g_return_val_if_fail (GAL_A11Y_IS_E_TABLE_ITEM (accessible), -1); if (!eti_a11y_get_gobject (accessible)) return -1; a11y = GAL_A11Y_E_TABLE_ITEM (accessible); return GET_PRIVATE (a11y)->index_in_parent; } static void eti_get_extents (AtkComponent *component, gint *x, gint *y, gint *width, gint *height, AtkCoordType coord_type) { ETableItem *item; double real_width; double real_height; int fake_width; int fake_height; item = E_TABLE_ITEM (eti_a11y_get_gobject (ATK_OBJECT (component))); if (!item) return; if (component_parent_iface && component_parent_iface->get_extents) component_parent_iface->get_extents (component, x, y, &fake_width, &fake_height, coord_type); gtk_object_get (GTK_OBJECT (item), "width", &real_width, "height", &real_height, NULL); if (width) *width = real_width; if (height) *height = real_height; } static AtkObject* eti_ref_accessible_at_point (AtkComponent *component, gint x, gint y, AtkCoordType coord_type) { int row = -1; int col = -1; int x_origin, y_origin; ETableItem *item; item = E_TABLE_ITEM (eti_a11y_get_gobject (ATK_OBJECT (component))); if (!item) return NULL; atk_component_get_position (component, &x_origin, &y_origin, coord_type); x -= x_origin; y -= y_origin; e_table_item_compute_location (item, &x, &y, &row, &col); if (row != -1 && col != -1) { return atk_table_ref_at (ATK_TABLE (component), row, col); } else { return NULL; } } static void cell_destroyed (gpointer data) { gint index; GalA11yETableItem * item; GalA11yECell * cell; g_return_if_fail (GAL_A11Y_IS_E_CELL (data)); cell = GAL_A11Y_E_CELL (data); item = GAL_A11Y_E_TABLE_ITEM (atk_gobject_accessible_for_object (G_OBJECT (GAL_A11Y_E_CELL(data)->item))); g_return_if_fail (item && GAL_A11Y_IS_E_TABLE_ITEM (item)); g_return_if_fail (cell->row < GET_PRIVATE(item)->rows && cell->view_col < GET_PRIVATE(item)->cols); index = cell->row * cell->item->cols + cell->view_col; if (GET_PRIVATE (item)->cell_data && GET_PRIVATE (item)->cell_data [index] == data) GET_PRIVATE (item)->cell_data [index] = NULL; } /* atk table */ static AtkObject* eti_ref_at (AtkTable *table, gint row, gint column) { ETableItem *item; AtkObject* ret; item = E_TABLE_ITEM (eti_a11y_get_gobject (ATK_OBJECT (table))); if (!item) return NULL; if (column >= 0 && column < item->cols && row >= 0 && row < item->rows && item->cell_views_realized) { ECellView *cell_view = item->cell_views[column]; ETableCol *ecol = e_table_header_get_column (item->header, column); gpointer * cell_data; cell_data = eti_reinit_data (table, item); if (cell_data == NULL) return NULL; if (cell_data[row*item->cols + column] == NULL) { ret = gal_a11y_e_cell_registry_get_object (NULL, item, cell_view, ATK_OBJECT (table), ecol->col_idx, column, row); cell_data[row*item->cols + column] = ret; if (ATK_IS_OBJECT (ret)) { gal_a11y_e_cell_add_state(ret, ATK_STATE_SHOWING, FALSE); gal_a11y_e_cell_add_state(ret, ATK_STATE_VISIBLE, FALSE); g_object_weak_ref (G_OBJECT (ret), (GWeakNotify) cell_destroyed, ret); } else ret = NULL; } else { ret = (AtkObject *) cell_data[row*item->cols + column]; if (ATK_IS_OBJECT (ret)) { g_object_ref (ret); } else { ret = NULL; } } return ret; } return NULL; } static gint eti_get_index_at (AtkTable *table, gint row, gint column) { ETableItem *item; item = E_TABLE_ITEM (eti_a11y_get_gobject (ATK_OBJECT (table))); if (!item) return -1; return column + row * item->cols; } static gint eti_get_column_at_index (AtkTable *table, gint index) { ETableItem *item; item = E_TABLE_ITEM (eti_a11y_get_gobject (ATK_OBJECT (table))); if (!item) return -1; return index % item->cols; } static gint eti_get_row_at_index (AtkTable *table, gint index) { ETableItem *item; item = E_TABLE_ITEM (eti_a11y_get_gobject (ATK_OBJECT (table))); if (!item) return -1; return index / item->cols; } static gint eti_get_n_columns (AtkTable *table) { ETableItem *item; item = E_TABLE_ITEM (eti_a11y_get_gobject (ATK_OBJECT (table))); if (!item) return -1; return item->cols; } static gint eti_get_n_rows (AtkTable *table) { ETableItem *item; item = E_TABLE_ITEM (eti_a11y_get_gobject (ATK_OBJECT (table))); if (!item) return -1; return item->rows; } static gint eti_get_column_extent_at (AtkTable *table, gint row, gint column) { ETableItem *item; int width; item = E_TABLE_ITEM (eti_a11y_get_gobject (ATK_OBJECT (table))); if (!item) return -1; e_table_item_get_cell_geometry (item, &row, &column, NULL, NULL, &width, NULL); return width; } static gint eti_get_row_extent_at (AtkTable *table, gint row, gint column) { ETableItem *item; int height; item = E_TABLE_ITEM (eti_a11y_get_gobject (ATK_OBJECT (table))); if (!item) return -1; e_table_item_get_cell_geometry (item, &row, &column, NULL, NULL, NULL, &height); return height; } static AtkObject * eti_get_caption (AtkTable *table) { /* Unimplemented */ return NULL; } static G_CONST_RETURN gchar * eti_get_column_description (AtkTable *table, gint column) { ETableItem *item; ETableCol *ecol; item = E_TABLE_ITEM (eti_a11y_get_gobject (ATK_OBJECT (table))); if (!item) return NULL; ecol = e_table_header_get_column (item->header, column); return ecol->text; } static AtkObject * eti_get_column_header (AtkTable *table, gint column) { ETableItem *item; ETableCol *ecol; AtkObject *atk_obj = NULL; ECell *ecell; item = E_TABLE_ITEM (eti_a11y_get_gobject (ATK_OBJECT (table))); if (!item) return NULL; ecol = e_table_header_get_column (item->header, column); ecell = ecol->ecell; if (ecell) atk_obj = atk_gobject_accessible_for_object (G_OBJECT (ecell)); return atk_obj; } static G_CONST_RETURN gchar * eti_get_row_description (AtkTable *table, gint row) { /* Unimplemented */ return NULL; } static AtkObject * eti_get_row_header (AtkTable *table, gint row) { /* Unimplemented */ return NULL; } static AtkObject * eti_get_summary (AtkTable *table) { /* Unimplemented */ return NULL; } static gboolean table_is_row_selected (AtkTable *table, gint row) { ETableItem *item; item = E_TABLE_ITEM (eti_a11y_get_gobject (ATK_OBJECT (table))); if (!item) return FALSE; return e_selection_model_is_row_selected(item->selection, row); } static gboolean table_is_selected (AtkTable *table, gint row, gint column) { return table_is_row_selected (table, row); } static gint table_get_selected_rows (AtkTable *table, gint **rows_selected) { ETableItem *item; gint n_selected, row, index_selected; item = E_TABLE_ITEM (eti_a11y_get_gobject (ATK_OBJECT (table))); if (!item) return 0; n_selected = e_selection_model_selected_count (item->selection); if (rows_selected) { *rows_selected = (gint *) g_malloc (n_selected * sizeof (gint)); index_selected = 0; for (row = 0; row < item->rows && index_selected < n_selected; ++row) { if (atk_table_is_row_selected (table, row)) { (*rows_selected)[index_selected] = row; ++index_selected; } } } return n_selected; } static gboolean table_add_row_selection (AtkTable *table, gint row) { ETableItem *item; item = E_TABLE_ITEM (eti_a11y_get_gobject (ATK_OBJECT (table))); if (!item) return FALSE; if (table_is_row_selected (table, row)) return TRUE; e_selection_model_toggle_single_row (item->selection, view_to_model_row (item, row)); return TRUE; } static gboolean table_remove_row_selection (AtkTable *table, gint row) { ETableItem *item; item = E_TABLE_ITEM (eti_a11y_get_gobject (ATK_OBJECT (table))); if (!item) return FALSE; if (!atk_table_is_row_selected (table, row)) return TRUE; e_selection_model_toggle_single_row (item->selection, row); return TRUE; } static void eti_atk_table_iface_init (AtkTableIface *iface) { iface->ref_at = eti_ref_at; iface->get_index_at = eti_get_index_at; iface->get_column_at_index = eti_get_column_at_index; iface->get_row_at_index = eti_get_row_at_index; iface->get_n_columns = eti_get_n_columns; iface->get_n_rows = eti_get_n_rows; iface->get_column_extent_at = eti_get_column_extent_at; iface->get_row_extent_at = eti_get_row_extent_at; iface->get_caption = eti_get_caption; iface->get_column_description = eti_get_column_description; iface->get_column_header = eti_get_column_header; iface->get_row_description = eti_get_row_description; iface->get_row_header = eti_get_row_header; iface->get_summary = eti_get_summary; iface->is_row_selected = table_is_row_selected; iface->is_selected = table_is_selected; iface->get_selected_rows = table_get_selected_rows; iface->add_row_selection = table_add_row_selection; iface->remove_row_selection = table_remove_row_selection; } static void eti_atk_component_iface_init (AtkComponentIface *iface) { component_parent_iface = g_type_interface_peek_parent (iface); iface->ref_accessible_at_point = eti_ref_accessible_at_point; iface->get_extents = eti_get_extents; } static void eti_rows_inserted (ETableModel * model, int row, int count, AtkObject * table_item) { gint n_cols,n_rows,i,j; gpointer *cell_data; GalA11yETableItem * item_a11y; gint old_nrows; g_return_if_fail (table_item); item_a11y = GAL_A11Y_E_TABLE_ITEM (table_item); n_cols = atk_table_get_n_columns (ATK_TABLE(table_item)); n_rows = atk_table_get_n_rows (ATK_TABLE(table_item)); old_nrows = GET_PRIVATE(item_a11y)->rows; g_return_if_fail (n_cols > 0 && n_rows > 0); g_return_if_fail (old_nrows == n_rows - count); cell_data = GET_PRIVATE(table_item)->cell_data; GET_PRIVATE(table_item)->cell_data = g_realloc (cell_data, (n_rows*n_cols) * sizeof(gpointer)); cell_data = GET_PRIVATE(table_item)->cell_data; GET_PRIVATE(table_item)->rows = n_rows; /* If rows are insert in the middle of a table. */ if (row + count < n_rows ) { memmove(&cell_data[(row+count)*n_cols], &cell_data[row*n_cols], (old_nrows-row)*n_cols*sizeof(gpointer)); /* Update cell's index. */ for (i = row + count; i < n_rows; i ++) { for (j = 0; j < n_cols; j ++) if (cell_data[i*n_cols + j] != NULL) { AtkObject * a11y; a11y = ATK_OBJECT(cell_data[i*n_cols + j]); GAL_A11Y_E_CELL(a11y)->row = i; } } } /* Clear cache for the new added rows. */ for (i = row ; i < row+count; i ++) for (j = 0 ; j < n_cols; j ++) cell_data [i*n_cols + j] = NULL; g_signal_emit_by_name (table_item, "row-inserted", row, count, NULL); for (i = row; i < (row + count); i ++) { for (j = 0; j < n_cols; j ++) { g_signal_emit_by_name (table_item, "children_changed::add", ( (i*n_cols) + j), NULL, NULL); } } g_signal_emit_by_name (table_item, "visible-data-changed"); } /* * reinit the eti's private data * make sure it is synchronized with the gal-e-table-item */ static gpointer * eti_reinit_data (AtkTable *table, ETableItem *item) { gpointer * cell_data; int oldsize, newsize; cell_data = GET_PRIVATE (table)->cell_data; if (GET_PRIVATE (table)->rows != item->rows || GET_PRIVATE (table)->cols != item->cols ) { /* reinit cell_data */ oldsize = GET_PRIVATE (table)->cols * GET_PRIVATE (table)->rows; newsize = item->cols*item->rows; GET_PRIVATE (table)->cols = item->cols; GET_PRIVATE (table)->rows = item->rows; cell_data = g_realloc(cell_data, newsize*sizeof(gpointer)); if (newsize > oldsize) memset(&cell_data[oldsize], 0, (newsize-oldsize)*sizeof(gpointer)); GET_PRIVATE (table)->cell_data = cell_data; } return cell_data; } /* * clear all the AtkObjects stored in the cell_data * doesn't free the cell_data or resize it. */ static void eti_clear_rows (ETableModel * model, AtkObject * table_item, int row, int count) { gint i,j, n_rows, n_cols; gpointer *cell_data; g_return_if_fail (model && table_item); cell_data = GET_PRIVATE (table_item)->cell_data; g_return_if_fail (cell_data); n_rows = GET_PRIVATE (table_item)->rows; n_cols = GET_PRIVATE (table_item)->cols; g_return_if_fail( row >= 0 && count > 0 && row+count <= n_rows); /* DEFUNCT the deleted cells. */ for (i = row; i < row+count; i ++) { for (j = 0; j < n_cols; j ++) { if (cell_data[i*n_cols + j] != NULL) { AtkObject * a11y; a11y = ATK_OBJECT(cell_data[i*n_cols + j]); gal_a11y_e_cell_add_state (GAL_A11Y_E_CELL(a11y), ATK_STATE_DEFUNCT, TRUE); cell_data[i*n_cols + j] = NULL; } } } g_signal_emit_by_name (table_item, "row-deleted", row, count, NULL); for (i = row; i < row+count; i ++) { for (j = 0; j < n_cols; j ++) { g_signal_emit_by_name (table_item, "children_changed::remove", ( (i*n_cols) + j), NULL, NULL); } } g_signal_emit_by_name (table_item, "visible-data-changed"); } static void eti_rows_deleted (ETableModel * model, int row, int count, AtkObject * table_item) { gint i,j, n_rows, n_cols, old_nrows; gpointer *cell_data; n_rows = atk_table_get_n_rows (ATK_TABLE(table_item)); n_cols = atk_table_get_n_columns (ATK_TABLE(table_item)); cell_data = GET_PRIVATE(table_item)->cell_data; old_nrows = GET_PRIVATE(table_item)->rows; g_return_if_fail ( row+count <= old_nrows); g_return_if_fail (old_nrows == n_rows + count); GET_PRIVATE(table_item)->rows = n_rows; /* DEFUNCT the deleted cells. */ for (i = row; i < row+count; i ++) { for (j = 0; j < n_cols; j ++) { if (cell_data[i*n_cols + j] != NULL) { AtkObject * a11y; a11y = ATK_OBJECT(cell_data[i*n_cols + j]); gal_a11y_e_cell_add_state (GAL_A11Y_E_CELL(a11y), ATK_STATE_DEFUNCT, TRUE); cell_data[i*n_cols + j] = NULL; } } } /* If more rows left, update the a11y object. */ if (old_nrows > row + count) { /* Remove the defunct cells in cache. */ memmove (&cell_data[row*n_cols], &cell_data[(row+count)*n_cols], ( old_nrows-row-count)*n_cols*sizeof(gpointer)); GET_PRIVATE(table_item)->cell_data = g_realloc (cell_data, n_rows*n_cols*sizeof(gpointer)); cell_data = GET_PRIVATE(table_item)->cell_data; /* Update index of cells below the deleted rows. */ for (i = row; i < n_rows; i ++) { for (j = 0; j < n_cols; j ++) { if (cell_data[i*n_cols + j] != NULL) { AtkObject * a11y; a11y = ATK_OBJECT(cell_data[i*n_cols + j]); GAL_A11Y_E_CELL(a11y)->row = i; } } } } g_signal_emit_by_name (table_item, "row-deleted", row, count, NULL); for (i = row; i < (row + count); i ++) { for (j = 0; j < n_cols; j ++) { g_signal_emit_by_name (table_item, "children_changed::remove", ( (i*n_cols) + j), NULL, NULL); } } g_signal_emit_by_name (table_item, "visible-data-changed"); } static void eti_tree_model_node_changed_cb (ETreeModel *model, ETreePath node, ETableItem *eti) { AtkObject *atk_obj; GalA11yETableItem *a11y; g_return_if_fail (E_IS_TABLE_ITEM (eti)); atk_obj = atk_gobject_accessible_for_object (G_OBJECT (eti)); a11y = GAL_A11Y_E_TABLE_ITEM (atk_obj); /* we can't figure out which rows are changed, so just clear all of them ... */ if (GET_PRIVATE (a11y)->rows > 0) eti_clear_rows (eti->table_model, atk_obj, 0, GET_PRIVATE (a11y)->rows); } enum { ETI_HEADER_UNCHANGED = 0, ETI_HEADER_REORDERED, ETI_HEADER_NEW_ADDED, ETI_HEADER_REMOVED, }; /* * 1. Check what actually happened: column reorder, remove or add * 2. Update cache * 3. Emit signals */ static void eti_header_structure_changed (ETableHeader *eth, AtkObject *a11y) { gboolean reorder_found=FALSE, added_found=FALSE, removed_found=FALSE; GalA11yETableItem * a11y_item; ETableCol ** cols, **prev_cols; GalA11yETableItemPrivate *priv; gint *state = NULL, *prev_state = NULL, *reorder = NULL; gint i,j,n_rows,n_cols, prev_n_cols; gpointer * cell_data, * tmp; a11y_item = GAL_A11Y_E_TABLE_ITEM (a11y); priv = GET_PRIVATE (a11y_item); g_return_if_fail (priv && priv->cell_data); cell_data = priv->cell_data ; /* Assume rows do not changed. */ n_rows = priv->rows; prev_n_cols = priv->cols; prev_cols = priv->columns; cols = e_table_header_get_columns (eth); n_cols = eth->col_count; g_return_if_fail (cols && prev_cols && n_cols > 0); /* Init to ETI_HEADER_UNCHANGED. */ state = g_malloc0 (sizeof (gint) * n_cols); prev_state = g_malloc0 (sizeof (gint) * prev_n_cols); reorder = g_malloc0 (sizeof (gint) * n_cols); /* Compare with previously saved column headers. */ for ( i = 0 ; i < n_cols && cols[i]; i ++ ) { for ( j = 0 ; j < prev_n_cols && prev_cols[j]; j ++ ) { if ( prev_cols [j] == cols[i] && i != j ) { reorder_found = TRUE; state [i] = ETI_HEADER_REORDERED; reorder [i] = j; break; } else if (prev_cols[j] == cols[i]) { /* OK, this column is not changed. */ break; } } /* cols[i] is new added column. */ if ( j == prev_n_cols ) { added_found = TRUE; state[i] = ETI_HEADER_NEW_ADDED; } } /* Now try to find if there are removed columns. */ for (i = 0 ; i < prev_n_cols && prev_cols[i]; i ++) { for (j = 0 ; j < n_cols && cols[j]; j ++) if ( prev_cols [j] == cols[i] ) break; /* Removed columns found. */ if ( j == n_cols ) { removed_found = TRUE; prev_state[j] = ETI_HEADER_REMOVED; } } /* If nothing interesting just return. */ if (!reorder_found && !added_found && !removed_found) return; /* Now update our cache. */ tmp = g_malloc0 (n_rows*n_cols*sizeof(gpointer)); g_return_if_fail (tmp); for (i = 0 ; i < n_rows; i ++) { for ( j = 0 ; j < n_cols; j ++ ) { if ( state[j] == ETI_HEADER_REORDERED ) { tmp [i*n_cols+j] = cell_data[i*prev_n_cols+reorder[j]]; if (tmp[i*n_cols+j] && ATK_IS_OBJECT(tmp[i*n_cols+j])) { GAL_A11Y_E_CELL(tmp[i*n_cols+j])->view_col = j; } } else if (state[j] == ETI_HEADER_UNCHANGED) { tmp [i*n_cols+j] = cell_data[i*prev_n_cols+j]; } /* else: new added, keep NULL. */ } } g_free (cell_data); priv->cell_data = tmp; /* Emit signals */ if (reorder_found) g_signal_emit_by_name (G_OBJECT(a11y_item), "column_reordered"); if (removed_found) { for (i = 0; i < prev_n_cols; i ++ ) { if (prev_state[i] == ETI_HEADER_REMOVED) { g_signal_emit_by_name (G_OBJECT(a11y_item), "column-deleted", i, 1); for (j = 0 ; j < n_rows; j ++) g_signal_emit_by_name (G_OBJECT(a11y_item), "children_changed::remove", (j*prev_n_cols+i), NULL, NULL); } } } if (added_found) { for ( i = 0; i < n_cols; i ++ ) { if (state[i] == ETI_HEADER_NEW_ADDED) { g_signal_emit_by_name (G_OBJECT(a11y_item), "column-inserted", i, 1); for (j = 0 ; j < n_rows; j ++) g_signal_emit_by_name (G_OBJECT(a11y_item), "children_changed::add", (j*n_cols+i), NULL, NULL); } } } priv->cols = n_cols; g_free (state); g_free (reorder); g_free (prev_state); g_free (priv->columns); priv->columns = cols; } static void eti_real_initialize (AtkObject *obj, gpointer data) { ETableItem * eti; ETableModel * model; ATK_OBJECT_CLASS (parent_class)->initialize (obj, data); eti = E_TABLE_ITEM (data); model = eti->table_model; g_signal_connect (model, "model-rows-inserted", G_CALLBACK (eti_rows_inserted), obj); g_signal_connect (model, "model-rows-deleted", G_CALLBACK (eti_rows_deleted), obj); g_signal_connect (G_OBJECT (eti->header), "structure_change", G_CALLBACK (eti_header_structure_changed), obj); } static void eti_class_init (GalA11yETableItemClass *klass) { AtkObjectClass *atk_object_class = ATK_OBJECT_CLASS (klass); GObjectClass *object_class = G_OBJECT_CLASS (klass); quark_accessible_object = g_quark_from_static_string ("gtk-accessible-object"); parent_class = g_type_class_ref (PARENT_TYPE); object_class->dispose = eti_dispose; atk_object_class->get_parent = eti_get_parent; atk_object_class->get_n_children = eti_get_n_children; atk_object_class->ref_child = eti_ref_child; atk_object_class->get_index_in_parent = eti_get_index_in_parent; atk_object_class->initialize = eti_real_initialize; } static void eti_init (GalA11yETableItem *a11y) { GalA11yETableItemPrivate *priv; priv = GET_PRIVATE (a11y); priv->parent = NULL; priv->index_in_parent = -1; priv->selection_change_id = 0; priv->cursor_change_id = 0; priv->selection = NULL; } /* atk selection */ static void atk_selection_interface_init (AtkSelectionIface *iface); static gboolean selection_add_selection (AtkSelection *selection, gint i); static gboolean selection_clear_selection (AtkSelection *selection); static AtkObject* selection_ref_selection (AtkSelection *selection, gint i); static gint selection_get_selection_count (AtkSelection *selection); static gboolean selection_is_child_selected (AtkSelection *selection, gint i); /* callbacks */ static void eti_a11y_selection_model_removed_cb (ETableItem *eti, ESelectionModel *selection, gpointer data); static void eti_a11y_selection_model_added_cb (ETableItem *eti, ESelectionModel *selection, gpointer data); static void eti_a11y_selection_changed_cb (ESelectionModel *selection, GalA11yETableItem *a11y); static void eti_a11y_cursor_changed_cb (ESelectionModel *selection, int row, int col, GalA11yETableItem *a11y); /** * gal_a11y_e_table_item_get_type: * @void: * * Registers the &GalA11yETableItem class if necessary, and returns the type ID * associated to it. * * Return value: The type ID of the &GalA11yETableItem class. **/ GType gal_a11y_e_table_item_get_type (void) { static GType type = 0; if (!type) { AtkObjectFactory *factory; GTypeInfo info = { sizeof (GalA11yETableItemClass), (GBaseInitFunc) NULL, (GBaseFinalizeFunc) NULL, (GClassInitFunc) eti_class_init, (GClassFinalizeFunc) NULL, NULL, /* class_data */ sizeof (GalA11yETableItem), 0, (GInstanceInitFunc) eti_init, NULL /* value_table_item */ }; static const GInterfaceInfo atk_component_info = { (GInterfaceInitFunc) eti_atk_component_iface_init, (GInterfaceFinalizeFunc) NULL, NULL }; static const GInterfaceInfo atk_table_info = { (GInterfaceInitFunc) eti_atk_table_iface_init, (GInterfaceFinalizeFunc) NULL, NULL }; static const GInterfaceInfo atk_selection_info = { (GInterfaceInitFunc) atk_selection_interface_init, (GInterfaceFinalizeFunc) NULL, NULL }; factory = atk_registry_get_factory (atk_get_default_registry (), GNOME_TYPE_CANVAS_ITEM); parent_type = atk_object_factory_get_accessible_type (factory); type = gal_a11y_type_register_static_with_private (PARENT_TYPE, "GalA11yETableItem", &info, 0, sizeof (GalA11yETableItemPrivate), &priv_offset); g_type_add_interface_static (type, ATK_TYPE_COMPONENT, &atk_component_info); g_type_add_interface_static (type, ATK_TYPE_TABLE, &atk_table_info); g_type_add_interface_static (type, ATK_TYPE_SELECTION, &atk_selection_info); } return type; } AtkObject * gal_a11y_e_table_item_new (AtkObject *parent, ETableItem *item, int index_in_parent) { GalA11yETableItem *a11y; AtkObject *accessible; int n; g_return_val_if_fail (item && item->cols >= 0 && item->rows >= 0, NULL); a11y = g_object_new (gal_a11y_e_table_item_get_type (), NULL); atk_object_initialize (ATK_OBJECT (a11y), item); GET_PRIVATE (a11y)->parent = parent; GET_PRIVATE (a11y)->index_in_parent = index_in_parent; accessible = ATK_OBJECT(a11y); accessible->role = ATK_ROLE_TREE_TABLE; /* Initialize cell data. */ n = item->cols * item->rows; GET_PRIVATE (a11y)->cols = item->cols; GET_PRIVATE (a11y)->rows = item->rows; if (n > 0) { GET_PRIVATE (a11y)->cell_data = g_malloc0(n*sizeof(gpointer)); /* memory error. */ if ( GET_PRIVATE (a11y)->cell_data == NULL) return NULL; } else GET_PRIVATE (a11y)->cell_data = NULL; GET_PRIVATE (a11y)->columns = e_table_header_get_columns (item->header); if ( GET_PRIVATE (a11y)->columns == NULL) return NULL; if (item) { g_signal_connect (G_OBJECT(item), "selection_model_removed", G_CALLBACK (eti_a11y_selection_model_removed_cb), NULL); g_signal_connect (G_OBJECT(item), "selection_model_added", G_CALLBACK (eti_a11y_selection_model_added_cb), NULL); if (item->selection) gal_a11y_e_table_item_ref_selection (a11y, item->selection); /* find the TableItem's parent: table or tree */ GET_PRIVATE (a11y)->widget = gtk_widget_get_parent (GTK_WIDGET (item->parent.canvas)); if (E_IS_TREE (GET_PRIVATE (a11y)->widget)) { ETreeModel *model; model = e_tree_get_model (E_TREE (GET_PRIVATE (a11y)->widget)); g_signal_connect (G_OBJECT(model), "node_changed", G_CALLBACK (eti_tree_model_node_changed_cb), item); } } if (parent) g_object_ref (parent); #if 0 if (item) g_object_weak_ref (G_OBJECT (item), unref_accessible, a11y); #endif return ATK_OBJECT (a11y); } static gboolean gal_a11y_e_table_item_ref_selection (GalA11yETableItem *a11y, ESelectionModel *selection) { GalA11yETableItemPrivate *priv; g_return_val_if_fail (a11y && selection, FALSE); priv = GET_PRIVATE (a11y); priv->selection_change_id = g_signal_connect ( G_OBJECT(selection), "selection_changed", G_CALLBACK (eti_a11y_selection_changed_cb), a11y); priv->cursor_change_id = g_signal_connect ( G_OBJECT(selection), "cursor_changed", G_CALLBACK (eti_a11y_cursor_changed_cb), a11y); priv->selection = selection; g_object_ref (selection); return TRUE; } static gboolean gal_a11y_e_table_item_unref_selection (GalA11yETableItem *a11y) { GalA11yETableItemPrivate *priv; g_return_val_if_fail (a11y, FALSE); priv = GET_PRIVATE (a11y); g_return_val_if_fail (priv->selection_change_id != 0, FALSE); g_return_val_if_fail (priv->cursor_change_id != 0, FALSE); g_signal_handler_disconnect (priv->selection, priv->selection_change_id); g_signal_handler_disconnect (priv->selection, priv->cursor_change_id); priv->cursor_change_id = 0; priv->selection_change_id = 0; g_object_unref (priv->selection); priv->selection = NULL; return TRUE; } /* callbacks */ static void eti_a11y_selection_model_removed_cb (ETableItem *eti, ESelectionModel *selection, gpointer data) { AtkObject *atk_obj; GalA11yETableItem *a11y; g_return_if_fail (E_IS_TABLE_ITEM (eti)); g_return_if_fail (E_IS_SELECTION_MODEL (selection)); atk_obj = atk_gobject_accessible_for_object (G_OBJECT (eti)); a11y = GAL_A11Y_E_TABLE_ITEM (atk_obj); if (selection == GET_PRIVATE (a11y)->selection) gal_a11y_e_table_item_unref_selection (a11y); } static void eti_a11y_selection_model_added_cb (ETableItem *eti, ESelectionModel *selection, gpointer data) { AtkObject *atk_obj; GalA11yETableItem *a11y; g_return_if_fail (E_IS_TABLE_ITEM (eti)); g_return_if_fail (E_IS_SELECTION_MODEL (selection)); atk_obj = atk_gobject_accessible_for_object (G_OBJECT (eti)); a11y = GAL_A11Y_E_TABLE_ITEM (atk_obj); if (GET_PRIVATE (a11y)->selection) gal_a11y_e_table_item_unref_selection (a11y); gal_a11y_e_table_item_ref_selection (a11y, selection); } static void eti_a11y_selection_changed_cb (ESelectionModel *selection, GalA11yETableItem *a11y) { g_return_if_fail (GAL_A11Y_IS_E_TABLE_ITEM (a11y)); g_signal_emit_by_name (a11y, "selection_changed"); } static void eti_a11y_cursor_changed_cb (ESelectionModel *selection, int row, int col, GalA11yETableItem *a11y) { AtkObject * cell; int view_row, view_col; ETableItem *item; g_return_if_fail (GAL_A11Y_IS_E_TABLE_ITEM (a11y)); g_signal_emit_by_name (a11y, "selection_changed"); item = E_TABLE_ITEM (eti_a11y_get_gobject (ATK_OBJECT (a11y))); g_return_if_fail (item); view_row = model_to_view_row (item, row); view_col = model_to_view_col (item, col); cell = atk_table_ref_at (ATK_TABLE (a11y), view_row, view_col); if (cell != NULL) { gal_a11y_e_cell_add_state (GAL_A11Y_E_CELL (cell), ATK_STATE_FOCUSED, FALSE); if (ATK_IS_OBJECT (cell)) g_signal_emit_by_name (a11y, "active-descendant-changed", cell); atk_focus_tracker_notify (cell); } } /* atk selection */ static void atk_selection_interface_init (AtkSelectionIface *iface) { g_return_if_fail (iface != NULL); iface->add_selection = selection_add_selection; iface->clear_selection = selection_clear_selection; iface->ref_selection = selection_ref_selection; iface->get_selection_count = selection_get_selection_count; iface->is_child_selected = selection_is_child_selected; } static gboolean selection_add_selection (AtkSelection *selection, gint index) { AtkTable *table; gint row, col; ETableItem *item; item = E_TABLE_ITEM (eti_a11y_get_gobject (ATK_OBJECT (selection))); if (!item) return FALSE; table = ATK_TABLE (selection); row = atk_table_get_row_at_index (table, index); atk_table_add_row_selection (table, row); col = atk_table_get_column_at_index (table, index); e_selection_model_change_cursor (item->selection, view_to_model_row (item, row), view_to_model_col (item, col)); e_selection_model_cursor_changed (item->selection, view_to_model_row (item, row), view_to_model_col (item, col)); e_selection_model_cursor_activated (item->selection, view_to_model_row (item, row), view_to_model_col (item, col)); return TRUE; } static gboolean selection_clear_selection (AtkSelection *selection) { ETableItem *item; item = E_TABLE_ITEM (eti_a11y_get_gobject (ATK_OBJECT (selection))); if (!item) return FALSE; e_selection_model_clear (item->selection); return TRUE; } static AtkObject * selection_ref_selection (AtkSelection *selection, gint index) { AtkTable *table; gint row, col; table = ATK_TABLE (selection); row = atk_table_get_row_at_index (table, index); col = atk_table_get_column_at_index (table, index); if (!atk_table_is_row_selected (table, row)) return NULL; return atk_table_ref_at (table, row, col); } static gint selection_get_selection_count (AtkSelection *selection) { AtkTable *table; gint n_selected; table = ATK_TABLE (selection); n_selected = atk_table_get_selected_rows (table, NULL); if (n_selected > 0) n_selected *= atk_table_get_n_columns (table); return n_selected; } static gboolean selection_is_child_selected (AtkSelection *selection, gint i) { gint row; row = atk_table_get_row_at_index (ATK_TABLE (selection), i); return atk_table_is_row_selected (ATK_TABLE (selection), row); }