
This way, the a11y code knows if a column was reordered, added or removed and can do the right things instead of trying to guess and getting it wrong. Also, this patch finalizes the changes so that only visible columns exist to the accessibility interface.
3265 lines
103 KiB
C
3265 lines
103 KiB
C
/* GAIL - The GNOME Accessibility Implementation Library
|
|
* Copyright 2001, 2002, 2003 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 "config.h"
|
|
|
|
#include <gtk/gtk.h>
|
|
#ifdef GDK_WINDOWING_X11
|
|
#include <gdk/x11/gdkx.h>
|
|
#endif
|
|
|
|
#include "gtktreeprivate.h"
|
|
#include "gtkwidgetprivate.h"
|
|
|
|
#include "gtktreeviewaccessible.h"
|
|
#include "gtkrenderercellaccessible.h"
|
|
#include "gtkbooleancellaccessible.h"
|
|
#include "gtkimagecellaccessible.h"
|
|
#include "gtkcontainercellaccessible.h"
|
|
#include "gtktextcellaccessible.h"
|
|
#include "gtkcellaccessibleparent.h"
|
|
|
|
typedef struct _GtkTreeViewAccessibleCellInfo GtkTreeViewAccessibleCellInfo;
|
|
struct _GtkTreeViewAccessibleCellInfo
|
|
{
|
|
GtkCellAccessible *cell;
|
|
GtkRBTree *tree;
|
|
GtkRBNode *node;
|
|
GtkTreeViewColumn *cell_col_ref;
|
|
GtkTreeViewAccessible *view;
|
|
};
|
|
|
|
/* signal handling */
|
|
|
|
static gboolean row_expanded_cb (GtkTreeView *tree_view,
|
|
GtkTreeIter *iter,
|
|
GtkTreePath *path);
|
|
static gboolean row_collapsed_cb (GtkTreeView *tree_view,
|
|
GtkTreeIter *iter,
|
|
GtkTreePath *path);
|
|
static void size_allocate_cb (GtkWidget *widget,
|
|
GtkAllocation *allocation);
|
|
static void selection_changed_cb (GtkTreeSelection *selection,
|
|
gpointer data);
|
|
|
|
static void cursor_changed (GtkTreeView *tree_view,
|
|
GtkTreeViewAccessible *accessible);
|
|
static gboolean focus_in (GtkWidget *widget);
|
|
static gboolean focus_out (GtkWidget *widget);
|
|
|
|
static void destroy_count_func (GtkTreeView *tree_view,
|
|
GtkTreePath *path,
|
|
gint count,
|
|
gpointer user_data);
|
|
|
|
/* Misc */
|
|
|
|
static void set_iter_nth_row (GtkTreeView *tree_view,
|
|
GtkTreeIter *iter,
|
|
gint row);
|
|
static gint get_row_from_tree_path (GtkTreeView *tree_view,
|
|
GtkTreePath *path);
|
|
static void iterate_thru_children (GtkTreeView *tree_view,
|
|
GtkTreeModel *tree_model,
|
|
GtkTreePath *tree_path,
|
|
GtkTreePath *orig,
|
|
gint *count,
|
|
gint depth);
|
|
static int cell_info_get_index (GtkTreeView *tree_view,
|
|
GtkTreeViewAccessibleCellInfo *info);
|
|
static void clean_cols (GtkTreeViewAccessible *tree_view,
|
|
GtkTreeViewColumn *tv_col);
|
|
static void traverse_cells (GtkTreeViewAccessible *tree_view,
|
|
GtkTreePath *tree_path,
|
|
gboolean inc_row);
|
|
static gboolean update_cell_value (GtkRendererCellAccessible *renderer_cell,
|
|
GtkTreeViewAccessible *accessible,
|
|
gboolean emit_change_signal);
|
|
static void set_cell_visibility (GtkTreeView *tree_view,
|
|
GtkCellAccessible *cell,
|
|
GtkTreeViewColumn *tv_col,
|
|
GtkTreePath *tree_path,
|
|
gboolean emit_signal);
|
|
static gboolean is_cell_showing (GtkTreeView *tree_view,
|
|
GdkRectangle *cell_rect);
|
|
static void set_expand_state (GtkTreeView *tree_view,
|
|
GtkTreeModel *tree_model,
|
|
GtkTreeViewAccessible *accessible,
|
|
GtkTreePath *tree_path,
|
|
gboolean set_on_ancestor);
|
|
static void set_cell_expandable (GtkCellAccessible *cell);
|
|
static void add_cell_actions (GtkCellAccessible *cell,
|
|
gboolean editable);
|
|
|
|
static void toggle_cell_toggled (GtkCellAccessible *cell);
|
|
static void edit_cell (GtkCellAccessible *cell);
|
|
static void activate_cell (GtkCellAccessible *cell);
|
|
static void cell_destroyed (gpointer data);
|
|
static void cell_info_new (GtkTreeViewAccessible *accessible,
|
|
GtkTreeModel *tree_model,
|
|
GtkRBTree *tree,
|
|
GtkRBNode *node,
|
|
GtkTreeViewColumn *tv_col,
|
|
GtkCellAccessible *cell);
|
|
static GtkCellAccessible *find_cell (GtkTreeViewAccessible *accessible,
|
|
gint index);
|
|
static void connect_model_signals (GtkTreeView *view,
|
|
GtkTreeViewAccessible *accessible);
|
|
static void disconnect_model_signals (GtkTreeViewAccessible *accessible);
|
|
static gint get_column_number (GtkTreeView *tree_view,
|
|
GtkTreeViewColumn *column);
|
|
static gint get_focus_index (GtkTreeView *tree_view);
|
|
static gint get_index (GtkTreeView *tree_view,
|
|
GtkTreePath *path,
|
|
gint actual_column);
|
|
static void count_rows (GtkTreeModel *model,
|
|
GtkTreeIter *iter,
|
|
GtkTreePath *end_path,
|
|
gint *count,
|
|
gint level,
|
|
gint depth);
|
|
|
|
static gboolean get_rbtree_column_from_index (GtkTreeView *tree_view,
|
|
gint index,
|
|
GtkRBTree **tree,
|
|
GtkRBNode **node,
|
|
GtkTreeViewColumn **column);
|
|
|
|
static GtkTreeViewAccessibleCellInfo* find_cell_info (GtkTreeViewAccessible *view,
|
|
GtkCellAccessible *cell);
|
|
static AtkObject * get_header_from_column (GtkTreeViewColumn *tv_col);
|
|
|
|
|
|
static void atk_table_interface_init (AtkTableIface *iface);
|
|
static void atk_selection_interface_init (AtkSelectionIface *iface);
|
|
static void atk_component_interface_init (AtkComponentIface *iface);
|
|
static void gtk_cell_accessible_parent_interface_init (GtkCellAccessibleParentIface *iface);
|
|
|
|
G_DEFINE_TYPE_WITH_CODE (GtkTreeViewAccessible, _gtk_tree_view_accessible, GTK_TYPE_CONTAINER_ACCESSIBLE,
|
|
G_IMPLEMENT_INTERFACE (ATK_TYPE_TABLE, atk_table_interface_init)
|
|
G_IMPLEMENT_INTERFACE (ATK_TYPE_SELECTION, atk_selection_interface_init)
|
|
G_IMPLEMENT_INTERFACE (ATK_TYPE_COMPONENT, atk_component_interface_init)
|
|
G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_ACCESSIBLE_PARENT, gtk_cell_accessible_parent_interface_init))
|
|
|
|
|
|
static void
|
|
adjustment_changed (GtkAdjustment *adjustment,
|
|
GtkWidget *widget)
|
|
{
|
|
GtkTreeViewAccessible *accessible;
|
|
|
|
accessible = GTK_TREE_VIEW_ACCESSIBLE (gtk_widget_get_accessible (widget));
|
|
traverse_cells (accessible, NULL, FALSE);
|
|
}
|
|
|
|
static void
|
|
hadjustment_set_cb (GObject *widget,
|
|
GParamSpec *pspec,
|
|
gpointer data)
|
|
{
|
|
GtkTreeViewAccessible *accessible = data;
|
|
GtkAdjustment *adj;
|
|
|
|
g_object_get (widget, "hadjustment", &adj, NULL);
|
|
accessible->old_hadj = adj;
|
|
g_object_add_weak_pointer (G_OBJECT (accessible->old_hadj), (gpointer *)&accessible->old_hadj);
|
|
g_signal_connect (adj, "value-changed", G_CALLBACK (adjustment_changed), widget);
|
|
}
|
|
|
|
static void
|
|
vadjustment_set_cb (GObject *widget,
|
|
GParamSpec *pspec,
|
|
gpointer data)
|
|
{
|
|
GtkTreeViewAccessible *accessible = data;
|
|
GtkAdjustment *adj;
|
|
|
|
g_object_get (widget, "vadjustment", &adj, NULL);
|
|
accessible->old_vadj = adj;
|
|
g_object_add_weak_pointer (G_OBJECT (accessible->old_vadj), (gpointer *)&accessible->old_vadj);
|
|
g_signal_connect (adj, "value-changed",
|
|
G_CALLBACK (adjustment_changed), widget);
|
|
}
|
|
|
|
static GQuark
|
|
gtk_tree_view_accessible_get_data_quark (void)
|
|
{
|
|
static GQuark quark = 0;
|
|
|
|
if (G_UNLIKELY (quark == 0))
|
|
quark = g_quark_from_static_string ("gtk-tree-view-accessible-data");
|
|
|
|
return quark;
|
|
}
|
|
|
|
static void
|
|
cell_info_free (GtkTreeViewAccessibleCellInfo *cell_info)
|
|
{
|
|
if (cell_info->cell)
|
|
{
|
|
g_object_steal_qdata (G_OBJECT (cell_info->cell),
|
|
gtk_tree_view_accessible_get_data_quark ());
|
|
_gtk_cell_accessible_add_state (cell_info->cell, ATK_STATE_DEFUNCT, FALSE);
|
|
}
|
|
|
|
g_free (cell_info);
|
|
}
|
|
|
|
static GtkTreePath *
|
|
cell_info_get_path (GtkTreeViewAccessibleCellInfo *cell_info)
|
|
{
|
|
return _gtk_tree_view_find_path (NULL,
|
|
cell_info->tree,
|
|
cell_info->node);
|
|
}
|
|
|
|
static guint
|
|
cell_info_hash (gconstpointer info)
|
|
{
|
|
const GtkTreeViewAccessibleCellInfo *cell_info = info;
|
|
guint node, col;
|
|
|
|
node = GPOINTER_TO_UINT (cell_info->node);
|
|
col = GPOINTER_TO_UINT (cell_info->cell_col_ref);
|
|
|
|
return ((node << sizeof (guint) / 2) | (node >> sizeof (guint) / 2)) ^ col;
|
|
}
|
|
|
|
static gboolean
|
|
cell_info_equal (gconstpointer a, gconstpointer b)
|
|
{
|
|
const GtkTreeViewAccessibleCellInfo *cell_info_a = a;
|
|
const GtkTreeViewAccessibleCellInfo *cell_info_b = b;
|
|
|
|
return cell_info_a->node == cell_info_b->node &&
|
|
cell_info_a->cell_col_ref == cell_info_b->cell_col_ref;
|
|
}
|
|
|
|
static void
|
|
gtk_tree_view_accessible_initialize (AtkObject *obj,
|
|
gpointer data)
|
|
{
|
|
GtkTreeViewAccessible *accessible;
|
|
GtkTreeView *tree_view;
|
|
GtkTreeModel *tree_model;
|
|
GtkWidget *widget;
|
|
GtkTreeSelection *selection;
|
|
|
|
ATK_OBJECT_CLASS (_gtk_tree_view_accessible_parent_class)->initialize (obj, data);
|
|
|
|
accessible = GTK_TREE_VIEW_ACCESSIBLE (obj);
|
|
accessible->focus_cell = NULL;
|
|
accessible->old_hadj = NULL;
|
|
accessible->old_vadj = NULL;
|
|
accessible->idle_expand_id = 0;
|
|
accessible->idle_expand_path = NULL;
|
|
accessible->n_children_deleted = 0;
|
|
|
|
accessible->cell_infos = g_hash_table_new_full (cell_info_hash,
|
|
cell_info_equal, NULL, (GDestroyNotify) cell_info_free);
|
|
|
|
widget = GTK_WIDGET (data);
|
|
tree_view = GTK_TREE_VIEW (widget);
|
|
tree_model = gtk_tree_view_get_model (tree_view);
|
|
selection = gtk_tree_view_get_selection (tree_view);
|
|
|
|
g_signal_connect_after (widget, "row-collapsed",
|
|
G_CALLBACK (row_collapsed_cb), NULL);
|
|
g_signal_connect (widget, "row-expanded",
|
|
G_CALLBACK (row_expanded_cb), NULL);
|
|
g_signal_connect (widget, "size-allocate",
|
|
G_CALLBACK (size_allocate_cb), NULL);
|
|
g_signal_connect (selection, "changed",
|
|
G_CALLBACK (selection_changed_cb), obj);
|
|
|
|
g_signal_connect (tree_view, "cursor-changed",
|
|
G_CALLBACK (cursor_changed), accessible);
|
|
g_signal_connect (tree_view, "focus-in-event",
|
|
G_CALLBACK (focus_in), NULL);
|
|
g_signal_connect (tree_view, "focus-out-event",
|
|
G_CALLBACK (focus_out), NULL);
|
|
|
|
accessible->tree_model = tree_model;
|
|
if (tree_model)
|
|
{
|
|
g_object_add_weak_pointer (G_OBJECT (accessible->tree_model), (gpointer *)&accessible->tree_model);
|
|
connect_model_signals (tree_view, accessible);
|
|
|
|
if (gtk_tree_model_get_flags (tree_model) & GTK_TREE_MODEL_LIST_ONLY)
|
|
obj->role = ATK_ROLE_TABLE;
|
|
else
|
|
obj->role = ATK_ROLE_TREE_TABLE;
|
|
}
|
|
|
|
hadjustment_set_cb (G_OBJECT (widget), NULL, accessible);
|
|
vadjustment_set_cb (G_OBJECT (widget), NULL, accessible);
|
|
g_signal_connect (widget, "notify::hadjustment",
|
|
G_CALLBACK (hadjustment_set_cb), accessible);
|
|
g_signal_connect (widget, "notify::vadjustment",
|
|
G_CALLBACK (vadjustment_set_cb), accessible);
|
|
|
|
gtk_tree_view_set_destroy_count_func (tree_view,
|
|
destroy_count_func,
|
|
NULL, NULL);
|
|
}
|
|
|
|
static void
|
|
gtk_tree_view_accessible_finalize (GObject *object)
|
|
{
|
|
GtkTreeViewAccessible *accessible = GTK_TREE_VIEW_ACCESSIBLE (object);
|
|
|
|
/* remove any idle handlers still pending */
|
|
if (accessible->idle_expand_id)
|
|
g_source_remove (accessible->idle_expand_id);
|
|
|
|
if (accessible->tree_model)
|
|
disconnect_model_signals (accessible);
|
|
|
|
if (accessible->cell_infos)
|
|
g_hash_table_destroy (accessible->cell_infos);
|
|
|
|
G_OBJECT_CLASS (_gtk_tree_view_accessible_parent_class)->finalize (object);
|
|
}
|
|
|
|
static void
|
|
gtk_tree_view_accessible_notify_gtk (GObject *obj,
|
|
GParamSpec *pspec)
|
|
{
|
|
GtkWidget *widget;
|
|
GtkTreeView *tree_view;
|
|
GtkTreeViewAccessible *accessible;
|
|
GtkAdjustment *adj;
|
|
|
|
widget = GTK_WIDGET (obj);
|
|
accessible = GTK_TREE_VIEW_ACCESSIBLE (gtk_widget_get_accessible (widget));
|
|
tree_view = GTK_TREE_VIEW (widget);
|
|
|
|
if (g_strcmp0 (pspec->name, "model") == 0)
|
|
{
|
|
GtkTreeModel *tree_model;
|
|
AtkRole role;
|
|
|
|
tree_model = gtk_tree_view_get_model (tree_view);
|
|
if (accessible->tree_model)
|
|
disconnect_model_signals (accessible);
|
|
g_hash_table_remove_all (accessible->cell_infos);
|
|
accessible->tree_model = tree_model;
|
|
|
|
if (tree_model)
|
|
{
|
|
g_object_add_weak_pointer (G_OBJECT (accessible->tree_model), (gpointer *)&accessible->tree_model);
|
|
connect_model_signals (tree_view, accessible);
|
|
|
|
if (gtk_tree_model_get_flags (tree_model) & GTK_TREE_MODEL_LIST_ONLY)
|
|
role = ATK_ROLE_TABLE;
|
|
else
|
|
role = ATK_ROLE_TREE_TABLE;
|
|
}
|
|
else
|
|
{
|
|
role = ATK_ROLE_UNKNOWN;
|
|
}
|
|
atk_object_set_role (ATK_OBJECT (accessible), role);
|
|
g_object_freeze_notify (G_OBJECT (accessible));
|
|
g_signal_emit_by_name (accessible, "model-changed");
|
|
g_signal_emit_by_name (accessible, "visible-data-changed");
|
|
g_object_thaw_notify (G_OBJECT (accessible));
|
|
}
|
|
else if (g_strcmp0 (pspec->name, "hadjustment") == 0)
|
|
{
|
|
g_object_get (tree_view, "hadjustment", &adj, NULL);
|
|
g_signal_handlers_disconnect_by_func (accessible->old_hadj,
|
|
(gpointer) adjustment_changed,
|
|
widget);
|
|
accessible->old_hadj = adj;
|
|
g_object_add_weak_pointer (G_OBJECT (accessible->old_hadj), (gpointer *)&accessible->old_hadj);
|
|
g_signal_connect (adj, "value-changed", G_CALLBACK (adjustment_changed), tree_view);
|
|
}
|
|
else if (g_strcmp0 (pspec->name, "vadjustment") == 0)
|
|
{
|
|
g_object_get (tree_view, "vadjustment", &adj, NULL);
|
|
g_signal_handlers_disconnect_by_func (accessible->old_vadj,
|
|
(gpointer) adjustment_changed,
|
|
widget);
|
|
accessible->old_vadj = adj;
|
|
g_object_add_weak_pointer (G_OBJECT (accessible->old_hadj), (gpointer *)&accessible->old_vadj);
|
|
g_signal_connect (adj, "value-changed", G_CALLBACK (adjustment_changed), tree_view);
|
|
}
|
|
else
|
|
GTK_WIDGET_ACCESSIBLE_CLASS (_gtk_tree_view_accessible_parent_class)->notify_gtk (obj, pspec);
|
|
}
|
|
|
|
static void
|
|
gtk_tree_view_accessible_destroyed (GtkWidget *widget,
|
|
GtkAccessible *gtk_accessible)
|
|
{
|
|
GtkAdjustment *adj;
|
|
GtkTreeViewAccessible *accessible;
|
|
|
|
if (!GTK_IS_TREE_VIEW (widget))
|
|
return;
|
|
|
|
accessible = GTK_TREE_VIEW_ACCESSIBLE (gtk_accessible);
|
|
adj = accessible->old_hadj;
|
|
if (adj)
|
|
g_signal_handlers_disconnect_by_func (adj,
|
|
(gpointer) adjustment_changed,
|
|
widget);
|
|
adj = accessible->old_vadj;
|
|
if (adj)
|
|
g_signal_handlers_disconnect_by_func (adj,
|
|
(gpointer) adjustment_changed,
|
|
widget);
|
|
if (accessible->tree_model)
|
|
{
|
|
disconnect_model_signals (accessible);
|
|
accessible->tree_model = NULL;
|
|
}
|
|
if (accessible->focus_cell)
|
|
{
|
|
g_object_unref (accessible->focus_cell);
|
|
accessible->focus_cell = NULL;
|
|
}
|
|
if (accessible->idle_expand_id)
|
|
{
|
|
g_source_remove (accessible->idle_expand_id);
|
|
accessible->idle_expand_id = 0;
|
|
}
|
|
}
|
|
|
|
static void
|
|
gtk_tree_view_accessible_connect_widget_destroyed (GtkAccessible *accessible)
|
|
{
|
|
GtkWidget *widget;
|
|
|
|
widget = gtk_accessible_get_widget (accessible);
|
|
if (widget)
|
|
g_signal_connect_after (widget, "destroy",
|
|
G_CALLBACK (gtk_tree_view_accessible_destroyed), accessible);
|
|
|
|
GTK_ACCESSIBLE_CLASS (_gtk_tree_view_accessible_parent_class)->connect_widget_destroyed (accessible);
|
|
}
|
|
|
|
static gint
|
|
get_n_rows (GtkTreeView *tree_view)
|
|
{
|
|
GtkRBTree *tree;
|
|
|
|
tree = _gtk_tree_view_get_rbtree (tree_view);
|
|
|
|
if (tree == NULL)
|
|
return 0;
|
|
|
|
return tree->root->total_count;
|
|
}
|
|
|
|
static gint
|
|
get_n_columns (GtkTreeView *tree_view)
|
|
{
|
|
guint i, visible_columns;
|
|
|
|
visible_columns = 0;
|
|
|
|
for (i = 0; i < gtk_tree_view_get_n_columns (tree_view); i++)
|
|
{
|
|
GtkTreeViewColumn *column = gtk_tree_view_get_column (tree_view, i);
|
|
|
|
if (gtk_tree_view_column_get_visible (column))
|
|
visible_columns++;
|
|
}
|
|
|
|
return visible_columns;
|
|
}
|
|
static gint
|
|
gtk_tree_view_accessible_get_n_children (AtkObject *obj)
|
|
{
|
|
GtkWidget *widget;
|
|
GtkTreeView *tree_view;
|
|
|
|
widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (obj));
|
|
if (widget == NULL)
|
|
return 0;
|
|
|
|
tree_view = GTK_TREE_VIEW (widget);
|
|
return (get_n_rows (tree_view) + 1) * get_n_columns (tree_view);
|
|
}
|
|
|
|
static GtkTreeViewColumn *
|
|
get_visible_column (GtkTreeView *tree_view,
|
|
guint id)
|
|
{
|
|
guint i;
|
|
|
|
for (i = 0; i < gtk_tree_view_get_n_columns (tree_view); i++)
|
|
{
|
|
GtkTreeViewColumn *column = gtk_tree_view_get_column (tree_view, i);
|
|
|
|
if (!gtk_tree_view_column_get_visible (column))
|
|
continue;
|
|
|
|
if (id == 0)
|
|
return column;
|
|
|
|
id--;
|
|
}
|
|
|
|
g_return_val_if_reached (NULL);
|
|
}
|
|
|
|
static AtkObject *
|
|
gtk_tree_view_accessible_ref_child (AtkObject *obj,
|
|
gint i)
|
|
{
|
|
GtkWidget *widget;
|
|
GtkTreeViewAccessible *accessible;
|
|
GtkCellAccessible *cell;
|
|
GtkTreeView *tree_view;
|
|
GtkTreeModel *tree_model;
|
|
GtkCellRenderer *renderer;
|
|
GtkTreeIter iter;
|
|
GtkTreeViewColumn *tv_col;
|
|
GtkTreeSelection *selection;
|
|
GtkTreePath *path;
|
|
GtkRBTree *tree;
|
|
GtkRBNode *node;
|
|
AtkObject *child;
|
|
AtkObject *parent;
|
|
GtkTreeViewColumn *expander_tv;
|
|
GList *renderer_list;
|
|
GList *l;
|
|
GtkContainerCellAccessible *container = NULL;
|
|
GtkRendererCellAccessible *renderer_cell;
|
|
gboolean is_expander, is_expanded, retval;
|
|
gboolean editable = FALSE;
|
|
gint focus_index;
|
|
|
|
widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (obj));
|
|
if (widget == NULL)
|
|
return NULL;
|
|
|
|
if (i >= gtk_tree_view_accessible_get_n_children (obj))
|
|
return NULL;
|
|
|
|
accessible = GTK_TREE_VIEW_ACCESSIBLE (obj);
|
|
tree_view = GTK_TREE_VIEW (widget);
|
|
if (i < get_n_columns (tree_view))
|
|
{
|
|
tv_col = get_visible_column (tree_view, i);
|
|
child = get_header_from_column (tv_col);
|
|
if (child)
|
|
g_object_ref (child);
|
|
return child;
|
|
}
|
|
|
|
/* Check whether the child is cached */
|
|
cell = find_cell (accessible, i);
|
|
if (cell)
|
|
{
|
|
g_object_ref (cell);
|
|
return ATK_OBJECT (cell);
|
|
}
|
|
|
|
if (accessible->focus_cell == NULL)
|
|
focus_index = get_focus_index (tree_view);
|
|
else
|
|
focus_index = -1;
|
|
|
|
/* Find the RBTree and GtkTreeViewColumn for the index */
|
|
if (!get_rbtree_column_from_index (tree_view, i, &tree, &node, &tv_col))
|
|
return NULL;
|
|
|
|
path = _gtk_tree_view_find_path (tree_view, tree, node);
|
|
tree_model = gtk_tree_view_get_model (tree_view);
|
|
retval = gtk_tree_model_get_iter (tree_model, &iter, path);
|
|
if (!retval)
|
|
return NULL;
|
|
|
|
expander_tv = gtk_tree_view_get_expander_column (tree_view);
|
|
is_expander = FALSE;
|
|
is_expanded = FALSE;
|
|
if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_PARENT))
|
|
{
|
|
if (expander_tv == tv_col)
|
|
{
|
|
is_expander = TRUE;
|
|
is_expanded = node->children != NULL;
|
|
}
|
|
}
|
|
gtk_tree_view_column_cell_set_cell_data (tv_col, tree_model, &iter,
|
|
is_expander, is_expanded);
|
|
|
|
renderer_list = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (tv_col));
|
|
|
|
/* If there are more than one renderer in the list,
|
|
* make a container
|
|
*/
|
|
if (renderer_list && renderer_list->next)
|
|
{
|
|
GtkCellAccessible *container_cell;
|
|
|
|
container = _gtk_container_cell_accessible_new ();
|
|
|
|
container_cell = GTK_CELL_ACCESSIBLE (container);
|
|
_gtk_cell_accessible_initialise (container_cell, widget, ATK_OBJECT (accessible));
|
|
|
|
/* The GtkTreeViewAccessibleCellInfo structure for the container will
|
|
* be before the ones for the cells so that the first one we find for
|
|
* a position will be for the container
|
|
*/
|
|
cell_info_new (accessible, tree_model, tree, node, tv_col, container_cell);
|
|
parent = ATK_OBJECT (container);
|
|
}
|
|
else
|
|
parent = ATK_OBJECT (accessible);
|
|
|
|
child = NULL;
|
|
|
|
/* Now we make a fake cell_renderer if there is no cell
|
|
* in renderer_list
|
|
*/
|
|
if (renderer_list == NULL)
|
|
{
|
|
GtkCellRenderer *fake_renderer;
|
|
|
|
fake_renderer = g_object_new (GTK_TYPE_CELL_RENDERER_TEXT, NULL);
|
|
child = _gtk_text_cell_accessible_new ();
|
|
cell = GTK_CELL_ACCESSIBLE (child);
|
|
renderer_cell = GTK_RENDERER_CELL_ACCESSIBLE (child);
|
|
renderer_cell->renderer = fake_renderer;
|
|
|
|
/* Create the GtkTreeViewAccessibleCellInfo structure for this cell */
|
|
cell_info_new (accessible, tree_model, tree, node, tv_col, cell);
|
|
|
|
_gtk_cell_accessible_initialise (cell, widget, parent);
|
|
|
|
/* Set state if it is expandable */
|
|
if (is_expander)
|
|
{
|
|
set_cell_expandable (cell);
|
|
if (is_expanded)
|
|
_gtk_cell_accessible_add_state (cell, ATK_STATE_EXPANDED, FALSE);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (l = renderer_list; l; l = l->next)
|
|
{
|
|
renderer = GTK_CELL_RENDERER (l->data);
|
|
|
|
if (GTK_IS_CELL_RENDERER_TEXT (renderer))
|
|
{
|
|
g_object_get (G_OBJECT (renderer), "editable", &editable, NULL);
|
|
child = _gtk_text_cell_accessible_new ();
|
|
}
|
|
else if (GTK_IS_CELL_RENDERER_TOGGLE (renderer))
|
|
child = _gtk_boolean_cell_accessible_new ();
|
|
else if (GTK_IS_CELL_RENDERER_PIXBUF (renderer))
|
|
child = _gtk_image_cell_accessible_new ();
|
|
else
|
|
child = _gtk_renderer_cell_accessible_new ();
|
|
|
|
cell = GTK_CELL_ACCESSIBLE (child);
|
|
renderer_cell = GTK_RENDERER_CELL_ACCESSIBLE (child);
|
|
|
|
/* Create the GtkTreeViewAccessibleCellInfo for this cell */
|
|
if (parent == ATK_OBJECT (accessible))
|
|
cell_info_new (accessible, tree_model, tree, node, tv_col, cell);
|
|
|
|
_gtk_cell_accessible_initialise (cell, widget, parent);
|
|
|
|
if (container)
|
|
_gtk_container_cell_accessible_add_child (container, cell);
|
|
|
|
update_cell_value (renderer_cell, accessible, FALSE);
|
|
|
|
/* Add the actions appropriate for this cell */
|
|
add_cell_actions (cell, editable);
|
|
|
|
/* Set state if it is expandable */
|
|
if (is_expander)
|
|
{
|
|
set_cell_expandable (cell);
|
|
if (is_expanded)
|
|
_gtk_cell_accessible_add_state (cell, ATK_STATE_EXPANDED, FALSE);
|
|
}
|
|
|
|
/* If the column is visible, sets the cell's state */
|
|
if (gtk_tree_view_column_get_visible (tv_col))
|
|
set_cell_visibility (tree_view, cell, tv_col, path, FALSE);
|
|
|
|
/* If the row is selected, all cells on the row are selected */
|
|
selection = gtk_tree_view_get_selection (tree_view);
|
|
|
|
if (gtk_tree_selection_path_is_selected (selection, path))
|
|
_gtk_cell_accessible_add_state (cell, ATK_STATE_SELECTED, FALSE);
|
|
|
|
_gtk_cell_accessible_add_state (cell, ATK_STATE_FOCUSABLE, FALSE);
|
|
if (focus_index == i)
|
|
{
|
|
accessible->focus_cell = g_object_ref (cell);
|
|
_gtk_cell_accessible_add_state (cell, ATK_STATE_FOCUSED, FALSE);
|
|
g_signal_emit_by_name (accessible, "active-descendant-changed", cell);
|
|
}
|
|
}
|
|
g_list_free (renderer_list);
|
|
if (container)
|
|
child = ATK_OBJECT (container);
|
|
}
|
|
|
|
if (expander_tv == tv_col)
|
|
{
|
|
AtkRelationSet *relation_set;
|
|
AtkObject *accessible_array[1];
|
|
AtkRelation* relation;
|
|
AtkObject *parent_node;
|
|
|
|
relation_set = atk_object_ref_relation_set (ATK_OBJECT (child));
|
|
|
|
gtk_tree_path_up (path);
|
|
if (gtk_tree_path_get_depth (path) == 0)
|
|
parent_node = obj;
|
|
else
|
|
{
|
|
gint parent_index;
|
|
|
|
parent_index = get_index (tree_view, path, i % get_n_columns (tree_view));
|
|
parent_node = atk_object_ref_accessible_child (obj, parent_index);
|
|
}
|
|
accessible_array[0] = parent_node;
|
|
relation = atk_relation_new (accessible_array, 1,
|
|
ATK_RELATION_NODE_CHILD_OF);
|
|
atk_relation_set_add (relation_set, relation);
|
|
atk_object_add_relationship (parent_node, ATK_RELATION_NODE_PARENT_OF, child);
|
|
g_object_unref (relation);
|
|
g_object_unref (relation_set);
|
|
}
|
|
gtk_tree_path_free (path);
|
|
|
|
/* We do not increase the reference count here; when g_object_unref()
|
|
* is called for the cell then cell_destroyed() is called and this
|
|
* removes the cell from the cache.
|
|
*/
|
|
return child;
|
|
}
|
|
|
|
static AtkStateSet*
|
|
gtk_tree_view_accessible_ref_state_set (AtkObject *obj)
|
|
{
|
|
AtkStateSet *state_set;
|
|
GtkWidget *widget;
|
|
|
|
state_set = ATK_OBJECT_CLASS (_gtk_tree_view_accessible_parent_class)->ref_state_set (obj);
|
|
widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (obj));
|
|
|
|
if (widget != NULL)
|
|
atk_state_set_add_state (state_set, ATK_STATE_MANAGES_DESCENDANTS);
|
|
|
|
return state_set;
|
|
}
|
|
|
|
static void
|
|
_gtk_tree_view_accessible_class_init (GtkTreeViewAccessibleClass *klass)
|
|
{
|
|
AtkObjectClass *class = ATK_OBJECT_CLASS (klass);
|
|
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
|
GtkAccessibleClass *accessible_class = (GtkAccessibleClass*)klass;
|
|
GtkWidgetAccessibleClass *widget_class = (GtkWidgetAccessibleClass*)klass;
|
|
GtkContainerAccessibleClass *container_class = (GtkContainerAccessibleClass*)klass;
|
|
|
|
class->get_n_children = gtk_tree_view_accessible_get_n_children;
|
|
class->ref_child = gtk_tree_view_accessible_ref_child;
|
|
class->ref_state_set = gtk_tree_view_accessible_ref_state_set;
|
|
class->initialize = gtk_tree_view_accessible_initialize;
|
|
|
|
widget_class->notify_gtk = gtk_tree_view_accessible_notify_gtk;
|
|
|
|
accessible_class->connect_widget_destroyed = gtk_tree_view_accessible_connect_widget_destroyed;
|
|
|
|
/* The children of a GtkTreeView are the buttons at the top of the columns
|
|
* we do not represent these as children so we do not want to report
|
|
* children added or deleted when these changed.
|
|
*/
|
|
container_class->add_gtk = NULL;
|
|
container_class->remove_gtk = NULL;
|
|
|
|
gobject_class->finalize = gtk_tree_view_accessible_finalize;
|
|
}
|
|
|
|
static void
|
|
_gtk_tree_view_accessible_init (GtkTreeViewAccessible *view)
|
|
{
|
|
}
|
|
|
|
gint
|
|
get_focus_index (GtkTreeView *tree_view)
|
|
{
|
|
GtkTreePath *focus_path;
|
|
GtkTreeViewColumn *focus_column;
|
|
gint index;
|
|
|
|
gtk_tree_view_get_cursor (tree_view, &focus_path, &focus_column);
|
|
if (focus_path && focus_column)
|
|
index = get_index (tree_view, focus_path,
|
|
get_column_number (tree_view, focus_column));
|
|
else
|
|
index = -1;
|
|
|
|
if (focus_path)
|
|
gtk_tree_path_free (focus_path);
|
|
|
|
return index;
|
|
}
|
|
|
|
/* This function returns a reference to the accessible object
|
|
* for the cell in the treeview which has focus, if any
|
|
*/
|
|
static AtkObject *
|
|
gtk_tree_view_accessible_ref_focus_cell (GtkTreeView *tree_view)
|
|
{
|
|
AtkObject *focus_cell = NULL;
|
|
AtkObject *atk_obj;
|
|
gint focus_index;
|
|
|
|
focus_index = get_focus_index (tree_view);
|
|
if (focus_index >= 0)
|
|
{
|
|
atk_obj = gtk_widget_get_accessible (GTK_WIDGET (tree_view));
|
|
focus_cell = atk_object_ref_accessible_child (atk_obj, focus_index);
|
|
}
|
|
|
|
return focus_cell;
|
|
}
|
|
|
|
/* atkcomponent.h */
|
|
|
|
static AtkObject *
|
|
gtk_tree_view_accessible_ref_accessible_at_point (AtkComponent *component,
|
|
gint x,
|
|
gint y,
|
|
AtkCoordType coord_type)
|
|
{
|
|
GtkWidget *widget;
|
|
GtkTreeView *tree_view;
|
|
GtkTreePath *path;
|
|
GtkTreeViewColumn *tv_column;
|
|
gint x_pos, y_pos;
|
|
gint bx, by;
|
|
gboolean ret_val;
|
|
|
|
widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (component));
|
|
if (widget == NULL)
|
|
return NULL;
|
|
|
|
tree_view = GTK_TREE_VIEW (widget);
|
|
|
|
atk_component_get_extents (component, &x_pos, &y_pos, NULL, NULL, coord_type);
|
|
gtk_tree_view_convert_widget_to_bin_window_coords (tree_view, x, y, &bx, &by);
|
|
ret_val = gtk_tree_view_get_path_at_pos (tree_view,
|
|
bx - x_pos, by - y_pos,
|
|
&path, &tv_column, NULL, NULL);
|
|
if (ret_val)
|
|
{
|
|
gint index, column;
|
|
|
|
column = get_column_number (tree_view, tv_column);
|
|
index = get_index (tree_view, path, column);
|
|
gtk_tree_path_free (path);
|
|
|
|
return gtk_tree_view_accessible_ref_child (ATK_OBJECT (component), index);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void
|
|
atk_component_interface_init (AtkComponentIface *iface)
|
|
{
|
|
iface->ref_accessible_at_point = gtk_tree_view_accessible_ref_accessible_at_point;
|
|
}
|
|
|
|
/* atktable.h */
|
|
|
|
static gint
|
|
gtk_tree_view_accessible_get_index_at (AtkTable *table,
|
|
gint row,
|
|
gint column)
|
|
{
|
|
GtkWidget *widget;
|
|
gint n_cols, n_rows;
|
|
|
|
widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (table));
|
|
if (widget == NULL)
|
|
return -1;
|
|
|
|
n_cols = atk_table_get_n_columns (table);
|
|
n_rows = atk_table_get_n_rows (table);
|
|
|
|
if (row >= n_rows || column >= n_cols)
|
|
return -1;
|
|
|
|
return (row + 1) * n_cols + column;
|
|
}
|
|
|
|
static gint
|
|
gtk_tree_view_accessible_get_column_at_index (AtkTable *table,
|
|
gint index)
|
|
{
|
|
GtkWidget *widget;
|
|
gint n_columns;
|
|
|
|
widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (table));
|
|
if (widget == NULL)
|
|
return -1;
|
|
|
|
if (index >= gtk_tree_view_accessible_get_n_children (ATK_OBJECT (table)))
|
|
return -1;
|
|
|
|
n_columns = get_n_columns (GTK_TREE_VIEW (widget));
|
|
|
|
/* checked by the n_children() check above */
|
|
g_assert (n_columns > 0);
|
|
|
|
return index % n_columns;
|
|
}
|
|
|
|
static gint
|
|
gtk_tree_view_accessible_get_row_at_index (AtkTable *table,
|
|
gint index)
|
|
{
|
|
GtkWidget *widget;
|
|
GtkTreeView *tree_view;
|
|
|
|
widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (table));
|
|
if (widget == NULL)
|
|
return -1;
|
|
|
|
tree_view = GTK_TREE_VIEW (widget);
|
|
|
|
index /= get_n_columns (tree_view);
|
|
index--;
|
|
if (index >= get_n_rows (tree_view))
|
|
return -1;
|
|
|
|
return index;
|
|
}
|
|
|
|
static AtkObject *
|
|
gtk_tree_view_accessible_table_ref_at (AtkTable *table,
|
|
gint row,
|
|
gint column)
|
|
{
|
|
gint index;
|
|
|
|
index = gtk_tree_view_accessible_get_index_at (table, row, column);
|
|
if (index == -1)
|
|
return NULL;
|
|
|
|
return gtk_tree_view_accessible_ref_child (ATK_OBJECT (table), index);
|
|
}
|
|
|
|
static gint
|
|
gtk_tree_view_accessible_get_n_rows (AtkTable *table)
|
|
{
|
|
GtkWidget *widget;
|
|
|
|
widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (table));
|
|
if (widget == NULL)
|
|
return 0;
|
|
|
|
return get_n_rows (GTK_TREE_VIEW (widget));
|
|
}
|
|
|
|
static gint
|
|
gtk_tree_view_accessible_get_n_columns (AtkTable *table)
|
|
{
|
|
GtkWidget *widget;
|
|
|
|
widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (table));
|
|
if (widget == NULL)
|
|
return 0;
|
|
|
|
return get_n_columns (GTK_TREE_VIEW (widget));
|
|
}
|
|
|
|
static gboolean
|
|
gtk_tree_view_accessible_is_row_selected (AtkTable *table,
|
|
gint row)
|
|
{
|
|
GtkWidget *widget;
|
|
GtkTreeView *tree_view;
|
|
GtkTreeSelection *selection;
|
|
GtkTreeIter iter;
|
|
|
|
widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (table));
|
|
if (widget == NULL)
|
|
return FALSE;
|
|
|
|
if (row < 0)
|
|
return FALSE;
|
|
|
|
tree_view = GTK_TREE_VIEW (widget);
|
|
selection = gtk_tree_view_get_selection (tree_view);
|
|
|
|
set_iter_nth_row (tree_view, &iter, row);
|
|
return gtk_tree_selection_iter_is_selected (selection, &iter);
|
|
}
|
|
|
|
static gboolean
|
|
gtk_tree_view_accessible_is_selected (AtkTable *table,
|
|
gint row,
|
|
gint column)
|
|
{
|
|
return gtk_tree_view_accessible_is_row_selected (table, row);
|
|
}
|
|
|
|
static void
|
|
get_selected_rows (GtkTreeModel *model,
|
|
GtkTreePath *path,
|
|
GtkTreeIter *iter,
|
|
gpointer data)
|
|
{
|
|
GPtrArray *array = (GPtrArray *)data;
|
|
|
|
g_ptr_array_add (array, gtk_tree_path_copy (path));
|
|
}
|
|
|
|
static gint
|
|
gtk_tree_view_accessible_get_selected_rows (AtkTable *table,
|
|
gint **rows_selected)
|
|
{
|
|
GtkWidget *widget;
|
|
GtkTreeView *tree_view;
|
|
GtkTreeModel *tree_model;
|
|
GtkTreeIter iter;
|
|
GtkTreeSelection *selection;
|
|
GtkTreePath *tree_path;
|
|
gint ret_val = 0;
|
|
|
|
widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (table));
|
|
if (widget == NULL)
|
|
return 0;
|
|
|
|
tree_view = GTK_TREE_VIEW (widget);
|
|
selection = gtk_tree_view_get_selection (tree_view);
|
|
|
|
switch (gtk_tree_selection_get_mode (selection))
|
|
{
|
|
case GTK_SELECTION_SINGLE:
|
|
case GTK_SELECTION_BROWSE:
|
|
if (gtk_tree_selection_get_selected (selection, &tree_model, &iter))
|
|
{
|
|
gint row;
|
|
|
|
if (rows_selected)
|
|
{
|
|
*rows_selected = g_new (gint, 1);
|
|
tree_path = gtk_tree_model_get_path (tree_model, &iter);
|
|
row = get_row_from_tree_path (tree_view, tree_path);
|
|
gtk_tree_path_free (tree_path);
|
|
|
|
/* shouldn't ever happen */
|
|
g_return_val_if_fail (row != -1, 0);
|
|
|
|
*rows_selected[0] = row;
|
|
}
|
|
ret_val = 1;
|
|
}
|
|
break;
|
|
case GTK_SELECTION_MULTIPLE:
|
|
{
|
|
GPtrArray *array = g_ptr_array_new();
|
|
|
|
gtk_tree_selection_selected_foreach (selection, get_selected_rows, array);
|
|
ret_val = array->len;
|
|
|
|
if (rows_selected && ret_val)
|
|
{
|
|
gint i;
|
|
|
|
*rows_selected = g_new (gint, ret_val);
|
|
for (i = 0; i < ret_val; i++)
|
|
{
|
|
gint row;
|
|
|
|
tree_path = (GtkTreePath *) g_ptr_array_index (array, i);
|
|
row = get_row_from_tree_path (tree_view, tree_path);
|
|
gtk_tree_path_free (tree_path);
|
|
(*rows_selected)[i] = row;
|
|
}
|
|
}
|
|
g_ptr_array_free (array, FALSE);
|
|
}
|
|
break;
|
|
case GTK_SELECTION_NONE:
|
|
break;
|
|
}
|
|
return ret_val;
|
|
}
|
|
|
|
static gboolean
|
|
gtk_tree_view_accessible_add_row_selection (AtkTable *table,
|
|
gint row)
|
|
{
|
|
GtkWidget *widget;
|
|
GtkTreeView *tree_view;
|
|
GtkTreeModel *tree_model;
|
|
GtkTreeSelection *selection;
|
|
GtkTreePath *tree_path;
|
|
GtkTreeIter iter_to_row;
|
|
|
|
widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (table));
|
|
if (widget == NULL)
|
|
return FALSE;
|
|
|
|
if (!gtk_tree_view_accessible_is_row_selected (table, row))
|
|
{
|
|
tree_view = GTK_TREE_VIEW (widget);
|
|
tree_model = gtk_tree_view_get_model (tree_view);
|
|
selection = gtk_tree_view_get_selection (tree_view);
|
|
|
|
if (gtk_tree_model_get_flags (tree_model) & GTK_TREE_MODEL_LIST_ONLY)
|
|
{
|
|
tree_path = gtk_tree_path_new ();
|
|
gtk_tree_path_append_index (tree_path, row);
|
|
gtk_tree_selection_select_path (selection,tree_path);
|
|
gtk_tree_path_free (tree_path);
|
|
}
|
|
else
|
|
{
|
|
set_iter_nth_row (tree_view, &iter_to_row, row);
|
|
gtk_tree_selection_select_iter (selection, &iter_to_row);
|
|
}
|
|
}
|
|
|
|
return gtk_tree_view_accessible_is_row_selected (table, row);
|
|
}
|
|
|
|
static gboolean
|
|
gtk_tree_view_accessible_remove_row_selection (AtkTable *table,
|
|
gint row)
|
|
{
|
|
GtkWidget *widget;
|
|
GtkTreeView *tree_view;
|
|
GtkTreeSelection *selection;
|
|
|
|
widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (table));
|
|
if (widget == NULL)
|
|
return FALSE;
|
|
|
|
tree_view = GTK_TREE_VIEW (widget);
|
|
selection = gtk_tree_view_get_selection (tree_view);
|
|
|
|
if (gtk_tree_view_accessible_is_row_selected (table, row))
|
|
{
|
|
gtk_tree_selection_unselect_all (selection);
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static AtkObject *
|
|
gtk_tree_view_accessible_get_column_header (AtkTable *table,
|
|
gint in_col)
|
|
{
|
|
GtkWidget *widget;
|
|
GtkTreeView *tree_view;
|
|
GtkTreeViewColumn *tv_col;
|
|
|
|
widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (table));
|
|
if (widget == NULL)
|
|
return NULL;
|
|
|
|
tree_view = GTK_TREE_VIEW (widget);
|
|
if (in_col < 0 || in_col >= get_n_columns (tree_view))
|
|
return NULL;
|
|
|
|
tv_col = get_visible_column (tree_view, in_col);
|
|
return get_header_from_column (tv_col);
|
|
}
|
|
|
|
static const gchar *
|
|
gtk_tree_view_accessible_get_column_description (AtkTable *table,
|
|
gint in_col)
|
|
{
|
|
GtkWidget *widget;
|
|
GtkTreeView *tree_view;
|
|
GtkTreeViewColumn *tv_col;
|
|
|
|
widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (table));
|
|
if (widget == NULL)
|
|
return NULL;
|
|
|
|
tree_view = GTK_TREE_VIEW (widget);
|
|
if (in_col < 0 || in_col >= get_n_columns (tree_view))
|
|
return NULL;
|
|
|
|
tv_col = get_visible_column (tree_view, in_col);
|
|
return gtk_tree_view_column_get_title (tv_col);
|
|
}
|
|
|
|
static void
|
|
atk_table_interface_init (AtkTableIface *iface)
|
|
{
|
|
iface->ref_at = gtk_tree_view_accessible_table_ref_at;
|
|
iface->get_n_rows = gtk_tree_view_accessible_get_n_rows;
|
|
iface->get_n_columns = gtk_tree_view_accessible_get_n_columns;
|
|
iface->get_index_at = gtk_tree_view_accessible_get_index_at;
|
|
iface->get_column_at_index = gtk_tree_view_accessible_get_column_at_index;
|
|
iface->get_row_at_index = gtk_tree_view_accessible_get_row_at_index;
|
|
iface->is_row_selected = gtk_tree_view_accessible_is_row_selected;
|
|
iface->is_selected = gtk_tree_view_accessible_is_selected;
|
|
iface->get_selected_rows = gtk_tree_view_accessible_get_selected_rows;
|
|
iface->add_row_selection = gtk_tree_view_accessible_add_row_selection;
|
|
iface->remove_row_selection = gtk_tree_view_accessible_remove_row_selection;
|
|
iface->get_column_extent_at = NULL;
|
|
iface->get_row_extent_at = NULL;
|
|
iface->get_column_header = gtk_tree_view_accessible_get_column_header;
|
|
iface->get_column_description = gtk_tree_view_accessible_get_column_description;
|
|
}
|
|
|
|
/* atkselection.h */
|
|
|
|
static gboolean
|
|
gtk_tree_view_accessible_add_selection (AtkSelection *selection,
|
|
gint i)
|
|
{
|
|
AtkTable *table;
|
|
gint n_columns;
|
|
gint row;
|
|
|
|
table = ATK_TABLE (selection);
|
|
n_columns = gtk_tree_view_accessible_get_n_columns (table);
|
|
if (n_columns != 1)
|
|
return FALSE;
|
|
|
|
row = gtk_tree_view_accessible_get_row_at_index (table, i);
|
|
return gtk_tree_view_accessible_add_row_selection (table, row);
|
|
}
|
|
|
|
static gboolean
|
|
gtk_tree_view_accessible_clear_selection (AtkSelection *selection)
|
|
{
|
|
GtkWidget *widget;
|
|
GtkTreeView *tree_view;
|
|
GtkTreeSelection *tree_selection;
|
|
|
|
widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (selection));
|
|
if (widget == NULL)
|
|
return FALSE;
|
|
|
|
tree_view = GTK_TREE_VIEW (widget);
|
|
tree_selection = gtk_tree_view_get_selection (tree_view);
|
|
|
|
gtk_tree_selection_unselect_all (tree_selection);
|
|
return TRUE;
|
|
}
|
|
|
|
static AtkObject *
|
|
gtk_tree_view_accessible_ref_selection (AtkSelection *selection,
|
|
gint i)
|
|
{
|
|
AtkTable *table;
|
|
gint row;
|
|
gint n_selected;
|
|
gint n_columns;
|
|
gint *selected;
|
|
|
|
table = ATK_TABLE (selection);
|
|
n_columns = gtk_tree_view_accessible_get_n_columns (table);
|
|
n_selected = gtk_tree_view_accessible_get_selected_rows (table, &selected);
|
|
if (i >= n_columns * n_selected)
|
|
return NULL;
|
|
|
|
row = selected[i / n_columns];
|
|
g_free (selected);
|
|
|
|
return gtk_tree_view_accessible_table_ref_at (table, row, i % n_columns);
|
|
}
|
|
|
|
static gint
|
|
gtk_tree_view_accessible_get_selection_count (AtkSelection *selection)
|
|
{
|
|
AtkTable *table;
|
|
gint n_selected;
|
|
|
|
table = ATK_TABLE (selection);
|
|
n_selected = gtk_tree_view_accessible_get_selected_rows (table, NULL);
|
|
if (n_selected > 0)
|
|
n_selected *= gtk_tree_view_accessible_get_n_columns (table);
|
|
return n_selected;
|
|
}
|
|
|
|
static gboolean
|
|
gtk_tree_view_accessible_is_child_selected (AtkSelection *selection,
|
|
gint i)
|
|
{
|
|
GtkWidget *widget;
|
|
gint row;
|
|
|
|
widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (selection));
|
|
if (widget == NULL)
|
|
return FALSE;
|
|
|
|
row = atk_table_get_row_at_index (ATK_TABLE (selection), i);
|
|
|
|
return gtk_tree_view_accessible_is_row_selected (ATK_TABLE (selection), row);
|
|
}
|
|
|
|
static void atk_selection_interface_init (AtkSelectionIface *iface)
|
|
{
|
|
iface->add_selection = gtk_tree_view_accessible_add_selection;
|
|
iface->clear_selection = gtk_tree_view_accessible_clear_selection;
|
|
iface->ref_selection = gtk_tree_view_accessible_ref_selection;
|
|
iface->get_selection_count = gtk_tree_view_accessible_get_selection_count;
|
|
iface->is_child_selected = gtk_tree_view_accessible_is_child_selected;
|
|
}
|
|
|
|
#define EXTRA_EXPANDER_PADDING 4
|
|
|
|
static void
|
|
gtk_tree_view_accessible_get_cell_area (GtkCellAccessibleParent *parent,
|
|
GtkCellAccessible *cell,
|
|
GdkRectangle *cell_rect)
|
|
{
|
|
GtkWidget *widget;
|
|
GtkTreeView *tree_view;
|
|
GtkTreeViewColumn *tv_col;
|
|
GtkTreePath *path;
|
|
AtkObject *parent_cell;
|
|
GtkTreeViewAccessibleCellInfo *cell_info;
|
|
GtkCellAccessible *top_cell;
|
|
|
|
widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (parent));
|
|
if (widget == NULL)
|
|
return;
|
|
|
|
tree_view = GTK_TREE_VIEW (widget);
|
|
parent_cell = atk_object_get_parent (ATK_OBJECT (cell));
|
|
if (parent_cell != ATK_OBJECT (parent))
|
|
top_cell = GTK_CELL_ACCESSIBLE (parent_cell);
|
|
else
|
|
top_cell = cell;
|
|
cell_info = find_cell_info (GTK_TREE_VIEW_ACCESSIBLE (parent), top_cell);
|
|
if (!cell_info)
|
|
return;
|
|
path = cell_info_get_path (cell_info);
|
|
tv_col = cell_info->cell_col_ref;
|
|
if (path)
|
|
{
|
|
GtkTreeViewColumn *expander_column;
|
|
gint focus_line_width;
|
|
|
|
gtk_tree_view_get_cell_area (tree_view, path, tv_col, cell_rect);
|
|
expander_column = gtk_tree_view_get_expander_column (tree_view);
|
|
if (expander_column == tv_col)
|
|
{
|
|
gint expander_size;
|
|
gtk_widget_style_get (widget,
|
|
"expander-size", &expander_size,
|
|
NULL);
|
|
cell_rect->x += expander_size + EXTRA_EXPANDER_PADDING;
|
|
cell_rect->width -= expander_size + EXTRA_EXPANDER_PADDING;
|
|
}
|
|
gtk_widget_style_get (widget,
|
|
"focus-line-width", &focus_line_width,
|
|
NULL);
|
|
|
|
cell_rect->x += focus_line_width;
|
|
cell_rect->width -= 2 * focus_line_width;
|
|
|
|
gtk_tree_path_free (path);
|
|
|
|
/* A column has more than one renderer so we find the position
|
|
* and width of each
|
|
*/
|
|
if (top_cell != cell)
|
|
{
|
|
gint cell_index;
|
|
gboolean found;
|
|
gint cell_start;
|
|
gint cell_width;
|
|
GList *renderers;
|
|
GtkCellRenderer *renderer;
|
|
|
|
cell_index = atk_object_get_index_in_parent (ATK_OBJECT (cell));
|
|
renderers = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (tv_col));
|
|
renderer = g_list_nth_data (renderers, cell_index);
|
|
|
|
found = gtk_tree_view_column_cell_get_position (tv_col, renderer, &cell_start, &cell_width);
|
|
if (found)
|
|
{
|
|
cell_rect->x += cell_start;
|
|
cell_rect->width = cell_width;
|
|
}
|
|
g_list_free (renderers);
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
static void
|
|
gtk_tree_view_accessible_get_cell_extents (GtkCellAccessibleParent *parent,
|
|
GtkCellAccessible *cell,
|
|
gint *x,
|
|
gint *y,
|
|
gint *width,
|
|
gint *height,
|
|
AtkCoordType coord_type)
|
|
{
|
|
GtkWidget *widget;
|
|
GtkTreeView *tree_view;
|
|
GdkWindow *bin_window;
|
|
GdkRectangle cell_rect;
|
|
gint w_x, w_y;
|
|
|
|
widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (parent));
|
|
if (widget == NULL)
|
|
return;
|
|
|
|
tree_view = GTK_TREE_VIEW (widget);
|
|
gtk_tree_view_accessible_get_cell_area (parent, cell, &cell_rect);
|
|
bin_window = gtk_tree_view_get_bin_window (tree_view);
|
|
gdk_window_get_origin (bin_window, &w_x, &w_y);
|
|
|
|
if (coord_type == ATK_XY_WINDOW)
|
|
{
|
|
GdkWindow *window;
|
|
gint x_toplevel, y_toplevel;
|
|
|
|
window = gdk_window_get_toplevel (bin_window);
|
|
gdk_window_get_origin (window, &x_toplevel, &y_toplevel);
|
|
|
|
w_x -= x_toplevel;
|
|
w_y -= y_toplevel;
|
|
}
|
|
|
|
*width = cell_rect.width;
|
|
*height = cell_rect.height;
|
|
if (is_cell_showing (tree_view, &cell_rect))
|
|
{
|
|
*x = cell_rect.x + w_x;
|
|
*y = cell_rect.y + w_y;
|
|
}
|
|
else
|
|
{
|
|
*x = G_MININT;
|
|
*y = G_MININT;
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
gtk_tree_view_accessible_grab_cell_focus (GtkCellAccessibleParent *parent,
|
|
GtkCellAccessible *cell)
|
|
{
|
|
GtkWidget *widget;
|
|
GtkTreeView *tree_view;
|
|
GtkTreeViewColumn *tv_col;
|
|
GtkTreePath *path;
|
|
AtkObject *parent_cell;
|
|
AtkObject *cell_object;
|
|
GtkTreeViewAccessibleCellInfo *cell_info;
|
|
GtkCellRenderer *renderer = NULL;
|
|
GtkWidget *toplevel;
|
|
gint index;
|
|
|
|
widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (parent));
|
|
if (widget == NULL)
|
|
return FALSE;
|
|
|
|
tree_view = GTK_TREE_VIEW (widget);
|
|
|
|
cell_info = find_cell_info (GTK_TREE_VIEW_ACCESSIBLE (parent), cell);
|
|
if (!cell_info)
|
|
return FALSE;
|
|
cell_object = ATK_OBJECT (cell);
|
|
parent_cell = atk_object_get_parent (cell_object);
|
|
tv_col = cell_info->cell_col_ref;
|
|
if (parent_cell != ATK_OBJECT (parent))
|
|
{
|
|
/* GtkCellAccessible is in a GtkContainerCellAccessible.
|
|
* The GtkTreeViewColumn has multiple renderers;
|
|
* find the corresponding one.
|
|
*/
|
|
GList *renderers;
|
|
|
|
renderers = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (tv_col));
|
|
index = atk_object_get_index_in_parent (cell_object);
|
|
renderer = g_list_nth_data (renderers, index);
|
|
g_list_free (renderers);
|
|
}
|
|
path = cell_info_get_path (cell_info);
|
|
if (path)
|
|
{
|
|
if (renderer)
|
|
gtk_tree_view_set_cursor_on_cell (tree_view, path, tv_col, renderer, FALSE);
|
|
else
|
|
gtk_tree_view_set_cursor (tree_view, path, tv_col, FALSE);
|
|
|
|
gtk_tree_path_free (path);
|
|
gtk_widget_grab_focus (widget);
|
|
toplevel = gtk_widget_get_toplevel (widget);
|
|
if (gtk_widget_is_toplevel (toplevel))
|
|
{
|
|
#ifdef GDK_WINDOWING_X11
|
|
gtk_window_present_with_time (GTK_WINDOW (toplevel),
|
|
gdk_x11_get_server_time (gtk_widget_get_window (widget)));
|
|
#else
|
|
gtk_window_present (GTK_WINDOW (toplevel));
|
|
#endif
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
else
|
|
return FALSE;
|
|
}
|
|
|
|
static int
|
|
gtk_tree_view_accessible_get_child_index (GtkCellAccessibleParent *parent,
|
|
GtkCellAccessible *cell)
|
|
{
|
|
GtkTreeViewAccessibleCellInfo *cell_info;
|
|
GtkTreeView *tree_view;
|
|
|
|
cell_info = find_cell_info (GTK_TREE_VIEW_ACCESSIBLE (parent), cell);
|
|
if (!cell_info)
|
|
return -1;
|
|
|
|
tree_view = GTK_TREE_VIEW (gtk_accessible_get_widget (GTK_ACCESSIBLE (parent)));
|
|
|
|
return cell_info_get_index (tree_view, cell_info);
|
|
}
|
|
|
|
static void
|
|
gtk_cell_accessible_parent_interface_init (GtkCellAccessibleParentIface *iface)
|
|
{
|
|
iface->get_cell_extents = gtk_tree_view_accessible_get_cell_extents;
|
|
iface->get_cell_area = gtk_tree_view_accessible_get_cell_area;
|
|
iface->grab_focus = gtk_tree_view_accessible_grab_cell_focus;
|
|
iface->get_child_index = gtk_tree_view_accessible_get_child_index;
|
|
}
|
|
|
|
/* signal handling */
|
|
|
|
static gboolean
|
|
idle_expand_row (gpointer data)
|
|
{
|
|
GtkTreeViewAccessible *accessible = data;
|
|
GtkTreePath *path;
|
|
GtkTreeView *tree_view;
|
|
GtkTreeIter iter;
|
|
GtkTreeModel *tree_model;
|
|
gint n_inserted, row;
|
|
|
|
accessible->idle_expand_id = 0;
|
|
|
|
path = accessible->idle_expand_path;
|
|
tree_view = GTK_TREE_VIEW (gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible)));
|
|
|
|
tree_model = gtk_tree_view_get_model (tree_view);
|
|
if (!tree_model)
|
|
return FALSE;
|
|
|
|
if (!path || !gtk_tree_model_get_iter (tree_model, &iter, path))
|
|
return FALSE;
|
|
|
|
/* Update visibility of cells below expansion row */
|
|
traverse_cells (accessible, path, FALSE);
|
|
|
|
/* Figure out number of visible children, the following test
|
|
* should not fail
|
|
*/
|
|
if (gtk_tree_model_iter_has_child (tree_model, &iter))
|
|
{
|
|
GtkTreePath *path_copy;
|
|
|
|
/* By passing path into this function, we find the number of
|
|
* visible children of path.
|
|
*/
|
|
path_copy = gtk_tree_path_copy (path);
|
|
gtk_tree_path_append_index (path_copy, 0);
|
|
|
|
n_inserted = 0;
|
|
iterate_thru_children (tree_view, tree_model,
|
|
path_copy, NULL, &n_inserted, 0);
|
|
gtk_tree_path_free (path_copy);
|
|
}
|
|
else
|
|
{
|
|
/* We can get here if the row expanded callback deleted the row */
|
|
return FALSE;
|
|
}
|
|
|
|
/* Set expand state */
|
|
set_expand_state (tree_view, tree_model, accessible, path, TRUE);
|
|
|
|
row = get_row_from_tree_path (tree_view, path);
|
|
|
|
/* shouldn't ever happen */
|
|
if (row == -1)
|
|
g_assert_not_reached ();
|
|
|
|
/* Must add 1 because the "added rows" are below the row being expanded */
|
|
row += 1;
|
|
|
|
g_signal_emit_by_name (accessible, "row-inserted", row, n_inserted);
|
|
|
|
accessible->idle_expand_path = NULL;
|
|
|
|
gtk_tree_path_free (path);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
row_expanded_cb (GtkTreeView *tree_view,
|
|
GtkTreeIter *iter,
|
|
GtkTreePath *path)
|
|
{
|
|
AtkObject *atk_obj;
|
|
GtkTreeViewAccessible *accessible;
|
|
|
|
atk_obj = gtk_widget_get_accessible (GTK_WIDGET (tree_view));
|
|
accessible = GTK_TREE_VIEW_ACCESSIBLE (atk_obj);
|
|
|
|
/*
|
|
* The visible rectangle has not been updated when this signal is emitted
|
|
* so we process the signal when the GTK processing is completed
|
|
*/
|
|
/* this seems wrong since it overwrites any other pending expand handlers... */
|
|
accessible->idle_expand_path = gtk_tree_path_copy (path);
|
|
if (accessible->idle_expand_id)
|
|
g_source_remove (accessible->idle_expand_id);
|
|
accessible->idle_expand_id = gdk_threads_add_idle (idle_expand_row, accessible);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
row_collapsed_cb (GtkTreeView *tree_view,
|
|
GtkTreeIter *iter,
|
|
GtkTreePath *path)
|
|
{
|
|
GtkTreeModel *tree_model;
|
|
AtkObject *atk_obj;
|
|
GtkTreeViewAccessible *accessible;
|
|
gint row;
|
|
|
|
atk_obj = gtk_widget_get_accessible (GTK_WIDGET (tree_view));
|
|
accessible = GTK_TREE_VIEW_ACCESSIBLE (atk_obj);
|
|
tree_model = gtk_tree_view_get_model (tree_view);
|
|
|
|
/* Update visibility of cells below collapsed row */
|
|
traverse_cells (accessible, path, FALSE);
|
|
|
|
/* Set collapse state */
|
|
set_expand_state (tree_view, tree_model, accessible, path, FALSE);
|
|
if (accessible->n_children_deleted == 0)
|
|
return FALSE;
|
|
row = get_row_from_tree_path (tree_view, path);
|
|
if (row == -1)
|
|
return FALSE;
|
|
g_signal_emit_by_name (atk_obj, "row-deleted", row,
|
|
accessible->n_children_deleted);
|
|
accessible->n_children_deleted = 0;
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
size_allocate_cb (GtkWidget *widget,
|
|
GtkAllocation *allocation)
|
|
{
|
|
AtkObject *atk_obj;
|
|
GtkTreeViewAccessible *accessible;
|
|
|
|
atk_obj = gtk_widget_get_accessible (widget);
|
|
accessible = GTK_TREE_VIEW_ACCESSIBLE (atk_obj);
|
|
|
|
/* If the size allocation changes, the visibility of cells
|
|
* may change so update the cells visibility.
|
|
*/
|
|
traverse_cells (accessible, NULL, FALSE);
|
|
}
|
|
|
|
static void
|
|
selection_changed_cb (GtkTreeSelection *selection,
|
|
gpointer data)
|
|
{
|
|
GtkTreeViewAccessible *accessible;
|
|
GtkTreeView *tree_view;
|
|
GtkWidget *widget;
|
|
GtkTreeViewAccessibleCellInfo *info;
|
|
GtkTreeSelection *tree_selection;
|
|
GtkTreePath *path;
|
|
GHashTableIter iter;
|
|
|
|
accessible = GTK_TREE_VIEW_ACCESSIBLE (data);
|
|
widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible));
|
|
if (widget == NULL)
|
|
return;
|
|
|
|
tree_view = GTK_TREE_VIEW (widget);
|
|
tree_selection = gtk_tree_view_get_selection (tree_view);
|
|
|
|
/* FIXME: clean rows iterates through all cells too */
|
|
g_hash_table_iter_init (&iter, accessible->cell_infos);
|
|
while (g_hash_table_iter_next (&iter, NULL, (gpointer *)&info))
|
|
{
|
|
_gtk_cell_accessible_remove_state (info->cell, ATK_STATE_SELECTED, TRUE);
|
|
|
|
path = cell_info_get_path (info);
|
|
if (path && gtk_tree_selection_path_is_selected (tree_selection, path))
|
|
_gtk_cell_accessible_add_state (info->cell, ATK_STATE_SELECTED, TRUE);
|
|
gtk_tree_path_free (path);
|
|
}
|
|
if (gtk_widget_get_realized (widget))
|
|
g_signal_emit_by_name (accessible, "selection-changed");
|
|
}
|
|
|
|
static void
|
|
cursor_changed (GtkTreeView *tree_view,
|
|
GtkTreeViewAccessible *accessible)
|
|
{
|
|
AtkObject *cell;
|
|
|
|
cell = gtk_tree_view_accessible_ref_focus_cell (tree_view);
|
|
if (cell)
|
|
{
|
|
if (cell != accessible->focus_cell)
|
|
{
|
|
if (accessible->focus_cell)
|
|
{
|
|
_gtk_cell_accessible_remove_state (GTK_CELL_ACCESSIBLE (accessible->focus_cell), ATK_STATE_ACTIVE, FALSE);
|
|
_gtk_cell_accessible_remove_state (GTK_CELL_ACCESSIBLE (accessible->focus_cell), ATK_STATE_FOCUSED, FALSE);
|
|
g_object_unref (accessible->focus_cell);
|
|
accessible->focus_cell = cell;
|
|
}
|
|
|
|
if (gtk_widget_has_focus (GTK_WIDGET (tree_view)))
|
|
{
|
|
_gtk_cell_accessible_add_state (GTK_CELL_ACCESSIBLE (cell), ATK_STATE_ACTIVE, FALSE);
|
|
_gtk_cell_accessible_add_state (GTK_CELL_ACCESSIBLE (cell), ATK_STATE_FOCUSED, FALSE);
|
|
}
|
|
|
|
g_signal_emit_by_name (accessible, "active-descendant-changed", cell);
|
|
}
|
|
else
|
|
g_object_unref (cell);
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
focus_in (GtkWidget *widget)
|
|
{
|
|
GtkTreeView *tree_view;
|
|
GtkTreeViewAccessible *accessible;
|
|
AtkStateSet *state_set;
|
|
AtkObject *cell;
|
|
|
|
tree_view = GTK_TREE_VIEW (widget);
|
|
accessible = GTK_TREE_VIEW_ACCESSIBLE (gtk_widget_get_accessible (widget));
|
|
|
|
if (accessible->focus_cell == NULL)
|
|
{
|
|
cell = gtk_tree_view_accessible_ref_focus_cell (tree_view);
|
|
if (cell)
|
|
{
|
|
state_set = atk_object_ref_state_set (cell);
|
|
if (state_set)
|
|
{
|
|
if (!atk_state_set_contains_state (state_set, ATK_STATE_FOCUSED))
|
|
{
|
|
_gtk_cell_accessible_add_state (GTK_CELL_ACCESSIBLE (cell), ATK_STATE_ACTIVE, FALSE);
|
|
accessible->focus_cell = cell;
|
|
_gtk_cell_accessible_add_state (GTK_CELL_ACCESSIBLE (cell), ATK_STATE_FOCUSED, FALSE);
|
|
g_signal_emit_by_name (accessible, "active-descendant-changed", cell);
|
|
}
|
|
g_object_unref (state_set);
|
|
}
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
focus_out (GtkWidget *widget)
|
|
{
|
|
GtkTreeViewAccessible *accessible;
|
|
|
|
accessible = GTK_TREE_VIEW_ACCESSIBLE (gtk_widget_get_accessible (widget));
|
|
if (accessible->focus_cell)
|
|
{
|
|
_gtk_cell_accessible_remove_state (GTK_CELL_ACCESSIBLE (accessible->focus_cell), ATK_STATE_ACTIVE, FALSE);
|
|
_gtk_cell_accessible_remove_state (GTK_CELL_ACCESSIBLE (accessible->focus_cell), ATK_STATE_FOCUSED, FALSE);
|
|
g_object_unref (accessible->focus_cell);
|
|
accessible->focus_cell = NULL;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
model_row_changed (GtkTreeModel *tree_model,
|
|
GtkTreePath *path,
|
|
GtkTreeIter *iter,
|
|
gpointer user_data)
|
|
{
|
|
GtkTreeView *tree_view = GTK_TREE_VIEW (user_data);
|
|
GtkTreeViewAccessible *accessible;
|
|
GtkTreePath *cell_path;
|
|
GtkTreeViewAccessibleCellInfo *cell_info;
|
|
GHashTableIter hash_iter;
|
|
|
|
accessible = GTK_TREE_VIEW_ACCESSIBLE (gtk_widget_get_accessible (GTK_WIDGET (tree_view)));
|
|
|
|
/* Loop through our cached cells */
|
|
/* Must loop through them all */
|
|
g_hash_table_iter_init (&hash_iter, accessible->cell_infos);
|
|
while (g_hash_table_iter_next (&hash_iter, NULL, (gpointer *)&cell_info))
|
|
{
|
|
cell_path = cell_info_get_path (cell_info);
|
|
|
|
if (cell_path != NULL)
|
|
{
|
|
if (path && gtk_tree_path_compare (cell_path, path) == 0)
|
|
{
|
|
if (GTK_IS_RENDERER_CELL_ACCESSIBLE (cell_info->cell))
|
|
update_cell_value (GTK_RENDERER_CELL_ACCESSIBLE (cell_info->cell),
|
|
accessible, TRUE);
|
|
}
|
|
gtk_tree_path_free (cell_path);
|
|
}
|
|
}
|
|
g_signal_emit_by_name (accessible, "visible-data-changed");
|
|
}
|
|
|
|
static void
|
|
model_row_inserted (GtkTreeModel *tree_model,
|
|
GtkTreePath *path,
|
|
GtkTreeIter *iter,
|
|
gpointer user_data)
|
|
{
|
|
GtkTreeView *tree_view = (GtkTreeView *)user_data;
|
|
AtkObject *atk_obj;
|
|
GtkTreeViewAccessible *accessible;
|
|
GtkTreePath *path_copy;
|
|
gint row, n_inserted, child_row;
|
|
|
|
atk_obj = gtk_widget_get_accessible (GTK_WIDGET (tree_view));
|
|
accessible = GTK_TREE_VIEW_ACCESSIBLE (atk_obj);
|
|
|
|
if (accessible->idle_expand_id)
|
|
{
|
|
g_source_remove (accessible->idle_expand_id);
|
|
accessible->idle_expand_id = 0;
|
|
|
|
/* don't do this if the insertion precedes the idle path,
|
|
* since it will now be invalid
|
|
*/
|
|
if (path && accessible->idle_expand_path &&
|
|
(gtk_tree_path_compare (path, accessible->idle_expand_path) > 0))
|
|
set_expand_state (tree_view, tree_model, accessible, accessible->idle_expand_path, FALSE);
|
|
if (accessible->idle_expand_path)
|
|
gtk_tree_path_free (accessible->idle_expand_path);
|
|
}
|
|
/* Check to see if row is visible */
|
|
row = get_row_from_tree_path (tree_view, path);
|
|
|
|
/* A row insert is not necessarily visible. For example,
|
|
* a row can be draged & dropped into another row, which
|
|
* causes an insert on the model that isn't visible in the
|
|
* view. Only generate a signal if the inserted row is
|
|
* visible.
|
|
*/
|
|
if (row != -1)
|
|
{
|
|
GtkTreeIter tmp_iter;
|
|
gint n_cols, col;
|
|
|
|
gtk_tree_model_get_iter (tree_model, &tmp_iter, path);
|
|
|
|
/* Figure out number of visible children. */
|
|
if (gtk_tree_model_iter_has_child (tree_model, &tmp_iter))
|
|
{
|
|
GtkTreePath *path2;
|
|
/*
|
|
* By passing path into this function, we find the number of
|
|
* visible children of path.
|
|
*/
|
|
n_inserted = 0;
|
|
/* iterate_thru_children modifies path, we don't want that, so give
|
|
* it a copy */
|
|
path2 = gtk_tree_path_copy (path);
|
|
iterate_thru_children (tree_view, tree_model,
|
|
path2, NULL, &n_inserted, 0);
|
|
gtk_tree_path_free (path2);
|
|
|
|
/* Must add one to include the row that is being added */
|
|
n_inserted++;
|
|
}
|
|
else
|
|
n_inserted = 1;
|
|
|
|
traverse_cells (accessible, path, TRUE);
|
|
|
|
/* Generate row-inserted signal */
|
|
g_signal_emit_by_name (atk_obj, "row-inserted", row, n_inserted);
|
|
|
|
/* Generate children-changed signals */
|
|
n_cols = get_n_columns (tree_view);
|
|
for (child_row = row; child_row < (row + n_inserted); child_row++)
|
|
{
|
|
for (col = 0; col < n_cols; col++)
|
|
{
|
|
/* Pass NULL as the child object, i.e. 4th argument */
|
|
g_signal_emit_by_name (atk_obj, "children-changed::add",
|
|
((row * n_cols) + col), NULL, NULL);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* The row has been inserted inside another row. This can
|
|
* cause a row that previously couldn't be expanded to now
|
|
* be expandable.
|
|
*/
|
|
path_copy = gtk_tree_path_copy (path);
|
|
gtk_tree_path_up (path_copy);
|
|
set_expand_state (tree_view, tree_model, accessible, path_copy, TRUE);
|
|
gtk_tree_path_free (path_copy);
|
|
}
|
|
}
|
|
|
|
static void
|
|
model_row_deleted (GtkTreeModel *tree_model,
|
|
GtkTreePath *path,
|
|
gpointer user_data)
|
|
{
|
|
GtkTreeView *tree_view = (GtkTreeView *)user_data;
|
|
GtkTreePath *path_copy;
|
|
AtkObject *atk_obj;
|
|
GtkTreeViewAccessible *accessible;
|
|
gint row, col;
|
|
|
|
atk_obj = gtk_widget_get_accessible (GTK_WIDGET (tree_view));
|
|
accessible = GTK_TREE_VIEW_ACCESSIBLE (atk_obj);
|
|
|
|
if (accessible->idle_expand_id)
|
|
{
|
|
g_source_remove (accessible->idle_expand_id);
|
|
gtk_tree_path_free (accessible->idle_expand_path);
|
|
accessible->idle_expand_id = 0;
|
|
}
|
|
|
|
traverse_cells (accessible, path, TRUE);
|
|
|
|
/* If deleting a row with a depth > 1, then this may affect the
|
|
* expansion/contraction of its parent(s). Make sure this is
|
|
* handled.
|
|
*/
|
|
if (gtk_tree_path_get_depth (path) > 1)
|
|
{
|
|
path_copy = gtk_tree_path_copy (path);
|
|
gtk_tree_path_up (path_copy);
|
|
set_expand_state (tree_view, tree_model, accessible, path_copy, TRUE);
|
|
gtk_tree_path_free (path_copy);
|
|
}
|
|
row = get_row_from_tree_path (tree_view, path);
|
|
|
|
/* If the row which is deleted is not visible because it is a child of
|
|
* a collapsed row then row will be -1
|
|
*/
|
|
if (row > 0)
|
|
g_signal_emit_by_name (atk_obj, "row-deleted", row,
|
|
accessible->n_children_deleted + 1);
|
|
accessible->n_children_deleted = 0;
|
|
|
|
/* Generate children-changed signals */
|
|
for (col = 0; col < get_n_columns (tree_view); col++)
|
|
{
|
|
/* Pass NULL as the child object, 4th argument */
|
|
g_signal_emit_by_name (atk_obj, "children-changed::remove",
|
|
((row * get_n_columns (tree_view)) + col), NULL, NULL);
|
|
}
|
|
}
|
|
|
|
/* This function gets called when a row is deleted or when rows are
|
|
* removed from the view due to a collapse event. Note that the
|
|
* count is the number of visible *children* of the deleted row,
|
|
* so it does not include the row being deleted.
|
|
*
|
|
* As this function is called before the rows are removed we just note
|
|
* the number of rows and then deal with it when we get a notification
|
|
* that rows were deleted or collapsed.
|
|
*/
|
|
static void
|
|
destroy_count_func (GtkTreeView *tree_view,
|
|
GtkTreePath *path,
|
|
gint count,
|
|
gpointer user_data)
|
|
{
|
|
AtkObject *atk_obj;
|
|
GtkTreeViewAccessible *accessible;
|
|
|
|
atk_obj = gtk_widget_get_accessible (GTK_WIDGET (tree_view));
|
|
accessible = GTK_TREE_VIEW_ACCESSIBLE (atk_obj);
|
|
|
|
if (accessible->n_children_deleted != 0)
|
|
return;
|
|
|
|
accessible->n_children_deleted = count;
|
|
}
|
|
|
|
static void
|
|
model_rows_reordered (GtkTreeModel *tree_model,
|
|
GtkTreePath *path,
|
|
GtkTreeIter *iter,
|
|
gint *new_order,
|
|
gpointer user_data)
|
|
{
|
|
GtkTreeView *tree_view = (GtkTreeView *)user_data;
|
|
AtkObject *atk_obj;
|
|
GtkTreeViewAccessible *accessible;
|
|
|
|
atk_obj = gtk_widget_get_accessible (GTK_WIDGET (tree_view));
|
|
accessible = GTK_TREE_VIEW_ACCESSIBLE (atk_obj);
|
|
|
|
if (accessible->idle_expand_id)
|
|
{
|
|
g_source_remove (accessible->idle_expand_id);
|
|
gtk_tree_path_free (accessible->idle_expand_path);
|
|
accessible->idle_expand_id = 0;
|
|
}
|
|
traverse_cells (accessible, NULL, FALSE);
|
|
|
|
g_signal_emit_by_name (atk_obj, "row-reordered");
|
|
}
|
|
|
|
static void
|
|
set_cell_visibility (GtkTreeView *tree_view,
|
|
GtkCellAccessible *cell,
|
|
GtkTreeViewColumn *tv_col,
|
|
GtkTreePath *tree_path,
|
|
gboolean emit_signal)
|
|
{
|
|
GdkRectangle cell_rect;
|
|
|
|
/* Get these three values in tree coords */
|
|
if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
|
|
gtk_tree_view_get_cell_area (tree_view, tree_path, tv_col, &cell_rect);
|
|
else
|
|
cell_rect.height = 0;
|
|
|
|
if (cell_rect.height > 0)
|
|
{
|
|
/* The height will be zero for a cell for which an antecedent
|
|
* is not expanded
|
|
*/
|
|
_gtk_cell_accessible_add_state (cell, ATK_STATE_VISIBLE, emit_signal);
|
|
if (is_cell_showing (tree_view, &cell_rect))
|
|
_gtk_cell_accessible_add_state (cell, ATK_STATE_SHOWING, emit_signal);
|
|
else
|
|
_gtk_cell_accessible_remove_state (cell, ATK_STATE_SHOWING, emit_signal);
|
|
}
|
|
else
|
|
{
|
|
_gtk_cell_accessible_remove_state (cell, ATK_STATE_VISIBLE, emit_signal);
|
|
_gtk_cell_accessible_remove_state (cell, ATK_STATE_SHOWING, emit_signal);
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
is_cell_showing (GtkTreeView *tree_view,
|
|
GdkRectangle *cell_rect)
|
|
{
|
|
GdkRectangle rect, *visible_rect;
|
|
GdkRectangle rect1, *tree_cell_rect;
|
|
gint bx, by;
|
|
gboolean is_showing;
|
|
|
|
/* A cell is considered "SHOWING" if any part of the cell is
|
|
* in the visible area. Other ways we could do this is by a
|
|
* cell's midpoint or if the cell is fully in the visible range.
|
|
* Since we have the cell_rect x, y, width, height of the cell,
|
|
* any of these is easy to compute.
|
|
*
|
|
* It is assumed that cell's rectangle is in widget coordinates
|
|
* so we must transform to tree cordinates.
|
|
*/
|
|
visible_rect = ▭
|
|
tree_cell_rect = &rect1;
|
|
tree_cell_rect->x = cell_rect->x;
|
|
tree_cell_rect->y = cell_rect->y;
|
|
tree_cell_rect->width = cell_rect->width;
|
|
tree_cell_rect->height = cell_rect->height;
|
|
|
|
gtk_tree_view_get_visible_rect (tree_view, visible_rect);
|
|
gtk_tree_view_convert_tree_to_bin_window_coords (tree_view, visible_rect->x,
|
|
visible_rect->y, &bx, &by);
|
|
|
|
if (((tree_cell_rect->x + tree_cell_rect->width) < bx) ||
|
|
((tree_cell_rect->y + tree_cell_rect->height) < by) ||
|
|
(tree_cell_rect->x > (bx + visible_rect->width)) ||
|
|
(tree_cell_rect->y > (by + visible_rect->height)))
|
|
is_showing = FALSE;
|
|
else
|
|
is_showing = TRUE;
|
|
|
|
return is_showing;
|
|
}
|
|
|
|
/* Misc Public */
|
|
|
|
/* This function is called when a cell's flyweight is created in
|
|
* gtk_tree_view_accessible_table_ref_at with emit_change_signal
|
|
* set to FALSE and in model_row_changed() on receipt of "row-changed"
|
|
* signal when emit_change_signal is set to TRUE
|
|
*/
|
|
static gboolean
|
|
update_cell_value (GtkRendererCellAccessible *renderer_cell,
|
|
GtkTreeViewAccessible *accessible,
|
|
gboolean emit_change_signal)
|
|
{
|
|
GtkTreeViewAccessibleCellInfo *cell_info;
|
|
GtkTreeView *tree_view;
|
|
GtkTreeModel *tree_model;
|
|
GtkTreePath *path;
|
|
GtkTreeIter iter;
|
|
GList *renderers, *cur_renderer;
|
|
GParamSpec *spec;
|
|
GtkRendererCellAccessibleClass *renderer_cell_class;
|
|
GtkCellRendererClass *gtk_cell_renderer_class;
|
|
GtkCellAccessible *cell;
|
|
gchar **prop_list;
|
|
AtkObject *parent;
|
|
gboolean is_expander, is_expanded;
|
|
|
|
renderer_cell_class = GTK_RENDERER_CELL_ACCESSIBLE_GET_CLASS (renderer_cell);
|
|
if (renderer_cell->renderer)
|
|
gtk_cell_renderer_class = GTK_CELL_RENDERER_GET_CLASS (renderer_cell->renderer);
|
|
else
|
|
gtk_cell_renderer_class = NULL;
|
|
|
|
prop_list = renderer_cell_class->property_list;
|
|
|
|
cell = GTK_CELL_ACCESSIBLE (renderer_cell);
|
|
cell_info = find_cell_info (accessible, cell);
|
|
if (!cell_info)
|
|
return FALSE;
|
|
|
|
if (emit_change_signal)
|
|
{
|
|
tree_view = GTK_TREE_VIEW (gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible)));
|
|
tree_model = gtk_tree_view_get_model (tree_view);
|
|
path = cell_info_get_path (cell_info);
|
|
if (path == NULL)
|
|
return FALSE;
|
|
|
|
gtk_tree_model_get_iter (tree_model, &iter, path);
|
|
is_expander = FALSE;
|
|
is_expanded = FALSE;
|
|
if (gtk_tree_model_iter_has_child (tree_model, &iter))
|
|
{
|
|
GtkTreeViewColumn *expander_tv;
|
|
|
|
expander_tv = gtk_tree_view_get_expander_column (tree_view);
|
|
if (expander_tv == cell_info->cell_col_ref)
|
|
{
|
|
is_expander = TRUE;
|
|
is_expanded = gtk_tree_view_row_expanded (tree_view, path);
|
|
}
|
|
}
|
|
gtk_tree_path_free (path);
|
|
gtk_tree_view_column_cell_set_cell_data (cell_info->cell_col_ref,
|
|
tree_model, &iter,
|
|
is_expander, is_expanded);
|
|
}
|
|
renderers = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (cell_info->cell_col_ref));
|
|
if (!renderers)
|
|
return FALSE;
|
|
|
|
/* If the cell is in a container, its index is used to find the renderer
|
|
* in the list. Otherwise, we assume that the cell is represented
|
|
* by the first renderer in the list
|
|
*/
|
|
parent = atk_object_get_parent (ATK_OBJECT (cell));
|
|
|
|
if (GTK_IS_CONTAINER_CELL_ACCESSIBLE (parent))
|
|
cur_renderer = g_list_nth (renderers, atk_object_get_index_in_parent (ATK_OBJECT (cell)));
|
|
else
|
|
cur_renderer = renderers;
|
|
|
|
if (cur_renderer == NULL)
|
|
return FALSE;
|
|
|
|
if (gtk_cell_renderer_class)
|
|
{
|
|
while (*prop_list)
|
|
{
|
|
spec = g_object_class_find_property
|
|
(G_OBJECT_CLASS (gtk_cell_renderer_class), *prop_list);
|
|
|
|
if (spec != NULL)
|
|
{
|
|
GValue value = G_VALUE_INIT;
|
|
|
|
g_value_init (&value, spec->value_type);
|
|
g_object_get_property (cur_renderer->data, *prop_list, &value);
|
|
g_object_set_property (G_OBJECT (renderer_cell->renderer),
|
|
*prop_list, &value);
|
|
g_value_unset (&value);
|
|
}
|
|
else
|
|
g_warning ("Invalid property: %s\n", *prop_list);
|
|
prop_list++;
|
|
}
|
|
}
|
|
g_list_free (renderers);
|
|
|
|
return _gtk_renderer_cell_accessible_update_cache (renderer_cell, emit_change_signal);
|
|
}
|
|
|
|
static gint
|
|
get_row_from_tree_path (GtkTreeView *tree_view,
|
|
GtkTreePath *path)
|
|
{
|
|
GtkTreeModel *tree_model;
|
|
GtkTreePath *root_tree;
|
|
gint row;
|
|
|
|
tree_model = gtk_tree_view_get_model (tree_view);
|
|
|
|
if (gtk_tree_model_get_flags (tree_model) & GTK_TREE_MODEL_LIST_ONLY)
|
|
row = gtk_tree_path_get_indices (path)[0];
|
|
else
|
|
{
|
|
root_tree = gtk_tree_path_new_first ();
|
|
row = 0;
|
|
iterate_thru_children (tree_view, tree_model, root_tree, path, &row, 0);
|
|
gtk_tree_path_free (root_tree);
|
|
}
|
|
|
|
return row;
|
|
}
|
|
|
|
/* Misc Private */
|
|
|
|
/* Helper recursive function that returns an iter to nth row
|
|
*/
|
|
static GtkTreeIter *
|
|
return_iter_nth_row (GtkTreeView *tree_view,
|
|
GtkTreeModel *tree_model,
|
|
GtkTreeIter *iter,
|
|
gint increment,
|
|
gint row)
|
|
{
|
|
GtkTreePath *current_path;
|
|
GtkTreeIter new_iter;
|
|
gboolean row_expanded;
|
|
|
|
current_path = gtk_tree_model_get_path (tree_model, iter);
|
|
if (increment == row)
|
|
{
|
|
gtk_tree_path_free (current_path);
|
|
return iter;
|
|
}
|
|
|
|
row_expanded = gtk_tree_view_row_expanded (tree_view, current_path);
|
|
gtk_tree_path_free (current_path);
|
|
|
|
new_iter = *iter;
|
|
if ((row_expanded && gtk_tree_model_iter_children (tree_model, iter, &new_iter)) ||
|
|
(gtk_tree_model_iter_next (tree_model, iter)) ||
|
|
(gtk_tree_model_iter_parent (tree_model, iter, &new_iter) &&
|
|
(gtk_tree_model_iter_next (tree_model, iter))))
|
|
return return_iter_nth_row (tree_view, tree_model, iter,
|
|
++increment, row);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void
|
|
set_iter_nth_row (GtkTreeView *tree_view,
|
|
GtkTreeIter *iter,
|
|
gint row)
|
|
{
|
|
GtkTreeModel *tree_model;
|
|
|
|
tree_model = gtk_tree_view_get_model (tree_view);
|
|
gtk_tree_model_get_iter_first (tree_model, iter);
|
|
iter = return_iter_nth_row (tree_view, tree_model, iter, 0, row);
|
|
}
|
|
|
|
/* Recursively called until the row specified by orig is found.
|
|
*
|
|
* *count will be set to the visible row number of the child
|
|
* relative to the row that was initially passed in as tree_path.
|
|
* tree_path could be modified by this function.
|
|
*
|
|
* *count will be -1 if orig is not found as a child (a row that is
|
|
* not visible will not be found, e.g. if the row is inside a
|
|
* collapsed row). If NULL is passed in as orig, *count will
|
|
* be a count of the visible children.
|
|
*
|
|
* NOTE: the value for depth must be 0 when this recursive function
|
|
* is initially called, or it may not function as expected.
|
|
*/
|
|
static void
|
|
iterate_thru_children (GtkTreeView *tree_view,
|
|
GtkTreeModel *tree_model,
|
|
GtkTreePath *tree_path,
|
|
GtkTreePath *orig,
|
|
gint *count,
|
|
gint depth)
|
|
{
|
|
GtkTreeIter iter;
|
|
|
|
if (!gtk_tree_model_get_iter (tree_model, &iter, tree_path))
|
|
return;
|
|
|
|
if (tree_path && orig && !gtk_tree_path_compare (tree_path, orig))
|
|
/* Found it! */
|
|
return;
|
|
|
|
if (tree_path && orig && gtk_tree_path_compare (tree_path, orig) > 0)
|
|
{
|
|
/* Past it, so return -1 */
|
|
*count = -1;
|
|
return;
|
|
}
|
|
else if (gtk_tree_view_row_expanded (tree_view, tree_path) &&
|
|
gtk_tree_model_iter_has_child (tree_model, &iter))
|
|
{
|
|
(*count)++;
|
|
gtk_tree_path_append_index (tree_path, 0);
|
|
iterate_thru_children (tree_view, tree_model, tree_path,
|
|
orig, count, (depth + 1));
|
|
return;
|
|
}
|
|
else if (gtk_tree_model_iter_next (tree_model, &iter))
|
|
{
|
|
(*count)++;
|
|
tree_path = gtk_tree_model_get_path (tree_model, &iter);
|
|
if (tree_path)
|
|
{
|
|
iterate_thru_children (tree_view, tree_model, tree_path,
|
|
orig, count, depth);
|
|
gtk_tree_path_free (tree_path);
|
|
}
|
|
return;
|
|
}
|
|
else if (gtk_tree_path_up (tree_path))
|
|
{
|
|
GtkTreeIter temp_iter;
|
|
gboolean exit_loop = FALSE;
|
|
gint new_depth = depth - 1;
|
|
|
|
(*count)++;
|
|
|
|
/* Make sure that we back up until we find a row
|
|
* where gtk_tree_path_next does not return NULL.
|
|
*/
|
|
while (!exit_loop)
|
|
{
|
|
if (gtk_tree_path_get_depth (tree_path) == 0)
|
|
/* depth is now zero so */
|
|
return;
|
|
gtk_tree_path_next (tree_path);
|
|
|
|
/* Verify that the next row is a valid row! */
|
|
exit_loop = gtk_tree_model_get_iter (tree_model, &temp_iter, tree_path);
|
|
|
|
if (!exit_loop)
|
|
{
|
|
/* Keep going up until we find a row that has a valid next */
|
|
if (gtk_tree_path_get_depth(tree_path) > 1)
|
|
{
|
|
new_depth--;
|
|
gtk_tree_path_up (tree_path);
|
|
}
|
|
else
|
|
{
|
|
/* If depth is 1 and gtk_tree_model_get_iter returns FALSE,
|
|
* then we are at the last row, so just return.
|
|
*/
|
|
if (orig != NULL)
|
|
*count = -1;
|
|
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* This guarantees that we will stop when we hit the end of the
|
|
* children.
|
|
*/
|
|
if (new_depth < 0)
|
|
return;
|
|
|
|
iterate_thru_children (tree_view, tree_model, tree_path,
|
|
orig, count, new_depth);
|
|
return;
|
|
}
|
|
|
|
/* If it gets here, then the path wasn't found. Situations
|
|
* that would cause this would be if the path passed in is
|
|
* invalid or contained within the last row, but not visible
|
|
* because the last row is not expanded. If NULL was passed
|
|
* in then a row count is desired, so only set count to -1
|
|
* if orig is not NULL.
|
|
*/
|
|
if (orig != NULL)
|
|
*count = -1;
|
|
|
|
return;
|
|
}
|
|
|
|
static void
|
|
clean_cols (GtkTreeViewAccessible *accessible,
|
|
GtkTreeViewColumn *tv_col)
|
|
{
|
|
GtkTreeViewAccessibleCellInfo *cell_info;
|
|
GHashTableIter iter;
|
|
|
|
/* Clean GtkTreeViewAccessibleCellInfo data */
|
|
g_hash_table_iter_init (&iter, accessible->cell_infos);
|
|
while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &cell_info))
|
|
{
|
|
/* If the cell has become invalid because the column tv_col
|
|
* has been removed, then set the cell's state to ATK_STATE_DEFUNCT
|
|
* and remove the cell from accessible->cell_data.
|
|
*/
|
|
if (cell_info->cell_col_ref == tv_col)
|
|
g_hash_table_iter_remove (&iter);
|
|
}
|
|
}
|
|
|
|
/* If tree_path is passed in as NULL, then all cells are acted on.
|
|
* Otherwise, just act on those cells that are on a row greater than
|
|
* the specified tree_path. If inc_row is passed in as TRUE, then rows
|
|
* greater and equal to the specified tree_path are acted on.
|
|
*
|
|
* The function set_cell_visibility() is called on all cells to be
|
|
* acted on to update the visibility of the cell.
|
|
*/
|
|
static void
|
|
traverse_cells (GtkTreeViewAccessible *accessible,
|
|
GtkTreePath *tree_path,
|
|
gboolean inc_row)
|
|
{
|
|
GtkTreeViewAccessibleCellInfo *cell_info;
|
|
GtkWidget *widget;
|
|
GHashTableIter iter;
|
|
|
|
widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible));
|
|
if (!widget)
|
|
return;
|
|
|
|
/* Must loop through them all */
|
|
g_hash_table_iter_init (&iter, accessible->cell_infos);
|
|
while (g_hash_table_iter_next (&iter, NULL, (gpointer *)&cell_info))
|
|
{
|
|
GtkTreePath *row_path;
|
|
gboolean act_on_cell;
|
|
|
|
row_path = cell_info_get_path (cell_info);
|
|
g_return_if_fail (row_path != NULL);
|
|
if (tree_path == NULL)
|
|
act_on_cell = TRUE;
|
|
else
|
|
{
|
|
gint comparison;
|
|
|
|
comparison = gtk_tree_path_compare (row_path, tree_path);
|
|
if ((comparison > 0) ||
|
|
(comparison == 0 && inc_row))
|
|
act_on_cell = TRUE;
|
|
else
|
|
act_on_cell = FALSE;
|
|
}
|
|
|
|
if (act_on_cell)
|
|
{
|
|
set_cell_visibility (GTK_TREE_VIEW (widget),
|
|
cell_info->cell,
|
|
cell_info->cell_col_ref,
|
|
row_path, TRUE);
|
|
}
|
|
gtk_tree_path_free (row_path);
|
|
}
|
|
|
|
g_signal_emit_by_name (accessible, "visible-data-changed");
|
|
}
|
|
|
|
/* If the tree_path passed in has children, then
|
|
* ATK_STATE_EXPANDABLE is set. If the row is expanded
|
|
* ATK_STATE_EXPANDED is turned on. If the row is
|
|
* collapsed, then ATK_STATE_EXPANDED is removed.
|
|
*
|
|
* If the tree_path passed in has no children, then
|
|
* ATK_STATE_EXPANDABLE and ATK_STATE_EXPANDED are removed.
|
|
*
|
|
* If set_on_ancestor is TRUE, then this function will also
|
|
* update all cells that are ancestors of the tree_path.
|
|
*/
|
|
static void
|
|
set_expand_state (GtkTreeView *tree_view,
|
|
GtkTreeModel *tree_model,
|
|
GtkTreeViewAccessible *accessible,
|
|
GtkTreePath *tree_path,
|
|
gboolean set_on_ancestor)
|
|
{
|
|
GtkTreeViewColumn *expander_tv;
|
|
GtkTreeViewAccessibleCellInfo *cell_info;
|
|
GtkTreePath *cell_path;
|
|
GtkTreeIter iter;
|
|
gboolean found;
|
|
GHashTableIter hash_iter;
|
|
|
|
g_hash_table_iter_init (&hash_iter, accessible->cell_infos);
|
|
while (g_hash_table_iter_next (&hash_iter, NULL, (gpointer *) &cell_info))
|
|
{
|
|
cell_path = cell_info_get_path (cell_info);
|
|
found = FALSE;
|
|
|
|
if (cell_path != NULL)
|
|
{
|
|
GtkCellAccessible *cell = GTK_CELL_ACCESSIBLE (cell_info->cell);
|
|
|
|
expander_tv = gtk_tree_view_get_expander_column (tree_view);
|
|
|
|
/* Only set state for the cell that is in the column with the
|
|
* expander toggle
|
|
*/
|
|
if (expander_tv == cell_info->cell_col_ref)
|
|
{
|
|
if (tree_path && gtk_tree_path_compare (cell_path, tree_path) == 0)
|
|
found = TRUE;
|
|
else if (set_on_ancestor &&
|
|
gtk_tree_path_get_depth (cell_path) <
|
|
gtk_tree_path_get_depth (tree_path) &&
|
|
gtk_tree_path_is_ancestor (cell_path, tree_path) == 1)
|
|
/* Only set if set_on_ancestor was passed in as TRUE */
|
|
found = TRUE;
|
|
}
|
|
|
|
/* Set ATK_STATE_EXPANDABLE and ATK_STATE_EXPANDED
|
|
* for ancestors and found cells.
|
|
*/
|
|
if (found)
|
|
{
|
|
/* Must check against cell_path since cell_path
|
|
* can be equal to or an ancestor of tree_path.
|
|
*/
|
|
gtk_tree_model_get_iter (tree_model, &iter, cell_path);
|
|
|
|
/* Set or unset ATK_STATE_EXPANDABLE as appropriate */
|
|
if (gtk_tree_model_iter_has_child (tree_model, &iter))
|
|
{
|
|
set_cell_expandable (cell);
|
|
|
|
if (gtk_tree_view_row_expanded (tree_view, cell_path))
|
|
_gtk_cell_accessible_add_state (cell, ATK_STATE_EXPANDED, TRUE);
|
|
else
|
|
_gtk_cell_accessible_remove_state (cell, ATK_STATE_EXPANDED, TRUE);
|
|
}
|
|
else
|
|
{
|
|
_gtk_cell_accessible_remove_state (cell, ATK_STATE_EXPANDED, TRUE);
|
|
if (_gtk_cell_accessible_remove_state (cell, ATK_STATE_EXPANDABLE, TRUE))
|
|
/* The state may have been propagated to the container cell */
|
|
if (!GTK_IS_CONTAINER_CELL_ACCESSIBLE (cell))
|
|
_gtk_cell_accessible_remove_action_by_name (cell,
|
|
"expand or contract");
|
|
}
|
|
|
|
/* We assume that each cell in the cache once and
|
|
* a container cell is before its child cells so we are
|
|
* finished if set_on_ancestor is not set to TRUE.
|
|
*/
|
|
if (!set_on_ancestor)
|
|
break;
|
|
}
|
|
}
|
|
gtk_tree_path_free (cell_path);
|
|
}
|
|
}
|
|
|
|
static void
|
|
add_cell_actions (GtkCellAccessible *cell,
|
|
gboolean editable)
|
|
{
|
|
if (GTK_IS_BOOLEAN_CELL_ACCESSIBLE (cell))
|
|
_gtk_cell_accessible_add_action (cell,
|
|
"toggle", "toggles the cell",
|
|
NULL, toggle_cell_toggled);
|
|
if (editable)
|
|
_gtk_cell_accessible_add_action (cell,
|
|
"edit", "creates a widget in which the contents of the cell can be edited",
|
|
NULL, edit_cell);
|
|
_gtk_cell_accessible_add_action (cell,
|
|
"activate", "activate the cell",
|
|
NULL, activate_cell);
|
|
}
|
|
|
|
static void
|
|
toggle_cell_expanded (GtkCellAccessible *cell)
|
|
{
|
|
GtkTreeViewAccessibleCellInfo *cell_info;
|
|
GtkTreeView *tree_view;
|
|
GtkTreePath *path;
|
|
AtkObject *parent;
|
|
AtkStateSet *stateset;
|
|
|
|
parent = atk_object_get_parent (ATK_OBJECT (cell));
|
|
if (GTK_IS_CONTAINER_CELL_ACCESSIBLE (parent))
|
|
parent = atk_object_get_parent (parent);
|
|
|
|
cell_info = find_cell_info (GTK_TREE_VIEW_ACCESSIBLE (parent), cell);
|
|
if (!cell_info)
|
|
return;
|
|
|
|
tree_view = GTK_TREE_VIEW (gtk_accessible_get_widget (GTK_ACCESSIBLE (parent)));
|
|
path = cell_info_get_path (cell_info);
|
|
if (!path)
|
|
return;
|
|
|
|
stateset = atk_object_ref_state_set (ATK_OBJECT (cell));
|
|
if (atk_state_set_contains_state (stateset, ATK_STATE_EXPANDED))
|
|
gtk_tree_view_collapse_row (tree_view, path);
|
|
else
|
|
gtk_tree_view_expand_row (tree_view, path, TRUE);
|
|
g_object_unref (stateset);
|
|
gtk_tree_path_free (path);
|
|
}
|
|
|
|
static void
|
|
toggle_cell_toggled (GtkCellAccessible *cell)
|
|
{
|
|
GtkTreeViewAccessibleCellInfo *cell_info;
|
|
GtkTreePath *path;
|
|
gchar *pathstring;
|
|
GList *renderers, *cur_renderer;
|
|
AtkObject *parent;
|
|
gboolean is_container_cell = FALSE;
|
|
|
|
parent = atk_object_get_parent (ATK_OBJECT (cell));
|
|
if (GTK_IS_CONTAINER_CELL_ACCESSIBLE (parent))
|
|
{
|
|
is_container_cell = TRUE;
|
|
parent = atk_object_get_parent (parent);
|
|
}
|
|
|
|
cell_info = find_cell_info (GTK_TREE_VIEW_ACCESSIBLE (parent), cell);
|
|
if (!cell_info)
|
|
return;
|
|
|
|
path = cell_info_get_path (cell_info);
|
|
if (!path)
|
|
return;
|
|
|
|
/* If the cell is in a container, its index is used to find the
|
|
* renderer in the list. Otherwise, we assume that the cell is
|
|
* represented by the first renderer in the list
|
|
*/
|
|
renderers = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (cell_info->cell_col_ref));
|
|
if (is_container_cell)
|
|
cur_renderer = g_list_nth (renderers, atk_object_get_index_in_parent (ATK_OBJECT (cell)));
|
|
else
|
|
cur_renderer = renderers;
|
|
|
|
if (cur_renderer)
|
|
{
|
|
pathstring = gtk_tree_path_to_string (path);
|
|
g_signal_emit_by_name (cur_renderer->data, "toggled", pathstring);
|
|
g_free (pathstring);
|
|
}
|
|
|
|
g_list_free (renderers);
|
|
gtk_tree_path_free (path);
|
|
}
|
|
|
|
static void
|
|
edit_cell (GtkCellAccessible *cell)
|
|
{
|
|
GtkTreeViewAccessibleCellInfo *cell_info;
|
|
GtkTreeView *tree_view;
|
|
GtkTreePath *path;
|
|
AtkObject *parent;
|
|
|
|
parent = atk_object_get_parent (ATK_OBJECT (cell));
|
|
if (GTK_IS_CONTAINER_CELL_ACCESSIBLE (parent))
|
|
parent = atk_object_get_parent (parent);
|
|
|
|
cell_info = find_cell_info (GTK_TREE_VIEW_ACCESSIBLE (parent), cell);
|
|
if (!cell_info)
|
|
return;
|
|
|
|
tree_view = GTK_TREE_VIEW (gtk_accessible_get_widget (GTK_ACCESSIBLE (parent)));
|
|
path = cell_info_get_path (cell_info);
|
|
if (!path)
|
|
return;
|
|
gtk_tree_view_set_cursor (tree_view, path, cell_info->cell_col_ref, TRUE);
|
|
gtk_tree_path_free (path);
|
|
}
|
|
|
|
static void
|
|
activate_cell (GtkCellAccessible *cell)
|
|
{
|
|
GtkTreeViewAccessibleCellInfo *cell_info;
|
|
GtkTreeView *tree_view;
|
|
GtkTreePath *path;
|
|
AtkObject *parent;
|
|
|
|
parent = atk_object_get_parent (ATK_OBJECT (cell));
|
|
if (GTK_IS_CONTAINER_CELL_ACCESSIBLE (parent))
|
|
parent = atk_object_get_parent (parent);
|
|
|
|
cell_info = find_cell_info (GTK_TREE_VIEW_ACCESSIBLE (parent), cell);
|
|
if (!cell_info)
|
|
return;
|
|
|
|
tree_view = GTK_TREE_VIEW (gtk_accessible_get_widget (GTK_ACCESSIBLE (parent)));
|
|
path = cell_info_get_path (cell_info);
|
|
if (!path)
|
|
return;
|
|
gtk_tree_view_row_activated (tree_view, path, cell_info->cell_col_ref);
|
|
gtk_tree_path_free (path);
|
|
}
|
|
|
|
static void
|
|
cell_destroyed (gpointer data)
|
|
{
|
|
GtkTreeViewAccessibleCellInfo *cell_info = data;
|
|
|
|
cell_info->cell = NULL;
|
|
|
|
g_hash_table_remove (cell_info->view->cell_infos, cell_info);
|
|
}
|
|
|
|
static int
|
|
cell_info_get_index (GtkTreeView *tree_view,
|
|
GtkTreeViewAccessibleCellInfo *info)
|
|
{
|
|
int index;
|
|
|
|
index = _gtk_rbtree_node_get_index (info->tree, info->node) + 1;
|
|
index *= get_n_columns (tree_view);
|
|
index += get_column_number (tree_view, info->cell_col_ref);
|
|
|
|
return index;
|
|
}
|
|
|
|
static void
|
|
cell_info_new (GtkTreeViewAccessible *accessible,
|
|
GtkTreeModel *tree_model,
|
|
GtkRBTree *tree,
|
|
GtkRBNode *node,
|
|
GtkTreeViewColumn *tv_col,
|
|
GtkCellAccessible *cell)
|
|
{
|
|
GtkTreeViewAccessibleCellInfo *cell_info;
|
|
|
|
cell_info = g_new (GtkTreeViewAccessibleCellInfo, 1);
|
|
|
|
cell_info->tree = tree;
|
|
cell_info->node = node;
|
|
cell_info->cell_col_ref = tv_col;
|
|
cell_info->cell = cell;
|
|
cell_info->view = accessible;
|
|
|
|
g_object_set_qdata_full (G_OBJECT (cell),
|
|
gtk_tree_view_accessible_get_data_quark (),
|
|
cell_info,
|
|
cell_destroyed);
|
|
|
|
g_hash_table_replace (accessible->cell_infos, cell_info, cell_info);
|
|
}
|
|
|
|
static GtkCellAccessible *
|
|
find_cell (GtkTreeViewAccessible *accessible,
|
|
gint index)
|
|
{
|
|
GtkTreeViewAccessibleCellInfo lookup, *cell_info;
|
|
GtkTreeView *tree_view;
|
|
|
|
tree_view = GTK_TREE_VIEW (gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible)));
|
|
|
|
if (!_gtk_rbtree_find_index (_gtk_tree_view_get_rbtree (tree_view),
|
|
index / get_n_columns (tree_view) - 1,
|
|
&lookup.tree,
|
|
&lookup.node))
|
|
{
|
|
g_assert_not_reached ();
|
|
}
|
|
lookup.cell_col_ref = get_visible_column (tree_view, index % get_n_columns (tree_view));
|
|
|
|
cell_info = g_hash_table_lookup (accessible->cell_infos, &lookup);
|
|
if (cell_info == NULL)
|
|
return NULL;
|
|
|
|
return cell_info->cell;
|
|
}
|
|
|
|
static void
|
|
connect_model_signals (GtkTreeView *view,
|
|
GtkTreeViewAccessible *accessible)
|
|
{
|
|
GObject *obj;
|
|
|
|
obj = G_OBJECT (accessible->tree_model);
|
|
g_signal_connect_data (obj, "row-changed",
|
|
G_CALLBACK (model_row_changed), view, NULL, 0);
|
|
g_signal_connect_data (obj, "row-inserted",
|
|
G_CALLBACK (model_row_inserted), view, NULL,
|
|
G_CONNECT_AFTER);
|
|
g_signal_connect_data (obj, "row-deleted",
|
|
G_CALLBACK (model_row_deleted), view, NULL,
|
|
G_CONNECT_AFTER);
|
|
g_signal_connect_data (obj, "rows-reordered",
|
|
G_CALLBACK (model_rows_reordered), view, NULL,
|
|
G_CONNECT_AFTER);
|
|
}
|
|
|
|
static void
|
|
disconnect_model_signals (GtkTreeViewAccessible *accessible)
|
|
{
|
|
GObject *obj;
|
|
GtkWidget *widget;
|
|
|
|
obj = G_OBJECT (accessible->tree_model);
|
|
widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible));
|
|
g_signal_handlers_disconnect_by_func (obj, model_row_changed, widget);
|
|
g_signal_handlers_disconnect_by_func (obj, model_row_inserted, widget);
|
|
g_signal_handlers_disconnect_by_func (obj, model_row_deleted, widget);
|
|
g_signal_handlers_disconnect_by_func (obj, model_rows_reordered, widget);
|
|
}
|
|
|
|
/* Returns the column number of the specified GtkTreeViewColumn
|
|
* The column must be visible.
|
|
*/
|
|
static gint
|
|
get_column_number (GtkTreeView *treeview,
|
|
GtkTreeViewColumn *column)
|
|
{
|
|
GtkTreeViewColumn *cur;
|
|
guint i, number;
|
|
|
|
number = 0;
|
|
|
|
for (i = 0; i < gtk_tree_view_get_n_columns (treeview); i++)
|
|
{
|
|
cur = gtk_tree_view_get_column (treeview, i);
|
|
|
|
if (!gtk_tree_view_column_get_visible (cur))
|
|
continue;
|
|
|
|
if (cur == column)
|
|
break;
|
|
|
|
number++;
|
|
}
|
|
|
|
g_return_val_if_fail (i < gtk_tree_view_get_n_columns (treeview), 0);
|
|
|
|
return number;
|
|
}
|
|
|
|
static gint
|
|
get_index (GtkTreeView *tree_view,
|
|
GtkTreePath *path,
|
|
gint actual_column)
|
|
{
|
|
gint depth = 0;
|
|
gint index = 1;
|
|
gint *indices = NULL;
|
|
|
|
if (path)
|
|
{
|
|
depth = gtk_tree_path_get_depth (path);
|
|
indices = gtk_tree_path_get_indices (path);
|
|
}
|
|
|
|
if (depth > 1)
|
|
{
|
|
GtkTreePath *copy_path;
|
|
GtkTreeModel *model;
|
|
|
|
model = gtk_tree_view_get_model (tree_view);
|
|
copy_path = gtk_tree_path_copy (path);
|
|
gtk_tree_path_up (copy_path);
|
|
count_rows (model, NULL, copy_path, &index, 0, depth);
|
|
gtk_tree_path_free (copy_path);
|
|
}
|
|
|
|
if (path)
|
|
index += indices[depth - 1];
|
|
index *= get_n_columns (tree_view);
|
|
index += actual_column;
|
|
return index;
|
|
}
|
|
|
|
/* The function count_rows counts the number of rows starting at iter
|
|
* and ending at end_path. The value of level is the depth of iter and
|
|
* the value of depth is the depth of end_path. Rows at depth before
|
|
* end_path are counted. This functions counts rows which are not visible
|
|
* because an ancestor is collapsed.
|
|
*/
|
|
static void
|
|
count_rows (GtkTreeModel *model,
|
|
GtkTreeIter *iter,
|
|
GtkTreePath *end_path,
|
|
gint *count,
|
|
gint level,
|
|
gint depth)
|
|
{
|
|
GtkTreeIter child_iter;
|
|
|
|
if (!model)
|
|
return;
|
|
|
|
level++;
|
|
*count += gtk_tree_model_iter_n_children (model, iter);
|
|
|
|
if (gtk_tree_model_get_flags (model) & GTK_TREE_MODEL_LIST_ONLY)
|
|
return;
|
|
|
|
if (level >= depth)
|
|
return;
|
|
|
|
if (gtk_tree_model_iter_children (model, &child_iter, iter))
|
|
{
|
|
gboolean ret_val = TRUE;
|
|
|
|
while (ret_val)
|
|
{
|
|
if (level == depth - 1)
|
|
{
|
|
GtkTreePath *iter_path;
|
|
gboolean finished = FALSE;
|
|
|
|
iter_path = gtk_tree_model_get_path (model, &child_iter);
|
|
if (end_path && gtk_tree_path_compare (iter_path, end_path) >= 0)
|
|
finished = TRUE;
|
|
gtk_tree_path_free (iter_path);
|
|
if (finished)
|
|
break;
|
|
}
|
|
if (gtk_tree_model_iter_has_child (model, &child_iter))
|
|
count_rows (model, &child_iter, end_path, count, level, depth);
|
|
ret_val = gtk_tree_model_iter_next (model, &child_iter);
|
|
}
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
get_rbtree_column_from_index (GtkTreeView *tree_view,
|
|
gint index,
|
|
GtkRBTree **tree,
|
|
GtkRBNode **node,
|
|
GtkTreeViewColumn **column)
|
|
{
|
|
guint n_columns = get_n_columns (tree_view);
|
|
|
|
if (n_columns == 0)
|
|
return FALSE;
|
|
/* First row is the column headers */
|
|
index -= n_columns;
|
|
if (index < 0)
|
|
return FALSE;
|
|
|
|
if (tree)
|
|
{
|
|
g_return_val_if_fail (node != NULL, FALSE);
|
|
|
|
if (!_gtk_rbtree_find_index (_gtk_tree_view_get_rbtree (tree_view),
|
|
index / n_columns,
|
|
tree,
|
|
node))
|
|
return FALSE;
|
|
}
|
|
|
|
if (column)
|
|
{
|
|
*column = get_visible_column (tree_view, index % n_columns);
|
|
if (*column == NULL)
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
set_cell_expandable (GtkCellAccessible *cell)
|
|
{
|
|
if (_gtk_cell_accessible_add_state (cell, ATK_STATE_EXPANDABLE, FALSE))
|
|
_gtk_cell_accessible_add_action (cell,
|
|
"expand or contract",
|
|
"expands or contracts the row in the tree view containing this cell",
|
|
NULL, toggle_cell_expanded);
|
|
}
|
|
|
|
static GtkTreeViewAccessibleCellInfo *
|
|
find_cell_info (GtkTreeViewAccessible *accessible,
|
|
GtkCellAccessible *cell)
|
|
{
|
|
AtkObject *parent;
|
|
|
|
parent = atk_object_get_parent (ATK_OBJECT (cell));
|
|
while (parent != ATK_OBJECT (accessible))
|
|
{
|
|
cell = GTK_CELL_ACCESSIBLE (parent);
|
|
parent = atk_object_get_parent (ATK_OBJECT (cell));
|
|
}
|
|
|
|
return g_object_get_qdata (G_OBJECT (cell),
|
|
gtk_tree_view_accessible_get_data_quark ());
|
|
}
|
|
|
|
static AtkObject *
|
|
get_header_from_column (GtkTreeViewColumn *tv_col)
|
|
{
|
|
AtkObject *rc;
|
|
GtkWidget *header_widget;
|
|
|
|
if (tv_col == NULL)
|
|
return NULL;
|
|
|
|
header_widget = gtk_tree_view_column_get_button (tv_col);
|
|
|
|
if (header_widget)
|
|
rc = gtk_widget_get_accessible (header_widget);
|
|
else
|
|
rc = NULL;
|
|
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* _gtk_rbtree_get_ancestor_node:
|
|
* @ancestor: the ancestor tree
|
|
* @child_tree: the potential child's tree
|
|
* @child_node: the potential child's node
|
|
*
|
|
* Finds the node that is the ancestor of @child_tree and @child_node
|
|
* and belongs to @ancestor. If @ancestor is not an ancestor tree
|
|
* of @child_node, %NULL is returned.
|
|
*
|
|
* Returns: the ancestor node or %NULL if @ancestor is not an ancestor.
|
|
**/
|
|
static GtkRBNode *
|
|
_gtk_rbtree_get_ancestor_node (GtkRBTree *ancestor,
|
|
GtkRBTree *child_tree,
|
|
GtkRBNode *child_node)
|
|
{
|
|
while (child_tree != NULL)
|
|
{
|
|
if (child_tree == ancestor)
|
|
return child_node;
|
|
|
|
child_node = child_tree->parent_node;
|
|
child_tree = child_tree->parent_tree;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void
|
|
_gtk_tree_view_accessible_remove (GtkTreeView *treeview,
|
|
GtkRBTree *tree,
|
|
GtkRBNode *node)
|
|
{
|
|
GtkTreeViewAccessibleCellInfo *cell_info;
|
|
GHashTableIter iter;
|
|
GtkTreeViewAccessible *accessible;
|
|
|
|
accessible = GTK_TREE_VIEW_ACCESSIBLE (_gtk_widget_peek_accessible (GTK_WIDGET (treeview)));
|
|
if (accessible == NULL)
|
|
return;
|
|
|
|
/* if this shows up in profiles, special-case node->children == NULL */
|
|
|
|
g_hash_table_iter_init (&iter, accessible->cell_infos);
|
|
while (g_hash_table_iter_next (&iter, NULL, (gpointer *)&cell_info))
|
|
{
|
|
GtkRBNode *child_node = _gtk_rbtree_get_ancestor_node (tree,
|
|
cell_info->tree,
|
|
cell_info->node);
|
|
|
|
if (child_node == NULL)
|
|
continue;
|
|
|
|
if (node == NULL || node == child_node)
|
|
g_hash_table_iter_remove (&iter);
|
|
}
|
|
}
|
|
|
|
/* NB: id is not checked, only columns < id are.
|
|
* This is important so the function works for notification of removal of a column */
|
|
guint
|
|
to_visible_column_id (GtkTreeView *treeview,
|
|
guint id)
|
|
{
|
|
guint i;
|
|
guint invisible;
|
|
|
|
invisible = 0;
|
|
|
|
for (i = 0; i < id; i++)
|
|
{
|
|
GtkTreeViewColumn *column = gtk_tree_view_get_column (treeview, i);
|
|
|
|
if (!gtk_tree_view_column_get_visible (column))
|
|
invisible++;
|
|
}
|
|
|
|
return id - invisible;
|
|
}
|
|
|
|
void
|
|
_gtk_tree_view_accessible_do_add_column (GtkTreeViewAccessible *accessible,
|
|
GtkTreeView *treeview,
|
|
GtkTreeViewColumn *column,
|
|
guint id)
|
|
{
|
|
guint row, n_rows, n_cols;
|
|
|
|
/* Generate column-inserted signal */
|
|
g_signal_emit_by_name (accessible, "column-inserted", id, 1);
|
|
|
|
n_rows = get_n_rows (treeview);
|
|
n_cols = get_n_columns (treeview);
|
|
|
|
/* Generate children-changed signals */
|
|
for (row = 0; row <= n_rows; row++)
|
|
{
|
|
/* Pass NULL as the child object, i.e. 4th argument */
|
|
g_signal_emit_by_name (accessible, "children-changed::add",
|
|
(row * n_cols) + id, NULL, NULL);
|
|
}
|
|
|
|
traverse_cells (accessible, NULL, FALSE);
|
|
}
|
|
|
|
void
|
|
_gtk_tree_view_accessible_add_column (GtkTreeView *treeview,
|
|
GtkTreeViewColumn *column,
|
|
guint id)
|
|
{
|
|
AtkObject *obj;
|
|
|
|
if (!gtk_tree_view_column_get_visible (column))
|
|
return;
|
|
|
|
obj = _gtk_widget_peek_accessible (GTK_WIDGET (treeview));
|
|
if (obj == NULL)
|
|
return;
|
|
|
|
_gtk_tree_view_accessible_do_add_column (GTK_TREE_VIEW_ACCESSIBLE (obj),
|
|
treeview,
|
|
column,
|
|
to_visible_column_id (treeview, id));
|
|
}
|
|
|
|
void
|
|
_gtk_tree_view_accessible_do_remove_column (GtkTreeViewAccessible *accessible,
|
|
GtkTreeView *treeview,
|
|
GtkTreeViewColumn *column,
|
|
guint id)
|
|
{
|
|
guint row, n_rows, n_cols;
|
|
|
|
clean_cols (accessible, column);
|
|
|
|
/* Generate column-deleted signal */
|
|
g_signal_emit_by_name (accessible, "column-deleted", id, 1);
|
|
|
|
n_rows = get_n_rows (treeview);
|
|
n_cols = get_n_columns (treeview);
|
|
|
|
/* Generate children-changed signals */
|
|
for (row = 0; row <= n_rows; row++)
|
|
{
|
|
/* Pass NULL as the child object, 4th argument */
|
|
g_signal_emit_by_name (accessible, "children-changed::remove",
|
|
(row * n_cols) + id, NULL, NULL);
|
|
}
|
|
|
|
traverse_cells (accessible, NULL, FALSE);
|
|
}
|
|
|
|
void
|
|
_gtk_tree_view_accessible_remove_column (GtkTreeView *treeview,
|
|
GtkTreeViewColumn *column,
|
|
guint id)
|
|
{
|
|
AtkObject *obj;
|
|
|
|
if (!gtk_tree_view_column_get_visible (column))
|
|
return;
|
|
|
|
obj = _gtk_widget_peek_accessible (GTK_WIDGET (treeview));
|
|
if (obj == NULL)
|
|
return;
|
|
|
|
_gtk_tree_view_accessible_do_remove_column (GTK_TREE_VIEW_ACCESSIBLE (obj),
|
|
treeview,
|
|
column,
|
|
to_visible_column_id (treeview, id));
|
|
}
|
|
|
|
void
|
|
_gtk_tree_view_accessible_reorder_column (GtkTreeView *treeview,
|
|
GtkTreeViewColumn *column)
|
|
{
|
|
AtkObject *obj;
|
|
|
|
obj = _gtk_widget_peek_accessible (GTK_WIDGET (treeview));
|
|
if (obj == NULL)
|
|
return;
|
|
|
|
g_signal_emit_by_name (obj, "column-reordered");
|
|
|
|
traverse_cells (GTK_TREE_VIEW_ACCESSIBLE (obj), NULL, FALSE);
|
|
}
|
|
|
|
void
|
|
_gtk_tree_view_accessible_toggle_visibility (GtkTreeView *treeview,
|
|
GtkTreeViewColumn *column)
|
|
{
|
|
AtkObject *obj;
|
|
guint id;
|
|
|
|
obj = _gtk_widget_peek_accessible (GTK_WIDGET (treeview));
|
|
if (obj == NULL)
|
|
return;
|
|
|
|
id = get_column_number (treeview, column);
|
|
|
|
if (gtk_tree_view_column_get_visible (column))
|
|
_gtk_tree_view_accessible_do_add_column (GTK_TREE_VIEW_ACCESSIBLE (obj),
|
|
treeview,
|
|
column,
|
|
id);
|
|
else
|
|
_gtk_tree_view_accessible_do_remove_column (GTK_TREE_VIEW_ACCESSIBLE (obj),
|
|
treeview,
|
|
column,
|
|
id);
|
|
}
|