From 9f419f033bbc4fb1e8a53769e5e2e1a866b3c7cd Mon Sep 17 00:00:00 2001 From: Charles Haley Date: Sun, 7 Jun 2020 13:56:18 +0100 Subject: [PATCH] Improvements to edit authors: 1) remove a linear list search 2) Don't recalculate sort keys on every comparison 3) Use set comprehensions to build the info needed to display 4) Add an Undo option to the context menu if the item has been changed --- .../gui2/dialogs/edit_authors_dialog.py | 89 +++++++++++++------ 1 file changed, 61 insertions(+), 28 deletions(-) diff --git a/src/calibre/gui2/dialogs/edit_authors_dialog.py b/src/calibre/gui2/dialogs/edit_authors_dialog.py index 1d39eef101..62adffc416 100644 --- a/src/calibre/gui2/dialogs/edit_authors_dialog.py +++ b/src/calibre/gui2/dialogs/edit_authors_dialog.py @@ -5,6 +5,8 @@ __copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net' __docformat__ = 'restructuredtext en' __license__ = 'GPL v3' +from functools import partial + from PyQt5.Qt import (Qt, QDialog, QTableWidgetItem, QAbstractItemView, QIcon, QDialogButtonBox, QFrame, QLabel, QTimer, QMenu, QApplication, QByteArray, QItemDelegate, QAction) @@ -22,11 +24,22 @@ QT_HIDDEN_CLEAR_ACTION = '_q_qlineeditclearaction' class tableItem(QTableWidgetItem): + def __init__(self, txt): + QTableWidgetItem.__init__(self, txt) + self.sort_key = sort_key(unicode_type(txt)) + + def setText(self, txt): + self.sort_key = sort_key(unicode_type(txt)) + QTableWidgetItem.setText(self, txt) + + def set_sort_key(self): + self.sort_key = sort_key(unicode_type(self.text())) + def __ge__(self, other): - return sort_key(unicode_type(self.text())) >= sort_key(unicode_type(other.text())) + return self.sort_key >= other.sort_key def __lt__(self, other): - return sort_key(unicode_type(self.text())) < sort_key(unicode_type(other.text())) + return self.sort_key < other.sort_key class EditColumnDelegate(QItemDelegate): @@ -146,6 +159,7 @@ class EditAuthorsDialog(QDialog, Ui_EditAuthorsDialog): 'link': v['link']} self.edited_icon = QIcon(I('modified.png')) + self.empty_icon = QIcon() if prefs['use_primary_find_in_search']: self.string_contains = primary_contains else: @@ -168,11 +182,13 @@ class EditAuthorsDialog(QDialog, Ui_EditAuthorsDialog): self.show_table(None, None, None, False) def show_table(self, id_to_select, select_sort, select_link, is_first_letter): + auts_to_show = {t[0] for t in + self.find_aut_func(use_virtual_library=self.apply_vl_checkbox.isChecked())} filter_text = icu_lower(unicode_type(self.filter_box.text())) - auts_to_show = [] - for t in self.find_aut_func(use_virtual_library=self.apply_vl_checkbox.isChecked()): - if self.string_contains(filter_text, icu_lower(t[1])): - auts_to_show.append(t[0]) + if filter_text: + auts_to_show = {id_ for id_ in auts_to_show + if self.string_contains(filter_text, icu_lower(self.authors[id_]['name']))} + self.table.blockSignals(True) self.table.clear() self.table.setColumnCount(3) @@ -183,22 +199,20 @@ class EditAuthorsDialog(QDialog, Ui_EditAuthorsDialog): if id_ not in auts_to_show: continue name, sort, link = (v['name'], v['sort'], v['link']) - orig = self.original_authors[id_] name = name.replace('|', ',') name_item = tableItem(name) name_item.setData(Qt.UserRole, id_) - if name != orig['name']: - name_item.setIcon(self.edited_icon) sort_item = tableItem(sort) - if sort != orig['sort']: - sort_item.setIcon(self.edited_icon) link_item = tableItem(link) - if link != orig['link']: - link_item.setIcon(self.edited_icon) + self.table.setItem(row, 0, name_item) self.table.setItem(row, 1, sort_item) self.table.setItem(row, 2, link_item) + + self.set_icon(name_item, id_) + self.set_icon(sort_item, id_) + self.set_icon(link_item, id_) row += 1 self.table.setItemDelegate(EditColumnDelegate(self.completion_data)) @@ -219,9 +233,10 @@ class EditAuthorsDialog(QDialog, Ui_EditAuthorsDialog): select_item = None use_as = tweaks['categories_use_field_for_author_name'] for row in range(0, len(auts_to_show)): - name_item = self.table.item(row, 1) if use_as else self.table.item(row, 0) if is_first_letter: - if primary_startswith(name_item.text(), id_to_select): + item_txt = unicode_type(self.table.item(row, 1).text() if use_as + else self.table.item(row, 0).text()) + if primary_startswith(item_txt, id_to_select): select_item = self.table.item(row, 1) break elif id_to_select == self.table.item(row, 0).data(Qt.UserRole): @@ -230,7 +245,8 @@ class EditAuthorsDialog(QDialog, Ui_EditAuthorsDialog): elif select_link: select_item = self.table.item(row, 2) else: - select_item = name_item + select_item = (self.table.item(row, 1) if use_as + else self.table.item(row, 0)) break if select_item: self.table.setCurrentItem(select_item) @@ -269,6 +285,9 @@ class EditAuthorsDialog(QDialog, Ui_EditAuthorsDialog): self.table.setColumnWidth(c, w) self.save_state() + def get_column_name(self, column): + return ['name', 'sort', 'link'][column] + def show_context_menu(self, point): self.context_item = self.table.itemAt(point) case_menu = QMenu(_('Change case')) @@ -285,12 +304,19 @@ class EditAuthorsDialog(QDialog, Ui_EditAuthorsDialog): action_capitalize.triggered.connect(self.capitalize) m = self.au_context_menu = QMenu() + idx = self.table.indexAt(point) + id_ = int(self.table.item(idx.row(), 0).data(Qt.UserRole)) + sub = self.get_column_name(idx.column()) + if self.context_item.text() != self.original_authors[id_][sub]: + ca = m.addAction(_('Undo')) + ca.triggered.connect(partial(self.undo_cell, + old_value=self.original_authors[id_][sub])) + m.addSeparator() ca = m.addAction(_('Copy')) ca.triggered.connect(self.copy_to_clipboard) ca = m.addAction(_('Paste')) ca.triggered.connect(self.paste_from_clipboard) m.addSeparator() - if self.context_item is not None and self.context_item.column() == 0: ca = m.addAction(_('Copy to author sort')) ca.triggered.connect(self.copy_au_to_aus) @@ -304,6 +330,9 @@ class EditAuthorsDialog(QDialog, Ui_EditAuthorsDialog): m.addMenu(case_menu) m.exec_(self.table.mapToGlobal(point)) + def undo_cell(self, old_value): + self.context_item.setText(old_value) + def search_in_book_list(self): from calibre.gui2.ui import get_gui row = self.context_item.row() @@ -416,10 +445,9 @@ class EditAuthorsDialog(QDialog, Ui_EditAuthorsDialog): item_aus = self.table.item(row, 1) # Sometimes trailing commas are left by changing between copy algs aus = unicode_type(author_to_author_sort(aut)).rstrip(',') - if aus != self.original_authors[id_]['sort']: - item_aus.setIcon(self.edited_icon) item_aus.setText(aus) self.authors[id_]['sort'] = aus + self.set_icon(item_aus, id_) self.table.setFocus(Qt.OtherFocusReason) self.table.cellChanged.connect(self.cell_changed) @@ -429,18 +457,23 @@ class EditAuthorsDialog(QDialog, Ui_EditAuthorsDialog): aus = unicode_type(self.table.item(row, 1).text()).strip() item_aut = self.table.item(row, 0) id_ = int(item_aut.data(Qt.UserRole)) - if aus != self.original_authors[id_]['name']: - item_aut.setIcon(self.edited_icon) item_aut.setText(aus) self.authors[id_]['name'] = aus + self.set_icon(item_aut, id_) self.table.setFocus(Qt.OtherFocusReason) self.table.cellChanged.connect(self.cell_changed) + def set_icon(self, item, id_): + col_name = self.get_column_name(item.column()) + if unicode_type(item.text()) != self.original_authors[id_][col_name]: + item.setIcon(self.edited_icon) + else: + item.setIcon(self.empty_icon) + def cell_changed(self, row, col): id_ = int(self.table.item(row, 0).data(Qt.UserRole)) if col == 0: item = self.table.item(row, 0) - item.setIcon(self.edited_icon) aut = unicode_type(item.text()).strip() aut_list = string_to_authors(aut) if len(aut_list) != 1: @@ -448,18 +481,18 @@ class EditAuthorsDialog(QDialog, Ui_EditAuthorsDialog): _('You cannot change an author to multiple authors.')).exec_() aut = ' % '.join(aut_list) self.table.item(row, 0).setText(aut) + item.set_sort_key() self.authors[id_]['name'] = aut + self.set_icon(item, id_) c = self.table.item(row, 1) txt = author_to_author_sort(aut) - c.setText(txt) self.authors[id_]['sort'] = txt + c.setText(txt) # This triggers another cellChanged event item = c else: item = self.table.item(row, col) - item.setIcon(self.edited_icon) - if col == 1: - self.authors[id_]['sort'] = unicode_type(item.text()) - else: - self.authors[id_]['link'] = unicode_type(item.text()) + item.set_sort_key() + self.set_icon(item, id_) + self.authors[id_][self.get_column_name(col)] = unicode_type(item.text()) self.table.setCurrentItem(item) self.table.scrollToItem(item)