diff --git a/src/calibre/gui2/dialogs/edit_authors_dialog.py b/src/calibre/gui2/dialogs/edit_authors_dialog.py index f5077abde5..2981e3f39d 100644 --- a/src/calibre/gui2/dialogs/edit_authors_dialog.py +++ b/src/calibre/gui2/dialogs/edit_authors_dialog.py @@ -15,7 +15,6 @@ 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 NotesItemWidget from calibre.utils.config import prefs from calibre.utils.config_base import tweaks from calibre.utils.icu import ( @@ -28,9 +27,9 @@ QT_HIDDEN_CLEAR_ACTION = '_q_qlineeditclearaction' class tableItem(QTableWidgetItem): - def __init__(self, txt): + def __init__(self, txt, skey=None): QTableWidgetItem.__init__(self, txt) - self.sort_key = sort_key(str(txt)) + self.sort_key = sort_key(str(txt)) if skey is None else skey def setText(self, txt): self.sort_key = sort_key(str(txt)) @@ -177,6 +176,7 @@ class EditAuthorsDialog(QDialog, Ui_EditAuthorsDialog): self.author_order = 1 self.author_sort_order = 0 self.link_order = 1 + self.notes_order = 1 self.show_table(id_to_select, select_sort, select_link, is_first_letter) @contextmanager @@ -214,6 +214,8 @@ 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 = '✓', sort_key('✓') + no, no_skey = '', sort_key('') for id_, v in self.authors.items(): if id_ not in auts_to_show: continue @@ -228,9 +230,10 @@ class EditAuthorsDialog(QDialog, Ui_EditAuthorsDialog): self.table.setItem(row, 0, name_item) self.table.setItem(row, 1, sort_item) self.table.setItem(row, 2, link_item) - - nw = NotesItemWidget('authors', id_, id_ in all_items_that_have_notes) - self.table.setCellWidget(row, 3, nw) + if id_ in all_items_that_have_notes: + self.table.setItem(row, 3, tableItem(yes, yes_skey)) + else: + self.table.setItem(row, 3, tableItem(no, no_skey)) self.set_icon(name_item, id_) self.set_icon(sort_item, id_) @@ -246,9 +249,12 @@ class EditAuthorsDialog(QDialog, Ui_EditAuthorsDialog): elif self.last_sorted_by == 'author': self.author_order = 1 - self.author_order self.do_sort_by_author() - else: + elif self.last_sorted_by == 'link': self.link_order = 1 - self.link_order self.do_sort_by_link() + else: + self.notes_order = 1 - self.notes_order + self.do_sort_by_notes() # Position on the desired item select_item = None @@ -443,7 +449,7 @@ class EditAuthorsDialog(QDialog, Ui_EditAuthorsDialog): self.not_found_label_timer.start(1500) def do_sort(self, section): - (self.do_sort_by_author, self.do_sort_by_author_sort, self.do_sort_by_link)[section]() + (self.do_sort_by_author, self.do_sort_by_author_sort, self.do_sort_by_link, self.do_sort_by_notes)[section]() def do_sort_by_author(self): self.last_sorted_by = 'author' @@ -460,6 +466,11 @@ class EditAuthorsDialog(QDialog, Ui_EditAuthorsDialog): self.link_order = 1 - self.link_order self.table.sortByColumn(2, Qt.SortOrder(self.link_order)) + def do_sort_by_notes(self): + self.last_sorted_by = 'notes' + self.notes_order = 1 - self.notes_order + self.table.sortByColumn(3, Qt.SortOrder(self.notes_order)) + def accepted(self): self.save_state() self.result = [] diff --git a/src/calibre/gui2/dialogs/tag_list_editor.py b/src/calibre/gui2/dialogs/tag_list_editor.py index 53bda5abaa..e0aaa3951c 100644 --- a/src/calibre/gui2/dialogs/tag_list_editor.py +++ b/src/calibre/gui2/dialogs/tag_list_editor.py @@ -5,16 +5,14 @@ from functools import partial from qt.core import ( QAbstractItemView, QAction, QApplication, QCheckBox, QColor, QDialog, - QDialogButtonBox, QEvent, QFrame, QHBoxLayout, QIcon, QItemDelegate, QLabel, QMenu, - QSize, Qt, QTableWidgetItem, QTimer, QToolButton, QWidget, pyqtSignal, sip, + QDialogButtonBox, QEvent, QFrame, QIcon, QItemDelegate, QLabel, QMenu, + QSize, Qt, QTableWidgetItem, QTimer, QToolButton, pyqtSignal, sip, ) -from calibre import sanitize_file_name -from calibre.gui2 import error_dialog, gprefs, question_dialog, choose_files, choose_save_file +from calibre.gui2 import error_dialog, gprefs, question_dialog from calibre.gui2.actions.show_quickview import get_quickview_action_plugin from calibre.gui2.complete2 import EditWithComplete from calibre.gui2.dialogs.confirm_delete import confirm -from calibre.gui2.dialogs.edit_category_notes import EditNoteDialog from calibre.gui2.dialogs.tag_list_editor_ui import Ui_TagListEditor from calibre.gui2.dialogs.tag_list_editor_table_widget import TleTableWidget from calibre.gui2.widgets import EnLineEdit @@ -172,158 +170,6 @@ class MyCheckBox(QCheckBox): self.event = partial(event, me=self, super_class=super(), context_menu_handler=context_menu_handler) -class NotesItemWidget(QWidget): - ''' - This is a self-contained widget for manipulating notes. It can be used in a - table (as a cellWidget) or in a layout. It currently contains a check box - indicating that the item has a note, and buttons to edit/create or delete a - note, or undo a deletion. - ''' - - 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') - - @property - def db(self): - from calibre.gui2.ui import get_gui - return get_gui().current_db.new_api - - @property - def item_val(self): - if self._item_val is None: - self._item_val = self.db.get_item_name(self.field, self._item_id) - return self._item_val - - @property - def item_id(self): - if self._item_id is None: - self._item_id = self.db.get_item_id(self.field, self._item_val) - if self._item_id is None: - raise KeyError(f'The value: {self._item_val} is not found in the field: {self.field}') - return self._item_id - - def __init__(self, field, item_id_or_val, has_notes): - ''' - :param db: A database instance, either old or new api - :param field: the lookup name of a field - :param item_id_or_val: Either the numeric item_id of an item in the field or - the item's string value - ''' - super().__init__() - self.field = field - self._item_val = self._item_id = None - self.has_notes = has_notes - if isinstance(item_id_or_val, str): - self._item_val = item_id_or_val - else: - self._item_id = item_id_or_val - self.can_undo = False - - self.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu) - - l = QHBoxLayout() - l.setContentsMargins(2, 0, 0, 0) - self.setLayout(l) - cb = self.cb = MyCheckBox(self.show_context_menu) - cb.setEnabled(False) - l.addWidget(cb) - - self.buttons = {} - for button_data in (('edit', 'Edit or create the note. Changes cannot be undone or cancelled'), - ('delete', 'Delete the note'), - ('undo_delete', 'Undo the deletion')): - button_name = button_data[0] - tool_tip = button_data[1] - b = self.buttons[button_name] = MyToolButton(self.show_context_menu) - b.setIcon(getattr(self, button_name + '_icon')) - b.setToolTip(tool_tip) - b.clicked.connect(getattr(self, 'do_' + button_name)) - b.setContentsMargins(0, 0, 0, 0) - l.addWidget(b) - l.addStretch(3) - - self.setFocusPolicy(Qt.FocusPolicy.StrongFocus) - self.set_checked(refresh=False) - self.customContextMenuRequested.connect(self.show_context_menu) - - @classmethod - def get_item_id(cls, db, field: str, value: str): - return db.new_api.get_item_id(field, value) - - def show_context_menu(self, point): - m = QMenu() - ac = m.addAction(self.edit_icon, _('Edit note') if self.cb.isChecked() else _('Create note')) - ac.triggered.connect(self.do_edit) - - ac = m.addAction(self.delete_icon, _('Delete note')) - ac.setEnabled(self.cb.isChecked()) - ac.triggered.connect(self.do_delete) - - ac = m.addAction(self.undo_delete_icon, _('Undo delete')) - ac.setEnabled(self.can_undo) - ac.triggered.connect(self.do_undo_delete) - - ac = m.addAction(self.export_icon, _('Export note to a file')) - ac.setEnabled(self.cb.isChecked()) - ac.triggered.connect(self.do_export) - - ac = m.addAction(self.import_icon, _('Import note from a file')) - ac.setEnabled(not self.cb.isChecked()) - ac.triggered.connect(self.do_import) - - m.exec(self.mapToGlobal(point)) - - def do_edit(self): - accepted = EditNoteDialog(self.field, self.item_id, self.db).exec() - # Continue to allow an undo if it was allowed before and the dialog was cancelled. - self.can_undo = not accepted and self.can_undo - self.set_checked() - - def do_delete(self): - self.db.set_notes_for(self.field, self.item_id, '') - self.can_undo = True - self.set_checked() - - def do_undo_delete(self): - if self.can_undo: - self.db.unretire_note_for(self.field, self.item_id) - self.can_undo = False - self.set_checked() - - def do_export(self): - dest = choose_save_file(self, 'save-exported-note', _('Export note to a file'), - filters=[(_('HTML files'), ['html'])], - initial_filename=f'{sanitize_file_name(self.item_val)}.html', - all_files=False) - if dest: - html = self.db.export_note(self.field, self.item_id) - with open(dest, 'wb') as f: - f.write(html.encode('utf-8')) - - def do_import(self): - src = choose_files(self, 'load-imported-note', _('Import note from a file'), - filters=[(_('HTML files'), ['html'])], - all_files=False, select_only_single_file=True) - if src: - self.db.import_note(self.field, self.item_id, src[0]) - self.can_undo = False - self.set_checked() - - def set_checked(self, refresh=True): - if refresh: - self.has_notes = bool(self.db.notes_for(self.field, self.item_id)) - self.cb.setChecked(self.has_notes) - self.buttons['delete'].setEnabled(self.has_notes) - self.buttons['undo_delete'].setEnabled(self.can_undo) - - def is_checked(self): - # returns True if the checkbox is checked, meaning the note contains text - return self.cb.isChecked() - - class TagListEditor(QDialog, Ui_TagListEditor): VALUE_COLUMN = 0 @@ -361,9 +207,9 @@ class TagListEditor(QDialog, Ui_TagListEditor): self.get_book_ids = get_book_ids self.text_before_editing = '' - self.sort_names = ('name', 'count', 'was', 'link') + self.sort_names = ('name', 'count', 'was', 'link', 'notes') self.last_sorted_by = 'name' - self.name_order = self.count_order = self.was_order = self.link_order = 0 + self.name_order = self.count_order = self.was_order = self.link_order = self.notes_order = 0 if prefs['case_sensitive']: self.string_contains = contains @@ -644,8 +490,9 @@ class TagListEditor(QDialog, Ui_TagListEditor): self.table.setHorizontalHeaderItem(4, self.link_col) self.table.setRowCount(len(tags)) - from calibre.gui2.ui import get_gui - all_items_that_have_notes = get_gui().current_db.new_api.get_all_items_that_have_notes(self.category) + if self.category is not None: + from calibre.gui2.ui import get_gui + all_items_that_have_notes = get_gui().current_db.new_api.get_all_items_that_have_notes(self.category) for row,tag in enumerate(tags): item = NameTableWidgetItem(self.sorter) is_deleted = self.all_tags[tag]['is_deleted'] @@ -697,8 +544,7 @@ class TagListEditor(QDialog, Ui_TagListEditor): self.table.setItem(row, self.LINK_COLUMN, item) if self.category is not None: - nw = NotesItemWidget(self.category, _id, _id in all_items_that_have_notes) - self.table.setCellWidget(row, 4, nw) + self.table.setItem(row, self.NOTES_COLUMN, QTableWidgetItem('✓' if _id in all_items_that_have_notes else '')) # re-sort the table column = self.sort_names.index(self.last_sorted_by)