[evolution] Bug #606301 - Slow sort by subject



commit 41dfcdb070f5c052908ca15bb304c91ae637795c
Author: Milan Crha <mcrha redhat com>
Date:   Wed Jan 20 15:48:31 2010 +0100

    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 1097749..1d3e0e1 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 bebec0f..3788668 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 a49b68d..335374c 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 11c7482..459fff3 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 4a378f2..b39af94 100644
--- a/calendar/gui/e-task-table.c
+++ b/calendar/gui/e-task-table.c
@@ -47,6 +47,7 @@
 #include <e-util/e-util-private.h>
 #include <table/e-cell-date-edit.h>
 #include <table/e-cell-percent.h>
+#include <table/e-table-sorting-utils.h>
 #include <libecal/e-cal-time-util.h>
 #include <libedataserver/e-time-utils.h>
 
@@ -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 a512bce..bb75cf1 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 3befb53..4e9802d 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 0a3add4..7b7c31d 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 eee1af9..101e3e4 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 1f4488b..55af6a1 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 2552cb6..26124b8 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 2124ea4..82ed1d8 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 344a7cf..8ad797a 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 318c331..3c60df1 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 8d8b0ba..e54317e 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)



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]