aboutsummaryrefslogtreecommitdiffstats
path: root/widgets
diff options
context:
space:
mode:
Diffstat (limited to 'widgets')
-rw-r--r--widgets/table/e-table-col.c5
-rw-r--r--widgets/table/e-table-col.h4
-rw-r--r--widgets/table/e-table-extras.c81
-rw-r--r--widgets/table/e-table-extras.h4
-rw-r--r--widgets/table/e-table-group-container.c16
-rw-r--r--widgets/table/e-table-sorter.c50
-rw-r--r--widgets/table/e-table-sorting-utils.c131
-rw-r--r--widgets/table/e-table-sorting-utils.h5
-rw-r--r--widgets/table/e-table-utils.c2
9 files changed, 242 insertions, 56 deletions
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 <string.h>
+#include <camel/camel-string-utils.h>
+
#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)