From 1daae0135d31040eba40093011821677fcd8be31 Mon Sep 17 00:00:00 2001 From: Chase Sterling Date: Wed, 7 Nov 2012 19:41:04 -0500 Subject: [PATCH] Implement pseudo stable sort in the gtk torrent view. --- deluge/ui/gtkui/listview.py | 73 +++++++++++++++++++++++++++++----- deluge/ui/gtkui/torrentview.py | 5 ++- 2 files changed, 66 insertions(+), 12 deletions(-) diff --git a/deluge/ui/gtkui/listview.py b/deluge/ui/gtkui/listview.py index 94d7a3a13..204f09bdb 100644 --- a/deluge/ui/gtkui/listview.py +++ b/deluge/ui/gtkui/listview.py @@ -222,6 +222,11 @@ class ListView: # their columns prior to having the state list saved on shutdown. self.removed_columns_state = [] + # Since gtk TreeModelSort doesn't do stable sort, remember last sort order so we can + self.last_sort_order = {} + self.unique_column_id = None + self.default_sort_column_id = None + # Create the model filter and column self.add_bool_column("filter", hidden=True) @@ -240,15 +245,49 @@ class ListView: if sort_info and sort_info[0] and sort_info[1] > -1: self.model_filter.set_sort_column_id(sort_info[0], sort_info[1]) self.set_sort_functions() + self.model_filter.connect("sort-column-changed", self.on_model_sort_changed) + self.model_filter.connect("row-inserted", self.on_model_row_inserted) self.treeview.set_model(self.model_filter) + def on_model_sort_changed(self, model): + if self.unique_column_id: + self.last_sort_order = {} + def record_position(model, path, iter, data): + self.last_sort_order[model[iter][self.unique_column_id]] = path[0] + model.foreach(record_position, None) + + def on_model_row_inserted(self, model, path, iter): + if self.unique_column_id: + self.last_sort_order.setdefault( + model[iter][self.unique_column_id], len(model) - 1) + + def stabilize_sort_func(self, sort_func): + def stabilized(model, iter1, iter2, data): + result = sort_func(model, iter1, iter2, data) + if result == 0 and self.unique_column_id: + unique1 = model[iter1][self.unique_column_id] + unique2 = model[iter2][self.unique_column_id] + if unique1 in self.last_sort_order and unique2 in self.last_sort_order: + result = cmp(self.last_sort_order[unique1], + self.last_sort_order[unique2]) + # If all else fails, fall back to sorting by unique column + if result == 0: + result = cmp(unique1, unique2) + + return result + return stabilized + + def generic_sort_func(self, model, iter1, iter2, data): + return cmp(model[iter1][data], model[iter2][data]) + def set_sort_functions(self): + self.model_filter.set_default_sort_func(None) for column in self.columns.values(): - if column.sort_func: - self.model_filter.set_sort_func( - column.sort_id, - column.sort_func, - column.sort_id) + sort_func = column.sort_func or self.generic_sort_func + self.model_filter.set_sort_func( + column.sort_id, + self.stabilize_sort_func(sort_func), + column.sort_id) def create_column_state(self, column, position=None): if not position: @@ -450,7 +489,8 @@ class ListView: def add_column(self, header, render, col_types, hidden, position, status_field, sortid, text=0, value=0, pixbuf=0, function=None, - column_type=None, sort_func=None, tooltip=None, default=True): + column_type=None, sort_func=None, tooltip=None, default=True, + unique=False, default_sort=False): """Adds a column to the ListView""" # Add the column types to liststore_columns column_indices = [] @@ -475,6 +515,11 @@ class ListView: self.columns[header].sort_func = sort_func self.columns[header].sort_id = column_indices[sortid] + if unique: + self.unique_column_id = column_indices[sortid] + if default_sort: + self.default_sort_column_id = column_indices[sortid] + # Create a new list with the added column self.create_new_liststore() @@ -530,6 +575,11 @@ class ListView: if tooltip: column.get_widget().set_tooltip_markup(tooltip) + if default_sort: + if self.model_filter.get_sort_column_id()[0] is None: + self.model_filter.set_sort_column_id(column_indices[sortid], + gtk.SORT_ASCENDING) + # Check for loaded state and apply column_in_state = False if self.state != None: @@ -569,13 +619,15 @@ class ListView: def add_text_column(self, header, col_type=str, hidden=False, position=None, status_field=None, sortid=0, column_type="text", - sort_func=None, tooltip=None, default=True): + sort_func=None, tooltip=None, default=True, unique=False, + default_sort=False): """Add a text column to the listview. Only the header name is required. """ render = gtk.CellRendererText() self.add_column(header, render, col_type, hidden, position, status_field, sortid, column_type=column_type, - sort_func=sort_func, tooltip=tooltip, default=default) + sort_func=sort_func, tooltip=tooltip, default=default, + unique=unique, default_sort=default_sort) return True @@ -619,14 +671,15 @@ class ListView: def add_texticon_column(self, header, col_types=[str, str], sortid=1, hidden=False, position=None, status_field=None, column_type="texticon", function=None, - tooltip=None, default=True): + tooltip=None, default=True, default_sort=False): """Adds a texticon column to the listview.""" render1 = gtk.CellRendererPixbuf() render2 = gtk.CellRendererText() self.add_column(header, (render1, render2), col_types, hidden, position, status_field, sortid, column_type=column_type, - function=function, pixbuf=0, text=1, tooltip=tooltip, default=default) + function=function, pixbuf=0, text=1, tooltip=tooltip, + default=default, default_sort=default_sort) return True diff --git a/deluge/ui/gtkui/torrentview.py b/deluge/ui/gtkui/torrentview.py index 1bb9ea20c..79fd99831 100644 --- a/deluge/ui/gtkui/torrentview.py +++ b/deluge/ui/gtkui/torrentview.py @@ -348,14 +348,15 @@ class TorrentView(listview.ListView, component.Component): self.register_checklist_menu(self.window.main_builder.get_object("menu_columns")) # Add the columns to the listview - self.add_text_column("torrent_id", hidden=True) + self.add_text_column("torrent_id", hidden=True, unique=True) self.add_bool_column("dirty", hidden=True) self.add_func_column("#", cell_data_queue, [int], status_field=["queue"], sort_func=queue_column_sort) self.add_texticon_column(_("Name"), status_field=["state", "name"], - function=cell_data_statusicon) + function=cell_data_statusicon, + default_sort=True) self.add_func_column(_("Size"), listview.cell_data_size, [gobject.TYPE_UINT64], status_field=["total_wanted"])