aboutsummaryrefslogtreecommitdiffstats
path: root/widgets
diff options
context:
space:
mode:
Diffstat (limited to 'widgets')
-rw-r--r--widgets/table/e-tree-selection-model.c505
-rw-r--r--widgets/table/e-tree-selection-model.h58
2 files changed, 563 insertions, 0 deletions
diff --git a/widgets/table/e-tree-selection-model.c b/widgets/table/e-tree-selection-model.c
new file mode 100644
index 0000000000..abc7f6cf2b
--- /dev/null
+++ b/widgets/table/e-tree-selection-model.c
@@ -0,0 +1,505 @@
+/* -*- 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 <gtk/gtksignal.h>
+#include "e-tree-selection-model.h"
+#include "gal/util/e-util.h"
+#include <gdk/gdkkeysyms.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,
+};
+
+static ETreePath
+etsm_node_at_row(ETreeSelectionModel *etsm, int row)
+{
+ ETreePath path;
+
+ path = e_tree_table_adapter_node_at_row(etsm->etta, row);
+
+ if (path)
+ path = e_tree_sorted_view_to_model_path(etsm->ets, path);
+
+ return path;
+}
+
+static int
+etsm_row_of_node(ETreeSelectionModel *etsm, ETreePath path)
+{
+ path = e_tree_sorted_model_to_view_path(etsm->ets, path);
+
+ if (path)
+ return e_tree_table_adapter_row_of_node(etsm->etta, path);
+ else
+ return 0;
+}
+
+static int
+etsm_cursor_row_real (ETreeSelectionModel *etsm)
+{
+ if (etsm->cursor_path)
+ return etsm_row_of_node(etsm, etsm->cursor_path);
+ else
+ return -1;
+}
+
+static void
+etsm_real_clear (ETreeSelectionModel *etsm)
+{
+ g_hash_table_destroy(etsm->data);
+ etsm->data = g_hash_table_new(NULL, NULL);
+}
+
+static void
+etsm_destroy (GtkObject *object)
+{
+ ETreeSelectionModel *etsm;
+
+ etsm = E_TREE_SELECTION_MODEL (object);
+
+ g_hash_table_destroy(etsm->data);
+}
+
+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->cursor_col;
+ break;
+
+ case ARG_MODEL:
+ GTK_VALUE_OBJECT(*arg) = (GtkObject *) etsm->model;
+ break;
+
+ case ARG_ETTA:
+ GTK_VALUE_OBJECT(*arg) = (GtkObject *) etsm->etta;
+ break;
+
+ case ARG_ETS:
+ GTK_VALUE_OBJECT(*arg) = (GtkObject *) etsm->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->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:
+ etsm->model = (ETreeModel *) GTK_VALUE_OBJECT(*arg);
+ break;
+
+ case ARG_ETTA:
+ etsm->etta = (ETreeTableAdapter *) GTK_VALUE_OBJECT(*arg);
+ break;
+
+ case ARG_ETS:
+ etsm->ets = (ETreeSorted *) GTK_VALUE_OBJECT(*arg);
+ break;
+ }
+}
+
+/**
+ * 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 n)
+{
+ ETreeSelectionModel *etsm = E_TREE_SELECTION_MODEL(selection);
+ ETreePath path;
+
+ path = etsm_node_at_row(etsm, n);
+ if (!etsm->invert_selection) {
+ if (path)
+ return (gboolean) g_hash_table_lookup (etsm->data, path);
+ else
+ return FALSE;
+ } else {
+ if (path)
+ return !(gboolean) g_hash_table_lookup (etsm->data, path);
+ else
+ return TRUE;
+ }
+}
+
+typedef struct {
+ ETreeSelectionModel *etsm;
+ EForeachFunc callback;
+ gpointer closure;
+} ModelAndCallback;
+
+static void
+etsm_foreach_callback (gpointer key, gpointer value, gpointer user_data)
+{
+ ModelAndCallback *mac = user_data;
+ ETreePath path = key;
+ int row;
+
+ row = etsm_row_of_node(mac->etsm, path);
+ if (row != -1)
+ 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);
+
+ if (etsm->invert_selection) {
+ g_warning ("FIXME: Not implemented yet line 167 of e-tree-selection-model.c");
+ } else {
+ ModelAndCallback mac;
+ mac.etsm = etsm;
+ mac.callback = callback;
+ mac.closure = closure;
+ g_hash_table_foreach(etsm->data, etsm_foreach_callback, &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->invert_selection = FALSE;
+
+ etsm->cursor_path = NULL;
+ etsm->cursor_col = -1;
+ e_selection_model_selection_changed(E_SELECTION_MODEL(etsm));
+ e_selection_model_cursor_changed(E_SELECTION_MODEL(etsm), -1, -1);
+}
+
+/**
+ * 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);
+
+ return g_hash_table_size(etsm->data);
+}
+
+/**
+ * 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->invert_selection = TRUE;
+
+ if (etsm->cursor_col == -1)
+ etsm->cursor_col = 0;
+ if (etsm->cursor_path == NULL)
+ etsm->cursor_path = etsm_node_at_row(etsm, 0);
+ etsm->selection_start_row = 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->cursor_col);
+}
+
+/**
+ * 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);
+
+ etsm->invert_selection = ! etsm->invert_selection;
+
+ etsm->cursor_col = -1;
+ etsm->cursor_path = NULL;
+ etsm->selection_start_row = 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->etta));
+}
+
+static void
+etsm_change_one_row(ESelectionModel *selection, int row, gboolean grow)
+{
+ ETreeSelectionModel *etsm = E_TREE_SELECTION_MODEL(selection);
+ ETreePath path = etsm_node_at_row(etsm, row);
+
+ if (!path)
+ return;
+
+ if ((grow && (!etsm->invert_selection)) ||
+ ((!grow) && etsm->invert_selection) )
+ g_hash_table_insert(etsm->data, path, path);
+ else
+ g_hash_table_remove(etsm->data, 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->cursor_path = NULL;
+ } else {
+ etsm->cursor_path = etsm_node_at_row(etsm, row);
+ }
+ etsm->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->cursor_col;
+}
+
+static void
+etsm_select_single_row (ESelectionModel *selection, int row)
+{
+ ETreeSelectionModel *etsm = E_TREE_SELECTION_MODEL(selection);
+
+ etsm_real_clear (etsm);
+ etsm->invert_selection = FALSE;
+ etsm_change_one_row(selection, row, TRUE);
+ etsm->selection_start_row = 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);
+ ETreePath path;
+
+ etsm->selection_start_row = row;
+
+ path = etsm_node_at_row(etsm, row);
+ if (path) {
+ if (g_hash_table_lookup(etsm->data, path))
+ g_hash_table_remove(etsm->data, path);
+ else
+ g_hash_table_insert(etsm->data, path, path);
+ 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;
+ if (selection->sorter && e_sorter_needs_sorting(selection->sorter)) {
+ old_start = MIN (e_sorter_model_to_sorted(selection->sorter, etsm->selection_start_row),
+ e_sorter_model_to_sorted(selection->sorter, etsm_cursor_row_real(etsm)));
+ old_end = MAX (e_sorter_model_to_sorted(selection->sorter, etsm->selection_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, etsm->selection_start_row),
+ e_sorter_model_to_sorted(selection->sorter, row));
+ new_end = MAX (e_sorter_model_to_sorted(selection->sorter, etsm->selection_start_row),
+ e_sorter_model_to_sorted(selection->sorter, row)) + 1;
+ } else {
+ old_start = MIN (etsm->selection_start_row, etsm_cursor_row_real(etsm));
+ old_end = MAX (etsm->selection_start_row, etsm_cursor_row_real(etsm)) + 1;
+ new_start = MIN (etsm->selection_start_row, row);
+ new_end = MAX (etsm->selection_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);
+ etsm_select_single_row(selection, etsm->selection_start_row);
+ if (etsm->selection_start_row != -1)
+ etsm->cursor_path = etsm_node_at_row(etsm, etsm->selection_start_row);
+ else
+ etsm->cursor_path = NULL;
+ e_selection_model_move_selection_end(selection, row);
+}
+
+
+static void
+e_tree_selection_model_init (ETreeSelectionModel *etsm)
+{
+ etsm->data = g_hash_table_new(NULL, NULL);
+ etsm->invert_selection = FALSE;
+ etsm->cursor_path = NULL;
+ etsm->cursor_col = -1;
+}
+
+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);
diff --git a/widgets/table/e-tree-selection-model.h b/widgets/table/e-tree-selection-model.h
new file mode 100644
index 0000000000..0d28f47c90
--- /dev/null
+++ b/widgets/table/e-tree-selection-model.h
@@ -0,0 +1,58 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+#ifndef _E_TREE_SELECTION_MODEL_H_
+#define _E_TREE_SELECTION_MODEL_H_
+
+#include <gtk/gtkobject.h>
+#include <gal/util/e-sorter.h>
+#include <gdk/gdktypes.h>
+#include <gal/widgets/e-selection-model.h>
+#include <gal/e-table/e-tree-model.h>
+#include <gal/e-table/e-tree-sorted.h>
+#include <gal/e-table/e-tree-table-adapter.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#define E_TREE_SELECTION_MODEL_TYPE (e_tree_selection_model_get_type ())
+#define E_TREE_SELECTION_MODEL(o) (GTK_CHECK_CAST ((o), E_TREE_SELECTION_MODEL_TYPE, ETreeSelectionModel))
+#define E_TREE_SELECTION_MODEL_CLASS(k) (GTK_CHECK_CLASS_CAST((k), E_TREE_SELECTION_MODEL_TYPE, ETreeSelectionModelClass))
+#define E_IS_TREE_SELECTION_MODEL(o) (GTK_CHECK_TYPE ((o), E_TREE_SELECTION_MODEL_TYPE))
+#define E_IS_TREE_SELECTION_MODEL_CLASS(k) (GTK_CHECK_CLASS_TYPE ((k), E_TREE_SELECTION_MODEL_TYPE))
+
+typedef struct {
+ ESelectionModel base;
+
+ ETreeTableAdapter *etta;
+ ETreeSorted *ets;
+ ETreeModel *model;
+
+ GHashTable *data;
+
+ gboolean invert_selection;
+
+ ETreePath cursor_path;
+ gint cursor_col;
+ gint selection_start_row;
+
+ guint model_changed_id;
+ guint model_row_inserted_id, model_row_deleted_id;
+
+ guint frozen : 1;
+ guint selection_model_changed : 1;
+ guint group_info_changed : 1;
+} ETreeSelectionModel;
+
+typedef struct {
+ ESelectionModelClass parent_class;
+} ETreeSelectionModelClass;
+
+GtkType e_tree_selection_model_get_type (void);
+ESelectionModel *e_tree_selection_model_new (void);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+
+#endif /* _E_TREE_SELECTION_MODEL_H_ */