From c24f59151ca08645224ba7fbccd22c24d7a95760 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 17 Dec 2022 15:01:10 +0530 Subject: [PATCH] Edit book: File browser: Allow using keyboard shortcuts to re-order the spine --- manual/edit.rst | 13 ++++--- src/calibre/gui2/tweak_book/file_list.py | 45 +++++++++++++++++++++++- 2 files changed, 52 insertions(+), 6 deletions(-) diff --git a/manual/edit.rst b/manual/edit.rst index a15be4b97d..ff3a8c669f 100644 --- a/manual/edit.rst +++ b/manual/edit.rst @@ -133,11 +133,14 @@ Changing text file order ^^^^^^^^^^^^^^^^^^^^^^^^^^ You can re-arrange the order in which text (HTML) files are opened when reading -the book by simply dragging and dropping them in the Files browser. For the -technically inclined, this is called re-ordering the book spine. Note that you -have to drop the items *between* other items, not on top of them, this can be a -little fiddly until you get used to it. Dropping on top of another file will -cause the files to be merged. +the book by simply dragging and dropping them in the Files browser or clicking +on the file to move and then pressing the :kbd:`Ctrl+Shift` modifiers with the +:kbd:`Up`, :kbd:`Down`, :kbd:`Home` or :kbd:`End` keys. For the technically +inclined, this is called re-ordering the book spine. + +Note that you have to drop the items *between* other items, not on top of them, +this can be a little fiddly until you get used to it. Dropping on top of +another file will cause the files to be merged. Marking the cover ^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/calibre/gui2/tweak_book/file_list.py b/src/calibre/gui2/tweak_book/file_list.py index 57e2b52b23..38efccf934 100644 --- a/src/calibre/gui2/tweak_book/file_list.py +++ b/src/calibre/gui2/tweak_book/file_list.py @@ -374,6 +374,9 @@ class FileList(QTreeWidget, OpenWithHandler): return new_names = new_names[:insertion_point] + names + new_names[insertion_point:] order = [[name, linear_map[name]] for name in new_names] + self.request_reorder(order) + + def request_reorder(self, order): # Ensure that all non-linear items are at the end, by making any non-linear # items not at the end, linear for i, (name, linear) in tuple(enumerate(order)): @@ -797,9 +800,20 @@ class FileList(QTreeWidget, OpenWithHandler): self.mark_requested.emit(name, 'nav') def keyPressEvent(self, ev): - if ev.key() in (Qt.Key.Key_Delete, Qt.Key.Key_Backspace): + k = ev.key() + mods = ev.modifiers() & ( + Qt.KeyboardModifier.ShiftModifier | Qt.KeyboardModifier.AltModifier | Qt.KeyboardModifier.ControlModifier | Qt.KeyboardModifier.MetaModifier) + if k in (Qt.Key.Key_Delete, Qt.Key.Key_Backspace): ev.accept() self.request_delete() + elif mods == (Qt.KeyboardModifier.ControlModifier | Qt.KeyboardModifier.ShiftModifier): + m = self.categories['text'].childCount() + amt = {Qt.Key.Key_Up: -1, Qt.Key.Key_Down: 1, Qt.Key.Key_Home: -m, Qt.Key.Key_End: m}.get(k, None) + if amt is not None: + ev.accept() + self.move_selected_text_items(amt) + else: + return QTreeWidget.keyPressEvent(self, ev) else: return QTreeWidget.keyPressEvent(self, ev) @@ -861,6 +875,35 @@ class FileList(QTreeWidget, OpenWithHandler): ans.discard('') return ans + def move_selected_text_items(self, amt: int) -> bool: + parent = self.categories['text'] + children = tuple(parent.child(i) for i in range(parent.childCount())) + selected_names = tuple(c.data(0, NAME_ROLE) for c in children if c.isSelected()) + if not selected_names or amt == 0: + return False + current_order = tuple(c.data(0, NAME_ROLE) for c in children) + linear_map = {c.data(0, NAME_ROLE):c.data(0, LINEAR_ROLE) for c in children} + order_map = {name: i for i, name in enumerate(current_order)} + new_order = list(current_order) + changed = False + items = reversed(selected_names) if amt > 0 else selected_names + if amt < 0: + items = selected_names + delta = max(amt, -order_map[selected_names[0]]) + else: + items = reversed(selected_names) + delta = min(amt, len(children) - 1 - order_map[selected_names[-1]]) + for name in items: + i = order_map[name] + new_i = min(max(0, i + delta), len(current_order) - 1) + if new_i != i: + changed = True + del new_order[i] + new_order.insert(new_i, name) + if changed: + self.request_reorder([[n, linear_map[n]] for n in new_order]) + return changed + def copy_selected_files(self): self.initiate_file_copy.emit(self.selected_names)