/* GAIL - The GNOME Accessibility Implementation Library * Copyright 2001 Sun Microsystems Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #include #include #include "gailclist.h" #include "gailclistcell.h" #include "gailcellparent.h" /* Copied from gtkclist.c */ /* this defigns the base grid spacing */ #define CELL_SPACING 1 /* added the horizontal space at the beginning and end of a row*/ #define COLUMN_INSET 3 /* gives the top pixel of the given row in context of * the clist's voffset */ #define ROW_TOP_YPIXEL(clist, row) (((clist)->row_height * (row)) + \ (((row) + 1) * CELL_SPACING) + \ (clist)->voffset) /* returns the row index from a y pixel location in the * context of the clist's voffset */ #define ROW_FROM_YPIXEL(clist, y) (((y) - (clist)->voffset) / \ ((clist)->row_height + CELL_SPACING)) /* gives the left pixel of the given column in context of * the clist's hoffset */ #define COLUMN_LEFT_XPIXEL(clist, colnum) ((clist)->column[(colnum)].area.x + \ (clist)->hoffset) /* returns the column index from a x pixel location in the * context of the clist's hoffset */ static inline gint COLUMN_FROM_XPIXEL (GtkCList * clist, gint x) { gint i, cx; for (i = 0; i < clist->columns; i++) if (clist->column[i].visible) { cx = clist->column[i].area.x + clist->hoffset; if (x >= (cx - (COLUMN_INSET + CELL_SPACING)) && x <= (cx + clist->column[i].area.width + COLUMN_INSET)) return i; } /* no match */ return -1; } /* returns the top pixel of the given row in the context of * the list height */ #define ROW_TOP(clist, row) (((clist)->row_height + CELL_SPACING) * (row)) /* returns the left pixel of the given column in the context of * the list width */ #define COLUMN_LEFT(clist, colnum) ((clist)->column[(colnum)].area.x) /* returns the total height of the list */ #define LIST_HEIGHT(clist) (((clist)->row_height * ((clist)->rows)) + \ (CELL_SPACING * ((clist)->rows + 1))) static inline gint LIST_WIDTH (GtkCList * clist) { gint last_column; for (last_column = clist->columns - 1; last_column >= 0 && !clist->column[last_column].visible; last_column--); if (last_column >= 0) return (clist->column[last_column].area.x + clist->column[last_column].area.width + COLUMN_INSET + CELL_SPACING); return 0; } /* returns the GList item for the nth row */ #define ROW_ELEMENT(clist, row) (((row) == (clist)->rows - 1) ? \ (clist)->row_list_end : \ g_list_nth ((clist)->row_list, (row))) typedef struct _GailCListRow GailCListRow; typedef struct _GailCListCellData GailCListCellData; static void gail_clist_class_init (GailCListClass *klass); static void gail_clist_real_initialize (AtkObject *obj, gpointer data); static void gail_clist_finalize (GObject *object); static gint gail_clist_get_n_children (AtkObject *obj); static AtkObject* gail_clist_ref_child (AtkObject *obj, gint i); static AtkStateSet* gail_clist_ref_state_set (AtkObject *obj); static void atk_selection_interface_init (AtkSelectionIface *iface); static gboolean gail_clist_clear_selection (AtkSelection *selection); static AtkObject* gail_clist_ref_selection (AtkSelection *selection, gint i); static gint gail_clist_get_selection_count (AtkSelection *selection); static gboolean gail_clist_is_child_selected (AtkSelection *selection, gint i); static gboolean gail_clist_select_all_selection (AtkSelection *selection); static void atk_table_interface_init (AtkTableIface *iface); static gint gail_clist_get_index_at (AtkTable *table, gint row, gint column); static gint gail_clist_get_column_at_index (AtkTable *table, gint index); static gint gail_clist_get_row_at_index (AtkTable *table, gint index); static AtkObject* gail_clist_ref_at (AtkTable *table, gint row, gint column); static AtkObject* gail_clist_ref_at_actual (AtkTable *table, gint row, gint column); static AtkObject* gail_clist_get_caption (AtkTable *table); static gint gail_clist_get_n_columns (AtkTable *table); static gint gail_clist_get_n_actual_columns (GtkCList *clist); static G_CONST_RETURN gchar* gail_clist_get_column_description(AtkTable *table, gint column); static AtkObject* gail_clist_get_column_header (AtkTable *table, gint column); static gint gail_clist_get_n_rows (AtkTable *table); static G_CONST_RETURN gchar* gail_clist_get_row_description (AtkTable *table, gint row); static AtkObject* gail_clist_get_row_header (AtkTable *table, gint row); static AtkObject* gail_clist_get_summary (AtkTable *table); static gboolean gail_clist_add_row_selection (AtkTable *table, gint row); static gboolean gail_clist_remove_row_selection (AtkTable *table, gint row); static gint gail_clist_get_selected_rows (AtkTable *table, gint **rows_selected); static gboolean gail_clist_is_row_selected (AtkTable *table, gint row); static gboolean gail_clist_is_selected (AtkTable *table, gint row, gint column); static void gail_clist_set_caption (AtkTable *table, AtkObject *caption); static void gail_clist_set_column_description(AtkTable *table, gint column, const gchar *description); static void gail_clist_set_column_header (AtkTable *table, gint column, AtkObject *header); static void gail_clist_set_row_description (AtkTable *table, gint row, const gchar *description); static void gail_clist_set_row_header (AtkTable *table, gint row, AtkObject *header); static void gail_clist_set_summary (AtkTable *table, AtkObject *accessible); /* gailcellparent.h */ static void gail_cell_parent_interface_init (GailCellParentIface *iface); static void gail_clist_get_cell_extents (GailCellParent *parent, GailCell *cell, gint *x, gint *y, gint *width, gint *height, AtkCoordType coord_type); static void gail_clist_get_cell_area (GailCellParent *parent, GailCell *cell, GdkRectangle *cell_rect); static void gail_clist_select_row_gtk (GtkCList *clist, int row, int column, GdkEvent *event, gpointer data); static void gail_clist_unselect_row_gtk (GtkCList *clist, int row, int column, GdkEvent *event, gpointer data); static gint gail_clist_get_visible_column (AtkTable *table, int column); static gint gail_clist_get_actual_column (AtkTable *table, int visible_column); static void gail_clist_set_row_data (AtkTable *table, gint row, const gchar *description, AtkObject *header, gboolean is_header); static GailCListRow* gail_clist_get_row_data (AtkTable *table, gint row); static void gail_clist_get_visible_rect (GtkCList *clist, GdkRectangle *clist_rect); static gboolean gail_clist_is_cell_visible (GdkRectangle *cell_rect, GdkRectangle *visible_rect); static void gail_clist_cell_data_new (GailCList *clist, GailCell *cell, gint column, gint row); static void gail_clist_cell_destroyed (gpointer data); static void gail_clist_cell_data_remove (GailCList *clist, GailCell *cell); static GailCell* gail_clist_find_cell (GailCList *clist, gint index); static void gail_clist_adjustment_changed (GtkAdjustment *adjustment, GtkCList *clist); struct _GailCListColumn { gchar *description; AtkObject *header; }; struct _GailCListRow { GtkCListRow *row_data; int row_number; gchar *description; AtkObject *header; }; struct _GailCListCellData { GtkCell *gtk_cell; GailCell *gail_cell; int row_number; int column_number; }; static gpointer parent_class = NULL; GType gail_clist_get_type (void) { static GType type = 0; if (!type) { static const GTypeInfo tinfo = { sizeof (GailCListClass), (GBaseInitFunc) NULL, /* base init */ (GBaseFinalizeFunc) NULL, /* base finalize */ (GClassInitFunc) gail_clist_class_init, /* class init */ (GClassFinalizeFunc) NULL, /* class finalize */ NULL, /* class data */ sizeof (GailCList), /* instance size */ 0, /* nb preallocs */ (GInstanceInitFunc) NULL, /* instance init */ NULL /* value table */ }; static const GInterfaceInfo atk_table_info = { (GInterfaceInitFunc) atk_table_interface_init, (GInterfaceFinalizeFunc) NULL, NULL }; static const GInterfaceInfo atk_selection_info = { (GInterfaceInitFunc) atk_selection_interface_init, (GInterfaceFinalizeFunc) NULL, NULL }; static const GInterfaceInfo gail_cell_parent_info = { (GInterfaceInitFunc) gail_cell_parent_interface_init, (GInterfaceFinalizeFunc) NULL, NULL }; type = g_type_register_static (GAIL_TYPE_CONTAINER, "GailCList", &tinfo, 0); g_type_add_interface_static (type, ATK_TYPE_TABLE, &atk_table_info); g_type_add_interface_static (type, ATK_TYPE_SELECTION, &atk_selection_info); g_type_add_interface_static (type, GAIL_TYPE_CELL_PARENT, &gail_cell_parent_info); } return type; } static void gail_clist_class_init (GailCListClass *klass) { AtkObjectClass *class = ATK_OBJECT_CLASS (klass); GObjectClass *gobject_class = G_OBJECT_CLASS (klass); parent_class = g_type_class_peek_parent (klass); class->get_n_children = gail_clist_get_n_children; class->ref_child = gail_clist_ref_child; class->ref_state_set = gail_clist_ref_state_set; class->initialize = gail_clist_real_initialize; gobject_class->finalize = gail_clist_finalize; } AtkObject* gail_clist_new (GtkWidget *widget) { GObject *object; AtkObject *accessible; g_return_val_if_fail (GTK_IS_CLIST (widget), NULL); object = g_object_new (GAIL_TYPE_CLIST, NULL); accessible = ATK_OBJECT (object); atk_object_initialize (accessible, widget); accessible->role = ATK_ROLE_TABLE; return accessible; } static void gail_clist_real_initialize (AtkObject *obj, gpointer data) { GailCList *clist; GtkCList *gtk_clist; gint i; ATK_OBJECT_CLASS (parent_class)->initialize (obj, data); clist = GAIL_CLIST (obj); clist->caption = NULL; clist->summary = NULL; clist->row_data = NULL; clist->cell_data = NULL; clist->previous_selected_cell = NULL; gtk_clist = GTK_CLIST (data); clist->n_cols = gtk_clist->columns; clist->columns = g_new (GailCListColumn, gtk_clist->columns); for (i = 0; i < gtk_clist->columns; i++) { clist->columns[i].description = NULL; clist->columns[i].header = NULL; } /* * Set up signal handlers for select-row and unselect-row */ g_signal_connect (gtk_clist, "select-row", G_CALLBACK (gail_clist_select_row_gtk), obj); g_signal_connect (gtk_clist, "unselect-row", G_CALLBACK (gail_clist_unselect_row_gtk), obj); /* * Adjustment callbacks */ if (gtk_clist->hadjustment) { g_signal_connect (gtk_clist->hadjustment, "value_changed", G_CALLBACK (gail_clist_adjustment_changed), gtk_clist); } if (gtk_clist->vadjustment) { g_signal_connect (gtk_clist->vadjustment, "value_changed", G_CALLBACK (gail_clist_adjustment_changed), gtk_clist); } } static void gail_clist_finalize (GObject *object) { GailCList *clist = GAIL_CLIST (object); gint i; GArray *array; if (clist->caption) g_object_unref (clist->caption); if (clist->summary) g_object_unref (clist->summary); for (i = 0; i < clist->n_cols; i++) { g_free (clist->columns[i].description); if (clist->columns[i].header) g_object_unref (clist->columns[i].header); } g_free (clist->columns); array = clist->row_data; if (clist->previous_selected_cell) g_object_unref (clist->previous_selected_cell); if (array) { for (i = 0; i < array->len; i++) { GailCListRow *row_data; row_data = g_array_index (array, GailCListRow*, i); if (row_data->header) g_object_unref (row_data->header); g_free (row_data->description); } } if (clist->cell_data) { GList *temp_list; for (temp_list = clist->cell_data; temp_list; temp_list = temp_list->next) { g_list_free (temp_list->data); } g_list_free (clist->cell_data); } G_OBJECT_CLASS (parent_class)->finalize (object); } static gint gail_clist_get_n_children (AtkObject *obj) { GtkWidget *widget; gint row, col; g_return_val_if_fail (GAIL_IS_CLIST (obj), 0); widget = GTK_ACCESSIBLE (obj)->widget; if (widget == NULL) /* * State is defunct */ return 0; row = gail_clist_get_n_rows (ATK_TABLE (obj)); col = gail_clist_get_n_actual_columns (GTK_CLIST (widget)); return (row * col); } static AtkObject* gail_clist_ref_child (AtkObject *obj, gint i) { GtkWidget *widget; gint row, col; gint n_columns; g_return_val_if_fail (GAIL_IS_CLIST (obj), NULL); g_return_val_if_fail (i >= 0, NULL); widget = GTK_ACCESSIBLE (obj)->widget; if (widget == NULL) /* * State is defunct */ return NULL; n_columns = gail_clist_get_n_actual_columns (GTK_CLIST (widget)); if (!n_columns) return NULL; row = i / n_columns; col = i % n_columns; return gail_clist_ref_at_actual (ATK_TABLE (obj), row, col); } static AtkStateSet* gail_clist_ref_state_set (AtkObject *obj) { AtkStateSet *state_set; GtkWidget *widget; state_set = ATK_OBJECT_CLASS (parent_class)->ref_state_set (obj); widget = GTK_ACCESSIBLE (obj)->widget; if (widget != NULL) atk_state_set_add_state (state_set, ATK_STATE_MANAGES_DESCENDANTS); return state_set; } static void atk_selection_interface_init (AtkSelectionIface *iface) { g_return_if_fail (iface != NULL); iface->clear_selection = gail_clist_clear_selection; iface->ref_selection = gail_clist_ref_selection; iface->get_selection_count = gail_clist_get_selection_count; iface->is_child_selected = gail_clist_is_child_selected; iface->select_all_selection = gail_clist_select_all_selection; } static gboolean gail_clist_clear_selection (AtkSelection *selection) { GtkCList *clist; GtkWidget *widget; widget = GTK_ACCESSIBLE (selection)->widget; if (widget == NULL) /* State is defunct */ return FALSE; clist = GTK_CLIST (widget); gtk_clist_unselect_all(clist); return TRUE; } static AtkObject* gail_clist_ref_selection (AtkSelection *selection, gint i) { gint visible_columns; gint selected_row; gint selected_column; gint *selected_rows; if ( i < 0 && i >= gail_clist_get_selection_count (selection)) return NULL; visible_columns = gail_clist_get_n_columns (ATK_TABLE (selection)); gail_clist_get_selected_rows (ATK_TABLE (selection), &selected_rows); selected_row = selected_rows[i / visible_columns]; g_free (selected_rows); selected_column = gail_clist_get_actual_column (ATK_TABLE (selection), i % visible_columns); return gail_clist_ref_at (ATK_TABLE (selection), selected_row, selected_column); } static gint gail_clist_get_selection_count (AtkSelection *selection) { gint n_rows_selected; n_rows_selected = gail_clist_get_selected_rows (ATK_TABLE (selection), NULL); if (n_rows_selected > 0) /* * The number of cells selected is the number of columns * times the number of selected rows */ return gail_clist_get_n_columns (ATK_TABLE (selection)) * n_rows_selected; return 0; } static gboolean gail_clist_is_child_selected (AtkSelection *selection, gint i) { gint row; row = atk_table_get_row_at_index (ATK_TABLE (selection), i); if (row == 0 && i >= gail_clist_get_n_columns (ATK_TABLE (selection))) return FALSE; return gail_clist_is_row_selected (ATK_TABLE (selection), row); } static gboolean gail_clist_select_all_selection (AtkSelection *selection) { GtkCList *clist; GtkWidget *widget; /* GtkArg arg; */ widget = GTK_ACCESSIBLE (selection)->widget; if (widget == NULL) /* State is defunct */ return FALSE; clist = GTK_CLIST (widget); gtk_clist_select_all(clist); return TRUE; } static void atk_table_interface_init (AtkTableIface *iface) { g_return_if_fail (iface != NULL); iface->ref_at = gail_clist_ref_at; iface->get_index_at = gail_clist_get_index_at; iface->get_column_at_index = gail_clist_get_column_at_index; iface->get_row_at_index = gail_clist_get_row_at_index; iface->get_caption = gail_clist_get_caption; iface->get_n_columns = gail_clist_get_n_columns; iface->get_column_description = gail_clist_get_column_description; iface->get_column_header = gail_clist_get_column_header; iface->get_n_rows = gail_clist_get_n_rows; iface->get_row_description = gail_clist_get_row_description; iface->get_row_header = gail_clist_get_row_header; iface->get_summary = gail_clist_get_summary; iface->add_row_selection = gail_clist_add_row_selection; iface->remove_row_selection = gail_clist_remove_row_selection; iface->get_selected_rows = gail_clist_get_selected_rows; iface->is_row_selected = gail_clist_is_row_selected; iface->is_selected = gail_clist_is_selected; iface->set_caption = gail_clist_set_caption; iface->set_column_description = gail_clist_set_column_description; iface->set_column_header = gail_clist_set_column_header; iface->set_row_description = gail_clist_set_row_description; iface->set_row_header = gail_clist_set_row_header; iface->set_summary = gail_clist_set_summary; } static AtkObject* gail_clist_ref_at (AtkTable *table, gint row, gint column) { GtkWidget *widget; gint actual_column; widget = GTK_ACCESSIBLE (table)->widget; if (widget == NULL) /* State is defunct */ return NULL; actual_column = gail_clist_get_actual_column (table, column); return gail_clist_ref_at_actual (table, row, actual_column); } static AtkObject* gail_clist_ref_at_actual (AtkTable *table, gint row, gint column) { /* * The column number pased to this function is the actual column number * whereas the column number passed to gail_clist_ref_at is the * visible column number */ GtkCList *clist; GtkWidget *widget; GtkCellType cellType; AtkObject *return_object; gint n_rows, n_columns; gint index; GailCell *cell; g_return_val_if_fail (GTK_IS_ACCESSIBLE (table), NULL); widget = GTK_ACCESSIBLE (table)->widget; if (widget == NULL) /* State is defunct */ return NULL; clist = GTK_CLIST (widget); n_rows = gail_clist_get_n_rows (table); n_columns = gail_clist_get_n_actual_columns (clist); if (row < 0 || row >= n_rows) return NULL; if (column < 0 || column >= n_columns) return NULL; /* * Check whether the child is cached */ index = column + row * n_columns; cell = gail_clist_find_cell (GAIL_CLIST (table), index); if (cell) { g_object_ref (cell); return ATK_OBJECT (cell); } cellType = gtk_clist_get_cell_type(clist, row, column); switch (cellType) { case GTK_CELL_TEXT: case GTK_CELL_PIXTEXT: return_object = gail_clist_cell_new (); break; case GTK_CELL_PIXMAP: return_object = NULL; break; default: /* Don't handle GTK_CELL_EMPTY or GTK_CELL_WIDGET, return NULL */ return_object = NULL; break; } if (return_object) { cell = GAIL_CELL (return_object); g_return_val_if_fail (ATK_IS_OBJECT (table), NULL); gail_cell_init (cell, widget, ATK_OBJECT (table), index); /* * Store the cell in a cache */ gail_clist_cell_data_new (GAIL_CLIST (table), cell, column, row); /* * If the column is visible, sets the cell's state */ if (clist->column[column].visible) { GdkRectangle cell_rect, visible_rect; gail_clist_get_cell_area (GAIL_CELL_PARENT (table), cell, &cell_rect); gail_clist_get_visible_rect (clist, &visible_rect); gail_cell_add_state (cell, ATK_STATE_VISIBLE, FALSE); if (gail_clist_is_cell_visible (&cell_rect, &visible_rect)) gail_cell_add_state (cell, ATK_STATE_SHOWING, FALSE); } /* * If a row is selected, all cells in the row are selected */ if (gail_clist_is_row_selected (table, row)) { gail_cell_add_state (cell, ATK_STATE_SELECTED, FALSE); if (clist->columns == 1) gail_cell_add_state (cell, ATK_STATE_FOCUSED, FALSE); } } return return_object; } static gint gail_clist_get_index_at (AtkTable *table, gint row, gint column) { gint n_cols, n_rows; n_cols = atk_table_get_n_columns (table); n_rows = atk_table_get_n_rows (table); g_return_val_if_fail (row < n_rows, 0); g_return_val_if_fail (column < n_cols, 0); return row * n_cols + column; } static gint gail_clist_get_column_at_index (AtkTable *table, gint index) { gint n_cols; n_cols = atk_table_get_n_columns (table); if (n_cols == 0) return 0; else return (gint) (index % n_cols); } static gint gail_clist_get_row_at_index (AtkTable *table, gint index) { gint n_cols; n_cols = atk_table_get_n_columns (table); if (n_cols == 0) return 0; else return (gint) (index / n_cols); } static AtkObject* gail_clist_get_caption (AtkTable *table) { GailCList* obj = GAIL_CLIST (table); return obj->caption; } static gint gail_clist_get_n_columns (AtkTable *table) { GtkWidget *widget; GtkCList *clist; widget = GTK_ACCESSIBLE (table)->widget; if (widget == NULL) /* State is defunct */ return 0; clist = GTK_CLIST (widget); return gail_clist_get_visible_column (table, gail_clist_get_n_actual_columns (clist)); } static gint gail_clist_get_n_actual_columns (GtkCList *clist) { return clist->columns; } static G_CONST_RETURN gchar* gail_clist_get_column_description (AtkTable *table, gint column) { GailCList *clist = GAIL_CLIST (table); GtkWidget *widget; gint actual_column; if (column < 0 || column >= gail_clist_get_n_columns (table)) return NULL; actual_column = gail_clist_get_actual_column (table, column); if (clist->columns[actual_column].description) return (clist->columns[actual_column].description); widget = GTK_ACCESSIBLE (clist)->widget; if (widget == NULL) return NULL; return gtk_clist_get_column_title (GTK_CLIST (widget), actual_column); } static AtkObject* gail_clist_get_column_header (AtkTable *table, gint column) { GailCList *clist = GAIL_CLIST (table); GtkWidget *widget; GtkWidget *return_widget; gint actual_column; if (column < 0 || column >= gail_clist_get_n_columns (table)) return NULL; actual_column = gail_clist_get_actual_column (table, column); if (clist->columns[actual_column].header) return (clist->columns[actual_column].header); widget = GTK_ACCESSIBLE (clist)->widget; if (widget == NULL) return NULL; return_widget = gtk_clist_get_column_widget (GTK_CLIST (widget), actual_column); if (return_widget == NULL) return NULL; g_return_val_if_fail (GTK_IS_BIN (return_widget), NULL); return_widget = gtk_bin_get_child (GTK_BIN(return_widget)); return gtk_widget_get_accessible (return_widget); } static gint gail_clist_get_n_rows (AtkTable *table) { GtkWidget *widget; GtkCList *clist; widget = GTK_ACCESSIBLE (table)->widget; if (widget == NULL) /* State is defunct */ return 0; clist = GTK_CLIST (widget); return clist->rows; } static G_CONST_RETURN gchar* gail_clist_get_row_description (AtkTable *table, gint row) { GailCListRow* row_data; row_data = gail_clist_get_row_data (table, row); if (row_data == NULL) return NULL; return row_data->description; } static AtkObject* gail_clist_get_row_header (AtkTable *table, gint row) { GailCListRow* row_data; row_data = gail_clist_get_row_data (table, row); if (row_data == NULL) return NULL; return row_data->header; } static AtkObject* gail_clist_get_summary (AtkTable *table) { GailCList* obj = GAIL_CLIST (table); return obj->summary; } static gboolean gail_clist_add_row_selection (AtkTable *table, gint row) { GtkWidget *widget; GtkCList *clist; widget = GTK_ACCESSIBLE (table)->widget; if (widget == NULL) /* State is defunct */ return 0; clist = GTK_CLIST (widget); gtk_clist_select_row (clist, row, -1); if (gail_clist_is_row_selected (table, row)) return TRUE; return FALSE; } static gboolean gail_clist_remove_row_selection (AtkTable *table, gint row) { GtkWidget *widget; GtkCList *clist; widget = GTK_ACCESSIBLE (table)->widget; if (widget == NULL) /* State is defunct */ return 0; clist = GTK_CLIST (widget); if (gail_clist_is_row_selected (table, row)) { gtk_clist_select_row (clist, row, -1); return TRUE; } return FALSE; } static gint gail_clist_get_selected_rows (AtkTable *table, gint **rows_selected) { GtkWidget *widget; GtkCList *clist; GList *list; gint n_selected; gint i; widget = GTK_ACCESSIBLE (table)->widget; if (widget == NULL) /* State is defunct */ return 0; clist = GTK_CLIST (widget); n_selected = g_list_length (clist->selection); if (n_selected == 0) return 0; if (rows_selected) { gint *selected_rows; selected_rows = (gint*) g_malloc (sizeof (gint) * n_selected); list = clist->selection; i = 0; while (list) { selected_rows[i++] = GPOINTER_TO_INT (list->data); list = list->next; } *rows_selected = selected_rows; } return n_selected; } static gboolean gail_clist_is_row_selected (AtkTable *table, gint row) { GList *elem; GtkWidget *widget; GtkCList *clist; GtkCListRow *clist_row; widget = GTK_ACCESSIBLE (table)->widget; if (widget == NULL) /* State is defunct */ return FALSE; clist = GTK_CLIST (widget); if (row < 0 || row >= clist->rows) return FALSE; elem = ROW_ELEMENT (clist, row); if (!elem) return FALSE; clist_row = elem->data; return (clist_row->state == GTK_STATE_SELECTED); } static gboolean gail_clist_is_selected (AtkTable *table, gint row, gint column) { return gail_clist_is_row_selected (table, row); } static void gail_clist_set_caption (AtkTable *table, AtkObject *caption) { GailCList* obj = GAIL_CLIST (table); AtkPropertyValues values = { NULL }; AtkObject *old_caption; old_caption = obj->caption; obj->caption = caption; if (obj->caption) g_object_ref (obj->caption); g_value_init (&values.old_value, G_TYPE_POINTER); g_value_set_pointer (&values.old_value, old_caption); g_value_init (&values.new_value, G_TYPE_POINTER); g_value_set_pointer (&values.new_value, obj->caption); values.property_name = "accessible-table-caption"; g_signal_emit_by_name (table, "property_change::accessible-table-caption", &values, NULL); if (old_caption) g_object_unref (old_caption); } static void gail_clist_set_column_description (AtkTable *table, gint column, const gchar *description) { GailCList *clist = GAIL_CLIST (table); AtkPropertyValues values = { NULL }; gint actual_column; if (column < 0 || column >= gail_clist_get_n_columns (table)) return; if (description == NULL) return; actual_column = gail_clist_get_actual_column (table, column); g_free (clist->columns[actual_column].description); clist->columns[actual_column].description = g_strdup (description); g_value_init (&values.new_value, G_TYPE_INT); g_value_set_int (&values.new_value, column); values.property_name = "accessible-table-column-description"; g_signal_emit_by_name (table, "property_change::accessible-table-column-description", &values, NULL); } static void gail_clist_set_column_header (AtkTable *table, gint column, AtkObject *header) { GailCList *clist = GAIL_CLIST (table); AtkPropertyValues values = { NULL }; gint actual_column; if (column < 0 || column >= gail_clist_get_n_columns (table)) return; actual_column = gail_clist_get_actual_column (table, column); if (clist->columns[actual_column].header) g_object_unref (clist->columns[actual_column].header); if (header) g_object_ref (header); clist->columns[actual_column].header = header; g_value_init (&values.new_value, G_TYPE_INT); g_value_set_int (&values.new_value, column); values.property_name = "accessible-table-column-header"; g_signal_emit_by_name (table, "property_change::accessible-table-column-header", &values, NULL); } static void gail_clist_set_row_description (AtkTable *table, gint row, const gchar *description) { gail_clist_set_row_data (table, row, description, NULL, FALSE); } static void gail_clist_set_row_header (AtkTable *table, gint row, AtkObject *header) { gail_clist_set_row_data (table, row, NULL, header, TRUE); } static void gail_clist_set_summary (AtkTable *table, AtkObject *accessible) { GailCList* obj = GAIL_CLIST (table); AtkPropertyValues values = { 0, }; AtkObject *old_summary; old_summary = obj->summary; obj->summary = accessible; if (obj->summary) g_object_ref (obj->summary); g_value_init (&values.old_value, G_TYPE_POINTER); g_value_set_pointer (&values.old_value, old_summary); g_value_init (&values.new_value, G_TYPE_POINTER); g_value_set_pointer (&values.new_value, obj->summary); values.property_name = "accessible-table-summary"; g_signal_emit_by_name (table, "property_change::accessible-table-summary", &values, NULL); if (old_summary) g_object_unref (old_summary); } static void gail_cell_parent_interface_init (GailCellParentIface *iface) { g_return_if_fail (iface); iface->get_cell_extents = gail_clist_get_cell_extents; iface->get_cell_area = gail_clist_get_cell_area; } static void gail_clist_get_cell_extents (GailCellParent *parent, GailCell *cell, gint *x, gint *y, gint *width, gint *height, AtkCoordType coord_type) { GtkWidget* widget; GtkCList *clist; gint widget_x, widget_y, widget_width, widget_height; GdkRectangle cell_rect; GdkRectangle visible_rect; widget = GTK_ACCESSIBLE (parent)->widget; if (widget == NULL) return; clist = GTK_CLIST (widget); atk_component_get_extents (ATK_COMPONENT (parent), &widget_x, &widget_y, &widget_width, &widget_height, coord_type); gail_clist_get_cell_area (parent, cell, &cell_rect); *width = cell_rect.width; *height = cell_rect.height; gail_clist_get_visible_rect (clist, &visible_rect); if (gail_clist_is_cell_visible (&cell_rect, &visible_rect)) { *x = cell_rect.x + widget_x; *y = cell_rect.y + widget_y; } else { *x = G_MININT; *y = G_MININT; } } static void gail_clist_get_cell_area (GailCellParent *parent, GailCell *cell, GdkRectangle *cell_rect) { GtkWidget* widget; GtkCList *clist; gint column, row, n_columns; widget = GTK_ACCESSIBLE (parent)->widget; if (widget == NULL) return; clist = GTK_CLIST (widget); n_columns = gail_clist_get_n_actual_columns (clist); g_return_if_fail (n_columns > 0); column = cell->index % n_columns; row = cell->index / n_columns; cell_rect->x = COLUMN_LEFT (clist, column); cell_rect->y = ROW_TOP (clist, row); cell_rect->width = clist->column[column].area.width; cell_rect->height = clist->row_height; } static void gail_clist_select_row_gtk (GtkCList *clist, gint row, gint column, GdkEvent *event, gpointer data) { GailCList *gail_clist; GList *temp_list; AtkObject *selected_cell; gail_clist = GAIL_CLIST (data); for (temp_list = gail_clist->cell_data; temp_list; temp_list = temp_list->next) { GailCListCellData *cell_data; cell_data = (GailCListCellData *) (temp_list->data); if (row == cell_data->row_number) { /* * Row is selected */ gail_cell_add_state (cell_data->gail_cell, ATK_STATE_SELECTED, TRUE); } } if (clist->columns == 1) { selected_cell = gail_clist_ref_at (ATK_TABLE (data), row, 1); if (selected_cell) { if (gail_clist->previous_selected_cell) g_object_unref (gail_clist->previous_selected_cell); gail_clist->previous_selected_cell = selected_cell; gail_cell_add_state (GAIL_CELL (selected_cell), ATK_STATE_FOCUSED, FALSE); g_signal_emit_by_name (gail_clist, "active-descendant-changed", selected_cell); } } g_signal_emit_by_name (gail_clist, "selection_changed"); } static void gail_clist_unselect_row_gtk (GtkCList *clist, gint row, gint column, GdkEvent *event, gpointer data) { GailCList *gail_clist; GList *temp_list; gail_clist = GAIL_CLIST (data); for (temp_list = gail_clist->cell_data; temp_list; temp_list = temp_list->next) { GailCListCellData *cell_data; cell_data = (GailCListCellData *) (temp_list->data); if (row == cell_data->row_number) { /* * Row is unselected */ gail_cell_add_state (cell_data->gail_cell, ATK_STATE_FOCUSED, FALSE); gail_cell_remove_state (cell_data->gail_cell, ATK_STATE_SELECTED, TRUE); } } g_signal_emit_by_name (gail_clist, "selection_changed"); } /* * This function determines the number of visible columns * up to and including the specified column */ static gint gail_clist_get_visible_column (AtkTable *table, int column) { GtkWidget *widget; GtkCList *clist; gint i; gint vis_columns; widget = GTK_ACCESSIBLE (table)->widget; if (widget == NULL) /* State is defunct */ return 0; clist = GTK_CLIST (widget); for (i = 0, vis_columns = 0; i < column; i++) if (clist->column[i].visible) vis_columns++; return vis_columns; } static gint gail_clist_get_actual_column (AtkTable *table, int visible_column) { GtkWidget *widget; GtkCList *clist; gint i; gint vis_columns; widget = GTK_ACCESSIBLE (table)->widget; if (widget == NULL) /* State is defunct */ return 0; clist = GTK_CLIST (widget); for (i = 0, vis_columns = 0; i < clist->columns; i++) { if (clist->column[i].visible) { if (visible_column == vis_columns) return i; vis_columns++; } } return 0; } static void gail_clist_set_row_data (AtkTable *table, gint row, const gchar *description, AtkObject *header, gboolean is_header) { GtkWidget *widget; GtkCList *gtk_clist; GailCList *gail_clist; GArray *array; GailCListRow* row_data; gint i; gboolean found = FALSE; AtkPropertyValues values = { NULL }; gchar *signal_name; widget = GTK_ACCESSIBLE (table)->widget; if (widget == NULL) /* State is defunct */ return; gtk_clist = GTK_CLIST (widget); if (row < 0 || row >= gtk_clist->rows) return; gail_clist = GAIL_CLIST (table); if (gail_clist->row_data == NULL) gail_clist->row_data = g_array_sized_new (FALSE, TRUE, sizeof (GailCListRow *), 0); array = gail_clist->row_data; for (i = 0; i < array->len; i++) { row_data = g_array_index (array, GailCListRow*, i); if (row == row_data->row_number) { found = TRUE; if (is_header) { if (row_data->header) g_object_unref (row_data->header); row_data->header = header; if (row_data->header) g_object_ref (row_data->header); } else { g_free (row_data->description); row_data->description = g_strdup (row_data->description); } break; } } if (!found) { GList *elem; elem = ROW_ELEMENT (gtk_clist, row); g_return_if_fail (elem != NULL); row_data = g_new (GailCListRow, 1); row_data->row_number = row; row_data->row_data = elem->data; if (is_header) { row_data->header = header; if (row_data->header) g_object_ref (row_data->header); row_data->description = NULL; } else { row_data->description = g_strdup (row_data->description); row_data->header = NULL; } g_array_append_val (array, row_data); } g_value_init (&values.new_value, G_TYPE_INT); g_value_set_int (&values.new_value, row); if (is_header) { values.property_name = "accessible-table-row-header"; signal_name = "property_change::accessible-table-row-header"; } else { values.property_name = "accessible-table-row-description"; signal_name = "property_change::accessible-table-row-description"; } g_signal_emit_by_name (table, signal_name, &values, NULL); } static GailCListRow* gail_clist_get_row_data (AtkTable *table, gint row) { GtkWidget *widget; GtkCList *clist; GailCList *obj; GArray *array; GailCListRow* row_data; gint i; widget = GTK_ACCESSIBLE (table)->widget; if (widget == NULL) /* State is defunct */ return NULL; clist = GTK_CLIST (widget); if (row < 0 || row >= clist->rows) return NULL; obj = GAIL_CLIST (table); if (obj->row_data == NULL) return NULL; array = obj->row_data; for (i = 0; i < array->len; i++) { row_data = g_array_index (array, GailCListRow*, i); if (row == row_data->row_number) return row_data; } return NULL; } static void gail_clist_get_visible_rect (GtkCList *clist, GdkRectangle *clist_rect) { clist_rect->x = - clist->hoffset; clist_rect->y = - clist->voffset; clist_rect->width = clist->clist_window_width; clist_rect->height = clist->clist_window_height; } static gboolean gail_clist_is_cell_visible (GdkRectangle *cell_rect, GdkRectangle *visible_rect) { /* * A cell is reported as visible if any part of the cell is visible */ if (((cell_rect->x + cell_rect->width) < visible_rect->x) || ((cell_rect->y + cell_rect->height) < visible_rect->y) || (cell_rect->x > (visible_rect->x + visible_rect->width)) || (cell_rect->y > (visible_rect->y + visible_rect->height))) return FALSE; else return TRUE; } static void gail_clist_cell_data_new (GailCList *clist, GailCell *cell, gint column, gint row) { GList *elem; GailCListCellData *cell_data; GtkCList *gtk_clist; GtkCListRow *clist_row; gtk_clist = GTK_CLIST (GTK_ACCESSIBLE (clist)->widget); elem = g_list_nth (gtk_clist->row_list, row); g_return_if_fail (elem != NULL); clist_row = (GtkCListRow *) elem->data; cell_data = g_new (GailCListCellData, 1); cell_data->gail_cell = cell; cell_data->gtk_cell = &(clist_row->cell[column]); cell_data->column_number = column; cell_data->row_number = row; clist->cell_data = g_list_append (clist->cell_data, cell_data); g_object_weak_ref (G_OBJECT (cell), (GWeakNotify) gail_clist_cell_destroyed, cell); } static void gail_clist_cell_destroyed (gpointer data) { GailCell *cell = GAIL_CELL (data); AtkObject* parent; parent = atk_object_get_parent (ATK_OBJECT (cell)); gail_clist_cell_data_remove (GAIL_CLIST (parent), cell); } static void gail_clist_cell_data_remove (GailCList *clist, GailCell *cell) { GList *temp_list; for (temp_list = clist->cell_data; temp_list; temp_list = temp_list->next) { GailCListCellData *cell_data; cell_data = (GailCListCellData *) temp_list->data; if (cell_data->gail_cell == cell) { clist->cell_data = g_list_remove_link (clist->cell_data, temp_list); g_free (cell_data); return; } } g_warning ("No cell removed in gail_clist_cell_data_remove\n"); } static GailCell* gail_clist_find_cell (GailCList *clist, gint index) { GList *temp_list; gint n_cols; n_cols = clist->n_cols; for (temp_list = clist->cell_data; temp_list; temp_list = temp_list->next) { GailCListCellData *cell_data; gint real_index; cell_data = (GailCListCellData *) (temp_list->data); real_index = cell_data->column_number + n_cols * cell_data->row_number; if (real_index == index) return cell_data->gail_cell; } return NULL; } static void gail_clist_adjustment_changed (GtkAdjustment *adjustment, GtkCList *clist) { AtkObject *atk_obj; GdkRectangle visible_rect; GdkRectangle cell_rect; GailCList* obj; GList *temp_list; /* * The scrollbars have changed */ atk_obj = gtk_widget_get_accessible (GTK_WIDGET (clist)); obj = GAIL_CLIST (atk_obj); /* Get the currently visible area */ gail_clist_get_visible_rect (clist, &visible_rect); /* loop over the cells and report if they are visible or not. */ /* Must loop through them all */ for (temp_list = obj->cell_data; temp_list; temp_list = temp_list->next) { GailCell *cell; GailCListCellData *cell_data; cell_data = (GailCListCellData *) (temp_list->data); cell = cell_data->gail_cell; gail_clist_get_cell_area (GAIL_CELL_PARENT (atk_obj), cell, &cell_rect); if (gail_clist_is_cell_visible (&cell_rect, &visible_rect)) gail_cell_add_state (cell, ATK_STATE_SHOWING, TRUE); else gail_cell_remove_state (cell, ATK_STATE_SHOWING, TRUE); } g_signal_emit_by_name (atk_obj, "visible_data_changed"); }