From 27bea0a999653d52a69851febddfff329194b2f5 Mon Sep 17 00:00:00 2001 From: Charles Haley Date: Mon, 6 Nov 2023 12:56:19 +0000 Subject: [PATCH] Several changes: 1. Improve performance by caching all icons. 2. Use different icons instead of a check mark for notes. Sorting uses the icon. 3. In manage tags, make the edit button on the left work on all editable cells. Correct the tooltip. 4. In manage tags, put the button press shortcut descriptions into the tooltips. 5. In manage authors, add an "Edit cell" button that works with all editable cells. --- .../gui2/dialogs/edit_authors_dialog.py | 22 ++-- .../gui2/dialogs/edit_authors_dialog.ui | 14 ++- src/calibre/gui2/dialogs/tag_list_editor.py | 109 ++++++++++++------ src/calibre/gui2/dialogs/tag_list_editor.ui | 14 ++- 4 files changed, 109 insertions(+), 50 deletions(-) diff --git a/src/calibre/gui2/dialogs/edit_authors_dialog.py b/src/calibre/gui2/dialogs/edit_authors_dialog.py index d03ca2156b..7e23e2fdc9 100644 --- a/src/calibre/gui2/dialogs/edit_authors_dialog.py +++ b/src/calibre/gui2/dialogs/edit_authors_dialog.py @@ -15,7 +15,7 @@ from qt.core import ( from calibre.ebooks.metadata import author_to_author_sort, string_to_authors from calibre.gui2 import error_dialog, gprefs from calibre.gui2.dialogs.edit_authors_dialog_ui import Ui_EditAuthorsDialog -from calibre.gui2.dialogs.tag_list_editor import CHECK_MARK, NotesUtilities +from calibre.gui2.dialogs.tag_list_editor import NotesUtilities, NotesTableWidgetItem from calibre.utils.config import prefs from calibre.utils.config_base import tweaks from calibre.utils.icu import ( @@ -116,6 +116,7 @@ class EditAuthorsDialog(QDialog, Ui_EditAuthorsDialog): self.apply_vl_checkbox.toggled.connect(self.use_vl_changed) self.apply_selection_checkbox.setContentsMargins(0, 0, 0, 0) self.apply_selection_checkbox.toggled.connect(self.apply_selection_box_changed) + self.edit_current_cell.clicked.connect(self.edit_cell) self.table.setAlternatingRowColors(True) self.table.setSelectionMode(QAbstractItemView.SelectionMode.SingleSelection) @@ -205,6 +206,10 @@ class EditAuthorsDialog(QDialog, Ui_EditAuthorsDialog): self.notes_utilities, self.get_item_id)) self.show_table(id_to_select, select_sort, select_link, is_first_letter) + def edit_cell(self): + if self.table.currentIndex().isValid(): + self.table.editItem(self.table.currentItem()) + def get_item_id(self, item): return int(self.table.item(item.row(), AUTHOR_COLUMN).data(Qt.ItemDataRole.UserRole)) @@ -252,8 +257,6 @@ class EditAuthorsDialog(QDialog, Ui_EditAuthorsDialog): row = 0 from calibre.gui2.ui import get_gui all_items_that_have_notes = get_gui().current_db.new_api.get_all_items_that_have_notes('authors') - yes, yes_skey = CHECK_MARK, sort_key(CHECK_MARK) - no, no_skey = '', sort_key('') for id_, v in self.authors.items(): if id_ not in auts_to_show: continue @@ -268,16 +271,13 @@ class EditAuthorsDialog(QDialog, Ui_EditAuthorsDialog): self.table.setItem(row, AUTHOR_COLUMN, name_item) self.table.setItem(row, AUTHOR_SORT_COLUMN, sort_item) self.table.setItem(row, LINK_COLUMN, link_item) - if id_ in all_items_that_have_notes: - note_item = TableItem(yes, yes_skey) - else: - note_item = TableItem(no, no_skey) + note_item = NotesTableWidgetItem() self.table.setItem(row, NOTES_COLUMN, note_item) self.set_icon(name_item, id_) self.set_icon(sort_item, id_) self.set_icon(link_item, id_) - self.set_icon(note_item, id_) + self.notes_utilities.set_icon(note_item, id_, id_ in all_items_that_have_notes) row += 1 self.table.setHorizontalHeaderLabels([_('Author'), _('Author sort'), _('Link'), _('Notes')]) @@ -569,6 +569,8 @@ class EditAuthorsDialog(QDialog, Ui_EditAuthorsDialog): self.table.setFocus(Qt.FocusReason.OtherFocusReason) def set_icon(self, item, id_): + if item.column() == NOTES_COLUMN: + raise ValueError('got set_icon on notes column') modified = self.item_is_modified(item, id_) item.setIcon(self.edited_icon if modified else QIcon()) @@ -596,10 +598,10 @@ class EditAuthorsDialog(QDialog, Ui_EditAuthorsDialog): item = c else: item = self.table.item(row, col) - item.set_sort_key() - self.set_icon(item, id_) name = self.get_column_name(col) if name != 'notes': + item.set_sort_key() + self.set_icon(item, id_) self.authors[id_][self.get_column_name(col)] = str(item.text()) self.table.setCurrentItem(item) self.table.scrollToItem(item) diff --git a/src/calibre/gui2/dialogs/edit_authors_dialog.ui b/src/calibre/gui2/dialogs/edit_authors_dialog.ui index 5a17f81394..ea1dd36d35 100644 --- a/src/calibre/gui2/dialogs/edit_authors_dialog.ui +++ b/src/calibre/gui2/dialogs/edit_authors_dialog.ui @@ -182,7 +182,19 @@ after changing Preferences->Advanced->Tweaks->Author sort name algorith - + + + + <p>Edit the currently selected cell. If an author is edited then it is renamed + in every book where it is used. Double-clicking and pressing the edit + key also work.</p> + + + Edi&t current cell + + + + diff --git a/src/calibre/gui2/dialogs/tag_list_editor.py b/src/calibre/gui2/dialogs/tag_list_editor.py index 255261c086..51cc716336 100644 --- a/src/calibre/gui2/dialogs/tag_list_editor.py +++ b/src/calibre/gui2/dialogs/tag_list_editor.py @@ -33,6 +33,9 @@ CHECK_MARK = '✓' class NameTableWidgetItem(QTableWidgetItem): + empty_icon = QIcon() + trash_icon = QIcon.ic('trash.png') + def __init__(self, sort_key): QTableWidgetItem.__init__(self) self.initial_value = '' @@ -53,9 +56,9 @@ class NameTableWidgetItem(QTableWidgetItem): def set_is_deleted(self, to_what): if to_what: - self.setIcon(QIcon.ic('trash.png')) + self.setIcon(self.trash_icon) else: - self.setIcon(QIcon()) + self.setIcon(self.empty_icon) self.current_value = self.initial_value self.is_deleted = to_what @@ -116,8 +119,39 @@ class CountTableWidgetItem(QTableWidgetItem): return self._count < other._count +class NotesTableWidgetItem(QTableWidgetItem): + + # These define the sort order for notes columns + EMPTY = 0 + UNCHANGED = 1 + EDITED = 2 + DELETED = 3 + + def __init__(self): + QTableWidgetItem.__init__(self, '') + self.set_sort_val(self.EMPTY) + + def set_sort_val(self, val): + self._sort_val = val + + def __ge__(self, other): + return self._sort_val >= other._sort_val + + def __lt__(self, other): + return self._sort_val < other._sort_val + + class NotesUtilities(): + edit_icon = QIcon.ic('edit_input.png') + edited_icon = QIcon.ic('modified.png') + empty_icon = QIcon() + export_icon = QIcon.ic('forward.png') + import_icon = QIcon.ic('back.png') + pencil_icon = QIcon.ic('notes.png') + trash_icon = QIcon.ic('trash.png') + undo_delete_icon = QIcon.ic('edit-undo.png') + def __init__(self, table, category, item_id_getter): self.table = table self.modified_notes = {} @@ -141,9 +175,25 @@ class NotesUtilities(): db.set_notes_for(self.category, item_id, '') self.modified_notes.clear() - def change_text(self, item, val): + def set_icon(self, item, id_, has_value): with block_signals(self.table): - item.setText(CHECK_MARK if bool(val) else '') + if id_ not in self.modified_notes: + if not has_value: + item.setIcon(self.empty_icon) + item.set_sort_val(NotesTableWidgetItem.EMPTY) + else: + item.setIcon(self.pencil_icon) + item.set_sort_val(NotesTableWidgetItem.UNCHANGED) + else: + if has_value: + item.setIcon(self.edited_icon) + item.set_sort_val(NotesTableWidgetItem.EDITED) + elif not bool(self.modified_notes[id_]): + item.setIcon(self.empty_icon) + item.set_sort_val(NotesTableWidgetItem.EMPTY) + else: + item.setIcon(self.trash_icon) + item.set_sort_val(NotesTableWidgetItem.DELETED) self.table.cellChanged.emit(item.row(), item.column()) self.table.itemChanged.emit(item) @@ -158,7 +208,7 @@ class NotesUtilities(): after = db.notes_for(self.category, item_id) if item_id not in self.modified_notes: self.modified_notes[item_id] = note - self.change_text(item, after) + self.set_icon(item, item_id, bool(after)) def undo_note_edit(self, item): item_id = self.item_id_getter(item) @@ -169,7 +219,7 @@ class NotesUtilities(): db.import_note(self.category, item_id, before.encode('utf-8'), path_is_data=True) else: db.set_notes_for(self.category, item_id, '') - self.change_text(item, before) + self.set_icon(item, item_id, bool(before)) def delete_note(self, item): item_id = self.item_id_getter(item) @@ -177,7 +227,7 @@ class NotesUtilities(): if item_id not in self.modified_notes: self.modified_notes[item_id] = db.notes_for(self.category, item_id) db.set_notes_for(self.category, item_id, '') - self.change_text(item, False) + self.set_icon(item, item_id, False) def do_export(self, item, item_name): item_id = self.item_id_getter(item) @@ -202,13 +252,7 @@ class NotesUtilities(): self.modified_notes[item_id] = before db.import_note(self.category, item_id, src[0]) after = db.notes_for(self.category, item_id) - self.change_text(item, after) - - edit_icon = QIcon.ic('edit_input.png') - delete_icon = QIcon.ic('trash.png') - undo_delete_icon = QIcon.ic('edit-undo.png') - export_icon = QIcon.ic('forward.png') - import_icon = QIcon.ic('back.png') + self.set_icon(item, item_id, bool(after)) def context_menu(self, menu, item, item_name): m = menu @@ -224,7 +268,7 @@ class NotesUtilities(): ac = m.addAction(self.edit_icon, _('Edit note') if has_note else _('Create note')) ac.triggered.connect(partial(self.table.editItem, item)) - ac = m.addAction(self.delete_icon, _('Delete note')) + ac = m.addAction(self.trash_icon, _('Delete note')) ac.setEnabled(has_note) ac.triggered.connect(partial(self.delete_note, item)) @@ -297,6 +341,7 @@ def block_signals(widget): class TagListEditor(QDialog, Ui_TagListEditor): edited_icon = QIcon.ic('modified.png') + empty_icon = QIcon() def __init__(self, window, cat_name, tag_to_match, get_book_ids, sorter, ttm_is_first_letter=False, category=None, fm=None, link_map=None): @@ -348,7 +393,7 @@ class TagListEditor(QDialog, Ui_TagListEditor): self.string_contains = self.case_insensitive_compare self.delete_button.clicked.connect(self.delete_tags) - self.rename_button.clicked.connect(self.rename_tag) + self.rename_button.clicked.connect(self.edit_button_clicked) self.undo_button.clicked.connect(self.undo_edit) self.buttonBox.button(QDialogButtonBox.StandardButton.Ok).setText(_('&OK')) @@ -449,7 +494,7 @@ class TagListEditor(QDialog, Ui_TagListEditor): ca = m.addAction(_('Edit')) ca.setIcon(QIcon.ic('edit_input.png')) - ca.triggered.connect(self.rename_tag) + ca.triggered.connect(self.edit_button_clicked) ca.setEnabled(not item.is_deleted) ca = m.addAction(_('Delete')) @@ -622,7 +667,7 @@ class TagListEditor(QDialog, Ui_TagListEditor): self.table.setItemDelegateForColumn(NOTES_COLUMN, self.edit_delegate) self.table.delete_pressed.connect(self.delete_pressed) - self.table.itemDoubleClicked.connect(self._rename_tag) + self.table.itemDoubleClicked.connect(self.edit_item) self.table.itemChanged.connect(self.finish_editing) self.table.itemSelectionChanged.connect(self.selection_changed) @@ -665,7 +710,7 @@ class TagListEditor(QDialog, Ui_TagListEditor): if self.link_is_edited(id_): item.setIcon(self.edited_icon) else: - item.setIcon(QIcon()) + item.setIcon(self.empty_icon) def fill_in_table(self, tags, tag_to_match, ttm_is_first_letter): self.create_table() @@ -757,13 +802,12 @@ class TagListEditor(QDialog, Ui_TagListEditor): self.table.setItem(row, LINK_COLUMN, item) if self.supports_notes: - item = QTableWidgetItem() - self.notes_utilities.change_text(item, id_ in all_items_that_have_notes) + item = NotesTableWidgetItem() + self.notes_utilities.set_icon(item, id_, id_ in all_items_that_have_notes) if is_deleted: item.setFlags(item.flags() & ~(Qt.ItemFlag.ItemIsSelectable|Qt.ItemFlag.ItemIsEditable)) else: item.setFlags(item.flags() | (Qt.ItemFlag.ItemIsSelectable|Qt.ItemFlag.ItemIsEditable)) - item.setIcon(self.edited_icon if id_ in self.notes_utilities.modified_notes else QIcon()) self.table.setItem(row, NOTES_COLUMN, item) # re-sort the table @@ -851,9 +895,7 @@ class TagListEditor(QDialog, Ui_TagListEditor): return if edited_item.column() == NOTES_COLUMN: - id_ = self.get_item_id(edited_item) - with block_signals(self.table): - edited_item.setIcon(self.edited_icon if id_ in self.notes_utilities.modified_notes else QIcon()) + # Done elsewhere return # Item value column @@ -884,7 +926,7 @@ class TagListEditor(QDialog, Ui_TagListEditor): item.setIcon(self.edited_icon) orig.setData(Qt.ItemDataRole.DisplayRole, item.initial_text()) else: - item.setIcon(QIcon()) + item.setIcon(self.empty_icon) orig.setData(Qt.ItemDataRole.DisplayRole, '') def undo_link_edit(self, item, item_id): @@ -896,7 +938,7 @@ class TagListEditor(QDialog, Ui_TagListEditor): item = self.table.item(item.row(), LINK_COLUMN) item.setFlags(item.flags() | Qt.ItemFlag.ItemIsEditable | Qt.ItemFlag.ItemIsSelectable) item.setText(link_txt) - item.setIcon(QIcon()) + item.setIcon(self.empty_icon) def undo_value_edit(self, item, item_id): with block_signals(self.table): @@ -904,7 +946,7 @@ class TagListEditor(QDialog, Ui_TagListEditor): self.to_rename.pop(item_id, None) row = item.row() self.table.item(row, WAS_COLUMN).setData(Qt.ItemDataRole.DisplayRole, '') - item.setIcon(self.edited_icon if item.text_is_modified() else QIcon()) + item.setIcon(self.edited_icon if item.text_is_modified() else self.empty_icon) def undo_edit(self): col_zero_items = (self.table.item(item.row(), VALUE_COLUMN) for item in self.table.selectedItems()) @@ -934,7 +976,7 @@ class TagListEditor(QDialog, Ui_TagListEditor): item.setFlags(item.flags() | Qt.ItemFlag.ItemIsEditable | Qt.ItemFlag.ItemIsSelectable) if id_ in self.notes_utilities.modified_notes: self.notes_utilities.undo_note_edit(item) - item.setIcon(QIcon()) + item.setIcon(self.empty_icon) def selection_changed(self): if self.table.currentIndex().isValid(): @@ -957,13 +999,10 @@ class TagListEditor(QDialog, Ui_TagListEditor): return True return False - def rename_tag(self): - if self.table.currentColumn() != VALUE_COLUMN: - return - item = self.table.item(self.table.currentRow(), VALUE_COLUMN) - self._rename_tag(item) + def edit_button_clicked(self): + self.edit_item(self.table.currentItem()) - def _rename_tag(self, item): + def edit_item(self, item): if item is None: error_dialog(self, _('No item selected'), _('You must select one item from the list of available items.')).exec() diff --git a/src/calibre/gui2/dialogs/tag_list_editor.ui b/src/calibre/gui2/dialogs/tag_list_editor.ui index a075f7ee9d..cafbd25869 100644 --- a/src/calibre/gui2/dialogs/tag_list_editor.ui +++ b/src/calibre/gui2/dialogs/tag_list_editor.ui @@ -172,7 +172,9 @@ - Delete selected items from the database. This will unapply the items from all books and then remove them from the database. + <p>Delete selected items from the database. + This will unapply the items from all books and then remove them + from the database. This button's shortcut is Ctrl+D</p> ... @@ -208,7 +210,9 @@ - Rename the items in every book where they are used + <p>Edit the currently selected cell. If a tag is edited then it is renamed + in every book where it is used. Double-clicking and pressing the edit + key also work. This button's shortcut is Ctrl+E</p> ... @@ -224,7 +228,7 @@ - Ctrl+R + Ctrl+E @@ -244,7 +248,9 @@ - Undo any deletes or edits on the selected lines + <p>Undo all deletes or edits on the selected lines. + This button's shortcut is Ctrl+U</p> + ...