From 41dfcdb070f5c052908ca15bb304c91ae637795c Mon Sep 17 00:00:00 2001 From: Milan Crha Date: Wed, 20 Jan 2010 15:48:31 +0100 Subject: Bug #606301 - Slow sort by subject --- calendar/gui/e-cal-list-view.c | 30 +------- calendar/gui/e-cell-date-edit-text.c | 25 ++++++ calendar/gui/e-cell-date-edit-text.h | 2 + calendar/gui/e-memo-table.c | 31 +------- calendar/gui/e-task-table.c | 115 +++++++++++++++------------- mail/message-list.c | 11 ++- widgets/table/e-table-col.c | 5 +- widgets/table/e-table-col.h | 4 +- widgets/table/e-table-extras.c | 81 ++++++++++++++++++-- widgets/table/e-table-extras.h | 4 +- widgets/table/e-table-group-container.c | 16 +++- widgets/table/e-table-sorter.c | 50 +++++++----- widgets/table/e-table-sorting-utils.c | 131 +++++++++++++++++++++++++++----- widgets/table/e-table-sorting-utils.h | 5 ++ widgets/table/e-table-utils.c | 2 +- 15 files changed, 341 insertions(+), 171 deletions(-) diff --git a/calendar/gui/e-cal-list-view.c b/calendar/gui/e-cal-list-view.c index 1097749ffd..1d3e0e114a 100644 --- a/calendar/gui/e-cal-list-view.c +++ b/calendar/gui/e-cal-list-view.c @@ -101,34 +101,6 @@ e_cal_list_view_class_init (ECalListViewClass *class) view_class->get_visible_time_range = e_cal_list_view_get_visible_time_range; } -static gint -date_compare_cb (gconstpointer a, gconstpointer b) -{ - ECellDateEditValue *dv1 = (ECellDateEditValue *) a; - ECellDateEditValue *dv2 = (ECellDateEditValue *) b; - struct icaltimetype tt; - - /* First check if either is NULL. NULL dates sort last. */ - if (!dv1 || !dv2) { - if (dv1 == dv2) - return 0; - else if (dv1) - return -1; - else - return 1; - } - - /* Copy the 2nd value and convert it to the same timezone as the - first. */ - tt = dv2->tt; - - icaltimezone_convert_time (&tt, dv2->zone, dv1->zone); - - /* Now we can compare them. */ - - return icaltime_compare (dv1->tt, tt); -} - static void e_cal_list_view_init (ECalListView *cal_list_view) { @@ -269,7 +241,7 @@ setup_e_table (ECalListView *cal_list_view) /* Sorting */ e_table_extras_add_compare (extras, "date-compare", - date_compare_cb); + e_cell_date_edit_compare_cb); /* set proper format component for a default 'date' cell renderer */ cell = e_table_extras_get_cell (extras, "date"); diff --git a/calendar/gui/e-cell-date-edit-text.c b/calendar/gui/e-cell-date-edit-text.c index bebec0fcda..37886680e5 100644 --- a/calendar/gui/e-cell-date-edit-text.c +++ b/calendar/gui/e-cell-date-edit-text.c @@ -361,3 +361,28 @@ e_cell_date_edit_text_set_use_24_hour_format (ECellDateEditText *ecd, g_object_notify (G_OBJECT (ecd), "use-24-hour-format"); } +gint +e_cell_date_edit_compare_cb (gconstpointer a, gconstpointer b, gpointer cmp_cache) +{ + ECellDateEditValue *dv1 = (ECellDateEditValue *) a; + ECellDateEditValue *dv2 = (ECellDateEditValue *) b; + struct icaltimetype tt; + + /* First check if either is NULL. NULL dates sort last. */ + if (!dv1 || !dv2) { + if (dv1 == dv2) + return 0; + else if (dv1) + return -1; + else + return 1; + } + + /* Copy the 2nd value and convert it to the same timezone as the first. */ + tt = dv2->tt; + + icaltimezone_convert_time (&tt, dv2->zone, dv1->zone); + + /* Now we can compare them. */ + return icaltime_compare (dv1->tt, tt); +} diff --git a/calendar/gui/e-cell-date-edit-text.h b/calendar/gui/e-cell-date-edit-text.h index a49b68d36b..335374c141 100644 --- a/calendar/gui/e-cell-date-edit-text.h +++ b/calendar/gui/e-cell-date-edit-text.h @@ -81,6 +81,8 @@ void e_cell_date_edit_text_set_use_24_hour_format (ECellDateEditText *ecd, gboolean use_24_hour); +gint e_cell_date_edit_compare_cb (gconstpointer a, gconstpointer b, gpointer cmp_cache); + G_END_DECLS #endif /* _E_CELL_DATE_EDIT_TEXT_H_ */ diff --git a/calendar/gui/e-memo-table.c b/calendar/gui/e-memo-table.c index 11c74820a6..459fff3e15 100644 --- a/calendar/gui/e-memo-table.c +++ b/calendar/gui/e-memo-table.c @@ -164,35 +164,6 @@ memo_table_get_current_time (ECellDateEdit *ecde, return tmp_tm; } -static gint -memo_table_date_compare_cb (gconstpointer a, - gconstpointer b) -{ - ECellDateEditValue *dv1 = (ECellDateEditValue *) a; - ECellDateEditValue *dv2 = (ECellDateEditValue *) b; - struct icaltimetype tt; - - /* First check if either is NULL. NULL dates sort last. */ - if (!dv1 || !dv2) { - if (dv1 == dv2) - return 0; - else if (dv1) - return -1; - else - return 1; - } - - /* Copy the 2nd value and convert it to the same timezone as the - first. */ - tt = dv2->tt; - - icaltimezone_convert_time (&tt, dv2->zone, dv1->zone); - - /* Now we can compare them. */ - - return icaltime_compare (dv1->tt, tt); -} - static void memo_table_model_cal_view_progress_cb (EMemoTable *memo_table, const gchar *message, @@ -420,7 +391,7 @@ memo_table_constructed (GObject *object) /* Sorting */ e_table_extras_add_compare ( - extras, "date-compare", memo_table_date_compare_cb); + extras, "date-compare", e_cell_date_edit_compare_cb); /* Create pixmaps */ diff --git a/calendar/gui/e-task-table.c b/calendar/gui/e-task-table.c index 4a378f2daf..b39af94db0 100644 --- a/calendar/gui/e-task-table.c +++ b/calendar/gui/e-task-table.c @@ -47,6 +47,7 @@ #include #include #include
+#include
#include #include @@ -140,37 +141,7 @@ task_table_emit_user_created (ETaskTable *task_table) } static gint -task_table_date_compare_cb (gconstpointer a, - gconstpointer b) -{ - ECellDateEditValue *dv1 = (ECellDateEditValue *) a; - ECellDateEditValue *dv2 = (ECellDateEditValue *) b; - struct icaltimetype tt; - - /* First check if either is NULL. NULL dates sort last. */ - if (!dv1 || !dv2) { - if (dv1 == dv2) - return 0; - else if (dv1) - return -1; - else - return 1; - } - - /* Copy the 2nd value and convert it to the same timezone as the - first. */ - tt = dv2->tt; - - icaltimezone_convert_time (&tt, dv2->zone, dv1->zone); - - /* Now we can compare them. */ - - return icaltime_compare (dv1->tt, tt); -} - -static gint -task_table_percent_compare_cb (gconstpointer a, - gconstpointer b) +task_table_percent_compare_cb (gconstpointer a, gconstpointer b, gpointer cmp_cache) { gint percent1 = GPOINTER_TO_INT (a); gint percent2 = GPOINTER_TO_INT (b); @@ -179,8 +150,7 @@ task_table_percent_compare_cb (gconstpointer a, } static gint -task_table_priority_compare_cb (gconstpointer a, - gconstpointer b) +task_table_priority_compare_cb (gconstpointer a, gconstpointer b, gpointer cmp_cache) { gint priority1, priority2; @@ -197,9 +167,42 @@ task_table_priority_compare_cb (gconstpointer a, return (priority1 < priority2) ? -1 : (priority1 > priority2); } +static const gchar * +get_cache_str (gpointer cmp_cache, const gchar *str) +{ + const gchar *value; + + if (!cmp_cache || !str) + return str; + + value = e_table_sorting_utils_lookup_cmp_cache (cmp_cache, str); + if (!value) { + gchar *ckey; + + ckey = g_utf8_collate_key (str, -1); + e_table_sorting_utils_add_to_cmp_cache (cmp_cache, (gchar *) str, ckey); + value = ckey; + } + + return value; +} + +static gboolean +same_cache_string (gpointer cmp_cache, const gchar *str_a, const gchar *str_b) +{ + if (!cmp_cache) + return g_utf8_collate (str_a, str_b) == 0; + + str_b = get_cache_str (cmp_cache, str_b); + + g_return_val_if_fail (str_a != NULL, FALSE); + g_return_val_if_fail (str_b != NULL, FALSE); + + return strcmp (str_a, str_b) == 0; +} + static gint -task_table_status_compare_cb (gconstpointer a, - gconstpointer b) +task_table_status_compare_cb (gconstpointer a, gconstpointer b, gpointer cmp_cache) { const gchar *string_a = a; const gchar *string_b = b; @@ -208,25 +211,33 @@ task_table_status_compare_cb (gconstpointer a, if (string_a == NULL || *string_a == '\0') status_a = -1; - else if (!g_utf8_collate (string_a, _("Not Started"))) - status_a = 0; - else if (!g_utf8_collate (string_a, _("In Progress"))) - status_a = 1; - else if (!g_utf8_collate (string_a, _("Completed"))) - status_a = 2; - else if (!g_utf8_collate (string_a, _("Canceled"))) - status_a = 3; + else { + const gchar *cache_str = get_cache_str (cmp_cache, string_a); + + if (same_cache_string (cmp_cache, cache_str, _("Not Started"))) + status_a = 0; + else if (same_cache_string (cmp_cache, cache_str, _("In Progress"))) + status_a = 1; + else if (same_cache_string (cmp_cache, cache_str, _("Completed"))) + status_a = 2; + else if (same_cache_string (cmp_cache, cache_str, _("Canceled"))) + status_a = 3; + } if (string_b == NULL || *string_b == '\0') status_b = -1; - else if (!g_utf8_collate (string_b, _("Not Started"))) - status_b = 0; - else if (!g_utf8_collate (string_b, _("In Progress"))) - status_b = 1; - else if (!g_utf8_collate (string_b, _("Completed"))) - status_b = 2; - else if (!g_utf8_collate (string_b, _("Canceled"))) - status_b = 3; + else { + const gchar *cache_str = get_cache_str (cmp_cache, string_b); + + if (same_cache_string (cmp_cache, cache_str, _("Not Started"))) + status_b = 0; + else if (same_cache_string (cmp_cache, cache_str, _("In Progress"))) + status_b = 1; + else if (same_cache_string (cmp_cache, cache_str, _("Completed"))) + status_b = 2; + else if (same_cache_string (cmp_cache, cache_str, _("Canceled"))) + status_b = 3; + } return (status_a < status_b) ? -1 : (status_a > status_b); } @@ -589,7 +600,7 @@ task_table_constructed (GObject *object) e_table_extras_add_cell (extras, "calstatus", popup_cell); e_table_extras_add_compare (extras, "date-compare", - task_table_date_compare_cb); + e_cell_date_edit_compare_cb); e_table_extras_add_compare (extras, "percent-compare", task_table_percent_compare_cb); e_table_extras_add_compare (extras, "priority-compare", diff --git a/mail/message-list.c b/mail/message-list.c index a512bcec57..bb75cf1370 100644 --- a/mail/message-list.c +++ b/mail/message-list.c @@ -64,10 +64,9 @@ #include "table/e-cell-toggle.h" #include "table/e-cell-tree.h" #include "table/e-cell-vbox.h" +#include "table/e-table-sorting-utils.h" #include "table/e-tree-memory-callbacks.h" #include "table/e-tree-memory.h" -#include "table/e-cell-vbox.h" -#include "table/e-cell-hbox.h" #include "e-mail-label-list-store.h" #include "em-utils.h" @@ -366,7 +365,7 @@ e_mail_address_compare (gconstpointer address1, gconstpointer address2) #endif /* SMART_ADDRESS_COMPARE */ static gint -address_compare (gconstpointer address1, gconstpointer address2) +address_compare (gconstpointer address1, gconstpointer address2, gpointer cmp_cache) { #ifdef SMART_ADDRESS_COMPARE EMailAddress *addr1, *addr2; @@ -4208,6 +4207,7 @@ struct sort_array_data { MessageList *ml; GPtrArray *sort_columns; /* struct sort_column_data in order of sorting */ GHashTable *message_infos; /* uid -> struct sort_message_info_data */ + gpointer cmp_cache; }; static gint @@ -4248,7 +4248,7 @@ cmp_array_uids (gconstpointer a, gconstpointer b, gpointer user_data) } if (v1 != NULL && v2 != NULL) { - res = (*scol->col->compare) (v1, v2); + res = (*scol->col->compare) (v1, v2, sort_data->cmp_cache); } else if (v1 != NULL || v2 != NULL) { res = v1 == NULL ? -1 : 1; } @@ -4318,6 +4318,7 @@ ml_sort_uids_by_tree (MessageList *ml, GPtrArray *uids) sort_data.ml = ml; sort_data.sort_columns = g_ptr_array_sized_new (len); sort_data.message_infos = g_hash_table_new (g_str_hash, g_str_equal); + sort_data.cmp_cache = e_table_sorting_utils_create_cmp_cache (); for (i = 0; i < len; i++) { ETableSortColumn scol; @@ -4354,6 +4355,8 @@ ml_sort_uids_by_tree (MessageList *ml, GPtrArray *uids) g_ptr_array_foreach (sort_data.sort_columns, (GFunc) g_free, NULL); g_ptr_array_free (sort_data.sort_columns, TRUE); + + e_table_sorting_utils_free_cmp_cache (sort_data.cmp_cache); } /* ** REGENERATE MESSAGELIST ********************************************** */ diff --git a/widgets/table/e-table-col.c b/widgets/table/e-table-col.c index 3befb53bb4..4e9802d9b8 100644 --- a/widgets/table/e-table-col.c +++ b/widgets/table/e-table-col.c @@ -160,6 +160,9 @@ e_table_col_init (ETableCol *etc) * The @ecell argument is an ECell object that needs to know how to render the * data in the ETableModel for this specific row. * + * Data passed to @compare can be (if not %NULL) a cmp_cache, which can be accessed + * by @ref e_table_sorting_utils_add_to_cmp_cache and @ref e_table_sorting_utils_lookup_cmp_cache. + * * Returns: the newly created ETableCol object. */ ETableCol * @@ -169,7 +172,7 @@ e_table_col_new (gint col_idx, double expansion, gint min_width, ECell *ecell, - GCompareFunc compare, + GCompareDataFunc compare, gboolean resizable, gboolean disabled, gint priority) diff --git a/widgets/table/e-table-col.h b/widgets/table/e-table-col.h index 0a3add445a..7b7c31db7c 100644 --- a/widgets/table/e-table-col.h +++ b/widgets/table/e-table-col.h @@ -70,7 +70,7 @@ struct _ETableCol { gint width; gdouble expansion; gshort x; - GCompareFunc compare; + GCompareDataFunc compare; ETableSearchFunc search; guint selected:1; @@ -99,7 +99,7 @@ ETableCol * e_table_col_new (gint col_idx, double expansion, gint min_width, ECell *ecell, - GCompareFunc compare, + GCompareDataFunc compare, gboolean resizable, gboolean disabled, gint priority); diff --git a/widgets/table/e-table-extras.c b/widgets/table/e-table-extras.c index eee1af9014..101e3e426b 100644 --- a/widgets/table/e-table-extras.c +++ b/widgets/table/e-table-extras.c @@ -39,6 +39,7 @@ #include "e-cell-text.h" #include "e-cell-tree.h" #include "e-table-extras.h" +#include "e-table-sorting-utils.h" #define E_TABLE_EXTRAS_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE \ @@ -155,6 +156,72 @@ e_string_search (gconstpointer haystack, return FALSE; } +static gint +e_table_str_case_compare (gconstpointer x, gconstpointer y, gpointer cmp_cache) +{ + const gchar *cx = NULL, *cy = NULL; + + if (!cmp_cache) + return e_str_case_compare (x, y); + + if (x == NULL || y == NULL) { + if (x == y) + return 0; + else + return x ? -1 : 1; + } + + #define prepare_value(_z, _cz) \ + _cz = e_table_sorting_utils_lookup_cmp_cache (cmp_cache, _z); \ + if (!_cz) { \ + gchar *tmp = g_utf8_casefold (_z, -1); \ + _cz = g_utf8_collate_key (tmp, -1); \ + g_free (tmp); \ + \ + e_table_sorting_utils_add_to_cmp_cache ( \ + cmp_cache, _z, (gchar *) _cz); \ + } + + prepare_value (x, cx); + prepare_value (y, cy); + + #undef prepare_value + + return strcmp (cx, cy); +} + +static gint +e_table_collate_compare (gconstpointer x, gconstpointer y, gpointer cmp_cache) +{ + const gchar *cx = NULL, *cy = NULL; + + if (!cmp_cache) + return e_collate_compare (x, y); + + if (x == NULL || y == NULL) { + if (x == y) + return 0; + else + return x ? -1 : 1; + } + + #define prepare_value(_z, _cz) \ + _cz = e_table_sorting_utils_lookup_cmp_cache (cmp_cache, _z); \ + if (!_cz) { \ + _cz = g_utf8_collate_key (_z, -1); \ + \ + e_table_sorting_utils_add_to_cmp_cache ( \ + cmp_cache, _z, (gchar *) _cz); \ + } + + prepare_value (x, cx); + prepare_value (y, cy); + + #undef prepare_value + + return strcmp (cx, cy); +} + static void safe_unref (gpointer object) { @@ -189,11 +256,11 @@ ete_init (ETableExtras *extras) (GDestroyNotify) g_free, (GDestroyNotify) NULL); - e_table_extras_add_compare(extras, "string", e_str_compare); - e_table_extras_add_compare(extras, "stringcase", e_str_case_compare); - e_table_extras_add_compare(extras, "collate", e_collate_compare); - e_table_extras_add_compare(extras, "integer", e_int_compare); - e_table_extras_add_compare(extras, "string-integer", e_strint_compare); + e_table_extras_add_compare(extras, "string", (GCompareDataFunc) e_str_compare); + e_table_extras_add_compare(extras, "stringcase", e_table_str_case_compare); + e_table_extras_add_compare(extras, "collate", e_table_collate_compare); + e_table_extras_add_compare(extras, "integer", (GCompareDataFunc) e_int_compare); + e_table_extras_add_compare(extras, "string-integer", (GCompareDataFunc) e_strint_compare); e_table_extras_add_search(extras, "string", e_string_search); @@ -253,7 +320,7 @@ e_table_extras_get_cell (ETableExtras *extras, void e_table_extras_add_compare (ETableExtras *extras, const gchar *id, - GCompareFunc compare) + GCompareDataFunc compare) { g_return_if_fail (E_IS_TABLE_EXTRAS (extras)); g_return_if_fail (id != NULL); @@ -263,7 +330,7 @@ e_table_extras_add_compare (ETableExtras *extras, g_strdup (id), (gpointer) compare); } -GCompareFunc +GCompareDataFunc e_table_extras_get_compare (ETableExtras *extras, const gchar *id) { diff --git a/widgets/table/e-table-extras.h b/widgets/table/e-table-extras.h index 1f4488b6de..55af6a14d0 100644 --- a/widgets/table/e-table-extras.h +++ b/widgets/table/e-table-extras.h @@ -69,8 +69,8 @@ ECell * e_table_extras_get_cell (ETableExtras *extras, const gchar *id); void e_table_extras_add_compare (ETableExtras *extras, const gchar *id, - GCompareFunc compare); -GCompareFunc e_table_extras_get_compare (ETableExtras *extras, + GCompareDataFunc compare); +GCompareDataFunc e_table_extras_get_compare (ETableExtras *extras, const gchar *id); void e_table_extras_add_search (ETableExtras *extras, const gchar *id, diff --git a/widgets/table/e-table-group-container.c b/widgets/table/e-table-group-container.c index 2552cb6789..26124b824a 100644 --- a/widgets/table/e-table-group-container.c +++ b/widgets/table/e-table-group-container.c @@ -37,6 +37,7 @@ #include "e-table-group-container.h" #include "e-table-group-leaf.h" #include "e-table-item.h" +#include "e-table-sorting-utils.h" #define TITLE_HEIGHT 16 @@ -465,7 +466,8 @@ etgc_add (ETableGroup *etg, gint row) { ETableGroupContainer *etgc = E_TABLE_GROUP_CONTAINER (etg); gpointer val = e_table_model_value_at (etg->model, etgc->ecol->col_idx, row); - GCompareFunc comp = etgc->ecol->compare; + GCompareDataFunc comp = etgc->ecol->compare; + gpointer cmp_cache = e_table_sorting_utils_create_cmp_cache (); GList *list = etgc->children; ETableGroup *child; ETableGroupContainerChildNode *child_node; @@ -475,8 +477,9 @@ etgc_add (ETableGroup *etg, gint row) gint comp_val; child_node = list->data; - comp_val = (*comp)(child_node->key, val); + comp_val = (*comp)(child_node->key, val, cmp_cache); if (comp_val == 0) { + e_table_sorting_utils_free_cmp_cache (cmp_cache); child = child_node->child; child_node->count ++; e_table_group_add (child, row); @@ -487,6 +490,7 @@ etgc_add (ETableGroup *etg, gint row) (comp_val < 0 && (!etgc->ascending))) break; } + e_table_sorting_utils_free_cmp_cache (cmp_cache); child_node = create_child_node (etgc, val); child = child_node->child; child_node->count = 1; @@ -508,7 +512,8 @@ etgc_add_array (ETableGroup *etg, const gint *array, gint count) ETableGroupContainer *etgc = E_TABLE_GROUP_CONTAINER (etg); gpointer lastval = NULL; gint laststart = 0; - GCompareFunc comp = etgc->ecol->compare; + GCompareDataFunc comp = etgc->ecol->compare; + gpointer cmp_cache; ETableGroupContainerChildNode *child_node; ETableGroup *child; @@ -517,6 +522,7 @@ etgc_add_array (ETableGroup *etg, const gint *array, gint count) e_table_group_container_list_free (etgc); etgc->children = NULL; + cmp_cache = e_table_sorting_utils_create_cmp_cache (); lastval = e_table_model_value_at (etg->model, etgc->ecol->col_idx, array[0]); @@ -524,7 +530,7 @@ etgc_add_array (ETableGroup *etg, const gint *array, gint count) gpointer val = e_table_model_value_at (etg->model, etgc->ecol->col_idx, array[i]); gint comp_val; - comp_val = (*comp)(lastval, val); + comp_val = (*comp)(lastval, val, cmp_cache); if (comp_val != 0) { child_node = create_child_node(etgc, lastval); child = child_node->child; @@ -539,6 +545,8 @@ etgc_add_array (ETableGroup *etg, const gint *array, gint count) } } + e_table_sorting_utils_free_cmp_cache (cmp_cache); + child_node = create_child_node(etgc, lastval); child = child_node->child; diff --git a/widgets/table/e-table-sorter.c b/widgets/table/e-table-sorter.c index 2124ea4019..82ed1d860a 100644 --- a/widgets/table/e-table-sorter.c +++ b/widgets/table/e-table-sorter.c @@ -29,6 +29,7 @@ #include "e-util/e-util.h" #include "e-table-sorter.h" +#include "e-table-sorting-utils.h" #define d(x) @@ -260,26 +261,30 @@ ets_sort_info_changed (ETableSortInfo *info, ETableSorter *ets) ets_clean(ets); } -static ETableSorter *ets_closure; -static gpointer *vals_closure; -static gint cols_closure; -static gint *ascending_closure; -static GCompareFunc *compare_closure; +struct qsort_data { + ETableSorter *ets; + gpointer *vals; + gint cols; + gint *ascending; + GCompareDataFunc *compare; + gpointer cmp_cache; +}; /* FIXME: Make it not cache the second and later columns (as if anyone cares.) */ static gint -qsort_callback(gconstpointer data1, gconstpointer data2) +qsort_callback(gconstpointer data1, gconstpointer data2, gpointer user_data) { + struct qsort_data *qd = (struct qsort_data *) user_data; gint row1 = *(gint *)data1; gint row2 = *(gint *)data2; gint j; - gint sort_count = e_table_sort_info_sorting_get_count(ets_closure->sort_info) + e_table_sort_info_grouping_get_count(ets_closure->sort_info); + gint sort_count = e_table_sort_info_sorting_get_count (qd->ets->sort_info) + e_table_sort_info_grouping_get_count (qd->ets->sort_info); gint comp_val = 0; gint 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]; + comp_val = (*(qd->compare[j]))(qd->vals[qd->cols * row1 + j], qd->vals[qd->cols * row2 + j], qd->cmp_cache); + ascending = qd->ascending[j]; if (comp_val != 0) break; } @@ -314,6 +319,7 @@ ets_sort(ETableSorter *ets) gint j; gint cols; gint group_cols; + struct qsort_data qd; if (ets->sorted) return; @@ -326,12 +332,13 @@ ets_sort(ETableSorter *ets) for (i = 0; i < rows; i++) ets->sorted[i] = i; - cols_closure = cols; - ets_closure = ets; + qd.cols = cols; + qd.ets = ets; - vals_closure = g_new(gpointer , rows * cols); - ascending_closure = g_new(int, cols); - compare_closure = g_new(GCompareFunc, cols); + qd.vals = g_new (gpointer , rows * cols); + qd.ascending = g_new (int, cols); + qd.compare = g_new (GCompareDataFunc, cols); + qd.cmp_cache = e_table_sorting_utils_create_cmp_cache (); for (j = 0; j < cols; j++) { ETableSortColumn column; @@ -347,18 +354,19 @@ ets_sort(ETableSorter *ets) col = e_table_header_get_column (ets->full_header, e_table_header_count (ets->full_header) - 1); for (i = 0; i < rows; i++) { - vals_closure[i * cols + j] = e_table_model_value_at (ets->source, col->col_idx, i); + qd.vals[i * cols + j] = e_table_model_value_at (ets->source, col->col_idx, i); } - compare_closure[j] = col->compare; - ascending_closure[j] = column.ascending; + qd.compare[j] = col->compare; + qd.ascending[j] = column.ascending; } - qsort(ets->sorted, rows, sizeof(gint), qsort_callback); + g_qsort_with_data (ets->sorted, rows, sizeof(gint), qsort_callback, &qd); - g_free(vals_closure); - g_free(ascending_closure); - g_free(compare_closure); + g_free (qd.vals); + g_free (qd.ascending); + g_free (qd.compare); + e_table_sorting_utils_free_cmp_cache (qd.cmp_cache); } static void diff --git a/widgets/table/e-table-sorting-utils.c b/widgets/table/e-table-sorting-utils.c index 344a7cf6b1..8ad797a9d1 100644 --- a/widgets/table/e-table-sorting-utils.c +++ b/widgets/table/e-table-sorting-utils.c @@ -24,6 +24,8 @@ #include +#include + #include "e-util/e-util.h" #include "e-table-sorting-utils.h" @@ -32,7 +34,7 @@ /* This takes source rows. */ static gint -etsu_compare(ETableModel *source, ETableSortInfo *sort_info, ETableHeader *full_header, gint row1, gint row2) +etsu_compare(ETableModel *source, ETableSortInfo *sort_info, ETableHeader *full_header, gint row1, gint row2, gpointer cmp_cache) { gint j; gint sort_count = e_table_sort_info_sorting_get_count(sort_info); @@ -46,7 +48,8 @@ etsu_compare(ETableModel *source, ETableSortInfo *sort_info, ETableHeader *full_ if (col == NULL) col = e_table_header_get_column (full_header, e_table_header_count (full_header) - 1); comp_val = (*col->compare)(e_table_model_value_at (source, col->compare_col, row1), - e_table_model_value_at (source, col->compare_col, row2)); + e_table_model_value_at (source, col->compare_col, row2), + cmp_cache); ascending = column.ascending; if (comp_val != 0) break; @@ -66,13 +69,15 @@ typedef struct { gint cols; gpointer *vals; gint *ascending; - GCompareFunc *compare; + GCompareDataFunc *compare; + gpointer cmp_cache; } ETableSortClosure; typedef struct { ETreeModel *tree; ETableSortInfo *sort_info; ETableHeader *full_header; + gpointer cmp_cache; } ETreeSortClosure; /* FIXME: Make it not cache the second and later columns (as if anyone cares.) */ @@ -88,7 +93,7 @@ e_sort_callback(gconstpointer data1, gconstpointer data2, gpointer user_data) gint comp_val = 0; gint ascending = 1; for (j = 0; j < sort_count; j++) { - comp_val = (*(closure->compare[j]))(closure->vals[closure->cols * row1 + j], closure->vals[closure->cols * row2 + j]); + comp_val = (*(closure->compare[j]))(closure->vals[closure->cols * row1 + j], closure->vals[closure->cols * row2 + j], closure->cmp_cache); ascending = closure->ascending[j]; if (comp_val != 0) break; @@ -126,7 +131,8 @@ e_table_sorting_utils_sort(ETableModel *source, ETableSortInfo *sort_info, ETabl closure.vals = g_new(gpointer , total_rows * cols); closure.ascending = g_new(int, cols); - closure.compare = g_new(GCompareFunc, cols); + closure.compare = g_new(GCompareDataFunc, cols); + closure.cmp_cache = e_table_sorting_utils_create_cmp_cache (); for (j = 0; j < cols; j++) { ETableSortColumn column = e_table_sort_info_sorting_get_nth(sort_info, j); @@ -147,6 +153,7 @@ e_table_sorting_utils_sort(ETableModel *source, ETableSortInfo *sort_info, ETabl g_free(closure.vals); g_free(closure.ascending); g_free(closure.compare); + e_table_sorting_utils_free_cmp_cache (closure.cmp_cache); } gboolean @@ -181,12 +188,15 @@ gint e_table_sorting_utils_insert(ETableModel *source, ETableSortInfo *sort_info, ETableHeader *full_header, gint *map_table, gint rows, gint row) { gint i; + gpointer cmp_cache = e_table_sorting_utils_create_cmp_cache (); i = 0; /* handle insertions when we have a 'sort group' */ - while (i < rows && etsu_compare(source, sort_info, full_header, map_table[i], row) < 0) + while (i < rows && etsu_compare(source, sort_info, full_header, map_table[i], row, cmp_cache) < 0) i++; + e_table_sorting_utils_free_cmp_cache (cmp_cache); + return i; } @@ -196,26 +206,31 @@ e_table_sorting_utils_check_position (ETableModel *source, ETableSortInfo *sort_ { gint i; gint row; + gpointer cmp_cache; i = view_row; row = map_table[i]; + cmp_cache = e_table_sorting_utils_create_cmp_cache (); i = view_row; - if (i < rows - 1 && etsu_compare(source, sort_info, full_header, map_table[i + 1], row) < 0) { + if (i < rows - 1 && etsu_compare(source, sort_info, full_header, map_table[i + 1], row, cmp_cache) < 0) { i ++; - while (i < rows - 1 && etsu_compare(source, sort_info, full_header, map_table[i], row) < 0) + while (i < rows - 1 && etsu_compare(source, sort_info, full_header, map_table[i], row, cmp_cache) < 0) i ++; - } else if (i > 0 && etsu_compare(source, sort_info, full_header, map_table[i - 1], row) > 0) { + } else if (i > 0 && etsu_compare(source, sort_info, full_header, map_table[i - 1], row, cmp_cache) > 0) { i --; - while (i > 0 && etsu_compare(source, sort_info, full_header, map_table[i], row) > 0) + while (i > 0 && etsu_compare(source, sort_info, full_header, map_table[i], row, cmp_cache) > 0) i --; } + + e_table_sorting_utils_free_cmp_cache (cmp_cache); + return i; } /* This takes source rows. */ static gint -etsu_tree_compare(ETreeModel *source, ETableSortInfo *sort_info, ETableHeader *full_header, ETreePath path1, ETreePath path2) +etsu_tree_compare(ETreeModel *source, ETableSortInfo *sort_info, ETableHeader *full_header, ETreePath path1, ETreePath path2, gpointer cmp_cache) { gint j; gint sort_count = e_table_sort_info_sorting_get_count(sort_info); @@ -229,7 +244,8 @@ etsu_tree_compare(ETreeModel *source, ETableSortInfo *sort_info, ETableHeader *f if (col == NULL) col = e_table_header_get_column (full_header, e_table_header_count (full_header) - 1); comp_val = (*col->compare)(e_tree_model_value_at (source, path1, col->compare_col), - e_tree_model_value_at (source, path2, col->compare_col)); + e_tree_model_value_at (source, path2, col->compare_col), + cmp_cache); ascending = column.ascending; if (comp_val != 0) break; @@ -246,7 +262,7 @@ e_sort_tree_callback(gconstpointer data1, gconstpointer data2, gpointer user_dat ETreePath *path2 = *(ETreePath *)data2; ETreeSortClosure *closure = user_data; - return etsu_tree_compare(closure->tree, closure->sort_info, closure->full_header, path1, path2); + return etsu_tree_compare (closure->tree, closure->sort_info, closure->full_header, path1, path2, closure->cmp_cache); } void @@ -269,7 +285,8 @@ e_table_sorting_utils_tree_sort(ETreeModel *source, ETableSortInfo *sort_info, E closure.vals = g_new(gpointer , count * cols); closure.ascending = g_new(int, cols); - closure.compare = g_new(GCompareFunc, cols); + closure.compare = g_new (GCompareDataFunc, cols); + closure.cmp_cache = e_table_sorting_utils_create_cmp_cache (); for (j = 0; j < cols; j++) { ETableSortColumn column = e_table_sort_info_sorting_get_nth(sort_info, j); @@ -308,6 +325,7 @@ e_table_sorting_utils_tree_sort(ETreeModel *source, ETableSortInfo *sort_info, E g_free(closure.vals); g_free(closure.ascending); g_free(closure.compare); + e_table_sorting_utils_free_cmp_cache (closure.cmp_cache); } /* FIXME: This could be done in time log n instead of time n with a binary search. */ @@ -316,19 +334,23 @@ e_table_sorting_utils_tree_check_position (ETreeModel *source, ETableSortInfo *s { gint i; ETreePath path; + gpointer cmp_cache = e_table_sorting_utils_create_cmp_cache (); i = old_index; path = map_table[i]; - if (i < count - 1 && etsu_tree_compare(source, sort_info, full_header, map_table[i + 1], path) < 0) { + if (i < count - 1 && etsu_tree_compare(source, sort_info, full_header, map_table[i + 1], path, cmp_cache) < 0) { i ++; - while (i < count - 1 && etsu_tree_compare(source, sort_info, full_header, map_table[i], path) < 0) + while (i < count - 1 && etsu_tree_compare(source, sort_info, full_header, map_table[i], path, cmp_cache) < 0) i ++; - } else if (i > 0 && etsu_tree_compare(source, sort_info, full_header, map_table[i - 1], path) > 0) { + } else if (i > 0 && etsu_tree_compare(source, sort_info, full_header, map_table[i - 1], path, cmp_cache) > 0) { i --; - while (i > 0 && etsu_tree_compare(source, sort_info, full_header, map_table[i], path) > 0) + while (i > 0 && etsu_tree_compare(source, sort_info, full_header, map_table[i], path, cmp_cache) > 0) i --; } + + e_table_sorting_utils_free_cmp_cache (cmp_cache); + return i; } @@ -343,7 +365,80 @@ e_table_sorting_utils_tree_insert(ETreeModel *source, ETableSortInfo *sort_info, closure.tree = source; closure.sort_info = sort_info; closure.full_header = full_header; + closure.cmp_cache = e_table_sorting_utils_create_cmp_cache (); e_bsearch(&path, map_table, count, sizeof(ETreePath), e_sort_tree_callback, &closure, &start, &end); + + e_table_sorting_utils_free_cmp_cache (closure.cmp_cache); + return end; } + +/** + * e_table_sorting_utils_create_cmp_cache: + * + * Creates a new compare cache, which is storing pairs of string keys and string values. + * This can be accessed by @ref e_table_sorting_utils_lookup_cmp_cache and + * @ref e_table_sorting_utils_add_to_cmp_cache. + * + * Returned pointer should be freed with @ref e_table_sorting_utils_free_cmp_cache. + **/ +gpointer +e_table_sorting_utils_create_cmp_cache (void) +{ + return g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify) camel_pstring_free, g_free); +} + +/** + * e_table_sorting_utils_free_cmp_cache: + * @cmp_cache: a compare cache; cannot be %NULL + * + * Frees a compare cache previously created + * with @ref e_table_sorting_utils_create_cmp_cache. + **/ +void +e_table_sorting_utils_free_cmp_cache (gpointer cmp_cache) +{ + g_return_if_fail (cmp_cache != NULL); + + g_hash_table_destroy (cmp_cache); +} + +/** + * e_table_sorting_utils_add_to_cmp_cache: + * @cmp_cache: a compare cache; cannot be %NULL + * @key: unique key to a cache; cannot be %NULL + * @value: value to store for a key + * + * Adds a new value for a given key to a compare cache. If such key + * already exists in a cache then its value will be replaced. + * Note: Given @value will be stolen and later freed with g_free. + **/ +void +e_table_sorting_utils_add_to_cmp_cache (gpointer cmp_cache, const gchar *key, gchar *value) +{ + g_return_if_fail (cmp_cache != NULL); + g_return_if_fail (key != NULL); + + g_hash_table_insert (cmp_cache, (gchar *) camel_pstring_strdup (key), value); +} + +/** + * e_table_sorting_utils_lookup_cmp_cache: + * @cmp_cache: a compare cache + * @key: unique key to a cache + * + * Lookups for a key in a compare cache, which is passed in GCompareDataFunc as 'data'. + * Returns %NULL when not found or the cache wasn't provided, otherwise value stored + * with a key. + **/ +const gchar * +e_table_sorting_utils_lookup_cmp_cache (gpointer cmp_cache, const gchar *key) +{ + g_return_val_if_fail (key != NULL, NULL); + + if (!cmp_cache) + return NULL; + + return g_hash_table_lookup (cmp_cache, key); +} diff --git a/widgets/table/e-table-sorting-utils.h b/widgets/table/e-table-sorting-utils.h index 318c331e53..3c60df1838 100644 --- a/widgets/table/e-table-sorting-utils.h +++ b/widgets/table/e-table-sorting-utils.h @@ -69,6 +69,11 @@ gint e_table_sorting_utils_tree_insert (ETreeModel *source, gint count, ETreePath path); +gpointer e_table_sorting_utils_create_cmp_cache (void); +void e_table_sorting_utils_free_cmp_cache (gpointer cmp_cache); +void e_table_sorting_utils_add_to_cmp_cache (gpointer cmp_cache, const gchar *key, gchar *value); +const gchar *e_table_sorting_utils_lookup_cmp_cache (gpointer cmp_cache, const gchar *key); + G_END_DECLS #endif /* _E_TABLE_SORTING_UTILS_H_ */ diff --git a/widgets/table/e-table-utils.c b/widgets/table/e-table-utils.c index 8d8b0ba09f..e54317ec8b 100644 --- a/widgets/table/e-table-utils.c +++ b/widgets/table/e-table-utils.c @@ -79,7 +79,7 @@ et_col_spec_to_col (ETableColumnSpecification *col_spec, { ETableCol *col = NULL; ECell *cell = NULL; - GCompareFunc compare = NULL; + GCompareDataFunc compare = NULL; ETableSearchFunc search = NULL; if (col_spec->cell) -- cgit