+class Sorting():
+ """Representation of sort and filtering settings."""
+ _gallery_update = None
+ _gallery_items_attrs_full = None
+ _gallery_items_attrs_filtered = None
+
+ def __init__(self, sort_order):
+
+ def setup_sort_order_item(_, list_item):
+ vbox = Gtk.Box(orientation=OR_V)
+ hbox = Gtk.Box(orientation=OR_H)
+ hbox.append(Gtk.Label(hexpand=True))
+ hbox.append(Gtk.Entry.new())
+ hbox.get_last_child().props.placeholder_text = 'filter?'
+ vbox.append(hbox)
+ vals_listing = Gtk.Label(wrap=True, max_width_chars=35,
+ wrap_mode=Pango.WrapMode.WORD_CHAR)
+ vals_listing.hide()
+ vbox.append(vals_listing)
+ list_item.set_child(vbox)
+
+ def bind_sort_order_item(_, list_item):
+
+ def on_filter_enter(entry):
+ entry.remove_css_class('temp')
+ text = entry.get_buffer().get_text()
+ if '' != text.rstrip():
+ self.filter_inputs[sorter.name] = text
+ elif sorter.name in self.filter_inputs:
+ del self.filter_inputs[sorter.name]
+ self._gallery_update()
+
+ sorter = list_item.props.item
+ filter_text = self.filter_inputs.get(sorter.name, '')
+ vals_filtered = self._gallery_items_attrs_filtered[sorter.name]
+ vals_full = self._gallery_items_attrs_full[sorter.name]
+ sorter.setup_on_bind(list_item.props.child, on_filter_enter,
+ filter_text, vals_filtered, vals_full)
+
+ def select_sort_order(_a, _b, _c):
+ if self._last_selected:
+ self._last_selected.get_last_child().hide()
+ list_item = self.selection.props.selected_item.widget
+ list_item.get_parent().grab_focus()
+ self._last_selected = list_item
+
+ self.order = sort_order
+ self.filter_inputs = {}
+ self._last_selected = None
+ self.store = Gio.ListStore(item_type=Sorter)
+ self.selection = Gtk.SingleSelection.new(self.store)
+ self.selection.connect('selection-changed', select_sort_order)
+ factory = Gtk.SignalListItemFactory()
+ factory.connect('setup', setup_sort_order_item)
+ factory.connect('bind', bind_sort_order_item)
+ self.view = Gtk.ListView(model=self.selection, factory=factory)
+ self.box = Gtk.Box(orientation=OR_V)
+ self.box.append(Gtk.Label(label='** sort order **'))
+ self.box.append(self.view)
+ self.btn_activate = Gtk.Button(label='activate')
+ self.btn_activate.props.sensitive = False
+ self.btn_activate.connect(
+ 'clicked', lambda _: self.activate_order())
+ self.box.append(self.btn_activate)
+
+ @classmethod
+ def from_suggestion(cls, suggestion_fused):
+ """Parse suggestion_fused for/into initial sort order to build on."""
+ suggestion = suggestion_fused.split(',')
+ names = [p.lower() for p in GEN_PARAMS] + ['bookmarked']
+ sort_order = []
+ for name in names:
+ sort_order += [Sorter(name)]
+ new_sort_order = []
+ do_reverse = '-' in suggestion
+ for pattern in suggestion:
+ for sorter in [sorter for sorter in sort_order
+ if sorter.name.startswith(pattern)]:
+ sort_order.remove(sorter)
+ new_sort_order += [sorter]
+ sort_order = new_sort_order + sort_order
+ if do_reverse:
+ sort_order.reverse()
+ return cls(sort_order)
+
+ def bind_gallery(self, on_update, items_attrs_full, items_attrs_filtered):
+ """Connect to Gallery interfaces where necessary."""
+ self._gallery_update = on_update
+ self._gallery_items_attrs_full = items_attrs_full
+ self._gallery_items_attrs_filtered = items_attrs_filtered
+
+ def move_selection(self, direction):
+ """Move sort order selection by direction (-1 or +1)."""
+ min_idx, max_idx = 0, len(self.order) - 1
+ cur_idx = self.selection.props.selected
+ if (1 == direction and cur_idx < max_idx)\
+ or (-1 == direction and cur_idx > min_idx):
+ self.selection.props.selected = cur_idx + direction
+
+ def move_sorter(self, direction):
+ """Move selected item in sort order view, ensure temporary state."""
+ tmp_sort_order = []
+ for i in range(self.store.get_n_items()):
+ tmp_sort_order += [self.store.get_item(i)]
+ cur_idx = self.selection.props.selected
+ selected = tmp_sort_order[cur_idx]
+ if direction == -1 and cur_idx > 0:
+ prev_i = cur_idx - 1
+ old_prev = tmp_sort_order[prev_i]
+ tmp_sort_order[prev_i] = selected
+ tmp_sort_order[cur_idx] = old_prev
+ elif direction == 1 and cur_idx < (len(tmp_sort_order) - 1):
+ next_i = cur_idx + 1
+ old_next = tmp_sort_order[next_i]
+ tmp_sort_order[next_i] = selected
+ tmp_sort_order[cur_idx] = old_next
+ else: # to catch movement beyond limits
+ return
+ self.update_box(tmp_sort_order, cur_idx + direction)
+ self.selection.props.selected = cur_idx + direction
+ for i in range(self.store.get_n_items()):
+ sort_item = self.store.get_item(i)
+ sort_item.widget.add_css_class('temp')
+ self.btn_activate.props.sensitive = True
+
+ def update_box(self, alt_order=None, cur_selection=0):
+ """Rebuild .store from .order, or alt_order if provided."""
+ sort_order = alt_order if alt_order else self.order
+ self.store.remove_all()
+ for sorter in sort_order:
+ self.store.append(sorter)
+ self.selection.props.selected = cur_selection
+
+ def activate_order(self):
+ """Write sort order box order into .order, mark finalized."""
+ self.order.clear()
+ for i in range(self.store.get_n_items()):
+ sorter = self.store.get_item(i)
+ sorter.widget.remove_css_class('temp')
+ self.order += [sorter]
+ self.btn_activate.props.sensitive = False
+ self._gallery_update()
+
+