diff options
-rw-r--r-- | widgets/table/e-table-group-container.c | 123 | ||||
-rw-r--r-- | widgets/table/e-table-group-leaf.c | 79 | ||||
-rw-r--r-- | widgets/table/e-table-group-leaf.h | 7 | ||||
-rw-r--r-- | widgets/table/e-table-group.c | 47 | ||||
-rw-r--r-- | widgets/table/e-table-group.h | 11 | ||||
-rw-r--r-- | widgets/table/e-table-sorted-variable.c | 385 | ||||
-rw-r--r-- | widgets/table/e-table-sorted-variable.h | 3 | ||||
-rw-r--r-- | widgets/table/e-table-sorted.c | 324 | ||||
-rw-r--r-- | widgets/table/e-table-sorted.h | 17 | ||||
-rw-r--r-- | widgets/table/e-table-sorter.c | 26 | ||||
-rw-r--r-- | widgets/table/e-table-sorter.h | 25 | ||||
-rw-r--r-- | widgets/table/e-table-sorting-utils.c | 372 | ||||
-rw-r--r-- | widgets/table/e-table-sorting-utils.h | 29 | ||||
-rw-r--r-- | widgets/table/e-table-subset-variable.c | 34 | ||||
-rw-r--r-- | widgets/table/e-table-subset-variable.h | 16 | ||||
-rw-r--r-- | widgets/table/e-table-subset.c | 128 | ||||
-rw-r--r-- | widgets/table/e-table-subset.h | 9 | ||||
-rw-r--r-- | widgets/table/e-table.c | 230 | ||||
-rw-r--r-- | widgets/table/e-table.h | 2 |
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), >K_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), >K_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; |