mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Fix sorting by notes column in manage category dialogs
Also remove the notesitemwidget as its performance is unacceptable
This commit is contained in:
parent
b9e24ffb07
commit
823ec12f02
@ -15,7 +15,6 @@ from qt.core import (
|
|||||||
from calibre.ebooks.metadata import author_to_author_sort, string_to_authors
|
from calibre.ebooks.metadata import author_to_author_sort, string_to_authors
|
||||||
from calibre.gui2 import error_dialog, gprefs
|
from calibre.gui2 import error_dialog, gprefs
|
||||||
from calibre.gui2.dialogs.edit_authors_dialog_ui import Ui_EditAuthorsDialog
|
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 import prefs
|
||||||
from calibre.utils.config_base import tweaks
|
from calibre.utils.config_base import tweaks
|
||||||
from calibre.utils.icu import (
|
from calibre.utils.icu import (
|
||||||
@ -28,9 +27,9 @@ QT_HIDDEN_CLEAR_ACTION = '_q_qlineeditclearaction'
|
|||||||
|
|
||||||
class tableItem(QTableWidgetItem):
|
class tableItem(QTableWidgetItem):
|
||||||
|
|
||||||
def __init__(self, txt):
|
def __init__(self, txt, skey=None):
|
||||||
QTableWidgetItem.__init__(self, txt)
|
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):
|
def setText(self, txt):
|
||||||
self.sort_key = sort_key(str(txt))
|
self.sort_key = sort_key(str(txt))
|
||||||
@ -177,6 +176,7 @@ class EditAuthorsDialog(QDialog, Ui_EditAuthorsDialog):
|
|||||||
self.author_order = 1
|
self.author_order = 1
|
||||||
self.author_sort_order = 0
|
self.author_sort_order = 0
|
||||||
self.link_order = 1
|
self.link_order = 1
|
||||||
|
self.notes_order = 1
|
||||||
self.show_table(id_to_select, select_sort, select_link, is_first_letter)
|
self.show_table(id_to_select, select_sort, select_link, is_first_letter)
|
||||||
|
|
||||||
@contextmanager
|
@contextmanager
|
||||||
@ -214,6 +214,8 @@ class EditAuthorsDialog(QDialog, Ui_EditAuthorsDialog):
|
|||||||
row = 0
|
row = 0
|
||||||
from calibre.gui2.ui import get_gui
|
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')
|
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():
|
for id_, v in self.authors.items():
|
||||||
if id_ not in auts_to_show:
|
if id_ not in auts_to_show:
|
||||||
continue
|
continue
|
||||||
@ -228,9 +230,10 @@ class EditAuthorsDialog(QDialog, Ui_EditAuthorsDialog):
|
|||||||
self.table.setItem(row, 0, name_item)
|
self.table.setItem(row, 0, name_item)
|
||||||
self.table.setItem(row, 1, sort_item)
|
self.table.setItem(row, 1, sort_item)
|
||||||
self.table.setItem(row, 2, link_item)
|
self.table.setItem(row, 2, link_item)
|
||||||
|
if id_ in all_items_that_have_notes:
|
||||||
nw = NotesItemWidget('authors', id_, id_ in all_items_that_have_notes)
|
self.table.setItem(row, 3, tableItem(yes, yes_skey))
|
||||||
self.table.setCellWidget(row, 3, nw)
|
else:
|
||||||
|
self.table.setItem(row, 3, tableItem(no, no_skey))
|
||||||
|
|
||||||
self.set_icon(name_item, id_)
|
self.set_icon(name_item, id_)
|
||||||
self.set_icon(sort_item, id_)
|
self.set_icon(sort_item, id_)
|
||||||
@ -246,9 +249,12 @@ class EditAuthorsDialog(QDialog, Ui_EditAuthorsDialog):
|
|||||||
elif self.last_sorted_by == 'author':
|
elif self.last_sorted_by == 'author':
|
||||||
self.author_order = 1 - self.author_order
|
self.author_order = 1 - self.author_order
|
||||||
self.do_sort_by_author()
|
self.do_sort_by_author()
|
||||||
else:
|
elif self.last_sorted_by == 'link':
|
||||||
self.link_order = 1 - self.link_order
|
self.link_order = 1 - self.link_order
|
||||||
self.do_sort_by_link()
|
self.do_sort_by_link()
|
||||||
|
else:
|
||||||
|
self.notes_order = 1 - self.notes_order
|
||||||
|
self.do_sort_by_notes()
|
||||||
|
|
||||||
# Position on the desired item
|
# Position on the desired item
|
||||||
select_item = None
|
select_item = None
|
||||||
@ -443,7 +449,7 @@ class EditAuthorsDialog(QDialog, Ui_EditAuthorsDialog):
|
|||||||
self.not_found_label_timer.start(1500)
|
self.not_found_label_timer.start(1500)
|
||||||
|
|
||||||
def do_sort(self, section):
|
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):
|
def do_sort_by_author(self):
|
||||||
self.last_sorted_by = 'author'
|
self.last_sorted_by = 'author'
|
||||||
@ -460,6 +466,11 @@ class EditAuthorsDialog(QDialog, Ui_EditAuthorsDialog):
|
|||||||
self.link_order = 1 - self.link_order
|
self.link_order = 1 - self.link_order
|
||||||
self.table.sortByColumn(2, Qt.SortOrder(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):
|
def accepted(self):
|
||||||
self.save_state()
|
self.save_state()
|
||||||
self.result = []
|
self.result = []
|
||||||
|
@ -5,16 +5,14 @@
|
|||||||
from functools import partial
|
from functools import partial
|
||||||
from qt.core import (
|
from qt.core import (
|
||||||
QAbstractItemView, QAction, QApplication, QCheckBox, QColor, QDialog,
|
QAbstractItemView, QAction, QApplication, QCheckBox, QColor, QDialog,
|
||||||
QDialogButtonBox, QEvent, QFrame, QHBoxLayout, QIcon, QItemDelegate, QLabel, QMenu,
|
QDialogButtonBox, QEvent, QFrame, QIcon, QItemDelegate, QLabel, QMenu,
|
||||||
QSize, Qt, QTableWidgetItem, QTimer, QToolButton, QWidget, pyqtSignal, sip,
|
QSize, Qt, QTableWidgetItem, QTimer, QToolButton, pyqtSignal, sip,
|
||||||
)
|
)
|
||||||
|
|
||||||
from calibre import sanitize_file_name
|
from calibre.gui2 import error_dialog, gprefs, question_dialog
|
||||||
from calibre.gui2 import error_dialog, gprefs, question_dialog, choose_files, choose_save_file
|
|
||||||
from calibre.gui2.actions.show_quickview import get_quickview_action_plugin
|
from calibre.gui2.actions.show_quickview import get_quickview_action_plugin
|
||||||
from calibre.gui2.complete2 import EditWithComplete
|
from calibre.gui2.complete2 import EditWithComplete
|
||||||
from calibre.gui2.dialogs.confirm_delete import confirm
|
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_ui import Ui_TagListEditor
|
||||||
from calibre.gui2.dialogs.tag_list_editor_table_widget import TleTableWidget
|
from calibre.gui2.dialogs.tag_list_editor_table_widget import TleTableWidget
|
||||||
from calibre.gui2.widgets import EnLineEdit
|
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)
|
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):
|
class TagListEditor(QDialog, Ui_TagListEditor):
|
||||||
|
|
||||||
VALUE_COLUMN = 0
|
VALUE_COLUMN = 0
|
||||||
@ -361,9 +207,9 @@ class TagListEditor(QDialog, Ui_TagListEditor):
|
|||||||
self.get_book_ids = get_book_ids
|
self.get_book_ids = get_book_ids
|
||||||
self.text_before_editing = ''
|
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.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']:
|
if prefs['case_sensitive']:
|
||||||
self.string_contains = contains
|
self.string_contains = contains
|
||||||
@ -644,8 +490,9 @@ class TagListEditor(QDialog, Ui_TagListEditor):
|
|||||||
self.table.setHorizontalHeaderItem(4, self.link_col)
|
self.table.setHorizontalHeaderItem(4, self.link_col)
|
||||||
|
|
||||||
self.table.setRowCount(len(tags))
|
self.table.setRowCount(len(tags))
|
||||||
from calibre.gui2.ui import get_gui
|
if self.category is not None:
|
||||||
all_items_that_have_notes = get_gui().current_db.new_api.get_all_items_that_have_notes(self.category)
|
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):
|
for row,tag in enumerate(tags):
|
||||||
item = NameTableWidgetItem(self.sorter)
|
item = NameTableWidgetItem(self.sorter)
|
||||||
is_deleted = self.all_tags[tag]['is_deleted']
|
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)
|
self.table.setItem(row, self.LINK_COLUMN, item)
|
||||||
|
|
||||||
if self.category is not None:
|
if self.category is not None:
|
||||||
nw = NotesItemWidget(self.category, _id, _id in all_items_that_have_notes)
|
self.table.setItem(row, self.NOTES_COLUMN, QTableWidgetItem('✓' if _id in all_items_that_have_notes else ''))
|
||||||
self.table.setCellWidget(row, 4, nw)
|
|
||||||
|
|
||||||
# re-sort the table
|
# re-sort the table
|
||||||
column = self.sort_names.index(self.last_sorted_by)
|
column = self.sort_names.index(self.last_sorted_by)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user