Files
evolution/widgets/table/e-tree-selection-model.c
Christopher James Lahey 86ec9f6f49 Sometimes put off maybe showing the cursor until an idle callback.
2001-10-04  Christopher James Lahey  <clahey@ximian.com>

	* e-table-item.c (eti_idle_maybe_show_cursor): Sometimes put off
	maybe showing the cursor until an idle callback.

	* e-tree-selection-model.c (add_model): Use connect_after to
	connect to all the signals.  This means we can assume the sorted
	tree and table adapters are both done.  This also means that those
	objects cannot assume that the selection is already changed.

svn path=/trunk/; revision=13406
2001-10-04 09:42:53 +00:00

1237 lines
35 KiB
C

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
* e-tree-selection-model.c: a Selection Model
*
* Author:
* Christopher James Lahey <clahey@ximian.com>
*
* (C) 2000, 2001 Ximian, Inc.
*/
#include <config.h>
#include <gdk/gdkkeysyms.h>
#include <gtk/gtksignal.h>
#include "e-tree-selection-model.h"
#include <gal/util/e-bit-array.h>
#include <gal/util/e-sorter.h>
#include <gal/util/e-util.h>
#include <gdk/gdkkeysyms.h>
#include <gal/e-table/e-tree-sorted.h>
#include <gal/e-table/e-tree-table-adapter.h>
#define ETSM_CLASS(e) ((ETreeSelectionModelClass *)((GtkObject *)e)->klass)
#define PARENT_TYPE e_selection_model_get_type ()
static ESelectionModelClass *parent_class;
enum {
ARG_0,
ARG_CURSOR_ROW,
ARG_CURSOR_COL,
ARG_MODEL,
ARG_ETTA,
ARG_ETS,
};
typedef struct ETreeSelectionModelNode {
guint selected : 1;
guint all_children_selected : 1;
guint any_children_selected : 1;
EBitArray *all_children_selected_array;
EBitArray *any_children_selected_array;
struct ETreeSelectionModelNode **children;
int num_children;
} ETreeSelectionModelNode;
struct ETreeSelectionModelPriv {
ETreeTableAdapter *etta;
ETreeSorted *ets;
ETreeModel *model;
ETreeSelectionModelNode *root;
ETreePath cursor_path;
gint cursor_col;
ETreePath selection_start_path;
char *cursor_save_id;
int tree_model_pre_change_id;
int tree_model_node_changed_id;
int tree_model_node_data_changed_id;
int tree_model_node_col_changed_id;
int tree_model_node_inserted_id;
int tree_model_node_removed_id;
int sorted_model_node_resorted_id;
};
/* ETreeSelectionModelNode helpers */
static ETreeSelectionModelNode *
e_tree_selection_model_node_new (void)
{
ETreeSelectionModelNode *node = g_new(ETreeSelectionModelNode, 1);
node->selected = 0;
node->all_children_selected = 0;
node->any_children_selected = 0;
node->all_children_selected_array = NULL;
node->any_children_selected_array = NULL;
node->children = NULL;
node->num_children = -1;
return node;
}
static void
e_tree_selection_model_node_fill_children(ETreeSelectionModel *etsm, ETreePath path, ETreeSelectionModelNode *selection_node)
{
int i;
selection_node->num_children = e_tree_sorted_node_num_children(etsm->priv->ets, path);
selection_node->children = g_new(ETreeSelectionModelNode *, selection_node->num_children);
for (i = 0; i < selection_node->num_children; i++) {
selection_node->children[i] = NULL;
}
}
static void
e_tree_selection_model_node_free(ETreeSelectionModelNode *node)
{
int i;
if (node->all_children_selected_array)
gtk_object_unref(GTK_OBJECT(node->all_children_selected_array));
if (node->any_children_selected_array)
gtk_object_unref(GTK_OBJECT(node->any_children_selected_array));
for (i = 0; i < node->num_children; i++)
if (node->children[i])
e_tree_selection_model_node_free(node->children[i]);
g_free(node->children);
g_free(node);
}
/* Other helper functions */
static ETreePath
etsm_node_at_row(ETreeSelectionModel *etsm, int row)
{
ETreePath path;
if (!(row >= 0 && row < e_table_model_row_count(E_TABLE_MODEL(etsm->priv->etta))))
return NULL;
path = e_tree_table_adapter_node_at_row(etsm->priv->etta, row);
if (path)
path = e_tree_sorted_view_to_model_path(etsm->priv->ets, path);
return path;
}
static int
etsm_row_of_node(ETreeSelectionModel *etsm, ETreePath path)
{
path = e_tree_sorted_model_to_view_path(etsm->priv->ets, path);
if (path)
return e_tree_table_adapter_row_of_node(etsm->priv->etta, path);
else
return -1;
}
static int
etsm_cursor_row_real (ETreeSelectionModel *etsm)
{
if (etsm->priv->cursor_path)
return etsm_row_of_node(etsm, etsm->priv->cursor_path);
else
return -1;
}
static void
etsm_real_clear (ETreeSelectionModel *etsm)
{
if (etsm->priv->root) {
e_tree_selection_model_node_free(etsm->priv->root);
etsm->priv->root = NULL;
}
}
static ETreeSelectionModelNode *
etsm_find_node_unless_equals (ETreeSelectionModel *etsm,
ETreePath path,
gboolean grow)
{
ETreeSelectionModelNode *selection_node;
ETreeSorted *ets = etsm->priv->ets;
ETreePath parent;
parent = e_tree_model_node_get_parent(E_TREE_MODEL(ets), path);
if (parent) {
selection_node = etsm_find_node_unless_equals(etsm, parent, grow);
if (selection_node) {
int position = e_tree_sorted_orig_position(ets, path);
if (selection_node->all_children_selected && grow)
return NULL;
if (!(selection_node->any_children_selected || grow))
return NULL;
if (selection_node->all_children_selected_array && e_bit_array_value_at(selection_node->all_children_selected_array, position) && grow)
return NULL;
if (selection_node->any_children_selected_array && ! (e_bit_array_value_at(selection_node->any_children_selected_array, position) || grow))
return NULL;
if (selection_node->children == NULL) {
e_tree_selection_model_node_fill_children(etsm, parent, selection_node);
}
if (!selection_node->children[position])
selection_node->children[position] = e_tree_selection_model_node_new();
return selection_node->children[position];
} else
return NULL;
} else {
if (!etsm->priv->root)
etsm->priv->root = e_tree_selection_model_node_new();
return etsm->priv->root;
}
}
#if 0
static ETreeSelectionModelNode *
find_or_create_node (ETreeSelectionModel *etsm,
ETreePath path)
{
ETreeSelectionModelNode *selection_node;
ETreeSelectionModelNode **place = NULL;
ETreeSorted *ets = etsm->priv->ets;
ETreePath parent;
parent = e_tree_model_node_get_parent(E_TREE_MODEL(ets), path);
if (parent) {
selection_node = find_or_create_node(etsm, parent);
if (selection_node) {
int position = e_tree_sorted_orig_position(ets, path);
if (!selection_node->children) {
e_tree_selection_model_node_fill_children(etsm, parent, selection_node);
}
if (!selection_node->children[position])
slection_node->children[position] = e_tree_selection_model_node_new();
return selection_node->children[position];
} else
return NULL;
} else {
if (!etsm->priv->root)
etsm->priv->root = e_tree_selection_model_node_new();
return etsm->priv->root;
}
}
#endif
static void
update_parents (ETreeSelectionModel *etsm, ETreePath path)
{
int i;
int depth;
ETreeSorted *ets = etsm->priv->ets;
int *orig_position_sequence;
ETreeSelectionModelNode **node_sequence;
ETreePath parents;
if (!etsm->priv->root)
return;
depth = e_tree_model_node_depth (E_TREE_MODEL(ets), path);
orig_position_sequence = g_new(int, depth + 1);
node_sequence = g_new(ETreeSelectionModelNode *, depth + 1);
parents = path;
for (i = depth; i > 0; i--) {
if (!parents) {
g_free(orig_position_sequence);
g_free(node_sequence);
return;
}
orig_position_sequence[i] = e_tree_sorted_orig_position(etsm->priv->ets, parents);
parents = e_tree_model_node_get_parent(E_TREE_MODEL(etsm->priv->ets), parents);
}
node_sequence[0] = etsm->priv->root;
for (i = 0; i < depth; i++) {
node_sequence[i + 1] = NULL;
if (node_sequence[i]->children)
node_sequence[i + 1] = node_sequence[i]->children[orig_position_sequence[i + 1]];
if (node_sequence[i + 1] == NULL) {
g_free(orig_position_sequence);
g_free(node_sequence);
return;
}
}
if (node_sequence[depth]->num_children == -1)
e_tree_selection_model_node_fill_children(etsm, path, node_sequence[depth]);
if (!node_sequence[depth]->all_children_selected_array)
node_sequence[depth]->all_children_selected_array = e_bit_array_new(node_sequence[depth]->num_children);
if (!node_sequence[depth]->any_children_selected_array)
node_sequence[depth]->any_children_selected_array = e_bit_array_new(node_sequence[depth]->num_children);
node_sequence[depth]->all_children_selected =
e_bit_array_cross_and(node_sequence[depth]->all_children_selected_array) &&
node_sequence[depth]->selected;
node_sequence[depth]->any_children_selected =
e_bit_array_cross_or(node_sequence[depth]->any_children_selected_array) ||
node_sequence[depth]->selected;
for (i = depth - 1; i >= 0; i--) {
gboolean all_children, any_children;
if (!node_sequence[i]->all_children_selected_array)
node_sequence[i]->all_children_selected_array = e_bit_array_new(node_sequence[i]->num_children);
if (!node_sequence[i]->any_children_selected_array)
node_sequence[i]->any_children_selected_array = e_bit_array_new(node_sequence[i]->num_children);
e_bit_array_change_one_row(node_sequence[i]->all_children_selected_array,
orig_position_sequence[i + 1], node_sequence[i + 1]->all_children_selected);
e_bit_array_change_one_row(node_sequence[i]->any_children_selected_array,
orig_position_sequence[i + 1], node_sequence[i + 1]->any_children_selected);
all_children = node_sequence[i]->all_children_selected;
any_children = node_sequence[i]->any_children_selected;
node_sequence[i]->all_children_selected =
e_bit_array_cross_and(node_sequence[i]->all_children_selected_array) &&
node_sequence[i]->selected;
node_sequence[i]->any_children_selected =
e_bit_array_cross_or(node_sequence[i]->any_children_selected_array) ||
node_sequence[i]->selected;
if (all_children == node_sequence[i]->all_children_selected &&
any_children == node_sequence[i]->any_children_selected)
break;
}
g_free(orig_position_sequence);
g_free(node_sequence);
}
/* Signal handlers */
static void
etsm_pre_change (ETreeModel *etm, ETreeSelectionModel *etsm)
{
g_free (etsm->priv->cursor_save_id);
etsm->priv->cursor_save_id = NULL;
if (e_tree_model_has_get_node_by_id (etm) &&
e_tree_model_has_save_id (etm) &&
etsm->priv->cursor_path) {
etsm->priv->cursor_save_id = e_tree_model_get_save_id (etm, etsm->priv->cursor_path);
}
}
static void
etsm_node_changed (ETreeModel *etm, ETreePath node, ETreeSelectionModel *etsm)
{
int cursor_row;
etsm_real_clear (etsm);
etsm->priv->cursor_path = NULL;
if (e_tree_model_has_get_node_by_id (etm) && etsm->priv->cursor_save_id) {
ETreePath cursor_path;
cursor_path = e_tree_model_get_node_by_id (etm, etsm->priv->cursor_save_id);
etsm->priv->cursor_path = cursor_path;
if (cursor_path != NULL && etsm->priv->cursor_col == -1)
etsm->priv->cursor_col = 0;
e_tree_selection_model_select_single_path(etsm, cursor_path);
}
cursor_row = etsm_cursor_row_real (etsm);
e_selection_model_selection_changed(E_SELECTION_MODEL(etsm));
if (cursor_row != -1)
e_selection_model_cursor_changed(E_SELECTION_MODEL(etsm), cursor_row, etsm->priv->cursor_col);
else {
e_selection_model_cursor_changed(E_SELECTION_MODEL(etsm), -1, -1);
e_selection_model_cursor_activated(E_SELECTION_MODEL(etsm), -1, -1);
}
g_free (etsm->priv->cursor_save_id);
etsm->priv->cursor_save_id = NULL;
}
static void
etsm_node_data_changed (ETreeModel *etm, ETreePath node, ETreeSelectionModel *etsm)
{
g_free (etsm->priv->cursor_save_id);
etsm->priv->cursor_save_id = NULL;
}
static void
etsm_node_col_changed (ETreeModel *etm, ETreePath node, int col, ETreeSelectionModel *etsm)
{
g_free (etsm->priv->cursor_save_id);
etsm->priv->cursor_save_id = NULL;
}
static void
etsm_node_inserted (ETreeModel *etm, ETreePath parent, ETreePath child, ETreeSelectionModel *etsm)
{
etsm_node_changed(etm, parent, etsm);
#if 0
ETreeSelectionModelNode *node;
ETreePath path;
path = e_tree_sorted_model_to_view_path(etsm->priv->ets, parent);
if (!path)
return;
node = etsm_find_node_unless_equals (etsm, path, FALSE);
if (node) {
node->selected = FALSE;
update_parents(etsm, path);
}
#endif
}
static void
etsm_node_removed (ETreeModel *etm, ETreePath parent, ETreePath child, int old_position, ETreeSelectionModel *etsm)
{
etsm_node_changed(etm, parent, etsm);
}
static void
etsm_sorted_node_resorted (ETreeModel *etm, ETreePath node, ETreeSelectionModel *etsm)
{
int cursor_row = etsm_cursor_row_real (etsm);
e_selection_model_selection_changed(E_SELECTION_MODEL(etsm));
if (cursor_row != -1)
e_selection_model_cursor_changed(E_SELECTION_MODEL(etsm), cursor_row, etsm->priv->cursor_col);
else
e_selection_model_cursor_changed(E_SELECTION_MODEL(etsm), -1, -1);
}
static void
add_model(ETreeSelectionModel *etsm, ETreeModel *model)
{
ETreeSelectionModelPriv *priv = etsm->priv;
priv->model = model;
if (!priv->model)
return;
gtk_object_ref(GTK_OBJECT(priv->model));
priv->tree_model_pre_change_id = gtk_signal_connect_after (GTK_OBJECT (priv->model), "pre_change",
GTK_SIGNAL_FUNC (etsm_pre_change), etsm);
priv->tree_model_node_changed_id = gtk_signal_connect_after (GTK_OBJECT (priv->model), "node_changed",
GTK_SIGNAL_FUNC (etsm_node_changed), etsm);
priv->tree_model_node_data_changed_id = gtk_signal_connect_after (GTK_OBJECT (priv->model), "node_data_changed",
GTK_SIGNAL_FUNC (etsm_node_data_changed), etsm);
priv->tree_model_node_col_changed_id = gtk_signal_connect_after (GTK_OBJECT (priv->model), "node_col_changed",
GTK_SIGNAL_FUNC (etsm_node_col_changed), etsm);
priv->tree_model_node_inserted_id = gtk_signal_connect_after (GTK_OBJECT (priv->model), "node_inserted",
GTK_SIGNAL_FUNC (etsm_node_inserted), etsm);
priv->tree_model_node_removed_id = gtk_signal_connect_after (GTK_OBJECT (priv->model), "node_removed",
GTK_SIGNAL_FUNC (etsm_node_removed), etsm);
}
static void
drop_model(ETreeSelectionModel *etsm)
{
ETreeSelectionModelPriv *priv = etsm->priv;
if (!priv->model)
return;
gtk_signal_disconnect (GTK_OBJECT (priv->model),
priv->tree_model_pre_change_id);
gtk_signal_disconnect (GTK_OBJECT (priv->model),
priv->tree_model_node_changed_id);
gtk_signal_disconnect (GTK_OBJECT (priv->model),
priv->tree_model_node_data_changed_id);
gtk_signal_disconnect (GTK_OBJECT (priv->model),
priv->tree_model_node_col_changed_id);
gtk_signal_disconnect (GTK_OBJECT (priv->model),
priv->tree_model_node_inserted_id);
gtk_signal_disconnect (GTK_OBJECT (priv->model),
priv->tree_model_node_removed_id);
gtk_object_unref (GTK_OBJECT (priv->model));
priv->model = NULL;
priv->tree_model_pre_change_id = 0;
priv->tree_model_node_changed_id = 0;
priv->tree_model_node_data_changed_id = 0;
priv->tree_model_node_col_changed_id = 0;
priv->tree_model_node_inserted_id = 0;
priv->tree_model_node_removed_id = 0;
}
static void
add_ets(ETreeSelectionModel *etsm, ETreeSorted *ets)
{
ETreeSelectionModelPriv *priv = etsm->priv;
priv->ets = ets;
if (!priv->ets)
return;
gtk_object_ref(GTK_OBJECT(priv->ets));
priv->sorted_model_node_resorted_id = gtk_signal_connect (GTK_OBJECT (priv->ets), "node_resorted",
GTK_SIGNAL_FUNC (etsm_sorted_node_resorted), etsm);
}
static void
drop_ets(ETreeSelectionModel *etsm)
{
ETreeSelectionModelPriv *priv = etsm->priv;
if (!priv->ets)
return;
gtk_signal_disconnect (GTK_OBJECT (priv->ets),
priv->sorted_model_node_resorted_id);
gtk_object_unref (GTK_OBJECT (priv->ets));
priv->ets = NULL;
priv->sorted_model_node_resorted_id = 0;
}
/* Virtual functions */
static void
etsm_destroy (GtkObject *object)
{
ETreeSelectionModel *etsm;
etsm = E_TREE_SELECTION_MODEL (object);
etsm_real_clear (etsm);
etsm->priv->cursor_path = NULL;
drop_model(etsm);
drop_ets(etsm);
g_free (etsm->priv->cursor_save_id);
g_free(etsm->priv);
etsm->priv = NULL;
if (GTK_OBJECT_CLASS (parent_class)->destroy)
(* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
}
static void
etsm_get_arg (GtkObject *o, GtkArg *arg, guint arg_id)
{
ETreeSelectionModel *etsm = E_TREE_SELECTION_MODEL (o);
switch (arg_id){
case ARG_CURSOR_ROW:
GTK_VALUE_INT(*arg) = etsm_cursor_row_real(etsm);
break;
case ARG_CURSOR_COL:
GTK_VALUE_INT(*arg) = etsm->priv->cursor_col;
break;
case ARG_MODEL:
GTK_VALUE_OBJECT(*arg) = (GtkObject *) etsm->priv->model;
break;
case ARG_ETTA:
GTK_VALUE_OBJECT(*arg) = (GtkObject *) etsm->priv->etta;
break;
case ARG_ETS:
GTK_VALUE_OBJECT(*arg) = (GtkObject *) etsm->priv->ets;
break;
}
}
static void
etsm_set_arg (GtkObject *o, GtkArg *arg, guint arg_id)
{
ESelectionModel *esm = E_SELECTION_MODEL (o);
ETreeSelectionModel *etsm = E_TREE_SELECTION_MODEL (o);
switch (arg_id){
case ARG_CURSOR_ROW:
e_selection_model_do_something(esm, GTK_VALUE_INT(*arg), etsm->priv->cursor_col, 0);
break;
case ARG_CURSOR_COL:
e_selection_model_do_something(esm, etsm_cursor_row_real(etsm), GTK_VALUE_INT(*arg), 0);
break;
case ARG_MODEL:
drop_model(etsm);
add_model(etsm, (ETreeModel *) GTK_VALUE_OBJECT(*arg));
break;
case ARG_ETTA:
etsm->priv->etta = (ETreeTableAdapter *) GTK_VALUE_OBJECT(*arg);
break;
case ARG_ETS:
drop_ets(etsm);
add_ets(etsm, (ETreeSorted *) GTK_VALUE_OBJECT(*arg));
break;
}
}
static ETreeSelectionModelNode *
etsm_recurse_is_path_selected (ESelectionModel *selection,
ETreePath path,
gboolean *is_selected)
{
ETreeSelectionModelNode *selection_node;
ETreeSelectionModel *etsm = E_TREE_SELECTION_MODEL(selection);
ETreeSorted *ets = etsm->priv->ets;
ETreePath parent;
parent = e_tree_model_node_get_parent(E_TREE_MODEL(ets), path);
if (parent) {
selection_node = etsm_recurse_is_path_selected (selection, parent, is_selected);
if (selection_node) {
int position = e_tree_sorted_orig_position(ets, path);
if (position < 0 || position >= selection_node->num_children) {
*is_selected = FALSE;
return NULL;
}
if (selection_node->all_children_selected) {
*is_selected = TRUE;
return NULL;
}
if (! selection_node->any_children_selected) {
*is_selected = FALSE;
return NULL;
}
if (selection_node->all_children_selected_array && e_bit_array_value_at(selection_node->all_children_selected_array, position)) {
*is_selected = TRUE;
return NULL;
}
if (selection_node->any_children_selected_array && ! e_bit_array_value_at(selection_node->any_children_selected_array, position)) {
*is_selected = FALSE;
return NULL;
}
if (!selection_node->children) {
*is_selected = FALSE;
return NULL;
}
return selection_node->children[position];
} else
return NULL;
} else {
if (etsm->priv->root) {
return etsm->priv->root;
} else {
*is_selected = FALSE;
return NULL;
}
}
}
/**
* e_selection_model_is_row_selected
* @selection: #ESelectionModel to check
* @n: The row to check
*
* This routine calculates whether the given row is selected.
*
* Returns: %TRUE if the given row is selected
*/
static gboolean
etsm_is_row_selected (ESelectionModel *selection,
gint row)
{
ETreeSelectionModel *etsm = E_TREE_SELECTION_MODEL(selection);
ETreePath path;
ETreeSelectionModelNode *selection_node;
gboolean ret_val;
g_return_val_if_fail(row < e_table_model_row_count(E_TABLE_MODEL(etsm->priv->etta)), FALSE);
g_return_val_if_fail(row >= 0, FALSE);
g_return_val_if_fail(selection != NULL, FALSE);
path = e_tree_table_adapter_node_at_row(etsm->priv->etta, row);
selection_node = etsm_recurse_is_path_selected (selection, path, &ret_val);
if (selection_node)
ret_val = selection_node->selected;
return ret_val;
}
typedef struct {
ETreeSelectionModel *etsm;
EForeachFunc callback;
gpointer closure;
} ModelAndCallback;
static void
etsm_row_foreach_cb (ETreePath path, gpointer user_data)
{
ModelAndCallback *mac = user_data;
int row = etsm_row_of_node(mac->etsm, path);
if (row >= 0)
mac->callback(row, mac->closure);
}
/**
* e_selection_model_foreach
* @selection: #ESelectionModel to traverse
* @callback: The callback function to call back.
* @closure: The closure
*
* This routine calls the given callback function once for each
* selected row, passing closure as the closure.
*/
static void
etsm_foreach (ESelectionModel *selection,
EForeachFunc callback,
gpointer closure)
{
ETreeSelectionModel *etsm = E_TREE_SELECTION_MODEL(selection);
ModelAndCallback mac;
mac.etsm = etsm;
mac.callback = callback;
mac.closure = closure;
e_tree_selection_model_foreach(etsm, etsm_row_foreach_cb, &mac);
}
/**
* e_selection_model_clear
* @selection: #ESelectionModel to clear
*
* This routine clears the selection to no rows selected.
*/
static void
etsm_clear(ESelectionModel *selection)
{
ETreeSelectionModel *etsm = E_TREE_SELECTION_MODEL(selection);
etsm_real_clear (etsm);
etsm->priv->cursor_path = NULL;
etsm->priv->cursor_col = -1;
e_selection_model_selection_changed(E_SELECTION_MODEL(etsm));
e_selection_model_cursor_changed(E_SELECTION_MODEL(etsm), -1, -1);
}
/* Standard functions */
static void
etsm_selected_count_all_recurse (ETreeSelectionModel *etsm,
ETreePath path,
int *count)
{
ETreePath child;
(*count) ++;
child = e_tree_model_node_get_first_child(E_TREE_MODEL(etsm->priv->model), path);
for ( ; child; child = e_tree_model_node_get_next(E_TREE_MODEL(etsm->priv->model), child))
if (child)
etsm_selected_count_all_recurse (etsm, child, count);
}
static void
etsm_selected_count_recurse (ETreeSelectionModel *etsm,
ETreeSelectionModelNode *selection_node,
ETreePath path,
int *count)
{
if (selection_node->all_children_selected) {
if (path)
etsm_selected_count_all_recurse(etsm, path, count);
return;
}
if (!selection_node->any_children_selected)
return;
if (selection_node->selected) {
(*count) ++;
}
if (selection_node->children) {
ETreePath child = e_tree_model_node_get_first_child(E_TREE_MODEL(etsm->priv->model), path);
int i;
for (i = 0; child && i < selection_node->num_children; i++, child = e_tree_model_node_get_next(E_TREE_MODEL(etsm->priv->model), child))
if (selection_node->children[i])
etsm_selected_count_recurse (etsm, selection_node->children[i], child, count);
}
}
/**
* e_selection_model_selected_count
* @selection: #ESelectionModel to count
*
* This routine calculates the number of rows selected.
*
* Returns: The number of rows selected in the given model.
*/
static gint
etsm_selected_count (ESelectionModel *selection)
{
ETreeSelectionModel *etsm = E_TREE_SELECTION_MODEL(selection);
int count = 0;
if (etsm->priv->root) {
ETreePath model_root;
model_root = e_tree_model_get_root(etsm->priv->model);
etsm_selected_count_recurse(etsm, etsm->priv->root, model_root, &count);
}
return count;
}
/**
* e_selection_model_select_all
* @selection: #ESelectionModel to select all
*
* This routine selects all the rows in the given
* #ESelectionModel.
*/
static void
etsm_select_all (ESelectionModel *selection)
{
ETreeSelectionModel *etsm = E_TREE_SELECTION_MODEL(selection);
etsm_real_clear (etsm);
etsm->priv->root = e_tree_selection_model_node_new();
etsm->priv->root->selected = TRUE;
etsm->priv->root->all_children_selected = TRUE;
etsm->priv->root->any_children_selected = TRUE;
e_tree_selection_model_node_fill_children(etsm, e_tree_model_get_root(E_TREE_MODEL(etsm->priv->ets)), etsm->priv->root);
etsm->priv->root->all_children_selected_array = e_bit_array_new(etsm->priv->root->num_children);
etsm->priv->root->any_children_selected_array = e_bit_array_new(etsm->priv->root->num_children);
e_bit_array_select_all(etsm->priv->root->all_children_selected_array);
e_bit_array_select_all(etsm->priv->root->any_children_selected_array);
if (etsm->priv->cursor_col == -1)
etsm->priv->cursor_col = 0;
if (etsm->priv->cursor_path == NULL)
etsm->priv->cursor_path = etsm_node_at_row(etsm, 0);
etsm->priv->selection_start_path = etsm_node_at_row(etsm, 0);
e_selection_model_selection_changed(E_SELECTION_MODEL(etsm));
e_selection_model_cursor_changed(E_SELECTION_MODEL(etsm), etsm_cursor_row_real(etsm), etsm->priv->cursor_col);
}
static void
etsm_invert_selection_recurse (ETreeSelectionModel *etsm,
ETreeSelectionModelNode *selection_node)
{
gboolean temp;
EBitArray *temp_eba;
selection_node->selected = ! selection_node->selected;
temp = selection_node->all_children_selected;
selection_node->all_children_selected = ! selection_node->any_children_selected;
selection_node->any_children_selected = ! temp;
temp_eba = selection_node->all_children_selected_array;
selection_node->all_children_selected_array = selection_node->any_children_selected_array;
selection_node->any_children_selected_array = temp_eba;
if (selection_node->all_children_selected_array)
e_bit_array_invert_selection(selection_node->all_children_selected_array);
if (selection_node->any_children_selected_array)
e_bit_array_invert_selection(selection_node->any_children_selected_array);
if (selection_node->children) {
int i;
for (i = 0; i < selection_node->num_children; i++) {
if (selection_node->children[i])
etsm_invert_selection_recurse (etsm, selection_node->children[i]);
}
}
}
/**
* e_selection_model_invert_selection
* @selection: #ESelectionModel to invert
*
* This routine inverts all the rows in the given
* #ESelectionModel.
*/
static void
etsm_invert_selection (ESelectionModel *selection)
{
ETreeSelectionModel *etsm = E_TREE_SELECTION_MODEL(selection);
if (etsm->priv->root)
etsm_invert_selection_recurse (etsm, etsm->priv->root);
etsm->priv->cursor_col = -1;
etsm->priv->cursor_path = NULL;
etsm->priv->selection_start_path = etsm_node_at_row(etsm, 0);
e_selection_model_selection_changed(E_SELECTION_MODEL(etsm));
e_selection_model_cursor_changed(E_SELECTION_MODEL(etsm), -1, -1);
}
static int
etsm_row_count (ESelectionModel *selection)
{
ETreeSelectionModel *etsm = E_TREE_SELECTION_MODEL(selection);
return e_table_model_row_count(E_TABLE_MODEL(etsm->priv->etta));
}
static void
etsm_change_one_row(ESelectionModel *selection, int row, gboolean grow)
{
ETreeSelectionModel *etsm = E_TREE_SELECTION_MODEL(selection);
ETreeSelectionModelNode *node;
ETreePath path;
g_return_if_fail(row < e_table_model_row_count(E_TABLE_MODEL(etsm->priv->etta)));
g_return_if_fail(row >= 0);
g_return_if_fail(selection != NULL);
path = e_tree_table_adapter_node_at_row(etsm->priv->etta, row);
if (!path)
return;
node = etsm_find_node_unless_equals (etsm, path, grow);
if (node) {
node->selected = grow;
update_parents(etsm, path);
}
}
static void
etsm_change_one_path(ETreeSelectionModel *etsm, ETreePath path, gboolean grow)
{
ETreeSelectionModelNode *node;
if (!path)
return;
path = e_tree_sorted_model_to_view_path(etsm->priv->ets, path);
if (!path)
return;
node = etsm_find_node_unless_equals (etsm, path, grow);
if (node) {
node->selected = grow;
update_parents(etsm, path);
}
}
static void
etsm_change_cursor (ESelectionModel *selection, int row, int col)
{
ETreeSelectionModel *etsm;
g_return_if_fail(selection != NULL);
g_return_if_fail(E_IS_SELECTION_MODEL(selection));
etsm = E_TREE_SELECTION_MODEL(selection);
if (row == -1) {
etsm->priv->cursor_path = NULL;
} else {
etsm->priv->cursor_path = etsm_node_at_row(etsm, row);
}
etsm->priv->cursor_col = col;
}
static void
etsm_change_range(ESelectionModel *selection, int start, int end, gboolean grow)
{
int i;
if (start != end) {
if (selection->sorter && e_sorter_needs_sorting(selection->sorter)) {
for ( i = start; i < end; i++) {
e_selection_model_change_one_row(selection, e_sorter_sorted_to_model(selection->sorter, i), grow);
}
} else {
for ( i = start; i < end; i++) {
e_selection_model_change_one_row(selection, i, grow);
}
}
}
}
static int
etsm_cursor_row (ESelectionModel *selection)
{
return etsm_cursor_row_real(E_TREE_SELECTION_MODEL(selection));
}
static int
etsm_cursor_col (ESelectionModel *selection)
{
ETreeSelectionModel *etsm = E_TREE_SELECTION_MODEL(selection);
return etsm->priv->cursor_col;
}
static void
etsm_select_single_row (ESelectionModel *selection, int row)
{
ETreeSelectionModel *etsm = E_TREE_SELECTION_MODEL(selection);
etsm_real_clear (etsm);
etsm_change_one_row(selection, row, TRUE);
etsm->priv->selection_start_path = etsm_node_at_row(etsm, row);
e_selection_model_selection_changed(E_SELECTION_MODEL(etsm));
}
static void
etsm_toggle_single_row (ESelectionModel *selection, int row)
{
ETreeSelectionModel *etsm = E_TREE_SELECTION_MODEL(selection);
etsm->priv->selection_start_path = etsm_node_at_row(etsm, row);
etsm_change_one_row(selection, row, !etsm_is_row_selected(selection, row));
e_selection_model_selection_changed(E_SELECTION_MODEL(etsm));
}
static void
etsm_move_selection_end (ESelectionModel *selection, int row)
{
ETreeSelectionModel *etsm = E_TREE_SELECTION_MODEL(selection);
int old_start;
int old_end;
int new_start;
int new_end;
int start_row = etsm_row_of_node(etsm, etsm->priv->selection_start_path);
if (selection->sorter && e_sorter_needs_sorting(selection->sorter)) {
old_start = MIN (e_sorter_model_to_sorted(selection->sorter, start_row),
e_sorter_model_to_sorted(selection->sorter, etsm_cursor_row_real(etsm)));
old_end = MAX (e_sorter_model_to_sorted(selection->sorter, start_row),
e_sorter_model_to_sorted(selection->sorter, etsm_cursor_row_real(etsm))) + 1;
new_start = MIN (e_sorter_model_to_sorted(selection->sorter, start_row),
e_sorter_model_to_sorted(selection->sorter, row));
new_end = MAX (e_sorter_model_to_sorted(selection->sorter, start_row),
e_sorter_model_to_sorted(selection->sorter, row)) + 1;
} else {
old_start = MIN (start_row, etsm_cursor_row_real(etsm));
old_end = MAX (start_row, etsm_cursor_row_real(etsm)) + 1;
new_start = MIN (start_row, row);
new_end = MAX (start_row, row) + 1;
}
/* This wouldn't work nearly so smoothly if one end of the selection weren't held in place. */
if (old_start < new_start)
etsm_change_range(selection, old_start, new_start, FALSE);
if (new_start < old_start)
etsm_change_range(selection, new_start, old_start, TRUE);
if (old_end < new_end)
etsm_change_range(selection, old_end, new_end, TRUE);
if (new_end < old_end)
etsm_change_range(selection, new_end, old_end, FALSE);
e_selection_model_selection_changed(E_SELECTION_MODEL(etsm));
}
static void
etsm_set_selection_end (ESelectionModel *selection, int row)
{
ETreeSelectionModel *etsm = E_TREE_SELECTION_MODEL(selection);
e_tree_selection_model_select_single_path(etsm, etsm->priv->selection_start_path);
etsm->priv->cursor_path = etsm->priv->selection_start_path;
e_selection_model_move_selection_end(selection, row);
}
/* Standard functions */
static void
etsm_foreach_all_recurse (ETreeSelectionModel *etsm,
ETreePath path,
ETreeForeachFunc callback,
gpointer closure)
{
ETreePath child;
callback(path, closure);
child = e_tree_model_node_get_first_child(E_TREE_MODEL(etsm->priv->model), path);
for ( ; child; child = e_tree_model_node_get_next(E_TREE_MODEL(etsm->priv->model), child))
if (child)
etsm_foreach_all_recurse (etsm, child, callback, closure);
}
static void
etsm_foreach_recurse (ETreeSelectionModel *etsm,
ETreeSelectionModelNode *selection_node,
ETreePath path,
ETreeForeachFunc callback,
gpointer closure)
{
if (selection_node->all_children_selected) {
if (path)
etsm_foreach_all_recurse(etsm, path, callback, closure);
return;
}
if (!selection_node->any_children_selected)
return;
if (selection_node->selected) {
callback(path, closure);
}
if (selection_node->children) {
ETreePath child = e_tree_model_node_get_first_child(E_TREE_MODEL(etsm->priv->model), path);
int i;
for (i = 0; i < selection_node->num_children; i++, child = e_tree_model_node_get_next(E_TREE_MODEL(etsm->priv->model), child))
if (selection_node->children[i])
etsm_foreach_recurse (etsm, selection_node->children[i], child, callback, closure);
}
}
void
e_tree_selection_model_foreach (ETreeSelectionModel *etsm,
ETreeForeachFunc callback,
gpointer closure)
{
if (etsm->priv->root) {
ETreePath model_root;
model_root = e_tree_model_get_root(etsm->priv->model);
etsm_foreach_recurse(etsm, etsm->priv->root, model_root, callback, closure);
}
}
void
e_tree_selection_model_select_single_path (ETreeSelectionModel *etsm, ETreePath path)
{
etsm_real_clear (etsm);
etsm_change_one_path(etsm, path, TRUE);
etsm->priv->selection_start_path = path;
e_selection_model_selection_changed(E_SELECTION_MODEL(etsm));
}
void
e_tree_selection_model_add_to_selection (ETreeSelectionModel *etsm, ETreePath path)
{
etsm_change_one_path(etsm, path, TRUE);
etsm->priv->selection_start_path = path;
e_selection_model_selection_changed(E_SELECTION_MODEL(etsm));
}
void
e_tree_selection_model_change_cursor (ETreeSelectionModel *etsm, ETreePath path)
{
int row;
etsm->priv->cursor_path = path;
row = etsm_cursor_row_real(etsm);
e_selection_model_cursor_changed(E_SELECTION_MODEL(etsm), row, etsm->priv->cursor_col);
e_selection_model_cursor_activated(E_SELECTION_MODEL(etsm), row, etsm->priv->cursor_col);
}
static void
e_tree_selection_model_init (ETreeSelectionModel *etsm)
{
ETreeSelectionModelPriv *priv;
priv = g_new(ETreeSelectionModelPriv, 1);
etsm->priv = priv;
priv->etta = NULL;
priv->ets = NULL;
priv->model = NULL;
priv->root = NULL;
priv->cursor_path = NULL;
priv->cursor_col = -1;
priv->selection_start_path = NULL;
priv->cursor_save_id = NULL;
priv->tree_model_pre_change_id = 0;
priv->tree_model_node_changed_id = 0;
priv->tree_model_node_data_changed_id = 0;
priv->tree_model_node_col_changed_id = 0;
priv->tree_model_node_inserted_id = 0;
priv->tree_model_node_removed_id = 0;
priv->sorted_model_node_resorted_id = 0;
}
static void
e_tree_selection_model_class_init (ETreeSelectionModelClass *klass)
{
GtkObjectClass *object_class;
ESelectionModelClass *esm_class;
parent_class = gtk_type_class (e_selection_model_get_type ());
object_class = GTK_OBJECT_CLASS(klass);
esm_class = E_SELECTION_MODEL_CLASS(klass);
object_class->destroy = etsm_destroy;
object_class->get_arg = etsm_get_arg;
object_class->set_arg = etsm_set_arg;
esm_class->is_row_selected = etsm_is_row_selected ;
esm_class->foreach = etsm_foreach ;
esm_class->clear = etsm_clear ;
esm_class->selected_count = etsm_selected_count ;
esm_class->select_all = etsm_select_all ;
esm_class->invert_selection = etsm_invert_selection ;
esm_class->row_count = etsm_row_count ;
esm_class->change_one_row = etsm_change_one_row ;
esm_class->change_cursor = etsm_change_cursor ;
esm_class->cursor_row = etsm_cursor_row ;
esm_class->cursor_col = etsm_cursor_col ;
esm_class->select_single_row = etsm_select_single_row ;
esm_class->toggle_single_row = etsm_toggle_single_row ;
esm_class->move_selection_end = etsm_move_selection_end ;
esm_class->set_selection_end = etsm_set_selection_end ;
gtk_object_add_arg_type ("ETreeSelectionModel::cursor_row", GTK_TYPE_INT,
GTK_ARG_READWRITE, ARG_CURSOR_ROW);
gtk_object_add_arg_type ("ETreeSelectionModel::cursor_col", GTK_TYPE_INT,
GTK_ARG_READWRITE, ARG_CURSOR_COL);
gtk_object_add_arg_type ("ETreeSelectionModel::model", E_TREE_MODEL_TYPE,
GTK_ARG_READWRITE, ARG_MODEL);
gtk_object_add_arg_type ("ETreeSelectionModel::etta", E_TREE_TABLE_ADAPTER_TYPE,
GTK_ARG_READWRITE, ARG_ETTA);
gtk_object_add_arg_type ("ETreeSelectionModel::ets", E_TREE_SORTED_TYPE,
GTK_ARG_READWRITE, ARG_ETS);
}
ESelectionModel *
e_tree_selection_model_new (void)
{
return gtk_type_new(e_tree_selection_model_get_type());
}
E_MAKE_TYPE(e_tree_selection_model, "ETreeSelectionModel", ETreeSelectionModel,
e_tree_selection_model_class_init, e_tree_selection_model_init, PARENT_TYPE);