Hey everyone,
So with the introduction of grouped downloads in gtk1 some of you may
have noticed that column sorting performance has become abysmal. This
is due to the necessary move to ctree's from clist's. The attached
patch, while not giving us "instant" sorting results, goes a long way to
making column sorting bearable again.
(Gory details for those interested)
My approach was two pronged. First of all I added an analytical step to
determine what the data we were sorting was like. For example, an
extremely common situation is for a user to reverse the order of an
already sorted column (to sort any column descending you have to first
sort ascending). Well, the column is already sorted to reverse it
should only be O(n) complexity but if we just blindly apply a generic
sorting algorithm we are going to do much worse than that. So this code
looks at the first 50 or so items (if present) and makes a guess about
the way the data is sorted and sends it off to the appropriate sorting
algorithm.
I tried to make the structure easy to extend/modify in the event folks
come up with new cases or optimizations to the sort algorithms.
For example, I think we need to figure out what to do for the info and
count columns. They are unique because they have a limited number of
different values with mondo repetition. I'm using an insertion sort
right now which works ok for count but only marginally ok for info.
Quicksort (which I use for most cases) performs dismally on data with a
high amount of repetition as does the default gtk sorting mechanism.
Secondly: After optmizing the actual sorting this I found that the
sorting was efficient enough but actually modifing the ctree wasn't.
This is because the internal gtk functions were O(n*n) at least.
Luckily there was unneeded complexity here so I hacked up a streamlined
version of these functions for our use.
Anyway, seems useable to me now where before the delays were simply too
great to be reasonable.
Emile
? gtk-gnutella-current/.config
? gtk-gnutella-current/Makefile
? gtk-gnutella-current/config.h
? gtk-gnutella-current/config.sh
? gtk-gnutella-current/gtk-gnutella.spec
? gtk-gnutella-current/install
? gtk-gnutella-current/mkdep
? gtk-gnutella-current/pixmaps/Makefile
? gtk-gnutella-current/po/Makefile
? gtk-gnutella-current/po/es.gmo
? gtk-gnutella-current/po/fr.gmo
? gtk-gnutella-current/po/hu.gmo
? gtk-gnutella-current/po/nl.gmo
? gtk-gnutella-current/po/stamp-po
? gtk-gnutella-current/src/Makefile
? gtk-gnutella-current/src/getdate.c
? gtk-gnutella-current/src/gtk-gnutella
? gtk-gnutella-current/src/results.txt
? gtk-gnutella-current/src/search_guiback.c
Index: gtk-gnutella-current/src/search_gui.c
===================================================================
RCS file: /cvsroot/gtk-gnutella/gtk-gnutella-current/src/search_gui.c,v
retrieving revision 1.76
diff -u -r1.76 search_gui.c
--- gtk-gnutella-current/src/search_gui.c 10 Jan 2004 20:15:17 -0000 1.76
+++ gtk-gnutella-current/src/search_gui.c 11 Jan 2004 08:51:02 -0000
@@ -58,6 +58,17 @@
search_t *search_selected = NULL;
static GList *list_search_history = NULL;
+/* Characteristics of data in search results columns, used for sorting */
+enum {
+ SEARCH_COL_SORT_DATA_RANDOM = 0, /* Randomly distributed */
+ SEARCH_COL_SORT_DATA_SORTED_ASC, /* Already sorted or almost sorted */
+ SEARCH_COL_SORT_DATA_SORTED_DES, /* Same as above but descending */
+ SEARCH_COL_SORT_DATA_COUNT, /* Sorting by "count" column */
+ SEARCH_COL_SORT_DATA_INFO, /* Sorting by Info column */
+ SEARCH_COL_SORT_DATA_DEFAULT /* Catch all case */
+};
+
+
/*
* Private function prototypes
*/
@@ -166,6 +177,211 @@
/*
+ * search_gui_fast_ctree_unlink
+ *
+ * Functions like gtk_ctree_unlink for *Top level parent nodes only*. O(1)
+ */
+static void search_gui_fast_ctree_unlink (GtkCTree *ctree, GtkCTreeNode *node)
+{
+ GtkCList *clist;
+ gint rows;
+ gint level;
+ gint visible;
+ GtkCTreeNode *work, *prev;
+ GtkCTreeRow *prev_row;
+ GtkCTreeNode *sibling;
+ GList *list;
+
+ clist = GTK_CLIST (ctree);
+ visible = gtk_ctree_is_viewable (ctree, node);
+
+ /* clist->row_list_end unlinked ? */
+ if (visible && (GTK_CTREE_NODE_NEXT (node) == NULL ||
+ (GTK_CTREE_ROW (node)->children && gtk_ctree_is_ancestor
+ (ctree, node, GTK_CTREE_NODE (clist->row_list_end)))))
+ clist->row_list_end = (GList *) (GTK_CTREE_NODE_PREV (node));
+
+ /* update list */
+ rows = 0;
+ level = GTK_CTREE_ROW (node)->level;
+ work = GTK_CTREE_NODE_NEXT (node);
+
+ /* Counts children of node */
+ while (work && GTK_CTREE_ROW (work)->level > level) {
+ work = GTK_CTREE_NODE_NEXT (work);
+ rows++;
+ }
+
+ /* Subtract removed node and children from row count */
+ if (visible) {
+ clist->rows -= (rows + 1);
+ }
+
+ if (work) {
+ list = (GList *)GTK_CTREE_NODE_PREV (work);
+ list->next = NULL;
+ list = (GList *)work;
+ list->prev = (GList *)GTK_CTREE_NODE_PREV (node);
+ }
+
+ prev = GTK_CTREE_NODE_PREV (node);
+
+ if (prev && GTK_CTREE_NODE_NEXT (prev) == node) {
+ list = (GList *)GTK_CTREE_NODE_PREV (node);
+ list->next = (GList *)work;
+ }
+
+ if (clist->row_list == (GList *)node)
+ clist->row_list = (GList *) (GTK_CTREE_ROW (node)->sibling);
+ else {
+ /* We need to hook the previous node up to the node's sibling */
+ if (prev) {
+
+ prev_row = GTK_CTREE_ROW(prev);
+ while (NULL != prev_row->parent) {
+ prev = GTK_CTREE_NODE_PREV(prev);
+ prev_row = GTK_CTREE_ROW(prev);
+ }
+ sibling = prev;
+ g_assert(GTK_CTREE_ROW (sibling)->sibling == node);
+
+ GTK_CTREE_ROW (sibling)->sibling = GTK_CTREE_ROW (node)->sibling;
+ }
+ }
+}
+
+
+/*
+ * search_gui_fast_ctree_link
+ *
+ * Functions like gtk_ctree_link for *Top level parent nodes only*. This is
+ * optimized for data being linked at the beginning of the tree.
+ * O(1) if linking to beginning, O(n) otherwise.
+ */
+static void search_gui_fast_ctree_link(GtkCTree *ctree, GtkCTreeNode *node,
+ GtkCTreeNode *sibling)
+{
+ GtkCList *clist;
+ GList *list_end;
+ GList *list;
+ GList *work;
+ gboolean visible = FALSE;
+ gint rows = 0;
+
+ clist = GTK_CLIST (ctree);
+
+ /* Counts node and children */
+ for (rows = 1, list_end = (GList *)node; list_end->next;
+ list_end = list_end->next)
+ rows++;
+
+ GTK_CTREE_ROW (node)->parent = NULL;
+ GTK_CTREE_ROW (node)->sibling = sibling;
+
+ visible = TRUE;
+ clist->rows += rows;
+ work = clist->row_list;
+
+ /* O(1) if adding to top of list */
+ if (sibling) {
+ if (work != (GList *)sibling) {
+ /* Searches from beginning of list until it finds sibling */
+ while (GTK_CTREE_ROW (work)->sibling != sibling)
+ work = (GList *)(GTK_CTREE_ROW (work)->sibling);
+ GTK_CTREE_ROW (work)->sibling = node;
+ }
+
+ if (sibling == GTK_CTREE_NODE (clist->row_list))
+ clist->row_list = (GList *) node;
+
+ if (GTK_CTREE_NODE_PREV (sibling) &&
+ GTK_CTREE_NODE_NEXT (GTK_CTREE_NODE_PREV (sibling)) == sibling) {
+ list = (GList *)GTK_CTREE_NODE_PREV (sibling);
+ list->next = (GList *)node;
+ }
+
+ list = (GList *)node;
+ list->prev = (GList *)GTK_CTREE_NODE_PREV (sibling);
+ list_end->next = (GList *)sibling;
+ list = (GList *)sibling;
+ list->prev = list_end;
+
+ } else {
+
+ if (work) {
+
+ /* Look from beginning of list/parent all the way to the end. */
+ while (GTK_CTREE_ROW (work)->sibling)
+ work = (GList *)(GTK_CTREE_ROW (work)->sibling);
+ GTK_CTREE_ROW (work)->sibling = node;
+
+ /* find last child of sibling */
+ work = (GList *) gtk_ctree_last(ctree, GTK_CTREE_NODE (work));
+
+ list_end->next = work->next;
+
+ if (work->next)
+ list = work->next->prev = list_end;
+ work->next = (GList *)node;
+ list = (GList *)node;
+ list->prev = work;
+
+ } else {
+ clist->row_list = (GList *)node;
+ list = (GList *)node;
+ list->prev = NULL;
+ list_end->next = NULL;
+ }
+ }
+
+ if (clist->row_list_end == NULL ||
+ clist->row_list_end->next == (GList *)node)
+ clist->row_list_end = list_end;
+}
+
+
+/*
+ * search_gui_fast_ctree_move
+ *
+ * Functions like gtk_ctree_move for *Top level parent nodes only*. This is
+ * optimized for data being moved to the beginning of the tree and assumes
+ * ctree != NULL and node != NULL. O(1) as opposed to gtk's which is O(n).
+ */
+static void search_gui_fast_ctree_move (GtkCTree *ctree, GtkCTreeNode *node,
+ GtkCTreeNode *new_sibling)
+{
+ GtkCList *clist;
+ GtkCTreeNode *work;
+ gboolean visible = FALSE;
+
+ clist = GTK_CLIST (ctree);
+ visible = gtk_ctree_is_viewable (ctree, node);
+
+ /* return if it's already the right place */
+ if ((new_sibling == GTK_CTREE_ROW (node)->sibling)
+ || (new_sibling == node)) {
+ return;
+ }
+
+ work = NULL;
+ if (gtk_ctree_is_viewable (ctree, node))
+ work = GTK_CTREE_NODE (g_list_nth (clist->row_list, clist->focus_row));
+
+ search_gui_fast_ctree_unlink (ctree, node);
+ search_gui_fast_ctree_link (ctree, node, new_sibling);
+
+ if (work) {
+ while (work && !gtk_ctree_is_viewable (ctree, work))
+ work = GTK_CTREE_ROW (work)->parent;
+
+ clist->focus_row = g_list_position (clist->row_list, (GList *)work);
+ clist->undo_anchor = clist->focus_row;
+ }
+
+}
+
+
+/*
* search_gui_restart_search
*
*/
@@ -495,6 +711,14 @@
/* Searches results */
+
+
+/*
+ * search_gui_compare_records:
+ *
+ * If the value in sort_col for r1 is "greater than" r2 returns +1
+ * 0 if they're equal, and -1 if r1 is "less than" r2
+ */
gint search_gui_compare_records(
gint sort_col, const record_t *r1, const record_t *r2)
{
@@ -594,6 +822,369 @@
}
+
+/*
+ * search_gui_insert_with_sort:
+ *
+ * Inserts the given node into the given list in the proper position. Assumes
+ * list has at least one item already and is sorted.
+ */
+GList *search_gui_insert_with_sort(GList *list, GtkCTreeNode *node,
+ GtkCTree *ctree, gboolean ascending, gint sort_col)
+{
+ gint i, result;
+ record_t *rkey, *rlist;
+
+ rkey = gtk_ctree_node_get_row_data(ctree, node);
+
+ if (ascending) {
+
+ list = g_list_first(list);
+ for (i = 0;; list = g_list_next(list), i++) {
+
+ rlist = gtk_ctree_node_get_row_data(ctree, list->data);
+ result = search_gui_compare_records(sort_col, rkey, rlist);
+
+ if (result <= 0) { /* Our entry is "less" than the next */
+
+ if (0 == i) {
+ /* Inserting at the beginning. Cheaper to use prepend
+ * then to use "first" and then "insert"
+ */
+ list = g_list_prepend(list, node);
+ } else {
+ list = g_list_first(list);
+ list = g_list_insert(list, node, i);
+ }
+ break;
+ }
+
+ if (NULL == list->next) {
+ list = g_list_append(list, node);
+ break;
+ }
+ }
+
+ } else { /* sort descending */
+
+ list = g_list_first(list);
+ for (i = 0;; list = g_list_next(list), i++) {
+
+ rlist = gtk_ctree_node_get_row_data(ctree, list->data);
+ result = search_gui_compare_records(sort_col, rkey, rlist);
+
+ if (result >= 0) { /* Entry is "greater" than the next */
+
+ if (0 == i ) {
+ /* Inserting at the beginning. Cheaper to use prepend
+ * then to use "first" and then "insert"
+ */
+ list = g_list_prepend(list, node);
+ } else {
+ list = g_list_first(list);
+ list = g_list_insert(list, node, i);
+ }
+ break;
+ }
+
+ if (NULL == list->next) {
+ list = g_list_append(list, node);
+ break;
+ }
+ }
+ }
+
+ return list;
+}
+
+
+/*
+ * search_gui_quick_sort_array_swap:
+ *
+ * Swaps the values in the given array for the given indicies
+ */
+void search_gui_quick_sort_array_swap(GArray *array, gint i1, gint i2)
+{
+ GtkCTreeNode *n1;
+
+ n1 = g_array_index(array, GtkCTreeNode *, i1);
+ g_array_remove_index(array, i1);
+ g_array_insert_val(array, i1, g_array_index(array, GtkCTreeNode *, i2));
+ g_array_remove_index(array, i2);
+ g_array_insert_val(array, i2, n1);
+}
+
+
+/*
+ * search_gui_quick_sort:
+ *
+ * Performs a recursive quick sort on the given array between indicies beg and
+ * end.
+ */
+void search_gui_quick_sort(GArray *array, gint beg, gint end,
+ GtkCTree *ctree, gboolean ascending, gint sort_col)
+{
+ gint i, result, pivot_index = (beg + end) / 2;
+ record_t *rbeg, *ri;
+
+ if (beg >= end)
+ return;
+
+ /* Choose the item in the middle for the pivot, shift it to the front */
+ search_gui_quick_sort_array_swap(array, beg, pivot_index);
+
+ rbeg = gtk_ctree_node_get_row_data(ctree,
+ g_array_index(array, GtkCTreeNode *, beg));
+
+ for (pivot_index = beg, i = beg + 1; i <= end; i++) {
+
+ ri = gtk_ctree_node_get_row_data(ctree,
+ g_array_index(array, GtkCTreeNode *, i));
+
+ result = search_gui_compare_records(sort_col, rbeg, ri);
+
+ if (ascending) {
+ if (result == 1) {
+ search_gui_quick_sort_array_swap(array, ++pivot_index, i);
+ }
+ }
+ else {
+ if (result == -1) {
+ search_gui_quick_sort_array_swap(array, ++pivot_index, i);
+ }
+ }
+ }
+
+ /* put the pivot back where it belongs */
+ search_gui_quick_sort_array_swap(array, beg, pivot_index);
+
+ search_gui_quick_sort(array, beg, pivot_index - 1, ctree,
+ ascending, sort_col);
+ search_gui_quick_sort(array, pivot_index + 1, end, ctree,
+ ascending, sort_col);
+}
+
+
+/*
+ * search_gui_analyze_col_data:
+ *
+ * Analyze the data in the given column to decide what type of search should
+ * be performed. This function detects whether the data is alreadt sorted
+ * ascending, descending, appears to be random, is sorting via tha count column,
+ * or via the info column.
+ */
+gint search_gui_analyze_col_data(GtkCTree *ctree, gint sort_col)
+{
+ GtkCTreeNode *cur_node, *prev_node;
+ gboolean ascending = TRUE, descending = TRUE, random = FALSE;
+ gint i, result;
+ record_t *rcur, *rprev;
+
+
+ if (c_sr_count == sort_col)
+ return SEARCH_COL_SORT_DATA_COUNT;
+
+ if (c_sr_info == sort_col)
+ return SEARCH_COL_SORT_DATA_INFO;
+
+
+ prev_node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
+ rcur = gtk_ctree_node_get_row_data(ctree, prev_node);
+
+ /* No point anaylzing without enough data */
+ if (50 > g_list_length((GList *) prev_node))
+ return SEARCH_COL_SORT_DATA_DEFAULT;
+
+ for (cur_node = GTK_CTREE_NODE_NEXT (prev_node), i = 0; i < 50; i++,
+ prev_node = cur_node, cur_node = GTK_CTREE_NODE_NEXT (cur_node)) {
+
+ rprev = rcur;
+ rcur = gtk_ctree_node_get_row_data(ctree, cur_node);
+ result = search_gui_compare_records(sort_col, rcur, rprev);
+
+ if (1 == result)
+ descending = FALSE;
+
+ if (-1 == result)
+ ascending = FALSE;
+
+ if (!ascending && !descending) {
+ random = TRUE;
+ break;
+ }
+ }
+
+ if (random)
+ return SEARCH_COL_SORT_DATA_RANDOM;
+
+ if (ascending)
+ return SEARCH_COL_SORT_DATA_SORTED_ASC;
+
+ if (descending)
+ return SEARCH_COL_SORT_DATA_SORTED_DES;
+
+ return SEARCH_COL_SORT_DATA_DEFAULT;
+}
+
+
+/*
+ * search_gui_perform_sort
+ *
+ * Sorts the given ctree using a quicksort algorithm.
+ * Theoretically this should be O(nlogn)
+ *
+ * The default GtkCTree sort is a mergesort which works fine for small data
+ * sets. Due to the nature of GtkCTree's and the size of the structures being
+ * passed around, the speed is unacceptable for larger sets (>2000).
+ *
+ * We therefore implement an analytical approach to minimize sort times. We
+ * examine the first few elements of a list and try to determine the nature of
+ * the data set and then choose the best of the following algorithms.
+ *
+ * 1. Default Mergesort: The built-in merge sort works fine for random sets
+ * of varied data. eg. Sorting by name after retreiving fresh results. If the
+ * data seems random and we're not sorting by count or info, we use this
+ * algorithm.
+ *
+ * 2. Insertion Sort: Performs stunningly on ordered or almost ordered data
+ * with a complexity of about O(n). Fortunately for us it is a common case.
+ * Users will often sort a column ascending and then resort it descending.
+ * Merge/quicksort do not to so well with ordered data (besides which most of
+ * the work is done already).
+ *
+ * 3. ??? sort. The info and count columns contain very few different
+ * elements with a large amount of repetition. Insertion sort seems to work
+ * acceptably for count, but marginly for the info column. Quicksort performs
+ * dismally for both info and count. Probably some sort of pseudeo intelligent
+ * insertion sort will be needed, ie. one that makes an almost ordered list
+ * followed by a cleanup algorithm.
+ */
+void search_gui_perform_sort(GtkCTree *ctree, gboolean ascending, gint sort_col)
+{
+ GArray *array = g_array_new(FALSE, FALSE, sizeof(GtkCTreeNode *));
+ GtkCTreeNode *cur_node, *prev_node = NULL;
+ GList *temp_list;
+ gint n, size = 0, col_data;
+ record_t *rtemp;
+
+ /* Nothing to sort */
+ if (NULL == GTK_CLIST(ctree)->row_list)
+ return;
+
+ col_data = search_gui_analyze_col_data(ctree, sort_col);
+
+ switch (col_data) {
+
+ case SEARCH_COL_SORT_DATA_SORTED_ASC:
+ case SEARCH_COL_SORT_DATA_SORTED_DES:
+ case SEARCH_COL_SORT_DATA_COUNT:
+ case SEARCH_COL_SORT_DATA_INFO:
+
+ /* Use an insertion sort: O(n) for ordered data, <O(n*n) for unordered */
+
+ temp_list = NULL;
+ cur_node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
+
+ /* We add 1st node outside of the loop to get rid of N comparisons */
+ if (NULL != cur_node) {
+ temp_list = g_list_append(temp_list, cur_node);
+
+
+ /* Sort into a glist O(n) for ordered data, <O(n*n) for unordered*/
+ for (cur_node = GTK_CTREE_NODE_NEXT (cur_node);
+ (NULL != cur_node); cur_node = GTK_CTREE_NODE_NEXT (cur_node)) {
+
+ temp_list = search_gui_insert_with_sort(temp_list,
+ cur_node, ctree, ascending, sort_col);
+ }
+
+
+ /* Now order the CTree using the list O(n) */
+ gtk_clist_freeze(GTK_CLIST(ctree));
+
+ /* We move everything to the top of the old tree, backwards. This
+ * is because "move" has optimized this way.
+ */
+ prev_node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
+ for (temp_list = g_list_last(temp_list); NULL != temp_list;
+ temp_list = g_list_previous(temp_list)) {
+
+ cur_node = temp_list->data;
+
+ if (prev_node == cur_node)
+ continue;
+
+ search_gui_fast_ctree_move(ctree, cur_node, prev_node);
+ prev_node = cur_node;
+ }
+ gtk_clist_thaw(GTK_CLIST(ctree));
+
+ }
+
+ g_list_free(temp_list);
+ break; /* End of all cases using insertion sort */
+
+
+ case SEARCH_COL_SORT_DATA_RANDOM:
+ case SEARCH_COL_SORT_DATA_DEFAULT:
+
+ /* Use a quick sort: Average of O(nlogn) for random data */
+
+ /* Convert the search tree into an array O(n) */
+ for (cur_node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
+ (NULL != cur_node); cur_node = GTK_CTREE_NODE_NEXT (cur_node))
+ g_array_append_val(array, cur_node);
+
+ /* Sort the array O(nlogn) */
+ search_gui_quick_sort(array, 0, array->len - 1, ctree, ascending, sort_col);
+
+
+ /* Use the sorted array to sort the ctree widget O(n) */
+ gtk_clist_freeze(GTK_CLIST(ctree));
+ prev_node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
+
+ for (n = array->len - 1; n >= 0; n--) {
+ cur_node = g_array_index(array, GtkCTreeNode *, n);
+ search_gui_fast_ctree_move(ctree, cur_node, prev_node);
+ prev_node = cur_node;
+ }
+ gtk_clist_thaw(GTK_CLIST(ctree));
+
+ g_array_free(array, TRUE);
+ break; /* End of all cases using quicksort */
+
+
+#if 0
+
+ /* Use GTK's default sort functionality (a merge sort I think) */
+
+ /* set compare function */
+ gtk_clist_set_compare_func(GTK_CLIST(ctree),
+ search_results_compare_func);
+
+ /* set sort type */
+ switch (ascending) {
+ case SORT_ASC:
+ gtk_clist_set_sort_type(GTK_CLIST(ctree), GTK_SORT_ASCENDING);
+ break;
+
+ case SORT_DESC:
+ gtk_clist_set_sort_type(GTK_CLIST(ctree), GTK_SORT_DESCENDING);
+ break;
+ }
+
+ gtk_clist_set_sort_column(GTK_CLIST(ctree), sort_col);
+ gtk_ctree_sort_node(ctree, NULL);
+ break;
+#endif
+
+ default:
+ g_assert_not_reached();
+ }
+}
+
+
+
/*
* search_gui_sort_column
*
@@ -604,10 +1195,7 @@
void search_gui_sort_column(search_t *search, gint column)
{
GtkWidget * cw = NULL;
-
- /* set compare function */
- gtk_clist_set_compare_func
- (GTK_CLIST(search->ctree), search_results_compare_func);
+ gboolean ascending = TRUE;
/* destroy existing arrow */
if (search->arrow != NULL) {
@@ -615,19 +1203,15 @@
search->arrow = NULL;
}
- /* set sort type and create arrow */
+ /* create new arrow */
switch (search->sort_order) {
case SORT_ASC:
search->arrow = create_pixmap(main_window, "arrow_up.xpm");
- gtk_clist_set_sort_type(
- GTK_CLIST(search->ctree),
- GTK_SORT_ASCENDING);
+ ascending = TRUE;
break;
case SORT_DESC:
search->arrow = create_pixmap(main_window, "arrow_down.xpm");
- gtk_clist_set_sort_type(
- GTK_CLIST(search->ctree),
- GTK_SORT_DESCENDING);
+ ascending = FALSE;
break;
case SORT_NONE:
break;
@@ -645,21 +1229,10 @@
gtk_box_reorder_child(GTK_BOX(cw), search->arrow, 0);
gtk_widget_show(search->arrow);
}
- gtk_clist_set_sort_column(GTK_CLIST(search->ctree), column);
-
-
- /* FIXME
- * GTK uses a merge sort algorithm to sort the column items using our
- * function (search_gui_compare_records) as the comparation function.
- * this is too slow. However, I tried writing a custom quicksort
- * algorithm and the results weren't much better. It's possible to
- * cast the ctree as a clist and use the clist sort function but this
- * is just about as slow, doing this just ignores child nodes. We need
- * to fix this somehow. --- Emile Jan 03, 2004
- */
- gtk_ctree_sort_node(search->ctree, NULL);
+ search_gui_perform_sort(search->ctree, ascending, column);
search->sort = TRUE;
- } else {
+
+ } else {
search->sort = FALSE;
}
}