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> + ...