aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--widgets/table/e-table-group-container.c123
-rw-r--r--widgets/table/e-table-group-leaf.c79
-rw-r--r--widgets/table/e-table-group-leaf.h7
-rw-r--r--widgets/table/e-table-group.c47
-rw-r--r--widgets/table/e-table-group.h11
-rw-r--r--widgets/table/e-table-sorted-variable.c385
-rw-r--r--widgets/table/e-table-sorted-variable.h3
-rw-r--r--widgets/table/e-table-sorted.c324
-rw-r--r--widgets/table/e-table-sorted.h17
-rw-r--r--widgets/table/e-table-sorter.c26
-rw-r--r--widgets/table/e-table-sorter.h25
-rw-r--r--widgets/table/e-table-sorting-utils.c372
-rw-r--r--widgets/table/e-table-sorting-utils.h29
-rw-r--r--widgets/table/e-table-subset-variable.c34
-rw-r--r--widgets/table/e-table-subset-variable.h16
-rw-r--r--widgets/table/e-table-subset.c128
-rw-r--r--widgets/table/e-table-subset.h9
-rw-r--r--widgets/table/e-table.c230
-rw-r--r--widgets/table/e-table.h2
19 files changed, 1226 insertions, 641 deletions
diff --git a/widgets/table/e-table-group-container.c b/widgets/table/e-table-group-container.c
index b92498657e..9f2efb290a 100644
--- a/widgets/table/e-table-group-container.c
+++ b/widgets/table/e-table-group-container.c
@@ -387,33 +387,13 @@ child_key_press (ETableGroup *etg, int row, int col, GdkEvent *event,
return e_table_group_key_press (E_TABLE_GROUP (etgc), row, col, event);
}
-static void
-etgc_add (ETableGroup *etg, gint row)
+static ETableGroupContainerChildNode *
+create_child_node (ETableGroupContainer *etgc, void *val)
{
- ETableGroupContainer *etgc = E_TABLE_GROUP_CONTAINER (etg);
- void *val = e_table_model_value_at (etg->model, etgc->ecol->col_idx, row);
- GCompareFunc comp = etgc->ecol->compare;
- GList *list = etgc->children;
ETableGroup *child;
ETableGroupContainerChildNode *child_node;
- int i = 0;
+ ETableGroup *etg = E_TABLE_GROUP(etgc);
- for (; list; list = g_list_next (list), i++){
- int comp_val;
-
- child_node = list->data;
- comp_val = (*comp)(child_node->key, val);
- if (comp_val == 0) {
- child = child_node->child;
- child_node->count ++;
- e_table_group_add (child, row);
- compute_text (etgc, child_node);
- return;
- }
- if ((comp_val > 0 && etgc->ascending) ||
- (comp_val < 0 && (!etgc->ascending)))
- break;
- }
child_node = g_new (ETableGroupContainerChildNode, 1);
child_node->rect = gnome_canvas_item_new (GNOME_CANVAS_GROUP (etgc),
gnome_canvas_rect_get_type (),
@@ -453,6 +433,40 @@ etgc_add (ETableGroup *etg, gint row)
child_node->child = child;
child_node->key = e_table_model_duplicate_value (etg->model, etgc->ecol->col_idx, val);
child_node->string = e_table_model_value_to_string (etg->model, etgc->ecol->col_idx, val);
+ child_node->count = 0;
+
+ return child_node;
+}
+
+static void
+etgc_add (ETableGroup *etg, gint row)
+{
+ ETableGroupContainer *etgc = E_TABLE_GROUP_CONTAINER (etg);
+ void *val = e_table_model_value_at (etg->model, etgc->ecol->col_idx, row);
+ GCompareFunc comp = etgc->ecol->compare;
+ GList *list = etgc->children;
+ ETableGroup *child;
+ ETableGroupContainerChildNode *child_node;
+ int i = 0;
+
+ for (; list; list = g_list_next (list), i++){
+ int comp_val;
+
+ child_node = list->data;
+ comp_val = (*comp)(child_node->key, val);
+ if (comp_val == 0) {
+ child = child_node->child;
+ child_node->count ++;
+ e_table_group_add (child, row);
+ compute_text (etgc, child_node);
+ return;
+ }
+ if ((comp_val > 0 && etgc->ascending) ||
+ (comp_val < 0 && (!etgc->ascending)))
+ break;
+ }
+ child_node = create_child_node (etgc, val);
+ child = child_node->child;
child_node->count = 1;
e_table_group_add (child, row);
@@ -466,12 +480,66 @@ etgc_add (ETableGroup *etg, gint row)
}
static void
-etgc_add_all (ETableGroup *etg)
+etgc_add_array (ETableGroup *etg, const int *array, int count)
{
- int rows = e_table_model_row_count(etg->model);
int i;
- for (i = 0; i < rows; i++)
- etgc_add(etg, i);
+ ETableGroupContainer *etgc = E_TABLE_GROUP_CONTAINER (etg);
+ void *lastval = 0;
+ int laststart = 0;
+ GCompareFunc comp = etgc->ecol->compare;
+ ETableGroupContainerChildNode *child_node;
+ ETableGroup *child;
+
+ if (count <= 0)
+ return;
+
+ e_table_group_container_list_free (etgc);
+ etgc->children = NULL;
+
+ lastval = e_table_model_value_at (etg->model, etgc->ecol->col_idx, array[0]);
+
+ for (i = 1; i < count; i++) {
+ void *val = e_table_model_value_at (etg->model, etgc->ecol->col_idx, array[i]);
+ int comp_val;
+
+ comp_val = (*comp)(lastval, val);
+ if (comp_val != 0) {
+ child_node = create_child_node(etgc, lastval);
+ child = child_node->child;
+
+ e_table_group_add_array(child, array + laststart, i - laststart);
+ child_node->count = i - laststart;
+
+ etgc->children = g_list_append (etgc->children, child_node);
+ compute_text (etgc, child_node);
+ laststart = i;
+ lastval = val;
+ }
+ }
+
+ child_node = create_child_node(etgc, lastval);
+ child = child_node->child;
+
+ e_table_group_add_array(child, array + laststart, i - laststart);
+ child_node->count = i - laststart;
+
+ etgc->children = g_list_append (etgc->children, child_node);
+ compute_text (etgc, child_node);
+
+ e_canvas_item_request_reflow (GNOME_CANVAS_ITEM (etgc));
+}
+
+static void
+etgc_add_all (ETableGroup *etg)
+{
+ ETableGroupContainer *etgc = E_TABLE_GROUP_CONTAINER (etg);
+ ETableSorter *sorter = etgc->table_selection_model->sorter;
+ int *array;
+ int count;
+
+ e_table_sorter_get_sorted_to_model_array(sorter, &array, &count);
+
+ etgc_add_array(etg, array, count);
}
static gboolean
@@ -726,6 +794,7 @@ etgc_class_init (GtkObjectClass *object_class)
etgc_parent_class = gtk_type_class (PARENT_TYPE);
e_group_class->add = etgc_add;
+ e_group_class->add_array = etgc_add_array;
e_group_class->add_all = etgc_add_all;
e_group_class->remove = etgc_remove;
e_group_class->increment = etgc_increment;
diff --git a/widgets/table/e-table-group-leaf.c b/widgets/table/e-table-group-leaf.c
index 86703304b4..ccc9f6dfc4 100644
--- a/widgets/table/e-table-group-leaf.c
+++ b/widgets/table/e-table-group-leaf.c
@@ -13,6 +13,8 @@
#include <libgnomeui/gnome-canvas-rect-ellipse.h>
#include "e-table-group-leaf.h"
#include "e-table-item.h"
+#include "e-table-sorted-variable.h"
+#include "e-table-sorted.h"
#include "gal/util/e-util.h"
#include "gal/widgets/e-canvas.h"
@@ -41,8 +43,8 @@ static void
etgl_destroy (GtkObject *object)
{
ETableGroupLeaf *etgl = E_TABLE_GROUP_LEAF(object);
- if (etgl->subset)
- gtk_object_unref (GTK_OBJECT(etgl->subset));
+ if (etgl->ets)
+ gtk_object_unref (GTK_OBJECT(etgl->ets));
if (etgl->item)
gtk_object_destroy (GTK_OBJECT(etgl->item));
if (etgl->table_selection_model)
@@ -59,9 +61,13 @@ e_table_group_leaf_construct (GnomeCanvasGroup *parent,
ETableModel *model,
ETableSortInfo *sort_info)
{
- etgl->subset = E_TABLE_SUBSET_VARIABLE(e_table_sorted_variable_new (model, full_header, sort_info));
- gtk_object_ref(GTK_OBJECT(etgl->subset));
- gtk_object_sink(GTK_OBJECT(etgl->subset));
+ etgl->is_grouped = e_table_sort_info_grouping_get_count(sort_info);
+ if (etgl->is_grouped)
+ etgl->ets = E_TABLE_SUBSET(e_table_sorted_variable_new (model, full_header, sort_info));
+ else
+ etgl->ets = E_TABLE_SUBSET(e_table_sorted_new (model, full_header, sort_info));
+ gtk_object_ref(GTK_OBJECT(etgl->ets));
+ gtk_object_sink(GTK_OBJECT(etgl->ets));
e_table_group_construct (parent, E_TABLE_GROUP (etgl), full_header, header, model);
}
@@ -99,29 +105,29 @@ e_table_group_leaf_new (GnomeCanvasGroup *parent,
static void
etgl_cursor_change (GtkObject *object, gint row, ETableGroupLeaf *etgl)
{
- if (row < E_TABLE_SUBSET(etgl->subset)->n_map)
- e_table_group_cursor_change (E_TABLE_GROUP(etgl), E_TABLE_SUBSET(etgl->subset)->map_table[row]);
+ if (row < E_TABLE_SUBSET(etgl->ets)->n_map)
+ e_table_group_cursor_change (E_TABLE_GROUP(etgl), E_TABLE_SUBSET(etgl->ets)->map_table[row]);
}
static void
etgl_cursor_activated (GtkObject *object, gint row, ETableGroupLeaf *etgl)
{
- if (row < E_TABLE_SUBSET(etgl->subset)->n_map)
- e_table_group_cursor_activated (E_TABLE_GROUP(etgl), E_TABLE_SUBSET(etgl->subset)->map_table[row]);
+ if (row < E_TABLE_SUBSET(etgl->ets)->n_map)
+ e_table_group_cursor_activated (E_TABLE_GROUP(etgl), E_TABLE_SUBSET(etgl->ets)->map_table[row]);
}
static void
etgl_double_click (GtkObject *object, gint row, gint col, GdkEvent *event, ETableGroupLeaf *etgl)
{
- if (row < E_TABLE_SUBSET(etgl->subset)->n_map)
- e_table_group_double_click (E_TABLE_GROUP(etgl), E_TABLE_SUBSET(etgl->subset)->map_table[row], col, event);
+ if (row < E_TABLE_SUBSET(etgl->ets)->n_map)
+ e_table_group_double_click (E_TABLE_GROUP(etgl), E_TABLE_SUBSET(etgl->ets)->map_table[row], col, event);
}
static gint
etgl_key_press (GtkObject *object, gint row, gint col, GdkEvent *event, ETableGroupLeaf *etgl)
{
- if (row < E_TABLE_SUBSET(etgl->subset)->n_map)
- return e_table_group_key_press (E_TABLE_GROUP(etgl), E_TABLE_SUBSET(etgl->subset)->map_table[row], col, event);
+ if (row < E_TABLE_SUBSET(etgl->ets)->n_map)
+ return e_table_group_key_press (E_TABLE_GROUP(etgl), E_TABLE_SUBSET(etgl->ets)->map_table[row], col, event);
else
return 0;
}
@@ -129,8 +135,8 @@ etgl_key_press (GtkObject *object, gint row, gint col, GdkEvent *event, ETableGr
static gint
etgl_right_click (GtkObject *object, gint row, gint col, GdkEvent *event, ETableGroupLeaf *etgl)
{
- if (row < E_TABLE_SUBSET(etgl->subset)->n_map)
- return e_table_group_right_click (E_TABLE_GROUP(etgl), E_TABLE_SUBSET(etgl->subset)->map_table[row], col, event);
+ if (row < E_TABLE_SUBSET(etgl->ets)->n_map)
+ return e_table_group_right_click (E_TABLE_GROUP(etgl), E_TABLE_SUBSET(etgl->ets)->map_table[row], col, event);
else
return 0;
}
@@ -138,8 +144,8 @@ etgl_right_click (GtkObject *object, gint row, gint col, GdkEvent *event, ETable
static gint
etgl_click (GtkObject *object, gint row, gint col, GdkEvent *event, ETableGroupLeaf *etgl)
{
- if (row < E_TABLE_SUBSET(etgl->subset)->n_map)
- return e_table_group_click (E_TABLE_GROUP(etgl), E_TABLE_SUBSET(etgl->subset)->map_table[row], col, event);
+ if (row < E_TABLE_SUBSET(etgl->ets)->n_map)
+ return e_table_group_click (E_TABLE_GROUP(etgl), E_TABLE_SUBSET(etgl->ets)->map_table[row], col, event);
else
return 0;
}
@@ -168,7 +174,7 @@ etgl_realize (GnomeCanvasItem *item)
etgl->item = E_TABLE_ITEM(gnome_canvas_item_new (GNOME_CANVAS_GROUP(etgl),
e_table_item_get_type (),
"ETableHeader", E_TABLE_GROUP(etgl)->header,
- "ETableModel", etgl->subset,
+ "ETableModel", etgl->ets,
"drawgrid", etgl->draw_grid,
"drawfocus", etgl->draw_focus,
"cursor_mode", etgl->cursor_mode,
@@ -196,42 +202,62 @@ static void
etgl_add (ETableGroup *etg, gint row)
{
ETableGroupLeaf *etgl = E_TABLE_GROUP_LEAF (etg);
- e_table_subset_variable_add (etgl->subset, row);
+ if (E_IS_TABLE_SUBSET_VARIABLE(etgl->ets)) {
+ e_table_subset_variable_add (E_TABLE_SUBSET_VARIABLE(etgl->ets), row);
+ }
+}
+
+static void
+etgl_add_array (ETableGroup *etg, const gint *array, gint count)
+{
+ ETableGroupLeaf *etgl = E_TABLE_GROUP_LEAF (etg);
+ if (E_IS_TABLE_SUBSET_VARIABLE(etgl->ets)) {
+ e_table_subset_variable_add_array (E_TABLE_SUBSET_VARIABLE(etgl->ets), array, count);
+ }
}
static void
etgl_add_all (ETableGroup *etg)
{
ETableGroupLeaf *etgl = E_TABLE_GROUP_LEAF (etg);
- e_table_subset_variable_add_all (etgl->subset);
+ if (E_IS_TABLE_SUBSET_VARIABLE(etgl->ets)) {
+ e_table_subset_variable_add_all (E_TABLE_SUBSET_VARIABLE(etgl->ets));
+ }
}
static gboolean
etgl_remove (ETableGroup *etg, gint row)
{
ETableGroupLeaf *etgl = E_TABLE_GROUP_LEAF (etg);
- return e_table_subset_variable_remove (etgl->subset, row);
+ if (E_IS_TABLE_SUBSET_VARIABLE(etgl->ets)) {
+ return e_table_subset_variable_remove (E_TABLE_SUBSET_VARIABLE(etgl->ets), row);
+ }
+ return FALSE;
}
static void
etgl_increment (ETableGroup *etg, gint position, gint amount)
{
ETableGroupLeaf *etgl = E_TABLE_GROUP_LEAF (etg);
- e_table_subset_variable_increment (etgl->subset, position, amount);
+ if (E_IS_TABLE_SUBSET_VARIABLE(etgl->ets)) {
+ e_table_subset_variable_increment (E_TABLE_SUBSET_VARIABLE(etgl->ets), position, amount);
+ }
}
static void
etgl_decrement (ETableGroup *etg, gint position, gint amount)
{
ETableGroupLeaf *etgl = E_TABLE_GROUP_LEAF (etg);
- e_table_subset_variable_decrement (etgl->subset, position, amount);
+ if (E_IS_TABLE_SUBSET_VARIABLE(etgl->ets)) {
+ e_table_subset_variable_decrement (E_TABLE_SUBSET_VARIABLE(etgl->ets), position, amount);
+ }
}
static int
etgl_row_count (ETableGroup *etg)
{
ETableGroupLeaf *etgl = E_TABLE_GROUP_LEAF (etg);
- return e_table_model_row_count(E_TABLE_MODEL(etgl->subset));
+ return e_table_model_row_count(E_TABLE_MODEL(etgl->ets));
}
static void
@@ -239,7 +265,7 @@ etgl_set_focus (ETableGroup *etg, EFocus direction, gint view_col)
{
ETableGroupLeaf *etgl = E_TABLE_GROUP_LEAF (etg);
if (direction == E_FOCUS_END) {
- e_table_item_set_cursor (etgl->item, view_col, e_table_model_row_count(E_TABLE_MODEL(etgl->subset)) - 1);
+ e_table_item_set_cursor (etgl->item, view_col, e_table_model_row_count(E_TABLE_MODEL(etgl->ets)) - 1);
} else {
e_table_item_set_cursor (etgl->item, view_col, 0);
}
@@ -380,6 +406,7 @@ etgl_class_init (GtkObjectClass *object_class)
etgl_parent_class = gtk_type_class (PARENT_TYPE);
e_group_class->add = etgl_add;
+ e_group_class->add_array = etgl_add_array;
e_group_class->add_all = etgl_add_all;
e_group_class->remove = etgl_remove;
e_group_class->increment = etgl_increment;
@@ -420,7 +447,7 @@ etgl_init (GtkObject *object)
etgl->height = 1;
etgl->minimum_width = 0;
- etgl->subset = NULL;
+ etgl->ets = NULL;
etgl->item = NULL;
etgl->draw_grid = 1;
diff --git a/widgets/table/e-table-group-leaf.h b/widgets/table/e-table-group-leaf.h
index f941b79dcd..3ceedbe8a2 100644
--- a/widgets/table/e-table-group-leaf.h
+++ b/widgets/table/e-table-group-leaf.h
@@ -4,7 +4,7 @@
#include <libgnomeui/gnome-canvas.h>
#include <gal/e-table/e-table-group.h>
-#include <gal/e-table/e-table-sorted-variable.h>
+#include <gal/e-table/e-table-subset.h>
#include <gal/e-table/e-table-item.h>
BEGIN_GNOME_DECLS
@@ -27,10 +27,11 @@ typedef struct {
gdouble width;
gdouble minimum_width;
- ETableSubsetVariable *subset;
-
int length_threshold;
+ ETableSubset *ets;
+ guint is_grouped : 1;
+
guint draw_grid : 1;
guint draw_focus : 1;
ETableCursorMode cursor_mode;
diff --git a/widgets/table/e-table-group.c b/widgets/table/e-table-group.c
index d87546db0d..0b09b38351 100644
--- a/widgets/table/e-table-group.c
+++ b/widgets/table/e-table-group.c
@@ -135,6 +135,27 @@ e_table_group_add (ETableGroup *etg,
}
/**
+ * e_table_group_add_array
+ * @etg: The %ETableGroup to add to
+ * @array: The array to add.
+ * @count: The number of times to add
+ *
+ * This routine adds all the rows in the array to this set of rows.
+ * It assumes that the array is already sorted properly.
+ */
+void
+e_table_group_add_array (ETableGroup *etg,
+ const int *array,
+ int count)
+{
+ g_return_if_fail (etg != NULL);
+ g_return_if_fail (E_IS_TABLE_GROUP (etg));
+
+ if (ETG_CLASS (etg)->add_array)
+ ETG_CLASS (etg)->add_array (etg, array, count);
+}
+
+/**
* e_table_group_add_all
* @etg: The %ETableGroup to add to
*
@@ -346,6 +367,30 @@ e_table_group_compute_location (ETableGroup *etg, int *x, int *y, int *row, int
}
/**
+ * e_table_group_get_position
+ * @eti: %ETableGroup to look in.
+ * @x: A pointer to the location to store the found x location in.
+ * @y: A pointer to the location to store the found y location in.
+ * @row: A pointer to the row number to find.
+ * @col: A pointer to the col number to find.
+ *
+ * This routine finds the view cell (row, col) in the %ETableGroup.
+ * If that location is in the %ETableGroup *x and *y are set to the
+ * upper left hand corner of the cell found. If that location is not
+ * in the %ETableGroup, the number of rows in the %ETableGroup is
+ * removed from the value row points to.
+ */
+void
+e_table_group_get_position (ETableGroup *etg, int *x, int *y, int *row, int *col)
+{
+ g_return_if_fail (etg != NULL);
+ g_return_if_fail (E_IS_TABLE_GROUP (etg));
+
+ if (ETG_CLASS (etg)->get_position)
+ ETG_CLASS (etg)->get_position (etg, x, y, row, col);
+}
+
+/**
* e_table_group_cursor_change
* @eti: %ETableGroup to emit the signal on
* @row: The new cursor row (model row)
@@ -537,6 +582,7 @@ etg_class_init (GtkObjectClass *object_class)
klass->key_press = NULL;
klass->add = NULL;
+ klass->add_array = NULL;
klass->add_all = NULL;
klass->remove = NULL;
klass->row_count = NULL;
@@ -546,6 +592,7 @@ etg_class_init (GtkObjectClass *object_class)
klass->get_focus = etg_get_focus;
klass->get_printable = NULL;
klass->compute_location = NULL;
+ klass->get_position = NULL;
etg_parent_class = gtk_type_class (PARENT_TYPE);
diff --git a/widgets/table/e-table-group.h b/widgets/table/e-table-group.h
index 64ebc37ff0..f8d23168fe 100644
--- a/widgets/table/e-table-group.h
+++ b/widgets/table/e-table-group.h
@@ -56,6 +56,7 @@ typedef struct {
/* Virtual functions. */
void (*add) (ETableGroup *etg, gint row);
+ void (*add_array) (ETableGroup *etg, const int *array, int count);
void (*add_all) (ETableGroup *etg);
gboolean (*remove) (ETableGroup *etg, gint row);
gint (*row_count) (ETableGroup *etg);
@@ -66,12 +67,16 @@ typedef struct {
gint (*get_focus_column) (ETableGroup *etg);
EPrintable *(*get_printable) (ETableGroup *etg);
void (*compute_location) (ETableGroup *etg, int *x, int *y, int *row, int *col);
+ void (*get_position) (ETableGroup *etg, int *x, int *y, int *row, int *col);
} ETableGroupClass;
/* Virtual functions */
void e_table_group_add (ETableGroup *etg,
gint row);
+void e_table_group_add_array (ETableGroup *etg,
+ const int *array,
+ int count);
void e_table_group_add_all (ETableGroup *etg);
gboolean e_table_group_remove (ETableGroup *etg,
gint row);
@@ -94,7 +99,11 @@ void e_table_group_compute_location (ETableGroup *etg,
int *y,
int *row,
int *col);
-
+void e_table_group_get_position (ETableGroup *etg,
+ int *x,
+ int *y,
+ int *row,
+ int *col);
ETableGroup *e_table_group_new (GnomeCanvasGroup *parent,
ETableHeader *full_header,
ETableHeader *header,
diff --git a/widgets/table/e-table-sorted-variable.c b/widgets/table/e-table-sorted-variable.c
index 94362f547c..3416e5d9d3 100644
--- a/widgets/table/e-table-sorted-variable.c
+++ b/widgets/table/e-table-sorted-variable.c
@@ -13,6 +13,7 @@
#include <string.h>
#include "gal/util/e-util.h"
#include "e-table-sorted-variable.h"
+#include "e-table-sorting-utils.h"
#define d(x)
@@ -25,11 +26,6 @@
static ETableSubsetVariableClass *etsv_parent_class;
-static void etsv_proxy_model_changed (ETableModel *etm, ETableSortedVariable *etsv);
-#if 0
-static void etsv_proxy_model_row_changed (ETableModel *etm, int row, ETableSortedVariable *etsv);
-static void etsv_proxy_model_cell_changed (ETableModel *etm, int col, int row, ETableSortedVariable *etsv);
-#endif
static void etsv_sort_info_changed (ETableSortInfo *info, ETableSortedVariable *etsv);
static void etsv_sort (ETableSortedVariable *etsv);
static void etsv_add (ETableSubsetVariable *etssv, gint row);
@@ -39,16 +35,7 @@ static void
etsv_destroy (GtkObject *object)
{
ETableSortedVariable *etsv = E_TABLE_SORTED_VARIABLE (object);
- ETableSubset *etss = E_TABLE_SUBSET (object);
-
- gtk_signal_disconnect (GTK_OBJECT (etss->source),
- etsv->table_model_changed_id);
-#if 0
- gtk_signal_disconnect (GTK_OBJECT (etss->source),
- etsv->table_model_row_changed_id);
- gtk_signal_disconnect (GTK_OBJECT (etss->source),
- etsv->table_model_cell_changed_id);
-#endif
+
gtk_signal_disconnect (GTK_OBJECT (etsv->sort_info),
etsv->sort_info_changed_id);
@@ -59,10 +46,6 @@ etsv_destroy (GtkObject *object)
g_source_remove(etsv->insert_idle_id);
}
- etsv->table_model_changed_id = 0;
- etsv->table_model_row_changed_id = 0;
- etsv->table_model_cell_changed_id = 0;
-
if (etsv->sort_info)
gtk_object_unref(GTK_OBJECT(etsv->sort_info));
if (etsv->full_header)
@@ -90,9 +73,6 @@ etsv_init (ETableSortedVariable *etsv)
etsv->full_header = NULL;
etsv->sort_info = NULL;
- etsv->table_model_changed_id = 0;
- etsv->table_model_row_changed_id = 0;
- etsv->table_model_cell_changed_id = 0;
etsv->sort_info_changed_id = 0;
etsv->sort_idle_id = 0;
@@ -279,14 +259,6 @@ e_table_sorted_variable_new (ETableModel *source, ETableHeader *full_header, ETa
etsv->full_header = full_header;
gtk_object_ref(GTK_OBJECT(etsv->full_header));
- etsv->table_model_changed_id = gtk_signal_connect (GTK_OBJECT (source), "model_changed",
- GTK_SIGNAL_FUNC (etsv_proxy_model_changed), etsv);
-#if 0
- etsv->table_model_row_changed_id = gtk_signal_connect (GTK_OBJECT (source), "model_row_changed",
- GTK_SIGNAL_FUNC (etsv_proxy_model_row_changed), etsv);
- etsv->table_model_cell_changed_id = gtk_signal_connect (GTK_OBJECT (source), "model_cell_changed",
- GTK_SIGNAL_FUNC (etsv_proxy_model_cell_changed), etsv);
-#endif
etsv->sort_info_changed_id = gtk_signal_connect (GTK_OBJECT (sort_info), "sort_info_changed",
GTK_SIGNAL_FUNC (etsv_sort_info_changed), etsv);
@@ -294,375 +266,24 @@ e_table_sorted_variable_new (ETableModel *source, ETableHeader *full_header, ETa
}
static void
-etsv_proxy_model_changed (ETableModel *etm, ETableSortedVariable *etsv)
-{
- /* FIXME: do_resort (); */
-}
-#if 0
-static void
-etsv_proxy_model_row_changed (ETableModel *etm, int row, ETableSortedVariable *etsv)
-{
- ETableSubsetVariable *etssv = E_TABLE_SUBSET_VARIABLE(etsv);
-
- if (e_table_subset_variable_remove(etssv, row))
- e_table_subset_variable_add (etssv, row);
-}
-
-static void
-etsv_proxy_model_cell_changed (ETableModel *etm, int col, int row, ETableSortedVariable *etsv)
-{
- ETableSubsetVariable *etssv = E_TABLE_SUBSET_VARIABLE(etsv);
-
- if (e_table_subset_variable_remove(etssv, row))
- e_table_subset_variable_add (etssv, row);
-}
-#endif
-
-static void
etsv_sort_info_changed (ETableSortInfo *info, ETableSortedVariable *etsv)
{
etsv_sort(etsv);
}
-static ETableSortedVariable *etsv_closure;
-void **vals_closure;
-int cols_closure;
-int *ascending_closure;
-GCompareFunc *compare_closure;
-
-/* FIXME: Make it not cache the second and later columns (as if anyone cares.) */
-
-static int
-qsort_callback(const void *data1, const void *data2)
-{
- gint row1 = *(int *)data1;
- gint row2 = *(int *)data2;
- int j;
- int sort_count = e_table_sort_info_sorting_get_count(etsv_closure->sort_info);
- int comp_val = 0;
- int ascending = 1;
- for (j = 0; j < sort_count; j++) {
- comp_val = (*(compare_closure[j]))(vals_closure[cols_closure * row1 + j], vals_closure[cols_closure * row2 + j]);
- ascending = ascending_closure[j];
- if (comp_val != 0)
- break;
- }
- if (comp_val == 0) {
- if (row1 < row2)
- comp_val = -1;
- if (row1 > row2)
- comp_val = 1;
- }
- if (!ascending)
- comp_val = -comp_val;
- return comp_val;
-}
-
-struct _subinfo {
- int start;
- GArray *rowsort; /* an array of row info's */
-};
-
-struct _rowinfo {
- int row;
- struct _subinfo *subinfo;
- struct _group_info *groupinfo;
-};
-
-static int
-qsort_callback_complex(const void *data1, const void *data2)
-{
- gint row1 = ((struct _rowinfo *)data1)->row;
- gint row2 = ((struct _rowinfo *)data2)->row;
- int j;
- int sort_count = e_table_sort_info_sorting_get_count(etsv_closure->sort_info);
- int comp_val = 0;
- int ascending = 1;
- for (j = 0; j < sort_count; j++) {
- comp_val = (*(compare_closure[j]))(vals_closure[cols_closure * row1 + j], vals_closure[cols_closure * row2 + j]);
- ascending = ascending_closure[j];
- if (comp_val != 0)
- break;
- }
- if (comp_val == 0) {
- if (row1 < row2)
- comp_val = -1;
- if (row1 > row2)
- comp_val = 1;
- }
- if (!ascending)
- comp_val = -comp_val;
- return comp_val;
-}
-
-/* if sortgroup is like:
-0 1 1 1
-1 1 2 2
-2 2 3 2
-3 2 4 3
-4 3 5 3
-5 2 6 1
-6 1 0 1
-
- Want to sort the 1's first
- Then sort each group of 2's, offsetting into the output by the new root 1 location
- ... Recursively ...
-*/
-
-struct _group_info {
- char *group;
- int row;
-};
-
-#ifdef DEBUG
-#undef DEBUG
-#endif
-/*#define DEBUG*/
-
-#ifdef DEBUG
-static int total=0;
-static int total_sorted=0;
-#endif
-
-/* builds the info needed to sort everything */
-static struct _subinfo *
-etsv_sort_build_subset(ETableSortedVariable *etsv, struct _group_info *groupinfo, int start, int *end)
-{
-/* ETableSubset *etss = E_TABLE_SUBSET(etsv);*/
- int rows = E_TABLE_SUBSET(etsv)->n_map;
- int i, lastinsert;
- GArray *rowsort = g_array_new(0, 0, sizeof(struct _rowinfo));
- struct _subinfo *subinfo, *newsub;
- char *id, *newid;
- int idlen, newidlen;
- int cmp;
- int cmplen;
-
- subinfo = g_malloc0(sizeof(*subinfo));
- subinfo->rowsort = rowsort;
- subinfo->start = start;
- lastinsert = -1;
- id = groupinfo[start].group;
- newid = strrchr(id, '/');
- idlen = strlen(id);
- if (newid)
- cmplen = newid-id;
- else
- cmplen = idlen;
- d(printf("%d scanning level %s\n", start, id));
- for (i=start;i<rows;i++) {
- newid = groupinfo[i].group;
- newidlen = strlen(newid);
- d(printf("%d checking group %s\n", start, newid));
- cmp = strncmp(id, newid, cmplen);
- /* check for common parent */
- if (idlen == newidlen && cmp == 0) {
- struct _rowinfo rowinfo;
-
- d(printf("%d Same parent\n", start));
- rowinfo.row = groupinfo[i].row;
- rowinfo.subinfo = NULL;
- rowinfo.groupinfo = &groupinfo[i];
- lastinsert = rowsort->len;
- g_array_append_val(rowsort, rowinfo);
-#ifdef DEBUG
- total++;
-#endif
- } else if (newidlen > idlen) {
- /* must be a new subtree */
- d(printf("%d checking subtree instead\n", start));
- newsub = etsv_sort_build_subset(etsv, groupinfo, i, &i);
- d(printf("found %d nodes in subtree\n", newsub->rowsort->len));
- g_array_index(rowsort, struct _rowinfo, lastinsert).subinfo = newsub;
- } else {
- i--;
- break;
- }
- }
- if (end)
- *end = i;
- d(printf("finished level %s start was %d end was %d\n", id, start, i));
- return subinfo;
-}
-
-/* sort each level, and then sort each level below that level (once we know
- where the sublevel will fit in the overall list) */
-static int
-etsv_sort_subset(ETableSortedVariable *etsv, struct _subinfo *subinfo, int startoffset)
-{
- GArray *rowsort = subinfo->rowsort;
- ETableSubset *etss = E_TABLE_SUBSET(etsv);
- int offset, i;
-
- d(printf("sorting subset start %d rows %d\n", startoffset, rowsort->len));
-
- /* first, sort the actual data */
- qsort(rowsort->data, rowsort->len, sizeof(struct _rowinfo), qsort_callback_complex);
-
- /* then put it back in the map table, where appropriate */
- offset = startoffset;
- for (i=0;i<rowsort->len;i++) {
- struct _rowinfo *rowinfo;
-
- d(printf("setting offset %d\n", offset));
-
- rowinfo = &g_array_index(rowsort, struct _rowinfo, i);
- etss->map_table[offset] = rowinfo->row;
- if (rowinfo->subinfo) {
- offset = etsv_sort_subset(etsv, rowinfo->subinfo, offset+1);
- } else
- offset += 1;
- }
- d(printf("end sort subset start %d\n", startoffset));
-
- return offset;
-}
-
-static void
-etsv_sort_free_subset(ETableSortedVariable *etsv, struct _subinfo *subinfo)
-{
- int i;
-
- for (i=0;i<subinfo->rowsort->len;i++) {
- struct _rowinfo *rowinfo;
-
- rowinfo = &g_array_index(subinfo->rowsort, struct _rowinfo, i);
- if (rowinfo->subinfo)
- etsv_sort_free_subset(etsv, rowinfo->subinfo);
- }
- g_array_free(subinfo->rowsort, TRUE);
- g_free(subinfo);
-}
-
-static int
-sort_groups_compare(const void *ap, const void *bp)
-{
- struct _group_info *a = (struct _group_info *)ap;
- struct _group_info *b = (struct _group_info *)bp;
-
- return strcmp(a->group, b->group);
-}
-
-#ifdef DEBUG
-static void
-print_id(int key, int val, void *data)
-{
- printf("gained id %d\n", key);
-}
-#endif
-
-/* use the sort group to select subsorts */
-static void
-etsv_sort_by_group(ETableSortedVariable *etsv)
-{
- ETableSubset *etss = E_TABLE_SUBSET(etsv);
- int rows = E_TABLE_SUBSET(etsv)->n_map;
- struct _group_info *groups;
- struct _subinfo *subinfo;
- int i;
-#ifdef DEBUG
- GHashTable *members = g_hash_table_new(0, 0);
-
- total = 0;
- total_sorted = 0;
-#endif
-
- d(printf("sorting %d rows\n", rows));
-
- if (rows == 0)
- return;
-
- /* get the subset rows */
- groups = g_malloc(sizeof(struct _group_info) * rows);
- for (i=0;i<rows;i++) {
- groups[i].row = etss->map_table[i];
- groups[i].group = g_strdup(e_table_model_row_sort_group(etss->source, groups[i].row));
-#ifdef DEBUG
- g_hash_table_insert(members, etss->map_table[i], 1);
- etss->map_table[i] = 0;
-#endif
- }
-
- /* sort the group info */
- qsort(groups, rows, sizeof(struct _group_info), sort_groups_compare);
-
- d(printf("sorted groups:\n");
- for (i=0;i<rows;i++) {
- printf(" %s\n", groups[i].group);
- });
-
- /* now sort based on the group info */
- subinfo = etsv_sort_build_subset(etsv, groups, 0, NULL);
- for (i=0;i<rows;i++) {
- g_free(groups[i].group);
- }
- g_free(groups);
- etsv_sort_subset(etsv, subinfo, 0);
- etsv_sort_free_subset(etsv, subinfo);
-#ifdef DEBUG
- for (i=0;i<rows;i++) {
- if (g_hash_table_lookup(members, etss->map_table[i]) == 0) {
- printf("lost id %d\n", etss->map_table[i]);
- }
- g_hash_table_remove(members, etss->map_table[i]);
- }
- g_hash_table_foreach(members, print_id, 0);
-
- printf("total rows = %d, total processed = %d, total sorted = %d\n", rows, total, total_sorted);
-#endif
-
-}
-
static void
etsv_sort(ETableSortedVariable *etsv)
{
ETableSubset *etss = E_TABLE_SUBSET(etsv);
static int reentering = 0;
- int rows = E_TABLE_SUBSET(etsv)->n_map;
- int total_rows;
- int i;
- int j;
- int cols;
if (reentering)
return;
reentering = 1;
e_table_model_pre_change(E_TABLE_MODEL(etsv));
- total_rows = e_table_model_row_count(E_TABLE_SUBSET(etsv)->source);
- cols = e_table_sort_info_sorting_get_count(etsv->sort_info);
- cols_closure = cols;
- etsv_closure = etsv;
- vals_closure = g_new(void *, total_rows * cols);
- ascending_closure = g_new(int, cols);
- compare_closure = g_new(GCompareFunc, cols);
- for (j = 0; j < cols; j++) {
- ETableSortColumn column = e_table_sort_info_sorting_get_nth(etsv->sort_info, j);
- ETableCol *col;
- col = e_table_header_get_column_by_col_idx(etsv->full_header, column.column);
- if (col == NULL)
- col = e_table_header_get_column (etsv->full_header, e_table_header_count (etsv->full_header) - 1);
- for (i = 0; i < rows; i++) {
-#if 0
- if( !(i & 0xff) ) {
- while(gtk_events_pending())
- gtk_main_iteration();
- }
-#endif
- vals_closure[E_TABLE_SUBSET(etsv)->map_table[i] * cols + j] = e_table_model_value_at (etss->source, col->col_idx, E_TABLE_SUBSET(etsv)->map_table[i]);
- }
- compare_closure[j] = col->compare;
- ascending_closure[j] = column.ascending;
- }
+ e_table_sorting_utils_sort(etss->source, etsv->sort_info, etsv->full_header, etss->map_table, etss->n_map);
- if (e_table_model_has_sort_group(etss->source)) {
- etsv_sort_by_group(etsv);
- } else {
- qsort(E_TABLE_SUBSET(etsv)->map_table, rows, sizeof(int), qsort_callback);
- }
- g_free(vals_closure);
- g_free(ascending_closure);
- g_free(compare_closure);
e_table_model_changed (E_TABLE_MODEL(etsv));
reentering = 0;
}
diff --git a/widgets/table/e-table-sorted-variable.h b/widgets/table/e-table-sorted-variable.h
index a3030eb3d7..d326e26df8 100644
--- a/widgets/table/e-table-sorted-variable.h
+++ b/widgets/table/e-table-sorted-variable.h
@@ -25,9 +25,6 @@ typedef struct {
ETableHeader *full_header;
- int table_model_changed_id;
- int table_model_row_changed_id;
- int table_model_cell_changed_id;
int sort_info_changed_id;
int sort_idle_id;
int insert_idle_id;
diff --git a/widgets/table/e-table-sorted.c b/widgets/table/e-table-sorted.c
index e884c774f8..11c9d62ec0 100644
--- a/widgets/table/e-table-sorted.c
+++ b/widgets/table/e-table-sorted.c
@@ -1,3 +1,4 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
* E-table-sorted.c: Implements a table that sorts another table
*
@@ -8,82 +9,319 @@
*/
#include <config.h>
#include <stdlib.h>
+#include <gtk/gtksignal.h>
+#include <string.h>
#include "gal/util/e-util.h"
#include "e-table-sorted.h"
+#include "e-table-sorting-utils.h"
+
+#define d(x)
#define PARENT_TYPE E_TABLE_SUBSET_TYPE
+#define INCREMENT_AMOUNT 100
+
+/* maximum insertions between an idle event that we will do without scheduling an idle sort */
+#define ETS_INSERT_MAX (4)
+
static ETableSubsetClass *ets_parent_class;
+static void ets_sort_info_changed (ETableSortInfo *info, ETableSorted *ets);
+static void ets_sort (ETableSorted *ets);
+static void ets_proxy_model_changed (ETableSubset *etss, ETableModel *source);
+static void ets_proxy_model_row_changed (ETableSubset *etss, ETableModel *source, int row);
+static void ets_proxy_model_cell_changed (ETableSubset *etss, ETableModel *source, int col, int row);
+static void ets_proxy_model_row_inserted (ETableSubset *etss, ETableModel *source, int row);
+static void ets_proxy_model_row_deleted (ETableSubset *etss, ETableModel *source, int row);
+
+static void
+ets_destroy (GtkObject *object)
+{
+ ETableSorted *ets = E_TABLE_SORTED (object);
+
+ if (ets->sort_idle_id) {
+ g_source_remove(ets->sort_idle_id);
+ }
+ if (ets->insert_idle_id) {
+ g_source_remove(ets->insert_idle_id);
+ }
+
+ if (ets->sort_info) {
+ gtk_signal_disconnect (GTK_OBJECT (ets->sort_info),
+ ets->sort_info_changed_id);
+ gtk_object_unref(GTK_OBJECT(ets->sort_info));
+ }
+
+ if (ets->full_header)
+ gtk_object_unref(GTK_OBJECT(ets->full_header));
+
+ GTK_OBJECT_CLASS (ets_parent_class)->destroy (object);
+}
+
static void
-ets_class_init (GtkObjectClass *klass)
+ets_class_init (GtkObjectClass *object_class)
{
+ ETableSubsetClass *etss_class = E_TABLE_SUBSET_CLASS(object_class);
+
ets_parent_class = gtk_type_class (PARENT_TYPE);
+
+ etss_class->proxy_model_changed = ets_proxy_model_changed;
+ etss_class->proxy_model_row_changed = ets_proxy_model_row_changed;
+ etss_class->proxy_model_cell_changed = ets_proxy_model_cell_changed;
+ etss_class->proxy_model_row_inserted = ets_proxy_model_row_inserted;
+ etss_class->proxy_model_row_deleted = ets_proxy_model_row_deleted;
+
+ object_class->destroy = ets_destroy;
}
-E_MAKE_TYPE(e_table_sorted, "ETableSorted", ETableSorted, ets_class_init, NULL, PARENT_TYPE);
+static void
+ets_init (ETableSorted *ets)
+{
+ ets->full_header = NULL;
+ ets->sort_info = NULL;
-static ETableSorted *sort_ets;
+ ets->sort_info_changed_id = 0;
+ ets->sort_idle_id = 0;
+ ets->insert_count = 0;
+}
+
+E_MAKE_TYPE(e_table_sorted, "ETableSorted", ETableSorted, ets_class_init, ets_init, PARENT_TYPE);
+
+static gboolean
+ets_sort_idle(ETableSorted *ets)
+{
+ gtk_object_ref(GTK_OBJECT(ets));
+ ets_sort(ets);
+ ets->sort_idle_id = 0;
+ ets->insert_count = 0;
+ gtk_object_unref(GTK_OBJECT(ets));
+ return FALSE;
+}
+
+#if 0
+static gboolean
+ets_insert_idle(ETableSorted *ets)
+{
+ ets->insert_count = 0;
+ ets->insert_idle_id = 0;
+ return FALSE;
+}
+#endif
+
+#if 0
+/* This takes source rows. */
static int
-my_sort (const void *a, const void *b)
+ets_compare(ETableSorted *ets, int row1, int row2)
{
- ETableModel *source = E_TABLE_SUBSET (sort_ets)->source;
- const int *ia = (const int *) a;
- const int *ib = (const int *) b;
- void *va, *vb;
-
- va = e_table_model_value_at (source, sort_ets->sort_col, *ia);
- vb = e_table_model_value_at (source, sort_ets->sort_col, *ib);
-
- return (*sort_ets->compare) (va, vb);
+ int j;
+ int sort_count = e_table_sort_info_sorting_get_count(ets->sort_info);
+ int comp_val = 0;
+ int ascending = 1;
+ ETableSubset *etss = E_TABLE_SUBSET(ets);
+
+ for (j = 0; j < sort_count; j++) {
+ ETableSortColumn column = e_table_sort_info_sorting_get_nth(ets->sort_info, j);
+ ETableCol *col;
+ col = e_table_header_get_column_by_col_idx(ets->full_header, column.column);
+ if (col == NULL)
+ col = e_table_header_get_column (ets->full_header, e_table_header_count (ets->full_header) - 1);
+ comp_val = (*col->compare)(e_table_model_value_at (etss->source, col->col_idx, row1),
+ e_table_model_value_at (etss->source, col->col_idx, row2));
+ ascending = column.ascending;
+ if (comp_val != 0)
+ break;
+ }
+ if (comp_val == 0) {
+ if (row1 < row2)
+ comp_val = -1;
+ if (row1 > row2)
+ comp_val = 1;
+ }
+ if (!ascending)
+ comp_val = -comp_val;
+ return comp_val;
}
static void
-do_sort (ETableSorted *ets)
+ets_add (ETableSorted *ets
+ gint row)
{
- ETableSubset *etss = E_TABLE_SUBSET (ets);
- g_assert (sort_ets == NULL);
-
- sort_ets = ets;
- qsort (etss->map_table, etss->n_map, sizeof (unsigned int), my_sort);
- sort_ets = NULL;
+ ETableModel *etm = E_TABLE_MODEL(etssv);
+ ETableSubset *etss = E_TABLE_SUBSET(etssv);
+ ETableSorted *ets = E_TABLE_SORTED (etssv);
+ int i;
+
+ if (etss->n_map + 1 > etssv->n_vals_allocated) {
+ etssv->n_vals_allocated += INCREMENT_AMOUNT;
+ etss->map_table = g_realloc (etss->map_table, (etssv->n_vals_allocated) * sizeof(int));
+ }
+ i = etss->n_map;
+ if (ets->sort_idle_id == 0) {
+ /* this is to see if we're inserting a lot of things between idle loops.
+ If we are, we're busy, its faster to just append and perform a full sort later */
+ ets->insert_count++;
+ if (ets->insert_count > ETS_INSERT_MAX) {
+ /* schedule a sort, and append instead */
+ ets->sort_idle_id = g_idle_add_full(50, (GSourceFunc) ets_sort_idle, ets, NULL);
+ } else {
+ /* make sure we have an idle handler to reset the count every now and then */
+ if (ets->insert_idle_id == 0) {
+ ets->insert_idle_id = g_idle_add_full(40, (GSourceFunc) ets_insert_idle, ets, NULL);
+ }
+ i = 0;
+ /* handle insertions when we have a 'sort group' */
+ if (e_table_model_has_sort_group(etss->source)) {
+ /* find the row this row maps to */
+ char *group = g_strdup(e_table_model_row_sort_group(etss->source, row));
+ const char *newgroup;
+ int cmp, grouplen, newgrouplen;
+
+ newgroup = strrchr(group, '/');
+ grouplen = strlen(group);
+ if (newgroup)
+ cmp = newgroup-group;
+ else
+ cmp = grouplen;
+
+ /* find first common parent */
+ while (i<etss->n_map) {
+ newgroup = e_table_model_row_sort_group(etss->source, etss->map_table[i]);
+ if (strncmp(newgroup, group, cmp) == 0) {
+ break;
+ }
+ i++;
+ }
+
+ /* check matching records */
+ while (i<etss->n_map) {
+ newgroup = e_table_model_row_sort_group(etss->source, etss->map_table[i]);
+ newgrouplen = strlen(newgroup);
+ if (strncmp(newgroup, group, cmp) == 0) {
+ /* common parent, check for same level */
+ if (grouplen == newgrouplen) {
+ if (ets_compare(ets, etss->map_table[i], row) >= 0)
+ break;
+ } else if (strncmp(newgroup + cmp, group + cmp, grouplen - cmp) == 0)
+ /* Found a child of the inserted node. Insert here. */
+ break;
+ } else {
+ /* ran out of common parents, insert here */
+ break;
+ }
+ i++;
+ }
+ g_free(group);
+ } else {
+ while (i < etss->n_map && ets_compare(ets, etss->map_table[i], row) < 0)
+ i++;
+ }
+ memmove(etss->map_table + i + 1, etss->map_table + i, (etss->n_map - i) * sizeof(int));
+ }
+ }
+ etss->map_table[i] = row;
+ etss->n_map++;
+
+ e_table_model_row_inserted (etm, i);
}
+#endif
+
ETableModel *
-e_table_sorted_new (ETableModel *source, int col, GCompareFunc compare)
+e_table_sorted_new (ETableModel *source, ETableHeader *full_header, ETableSortInfo *sort_info)
{
ETableSorted *ets = gtk_type_new (E_TABLE_SORTED_TYPE);
ETableSubset *etss = E_TABLE_SUBSET (ets);
- const int nvals = e_table_model_row_count (source);
- int i;
- if (e_table_subset_construct (etss, source, nvals) == NULL){
+ if (e_table_subset_construct (etss, source, 0) == NULL){
gtk_object_unref (GTK_OBJECT (ets));
return NULL;
}
-
- ets->compare = compare;
- ets->sort_col = col;
-
- /* Init */
- for (i = 0; i < nvals; i++)
- etss->map_table [i] = i;
-
- do_sort (ets);
-
- return (ETableModel *) ets;
+
+ ets->sort_info = sort_info;
+ gtk_object_ref(GTK_OBJECT(ets->sort_info));
+ ets->full_header = full_header;
+ gtk_object_ref(GTK_OBJECT(ets->full_header));
+
+ ets_proxy_model_changed(etss, source);
+
+ ets->sort_info_changed_id = gtk_signal_connect (GTK_OBJECT (sort_info), "sort_info_changed",
+ GTK_SIGNAL_FUNC (ets_sort_info_changed), ets);
+
+ return E_TABLE_MODEL(ets);
+}
+
+static void
+ets_sort_info_changed (ETableSortInfo *info, ETableSorted *ets)
+{
+ ets_sort(ets);
}
-void
-e_table_sorted_resort (ETableSorted *ets, int col, GCompareFunc compare)
+static void
+ets_proxy_model_changed (ETableSubset *subset, ETableModel *source)
{
- if (col == -1 || compare == NULL)
- do_sort (ets);
- else {
- ets->sort_col = col;
- ets->compare = compare;
- do_sort (ets);
+ int rows, i;
+
+ rows = e_table_model_row_count(source);
+
+ g_free(subset->map_table);
+ subset->n_map = rows;
+ subset->map_table = g_new(int, rows);
+
+ for (i = 0; i < rows; i++) {
+ subset->map_table[i] = i;
}
+
+ if (!E_TABLE_SORTED(subset)->sort_idle_id)
+ E_TABLE_SORTED(subset)->sort_idle_id = g_idle_add_full(50, (GSourceFunc) ets_sort_idle, subset, NULL);
+
+ e_table_model_changed(E_TABLE_MODEL(subset));
+}
+
+static void
+ets_proxy_model_row_changed (ETableSubset *subset, ETableModel *source, int row)
+{
+ if (!E_TABLE_SORTED(subset)->sort_idle_id)
+ E_TABLE_SORTED(subset)->sort_idle_id = g_idle_add_full(50, (GSourceFunc) ets_sort_idle, subset, NULL);
}
+static void
+ets_proxy_model_cell_changed (ETableSubset *subset, ETableModel *source, int col, int row)
+{
+ ETableSorted *ets = E_TABLE_SORTED(subset);
+ if (e_table_sorting_utils_affects_sort(source, ets->sort_info, ets->full_header, col))
+ ets_proxy_model_row_changed(subset, source, row);
+ else if (ets_parent_class->proxy_model_cell_changed) {
+ (ets_parent_class->proxy_model_cell_changed) (subset, source, col, row);
+ }
+
+}
+
+static void
+ets_proxy_model_row_inserted (ETableSubset *subset, ETableModel *source, int row)
+{
+ ets_proxy_model_changed(subset, source);
+}
+
+static void
+ets_proxy_model_row_deleted (ETableSubset *subset, ETableModel *source, int row)
+{
+ ets_proxy_model_changed(subset, source);
+}
+
+static void
+ets_sort(ETableSorted *ets)
+{
+ ETableSubset *etss = E_TABLE_SUBSET(ets);
+ static int reentering = 0;
+ if (reentering)
+ return;
+ reentering = 1;
+
+ e_table_model_pre_change(E_TABLE_MODEL(ets));
+
+ e_table_sorting_utils_sort(etss->source, ets->sort_info, ets->full_header, etss->map_table, etss->n_map);
+
+ e_table_model_changed (E_TABLE_MODEL(ets));
+ reentering = 0;
+}
diff --git a/widgets/table/e-table-sorted.h b/widgets/table/e-table-sorted.h
index 390141b3c1..a138cdeb04 100644
--- a/widgets/table/e-table-sorted.h
+++ b/widgets/table/e-table-sorted.h
@@ -1,9 +1,12 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
#ifndef _E_TABLE_SORTED_H_
#define _E_TABLE_SORTED_H_
#include <gtk/gtkobject.h>
#include <gal/e-table/e-table-model.h>
#include <gal/e-table/e-table-subset.h>
+#include <gal/e-table/e-table-sort-info.h>
+#include <gal/e-table/e-table-header.h>
#ifdef __cplusplus
extern "C" {
@@ -18,8 +21,15 @@ extern "C" {
typedef struct {
ETableSubset base;
- short sort_col;
- GCompareFunc compare;
+ ETableSortInfo *sort_info;
+
+ ETableHeader *full_header;
+
+ int sort_info_changed_id;
+ int sort_idle_id;
+ int insert_idle_id;
+ int insert_count;
+
} ETableSorted;
typedef struct {
@@ -27,8 +37,7 @@ typedef struct {
} ETableSortedClass;
GtkType e_table_sorted_get_type (void);
-ETableModel *e_table_sorted_new (ETableModel *etm, int col, GCompareFunc compare);
-void e_table_sorted_resort (ETableSorted *ets, int col, GCompareFunc compare);
+ETableModel *e_table_sorted_new (ETableModel *etm, ETableHeader *header, ETableSortInfo *sort_info);
#ifdef __cplusplus
}
diff --git a/widgets/table/e-table-sorter.c b/widgets/table/e-table-sorter.c
index a103e3cd85..08c7c1b120 100644
--- a/widgets/table/e-table-sorter.c
+++ b/widgets/table/e-table-sorter.c
@@ -524,3 +524,29 @@ e_table_sorter_sorted_to_model (ETableSorter *sorter, int row)
else
return row;
}
+
+void
+e_table_sorter_get_model_to_sorted_array (ETableSorter *sorter, int **array, int *count)
+{
+ if (array || count) {
+ ets_backsort(sorter);
+
+ if (array)
+ *array = sorter->backsorted;
+ if (count)
+ *count = e_table_model_row_count(sorter->source);
+ }
+}
+
+void
+e_table_sorter_get_sorted_to_model_array (ETableSorter *sorter, int **array, int *count)
+{
+ if (array || count) {
+ ets_sort(sorter);
+
+ if (array)
+ *array = sorter->sorted;
+ if (count)
+ *count = e_table_model_row_count(sorter->source);
+ }
+}
diff --git a/widgets/table/e-table-sorter.h b/widgets/table/e-table-sorter.h
index 67c9d275d3..3bb2a63a6a 100644
--- a/widgets/table/e-table-sorter.h
+++ b/widgets/table/e-table-sorter.h
@@ -39,13 +39,24 @@ typedef struct {
GtkObjectClass parent_class;
} ETableSorterClass;
-GtkType e_table_sorter_get_type (void);
-ETableSorter *e_table_sorter_new (ETableModel *etm, ETableHeader *full_header, ETableSortInfo *sort_info);
-
-gint e_table_sorter_model_to_sorted (ETableSorter *sorter, int row);
-gint e_table_sorter_sorted_to_model (ETableSorter *sorter, int row);
-
-gboolean e_table_sorter_needs_sorting (ETableSorter *sorter);
+GtkType e_table_sorter_get_type (void);
+ETableSorter *e_table_sorter_new (ETableModel *etm,
+ ETableHeader *full_header,
+ ETableSortInfo *sort_info);
+
+gint e_table_sorter_model_to_sorted (ETableSorter *sorter,
+ int row);
+gint e_table_sorter_sorted_to_model (ETableSorter *sorter,
+ int row);
+
+void e_table_sorter_get_model_to_sorted_array (ETableSorter *sorter,
+ int **array,
+ int *count);
+void e_table_sorter_get_sorted_to_model_array (ETableSorter *sorter,
+ int **array,
+ int *count);
+
+gboolean e_table_sorter_needs_sorting (ETableSorter *sorter);
END_GNOME_DECLS
diff --git a/widgets/table/e-table-sorting-utils.c b/widgets/table/e-table-sorting-utils.c
new file mode 100644
index 0000000000..c5f58b8795
--- /dev/null
+++ b/widgets/table/e-table-sorting-utils.c
@@ -0,0 +1,372 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+#include <string.h>
+#include <e-table-sorting-utils.h>
+
+#define d(x)
+
+static ETableSortInfo *sort_info_closure;
+
+static void **vals_closure;
+static int cols_closure;
+static int *ascending_closure;
+static GCompareFunc *compare_closure;
+
+/* FIXME: Make it not cache the second and later columns (as if anyone cares.) */
+
+static int
+qsort_callback(const void *data1, const void *data2)
+{
+ gint row1 = *(int *)data1;
+ gint row2 = *(int *)data2;
+ int j;
+ int sort_count = e_table_sort_info_sorting_get_count(sort_info_closure);
+ int comp_val = 0;
+ int ascending = 1;
+ for (j = 0; j < sort_count; j++) {
+ comp_val = (*(compare_closure[j]))(vals_closure[cols_closure * row1 + j], vals_closure[cols_closure * row2 + j]);
+ ascending = ascending_closure[j];
+ if (comp_val != 0)
+ break;
+ }
+ if (comp_val == 0) {
+ if (row1 < row2)
+ comp_val = -1;
+ if (row1 > row2)
+ comp_val = 1;
+ }
+ if (!ascending)
+ comp_val = -comp_val;
+ return comp_val;
+}
+
+struct _subinfo {
+ int start;
+ GArray *rowsort; /* an array of row info's */
+};
+
+struct _rowinfo {
+ int row;
+ struct _subinfo *subinfo;
+ struct _group_info *groupinfo;
+};
+
+static int
+qsort_callback_complex(const void *data1, const void *data2)
+{
+ gint row1 = ((struct _rowinfo *)data1)->row;
+ gint row2 = ((struct _rowinfo *)data2)->row;
+ int j;
+ int sort_count = e_table_sort_info_sorting_get_count(sort_info_closure);
+ int comp_val = 0;
+ int ascending = 1;
+ for (j = 0; j < sort_count; j++) {
+ comp_val = (*(compare_closure[j]))(vals_closure[cols_closure * row1 + j], vals_closure[cols_closure * row2 + j]);
+ ascending = ascending_closure[j];
+ if (comp_val != 0)
+ break;
+ }
+ if (comp_val == 0) {
+ if (row1 < row2)
+ comp_val = -1;
+ if (row1 > row2)
+ comp_val = 1;
+ }
+ if (!ascending)
+ comp_val = -comp_val;
+ return comp_val;
+}
+
+/* if sortgroup is like:
+0 1 1 1
+1 1 2 2
+2 2 3 2
+3 2 4 3
+4 3 5 3
+5 2 6 1
+6 1 0 1
+
+ Want to sort the 1's first
+ Then sort each group of 2's, offsetting into the output by the new root 1 location
+ ... Recursively ...
+*/
+
+struct _group_info {
+ char *group;
+ int row;
+};
+
+#ifdef DEBUG
+#undef DEBUG
+#endif
+/*#define DEBUG*/
+
+#ifdef DEBUG
+static int total=0;
+static int total_sorted=0;
+#endif
+
+/* builds the info needed to sort everything */
+static struct _subinfo *
+etsu_sort_build_subset(int rows, struct _group_info *groupinfo, int start, int *end)
+{
+ int i, lastinsert;
+ GArray *rowsort = g_array_new(0, 0, sizeof(struct _rowinfo));
+ struct _subinfo *subinfo, *newsub;
+ char *id, *newid;
+ int idlen, newidlen;
+ int cmp;
+ int cmplen;
+
+ subinfo = g_malloc0(sizeof(*subinfo));
+ subinfo->rowsort = rowsort;
+ subinfo->start = start;
+ lastinsert = -1;
+ id = groupinfo[start].group;
+ newid = strrchr(id, '/');
+ idlen = strlen(id);
+ if (newid)
+ cmplen = newid-id;
+ else
+ cmplen = idlen;
+ d(printf("%d scanning level %s\n", start, id));
+ for (i=start;i<rows;i++) {
+ newid = groupinfo[i].group;
+ newidlen = strlen(newid);
+ d(printf("%d checking group %s\n", start, newid));
+ cmp = strncmp(id, newid, cmplen);
+ /* check for common parent */
+ if (idlen == newidlen && cmp == 0) {
+ struct _rowinfo rowinfo;
+
+ d(printf("%d Same parent\n", start));
+ rowinfo.row = groupinfo[i].row;
+ rowinfo.subinfo = NULL;
+ rowinfo.groupinfo = &groupinfo[i];
+ lastinsert = rowsort->len;
+ g_array_append_val(rowsort, rowinfo);
+#ifdef DEBUG
+ total++;
+#endif
+ } else if (newidlen > idlen) {
+ /* must be a new subtree */
+ d(printf("%d checking subtree instead\n", start));
+ newsub = etsu_sort_build_subset(rows, groupinfo, i, &i);
+ d(printf("found %d nodes in subtree\n", newsub->rowsort->len));
+ g_array_index(rowsort, struct _rowinfo, lastinsert).subinfo = newsub;
+ } else {
+ i--;
+ break;
+ }
+ }
+ if (end)
+ *end = i;
+ d(printf("finished level %s start was %d end was %d\n", id, start, i));
+ return subinfo;
+}
+
+/* sort each level, and then sort each level below that level (once we know
+ where the sublevel will fit in the overall list) */
+static int
+etsu_sort_subset(int *map_table, struct _subinfo *subinfo, int startoffset)
+{
+ GArray *rowsort = subinfo->rowsort;
+ int offset, i;
+
+ d(printf("sorting subset start %d rows %d\n", startoffset, rowsort->len));
+
+ /* first, sort the actual data */
+ qsort(rowsort->data, rowsort->len, sizeof(struct _rowinfo), qsort_callback_complex);
+
+ /* then put it back in the map table, where appropriate */
+ offset = startoffset;
+ for (i=0;i<rowsort->len;i++) {
+ struct _rowinfo *rowinfo;
+
+ d(printf("setting offset %d\n", offset));
+
+ rowinfo = &g_array_index(rowsort, struct _rowinfo, i);
+ map_table[offset] = rowinfo->row;
+ if (rowinfo->subinfo) {
+ offset = etsu_sort_subset(map_table, rowinfo->subinfo, offset+1);
+ } else
+ offset += 1;
+ }
+ d(printf("end sort subset start %d\n", startoffset));
+
+ return offset;
+}
+
+static void
+etsu_sort_free_subset(struct _subinfo *subinfo)
+{
+ int i;
+
+ for (i=0;i<subinfo->rowsort->len;i++) {
+ struct _rowinfo *rowinfo;
+
+ rowinfo = &g_array_index(subinfo->rowsort, struct _rowinfo, i);
+ if (rowinfo->subinfo)
+ etsu_sort_free_subset(rowinfo->subinfo);
+ }
+ g_array_free(subinfo->rowsort, TRUE);
+ g_free(subinfo);
+}
+
+static int
+sort_groups_compare(const void *ap, const void *bp)
+{
+ struct _group_info *a = (struct _group_info *)ap;
+ struct _group_info *b = (struct _group_info *)bp;
+
+ return strcmp(a->group, b->group);
+}
+
+#ifdef DEBUG
+static void
+print_id(int key, int val, void *data)
+{
+ printf("gained id %d\n", key);
+}
+#endif
+
+/* use the sort group to select subsorts */
+static void
+etsu_sort_by_group(ETableModel *source, int *map_table, int rows)
+{
+ struct _group_info *groups;
+ struct _subinfo *subinfo;
+ int i;
+#ifdef DEBUG
+ GHashTable *members = g_hash_table_new(0, 0);
+
+ total = 0;
+ total_sorted = 0;
+#endif
+
+ d(printf("sorting %d rows\n", rows));
+
+ if (rows == 0)
+ return;
+
+ /* get the subset rows */
+ groups = g_malloc(sizeof(struct _group_info) * rows);
+ for (i=0;i<rows;i++) {
+ groups[i].row = map_table[i];
+ groups[i].group = g_strdup(e_table_model_row_sort_group(source, groups[i].row));
+#ifdef DEBUG
+ g_hash_table_insert(members, map_table[i], 1);
+ map_table[i] = 0;
+#endif
+ }
+
+ /* sort the group info */
+ qsort(groups, rows, sizeof(struct _group_info), sort_groups_compare);
+
+ d(printf("sorted groups:\n");
+ for (i=0;i<rows;i++) {
+ printf(" %s\n", groups[i].group);
+ });
+
+ /* now sort based on the group info */
+ subinfo = etsu_sort_build_subset(rows, groups, 0, NULL);
+ for (i=0;i<rows;i++) {
+ g_free(groups[i].group);
+ }
+ g_free(groups);
+ etsu_sort_subset(map_table, subinfo, 0);
+ etsu_sort_free_subset(subinfo);
+#ifdef DEBUG
+ for (i=0;i<rows;i++) {
+ if (g_hash_table_lookup(members, map_table[i]) == 0) {
+ printf("lost id %d\n", map_table[i]);
+ }
+ g_hash_table_remove(members, map_table[i]);
+ }
+ g_hash_table_foreach(members, print_id, 0);
+
+ printf("total rows = %d, total processed = %d, total sorted = %d\n", rows, total, total_sorted);
+#endif
+
+}
+
+void
+e_table_sorting_utils_sort(ETableModel *source, ETableSortInfo *sort_info, ETableHeader *full_header, int *map_table, int rows)
+{
+ int total_rows;
+ int i;
+ int j;
+ int cols;
+
+ g_return_if_fail(source != NULL);
+ g_return_if_fail(E_IS_TABLE_MODEL(source));
+ g_return_if_fail(sort_info != NULL);
+ g_return_if_fail(E_IS_TABLE_SORT_INFO(sort_info));
+ g_return_if_fail(full_header != NULL);
+ g_return_if_fail(E_IS_TABLE_HEADER(full_header));
+
+ total_rows = e_table_model_row_count(source);
+ cols = e_table_sort_info_sorting_get_count(sort_info);
+ cols_closure = cols;
+ vals_closure = g_new(void *, total_rows * cols);
+ sort_info_closure = sort_info;
+ ascending_closure = g_new(int, cols);
+ compare_closure = g_new(GCompareFunc, cols);
+ for (j = 0; j < cols; j++) {
+ ETableSortColumn column = e_table_sort_info_sorting_get_nth(sort_info, j);
+ ETableCol *col;
+ col = e_table_header_get_column_by_col_idx(full_header, column.column);
+ if (col == NULL)
+ col = e_table_header_get_column (full_header, e_table_header_count (full_header) - 1);
+ for (i = 0; i < rows; i++) {
+#if 0
+ if( !(i & 0xff) ) {
+ while(gtk_events_pending())
+ gtk_main_iteration();
+ }
+#endif
+ vals_closure[map_table[i] * cols + j] = e_table_model_value_at (source, col->col_idx, map_table[i]);
+ }
+ compare_closure[j] = col->compare;
+ ascending_closure[j] = column.ascending;
+ }
+
+ if (e_table_model_has_sort_group(source)) {
+ etsu_sort_by_group(source, map_table, rows);
+ } else {
+ qsort(map_table, rows, sizeof(int), qsort_callback);
+ }
+ g_free(vals_closure);
+ g_free(ascending_closure);
+ g_free(compare_closure);
+}
+
+gboolean
+e_table_sorting_utils_affects_sort (ETableModel *source,
+ ETableSortInfo *sort_info,
+ ETableHeader *full_header,
+ int col)
+{
+ int j;
+ int cols;
+
+ g_return_val_if_fail(source != NULL, TRUE);
+ g_return_val_if_fail(E_IS_TABLE_MODEL(source), TRUE);
+ g_return_val_if_fail(sort_info != NULL, TRUE);
+ g_return_val_if_fail(E_IS_TABLE_SORT_INFO(sort_info), TRUE);
+ g_return_val_if_fail(full_header != NULL, TRUE);
+ g_return_val_if_fail(E_IS_TABLE_HEADER(full_header), TRUE);
+
+ cols = e_table_sort_info_sorting_get_count(sort_info);
+
+ for (j = 0; j < cols; j++) {
+ ETableSortColumn column = e_table_sort_info_sorting_get_nth(sort_info, j);
+ ETableCol *tablecol;
+ tablecol = e_table_header_get_column_by_col_idx(full_header, column.column);
+ if (tablecol == NULL)
+ tablecol = e_table_header_get_column (full_header, e_table_header_count (full_header) - 1);
+ if (col == tablecol->col_idx)
+ return TRUE;
+ }
+ return FALSE;
+}
diff --git a/widgets/table/e-table-sorting-utils.h b/widgets/table/e-table-sorting-utils.h
new file mode 100644
index 0000000000..7ad9467456
--- /dev/null
+++ b/widgets/table/e-table-sorting-utils.h
@@ -0,0 +1,29 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+#ifndef _E_TABLE_SORTING_UTILS_H_
+#define _E_TABLE_SORTING_UTILS_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#include <gal/e-table/e-table-model.h>
+#include <gal/e-table/e-table-sort-info.h>
+#include <gal/e-table/e-table-header.h>
+
+void e_table_sorting_utils_sort (ETableModel *source,
+ ETableSortInfo *sort_info,
+ ETableHeader *full_header,
+ int *map_table,
+ int rows);
+
+gboolean e_table_sorting_utils_affects_sort (ETableModel *source,
+ ETableSortInfo *sort_info,
+ ETableHeader *full_header,
+ int col);
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* _E_TABLE_SORTING_UTILS_H_ */
diff --git a/widgets/table/e-table-subset-variable.c b/widgets/table/e-table-subset-variable.c
index 35b451f693..5c2aea35e6 100644
--- a/widgets/table/e-table-subset-variable.c
+++ b/widgets/table/e-table-subset-variable.c
@@ -42,6 +42,27 @@ etssv_add (ETableSubsetVariable *etssv,
}
static void
+etssv_add_array (ETableSubsetVariable *etssv,
+ const gint *array,
+ gint count)
+{
+ ETableModel *etm = E_TABLE_MODEL(etssv);
+ ETableSubset *etss = E_TABLE_SUBSET(etssv);
+ int i;
+
+ e_table_model_pre_change(etm);
+
+ if (etss->n_map + count > etssv->n_vals_allocated){
+ etssv->n_vals_allocated += MAX(INCREMENT_AMOUNT, count);
+ etss->map_table = g_realloc (etss->map_table, etssv->n_vals_allocated * sizeof(int));
+ }
+ for (i = 0; i < count; i++)
+ etss->map_table[etss->n_map++] = array[i];
+
+ e_table_model_changed (etm);
+}
+
+static void
etssv_add_all (ETableSubsetVariable *etssv)
{
ETableModel *etm = E_TABLE_MODEL(etssv);
@@ -90,6 +111,7 @@ etssv_class_init (GtkObjectClass *object_class)
etssv_parent_class = gtk_type_class (PARENT_TYPE);
klass->add = etssv_add;
+ klass->add_array = etssv_add_array;
klass->add_all = etssv_add_all;
klass->remove = etssv_remove;
}
@@ -132,6 +154,18 @@ e_table_subset_variable_add (ETableSubsetVariable *etssv,
}
void
+e_table_subset_variable_add_array (ETableSubsetVariable *etssv,
+ const gint *array,
+ gint count)
+{
+ g_return_if_fail (etssv != NULL);
+ g_return_if_fail (E_IS_TABLE_SUBSET_VARIABLE(etssv));
+
+ if (ETSSV_CLASS(etssv)->add_array)
+ ETSSV_CLASS (etssv)->add_array (etssv, array, count);
+}
+
+void
e_table_subset_variable_add_all (ETableSubsetVariable *etssv)
{
g_return_if_fail (etssv != NULL);
diff --git a/widgets/table/e-table-subset-variable.h b/widgets/table/e-table-subset-variable.h
index 5436fbb8f2..f09c612f13 100644
--- a/widgets/table/e-table-subset-variable.h
+++ b/widgets/table/e-table-subset-variable.h
@@ -24,11 +24,14 @@ typedef struct {
typedef struct {
ETableSubsetClass parent_class;
- void (*add) (ETableSubsetVariable *ets,
- gint row);
- void (*add_all) (ETableSubsetVariable *ets);
- gboolean (*remove) (ETableSubsetVariable *ets,
- gint row);
+ void (*add) (ETableSubsetVariable *ets,
+ gint row);
+ void (*add_array) (ETableSubsetVariable *ets,
+ const gint *array,
+ gint count);
+ void (*add_all) (ETableSubsetVariable *ets);
+ gboolean (*remove) (ETableSubsetVariable *ets,
+ gint row);
} ETableSubsetVariableClass;
GtkType e_table_subset_variable_get_type (void);
@@ -37,6 +40,9 @@ ETableModel *e_table_subset_variable_construct (ETableSubsetVariable *etssv,
ETableModel *source);
void e_table_subset_variable_add (ETableSubsetVariable *ets,
gint row);
+void e_table_subset_variable_add_array (ETableSubsetVariable *ets,
+ const gint *array,
+ gint count);
void e_table_subset_variable_add_all (ETableSubsetVariable *ets);
gboolean e_table_subset_variable_remove (ETableSubsetVariable *ets,
gint row);
diff --git a/widgets/table/e-table-subset.c b/widgets/table/e-table-subset.c
index 88ec8e59f4..bcac5f8dc2 100644
--- a/widgets/table/e-table-subset.c
+++ b/widgets/table/e-table-subset.c
@@ -13,11 +13,18 @@
#include "gal/util/e-util.h"
#include "e-table-subset.h"
+static void etss_proxy_model_pre_change_real (ETableSubset *etss, ETableModel *etm);
+static void etss_proxy_model_changed_real (ETableSubset *etss, ETableModel *etm);
+static void etss_proxy_model_row_changed_real (ETableSubset *etss, ETableModel *etm, int row);
+static void etss_proxy_model_cell_changed_real (ETableSubset *etss, ETableModel *etm, int col, int row);
+
#define PARENT_TYPE E_TABLE_MODEL_TYPE
#define d(x)
static ETableModelClass *etss_parent_class;
+#define ETSS_CLASS(object) (E_TABLE_SUBSET_CLASS(GTK_OBJECT(object)->klass))
+
static void
etss_destroy (GtkObject *object)
{
@@ -32,21 +39,21 @@ etss_destroy (GtkObject *object)
etss->table_model_row_changed_id);
gtk_signal_disconnect (GTK_OBJECT (etss->source),
etss->table_model_cell_changed_id);
+ gtk_signal_disconnect (GTK_OBJECT (etss->source),
+ etss->table_model_row_inserted_id);
+ gtk_signal_disconnect (GTK_OBJECT (etss->source),
+ etss->table_model_row_deleted_id);
+
+ gtk_object_unref (GTK_OBJECT (etss->source));
+ etss->source = NULL;
- etss->table_model_pre_change_id = 0;
etss->table_model_changed_id = 0;
etss->table_model_row_changed_id = 0;
etss->table_model_cell_changed_id = 0;
-
- gtk_object_unref (GTK_OBJECT (etss->source));
- etss->source = NULL;
+ etss->table_model_row_inserted_id = 0;
+ etss->table_model_row_deleted_id = 0;
}
- etss->table_model_pre_change_id = 0;
- etss->table_model_changed_id = 0;
- etss->table_model_row_changed_id = 0;
- etss->table_model_cell_changed_id = 0;
-
g_free (etss->map_table);
GTK_OBJECT_CLASS (etss_parent_class)->destroy (object);
@@ -144,25 +151,33 @@ etss_value_to_string (ETableModel *etm, int col, const void *value)
}
static void
-etss_class_init (GtkObjectClass *klass)
+etss_class_init (GtkObjectClass *object_class)
{
- ETableModelClass *table_class = (ETableModelClass *) klass;
+ ETableSubsetClass *klass = (ETableSubsetClass *) object_class;
+ ETableModelClass *table_class = (ETableModelClass *) object_class;
- etss_parent_class = gtk_type_class (PARENT_TYPE);
+ etss_parent_class = gtk_type_class (PARENT_TYPE);
- klass->destroy = etss_destroy;
-
- table_class->column_count = etss_column_count;
- table_class->row_count = etss_row_count;
- table_class->value_at = etss_value_at;
- table_class->set_value_at = etss_set_value_at;
- table_class->is_cell_editable = etss_is_cell_editable;
- table_class->append_row = etss_append_row;
- table_class->duplicate_value = etss_duplicate_value;
- table_class->free_value = etss_free_value;
- table_class->initialize_value = etss_initialize_value;
- table_class->value_is_empty = etss_value_is_empty;
- table_class->value_to_string = etss_value_to_string;
+ object_class->destroy = etss_destroy;
+
+ table_class->column_count = etss_column_count;
+ table_class->row_count = etss_row_count;
+ table_class->value_at = etss_value_at;
+ table_class->set_value_at = etss_set_value_at;
+ table_class->is_cell_editable = etss_is_cell_editable;
+ table_class->append_row = etss_append_row;
+ table_class->duplicate_value = etss_duplicate_value;
+ table_class->free_value = etss_free_value;
+ table_class->initialize_value = etss_initialize_value;
+ table_class->value_is_empty = etss_value_is_empty;
+ table_class->value_to_string = etss_value_to_string;
+
+ klass->proxy_model_pre_change = etss_proxy_model_pre_change_real;
+ klass->proxy_model_changed = etss_proxy_model_changed_real;
+ klass->proxy_model_row_changed = etss_proxy_model_row_changed_real;
+ klass->proxy_model_cell_changed = etss_proxy_model_cell_changed_real;
+ klass->proxy_model_row_inserted = NULL;
+ klass->proxy_model_row_deleted = NULL;
}
static void
@@ -174,19 +189,19 @@ etss_init (ETableSubset *etss)
E_MAKE_TYPE(e_table_subset, "ETableSubset", ETableSubset, etss_class_init, etss_init, PARENT_TYPE);
static void
-etss_proxy_model_pre_change (ETableModel *etm, ETableSubset *etss)
+etss_proxy_model_pre_change_real (ETableSubset *etss, ETableModel *etm)
{
e_table_model_pre_change (E_TABLE_MODEL (etss));
}
static void
-etss_proxy_model_changed (ETableModel *etm, ETableSubset *etss)
+etss_proxy_model_changed_real (ETableSubset *etss, ETableModel *etm)
{
e_table_model_changed (E_TABLE_MODEL (etss));
}
static void
-etss_proxy_model_row_changed (ETableModel *etm, int row, ETableSubset *etss)
+etss_proxy_model_row_changed_real (ETableSubset *etss, ETableModel *etm, int row)
{
int limit;
const int n = etss->n_map;
@@ -224,7 +239,7 @@ etss_proxy_model_row_changed (ETableModel *etm, int row, ETableSubset *etss)
}
static void
-etss_proxy_model_cell_changed (ETableModel *etm, int col, int row, ETableSubset *etss)
+etss_proxy_model_cell_changed_real (ETableSubset *etss, ETableModel *etm, int col, int row)
{
int limit;
const int n = etss->n_map;
@@ -261,15 +276,60 @@ etss_proxy_model_cell_changed (ETableModel *etm, int col, int row, ETableSubset
}
}
+static void
+etss_proxy_model_pre_change (ETableModel *etm, ETableSubset *etss)
+{
+ if (ETSS_CLASS(etss)->proxy_model_pre_change)
+ (ETSS_CLASS(etss)->proxy_model_pre_change) (etss, etm);
+}
+
+static void
+etss_proxy_model_changed (ETableModel *etm, ETableSubset *etss)
+{
+ if (ETSS_CLASS(etss)->proxy_model_changed)
+ (ETSS_CLASS(etss)->proxy_model_changed) (etss, etm);
+}
+
+static void
+etss_proxy_model_row_changed (ETableModel *etm, int row, ETableSubset *etss)
+{
+ if (ETSS_CLASS(etss)->proxy_model_row_changed)
+ (ETSS_CLASS(etss)->proxy_model_row_changed) (etss, etm, row);
+}
+
+static void
+etss_proxy_model_cell_changed (ETableModel *etm, int row, int col, ETableSubset *etss)
+{
+ if (ETSS_CLASS(etss)->proxy_model_cell_changed)
+ (ETSS_CLASS(etss)->proxy_model_cell_changed) (etss, etm, col, row);
+}
+
+static void
+etss_proxy_model_row_inserted (ETableModel *etm, int row, ETableSubset *etss)
+{
+ if (ETSS_CLASS(etss)->proxy_model_row_inserted)
+ (ETSS_CLASS(etss)->proxy_model_row_inserted) (etss, etm, row);
+}
+
+static void
+etss_proxy_model_row_deleted (ETableModel *etm, int row, ETableSubset *etss)
+{
+ if (ETSS_CLASS(etss)->proxy_model_row_deleted)
+ (ETSS_CLASS(etss)->proxy_model_row_deleted) (etss, etm, row);
+}
+
ETableModel *
e_table_subset_construct (ETableSubset *etss, ETableModel *source, int nvals)
{
unsigned int *buffer;
int i;
- buffer = (unsigned int *) g_malloc (sizeof (unsigned int) * nvals);
- if (buffer == NULL)
- return NULL;
+ if (nvals) {
+ buffer = (unsigned int *) g_malloc (sizeof (unsigned int) * nvals);
+ if (buffer == NULL)
+ return NULL;
+ } else
+ buffer = NULL;
etss->map_table = buffer;
etss->n_map = nvals;
etss->source = source;
@@ -287,6 +347,10 @@ e_table_subset_construct (ETableSubset *etss, ETableModel *source, int nvals)
GTK_SIGNAL_FUNC (etss_proxy_model_row_changed), etss);
etss->table_model_cell_changed_id = gtk_signal_connect (GTK_OBJECT (source), "model_cell_changed",
GTK_SIGNAL_FUNC (etss_proxy_model_cell_changed), etss);
+ etss->table_model_row_inserted_id = gtk_signal_connect (GTK_OBJECT (source), "model_row_inserted",
+ GTK_SIGNAL_FUNC (etss_proxy_model_row_inserted), etss);
+ etss->table_model_row_deleted_id = gtk_signal_connect (GTK_OBJECT (source), "model_row_deleted",
+ GTK_SIGNAL_FUNC (etss_proxy_model_row_deleted), etss);
return E_TABLE_MODEL (etss);
}
diff --git a/widgets/table/e-table-subset.h b/widgets/table/e-table-subset.h
index 6521751637..bfa6132598 100644
--- a/widgets/table/e-table-subset.h
+++ b/widgets/table/e-table-subset.h
@@ -28,10 +28,19 @@ typedef struct {
int table_model_changed_id;
int table_model_row_changed_id;
int table_model_cell_changed_id;
+ int table_model_row_inserted_id;
+ int table_model_row_deleted_id;
} ETableSubset;
typedef struct {
ETableModelClass parent_class;
+
+ void (*proxy_model_pre_change) (ETableSubset *etss, ETableModel *etm);
+ void (*proxy_model_changed) (ETableSubset *etss, ETableModel *etm);
+ void (*proxy_model_row_changed) (ETableSubset *etss, ETableModel *etm, int row);
+ void (*proxy_model_cell_changed) (ETableSubset *etss, ETableModel *etm, int col, int row);
+ void (*proxy_model_row_inserted) (ETableSubset *etss, ETableModel *etm, int row);
+ void (*proxy_model_row_deleted) (ETableSubset *etss, ETableModel *etm, int row);
} ETableSubsetClass;
GtkType e_table_subset_get_type (void);
diff --git a/widgets/table/e-table.c b/widgets/table/e-table.c
index 25922fedf7..abda473a97 100644
--- a/widgets/table/e-table.c
+++ b/widgets/table/e-table.c
@@ -118,13 +118,9 @@ static gint e_table_drag_source_event_cb (GtkWidget *widget,
static gint et_focus (GtkContainer *container, GtkDirectionType direction);
-
static void
-et_destroy (GtkObject *object)
+et_disconnect_model (ETable *et)
{
- ETable *et = E_TABLE (object);
-
-
gtk_signal_disconnect (GTK_OBJECT (et->model),
et->table_model_change_id);
gtk_signal_disconnect (GTK_OBJECT (et->model),
@@ -135,6 +131,21 @@ et_destroy (GtkObject *object)
et->table_row_inserted_id);
gtk_signal_disconnect (GTK_OBJECT (et->model),
et->table_row_deleted_id);
+
+ et->table_model_change_id = 0;
+ et->table_row_change_id = 0;
+ et->table_cell_change_id = 0;
+ et->table_row_inserted_id = 0;
+ et->table_row_deleted_id = 0;
+}
+
+static void
+et_destroy (GtkObject *object)
+{
+ ETable *et = E_TABLE (object);
+
+ et_disconnect_model (et);
+
if (et->group_info_change_id)
gtk_signal_disconnect (GTK_OBJECT (et->sort_info),
et->group_info_change_id);
@@ -255,9 +266,12 @@ header_canvas_size_allocate (GtkWidget *widget, GtkAllocation *alloc, ETable *e_
static void
sort_info_changed (ETableSortInfo *info, ETable *et)
{
- et->need_rebuild = TRUE;
- if (!et->rebuild_idle_id)
- et->rebuild_idle_id = g_idle_add_full (20, changed_idle, et, NULL);
+ gboolean will_be_grouped = e_table_sort_info_grouping_get_count(info) > 0;
+ if (et->is_grouped || will_be_grouped) {
+ et->need_rebuild = TRUE;
+ if (!et->rebuild_idle_id)
+ et->rebuild_idle_id = g_idle_add_full (20, changed_idle, et, NULL);
+ }
}
static void
@@ -466,58 +480,6 @@ group_key_press (ETableGroup *etg, int row, int col, GdkEvent *event, ETable *et
return return_val;
}
-static gboolean
-changed_idle (gpointer data)
-{
- ETable *et = E_TABLE (data);
-
- if (et->need_rebuild) {
- gtk_object_destroy (GTK_OBJECT (et->group));
- et->group = e_table_group_new (GNOME_CANVAS_GROUP (et->canvas_vbox),
- et->full_header,
- et->header,
- et->model,
- et->sort_info,
- 0);
- e_canvas_vbox_add_item(E_CANVAS_VBOX(et->canvas_vbox), GNOME_CANVAS_ITEM(et->group));
- gnome_canvas_item_set(GNOME_CANVAS_ITEM(et->group),
- "drawgrid", et->draw_grid,
- "drawfocus", et->draw_focus,
- "cursor_mode", et->cursor_mode,
- "length_threshold", et->length_threshold,
- "table_selection_model", et->selection,
- NULL);
- gtk_signal_connect (GTK_OBJECT (et->group), "cursor_change",
- GTK_SIGNAL_FUNC (group_cursor_change), et);
- gtk_signal_connect (GTK_OBJECT (et->group), "cursor_activated",
- GTK_SIGNAL_FUNC (group_cursor_activated), et);
- gtk_signal_connect (GTK_OBJECT (et->group), "double_click",
- GTK_SIGNAL_FUNC (group_double_click), et);
- gtk_signal_connect (GTK_OBJECT (et->group), "right_click",
- GTK_SIGNAL_FUNC (group_right_click), et);
- gtk_signal_connect (GTK_OBJECT (et->group), "click",
- GTK_SIGNAL_FUNC (group_click), et);
- gtk_signal_connect (GTK_OBJECT (et->group), "key_press",
- GTK_SIGNAL_FUNC (group_key_press), et);
- e_table_fill_table (et, et->model);
-
- gtk_object_set (GTK_OBJECT (et->canvas_vbox),
- "width", (double) GTK_WIDGET (et->table_canvas)->allocation.width,
- NULL);
-
- if (GTK_WIDGET_REALIZED(et->table_canvas))
- table_canvas_size_allocate (GTK_WIDGET(et->table_canvas), &GTK_WIDGET(et->table_canvas)->allocation, et);
- }
-
- et->need_rebuild = 0;
- et->rebuild_idle_id = 0;
-
- if (et->horizontal_scrolling)
- e_table_header_update_horizontal(et->header);
-
- return FALSE;
-}
-
static void
et_table_model_changed (ETableModel *model, ETable *et)
{
@@ -571,6 +533,92 @@ et_table_row_deleted (ETableModel *table_model, int row, ETable *et)
}
static void
+et_build_groups (ETable *et)
+{
+ gboolean was_grouped = et->is_grouped;
+
+ et->is_grouped = e_table_sort_info_grouping_get_count(et->sort_info) > 0;
+
+ et->group = e_table_group_new (GNOME_CANVAS_GROUP (et->canvas_vbox),
+ et->full_header,
+ et->header,
+ et->model,
+ et->sort_info,
+ 0);
+ e_canvas_vbox_add_item(E_CANVAS_VBOX(et->canvas_vbox), GNOME_CANVAS_ITEM(et->group));
+ gnome_canvas_item_set(GNOME_CANVAS_ITEM(et->group),
+ "drawgrid", et->draw_grid,
+ "drawfocus", et->draw_focus,
+ "cursor_mode", et->cursor_mode,
+ "length_threshold", et->length_threshold,
+ "table_selection_model", et->selection,
+ NULL);
+
+ gtk_signal_connect (GTK_OBJECT (et->group), "cursor_change",
+ GTK_SIGNAL_FUNC (group_cursor_change), et);
+ gtk_signal_connect (GTK_OBJECT (et->group), "cursor_activated",
+ GTK_SIGNAL_FUNC (group_cursor_activated), et);
+ gtk_signal_connect (GTK_OBJECT (et->group), "double_click",
+ GTK_SIGNAL_FUNC (group_double_click), et);
+ gtk_signal_connect (GTK_OBJECT (et->group), "right_click",
+ GTK_SIGNAL_FUNC (group_right_click), et);
+ gtk_signal_connect (GTK_OBJECT (et->group), "click",
+ GTK_SIGNAL_FUNC (group_click), et);
+ gtk_signal_connect (GTK_OBJECT (et->group), "key_press",
+ GTK_SIGNAL_FUNC (group_key_press), et);
+
+
+ if (!(et->is_grouped) && was_grouped)
+ et_disconnect_model (et);
+
+ if (et->is_grouped && (!was_grouped)) {
+ et->table_model_change_id = gtk_signal_connect (GTK_OBJECT (et->model), "model_changed",
+ GTK_SIGNAL_FUNC (et_table_model_changed), et);
+
+ et->table_row_change_id = gtk_signal_connect (GTK_OBJECT (et->model), "model_row_changed",
+ GTK_SIGNAL_FUNC (et_table_row_changed), et);
+
+ et->table_cell_change_id = gtk_signal_connect (GTK_OBJECT (et->model), "model_cell_changed",
+ GTK_SIGNAL_FUNC (et_table_cell_changed), et);
+
+ et->table_row_inserted_id = gtk_signal_connect (GTK_OBJECT (et->model), "model_row_inserted",
+ GTK_SIGNAL_FUNC (et_table_row_inserted), et);
+
+ et->table_row_deleted_id = gtk_signal_connect (GTK_OBJECT (et->model), "model_row_deleted",
+ GTK_SIGNAL_FUNC (et_table_row_deleted), et);
+
+ }
+
+ if (et->is_grouped)
+ e_table_fill_table (et, et->model);
+}
+
+static gboolean
+changed_idle (gpointer data)
+{
+ ETable *et = E_TABLE (data);
+
+ if (et->need_rebuild) {
+ gtk_object_destroy (GTK_OBJECT (et->group));
+ et_build_groups(et);
+ gtk_object_set (GTK_OBJECT (et->canvas_vbox),
+ "width", (double) GTK_WIDGET (et->table_canvas)->allocation.width,
+ NULL);
+
+ if (GTK_WIDGET_REALIZED(et->table_canvas))
+ table_canvas_size_allocate (GTK_WIDGET(et->table_canvas), &GTK_WIDGET(et->table_canvas)->allocation, et);
+ }
+
+ et->need_rebuild = 0;
+ et->rebuild_idle_id = 0;
+
+ if (et->horizontal_scrolling)
+ e_table_header_update_horizontal(et->header);
+
+ return FALSE;
+}
+
+static void
et_canvas_realize (GtkWidget *canvas, ETable *e_table)
{
gnome_canvas_item_set(
@@ -676,54 +724,7 @@ e_table_setup_table (ETable *e_table, ETableHeader *full_header, ETableHeader *h
GTK_SIGNAL_FUNC(click_to_add_cursor_change), e_table);
}
- e_table->group = e_table_group_new (
- GNOME_CANVAS_GROUP (e_table->canvas_vbox),
- full_header, header,
- model, e_table->sort_info, 0);
- e_canvas_vbox_add_item(E_CANVAS_VBOX(e_table->canvas_vbox), GNOME_CANVAS_ITEM(e_table->group));
-
- gnome_canvas_item_set(
- GNOME_CANVAS_ITEM(e_table->group),
- "drawgrid", e_table->draw_grid,
- "drawfocus", e_table->draw_focus,
- "cursor_mode", e_table->cursor_mode,
- "length_threshold", e_table->length_threshold,
- "table_selection_model", e_table->selection,
- NULL);
-
- gtk_signal_connect (GTK_OBJECT (e_table->group), "cursor_change",
- GTK_SIGNAL_FUNC(group_cursor_change), e_table);
- gtk_signal_connect (GTK_OBJECT (e_table->group), "cursor_activated",
- GTK_SIGNAL_FUNC(group_cursor_activated), e_table);
- gtk_signal_connect (GTK_OBJECT (e_table->group), "double_click",
- GTK_SIGNAL_FUNC(group_double_click), e_table);
- gtk_signal_connect (GTK_OBJECT (e_table->group), "right_click",
- GTK_SIGNAL_FUNC(group_right_click), e_table);
- gtk_signal_connect (GTK_OBJECT (e_table->group), "click",
- GTK_SIGNAL_FUNC(group_click), e_table);
- gtk_signal_connect (GTK_OBJECT (e_table->group), "key_press",
- GTK_SIGNAL_FUNC(group_key_press), e_table);
-
- e_table->table_model_change_id = gtk_signal_connect (
- GTK_OBJECT (model), "model_changed",
- GTK_SIGNAL_FUNC (et_table_model_changed), e_table);
-
- e_table->table_row_change_id = gtk_signal_connect (
- GTK_OBJECT (model), "model_row_changed",
- GTK_SIGNAL_FUNC (et_table_row_changed), e_table);
-
- e_table->table_cell_change_id = gtk_signal_connect (
- GTK_OBJECT (model), "model_cell_changed",
- GTK_SIGNAL_FUNC (et_table_cell_changed), e_table);
-
- e_table->table_row_inserted_id = gtk_signal_connect (
- GTK_OBJECT (model), "model_row_inserted",
- GTK_SIGNAL_FUNC (et_table_row_inserted), e_table);
-
- e_table->table_row_deleted_id = gtk_signal_connect (
- GTK_OBJECT (model), "model_row_deleted",
- GTK_SIGNAL_FUNC (et_table_row_deleted), e_table);
-
+ et_build_groups(e_table);
}
static void
@@ -1807,6 +1808,19 @@ e_table_compute_location (ETable *table, GtkWidget *widget,
e_table_group_compute_location(table->group, &x, &y, row, col);
}
+#if 0
+void
+e_table_get_position (ETable *table, GtkWidget *widget,
+ int *x, int *y, int row, int col)
+{
+ if (!(x || y))
+ return;
+ e_table_group_get_position(table->group, x, y, &row, &col);
+ *x -= GTK_LAYOUT(table->table_canvas)->hadjustment->value;
+ *y -= GTK_LAYOUT(table->table_canvas)->vadjustment->value;
+}
+#endif
+
static void
et_drag_begin (GtkWidget *widget,
GdkDragContext *context,
diff --git a/widgets/table/e-table.h b/widgets/table/e-table.h
index b2161dd0ef..7364768559 100644
--- a/widgets/table/e-table.h
+++ b/widgets/table/e-table.h
@@ -78,6 +78,8 @@ typedef struct {
guint row_selection_active : 1;
guint horizontal_scrolling : 1;
+
+ guint is_grouped : 1;
char *click_to_add_message;
GnomeCanvasItem *click_to_add;