Add Category edit buttons to columns that have category editors.

Authors, Series, Tags, Publisher, custom columns
This commit is contained in:
Charles Haley 2023-04-02 12:31:33 +01:00
parent 8e1982c700
commit d2d69fedfe
3 changed files with 179 additions and 50 deletions

View File

@ -137,6 +137,27 @@ class Base:
def connect_data_changed(self, slot):
pass
def values_changed(self):
return self.getter() != self.initial_val and (self.getter() or self.initial_val)
def edit(self):
if self.values_changed():
d = _save_dialog(self.parent, _('Values changed'),
_('You have changed the values. In order to use this '
'editor, you must either discard or apply these '
'changes. Apply changes?'))
if d == QMessageBox.StandardButton.Cancel:
return
if d == QMessageBox.StandardButton.Yes:
self.commit(self.book_id)
self.db.commit()
self.initial_val = self.current_val
else:
self.setter(self.initial_val)
from calibre.gui2.ui import get_gui
get_gui().do_tags_list_edit(None, self.key)
self.initialize(self.book_id)
class SimpleText(Base):
@ -448,16 +469,22 @@ class Comments(Base):
class MultipleWidget(QWidget):
def __init__(self, parent):
def __init__(self, parent, only_manage_items=False, widget=EditWithComplete, name=None):
QWidget.__init__(self, parent)
layout = QHBoxLayout()
layout.setSpacing(5)
layout.setContentsMargins(0, 0, 0, 0)
self.tags_box = EditWithComplete(parent)
layout.addWidget(self.tags_box, stretch=1000)
self.edit_widget = widget(parent)
layout.addWidget(self.edit_widget, stretch=1000)
self.editor_button = QToolButton(self)
self.editor_button.setToolTip(_('Open Item editor. If CTRL or SHIFT is pressed, open Manage items'))
if name is None:
name = _('items')
if only_manage_items:
self.editor_button.setToolTip(_('Open the {} Category editor').format(name))
else:
self.editor_button.setToolTip(_('Open the {} editor. If CTRL or SHIFT '
'is pressed, open the {} Category editor').format(name, name))
self.editor_button.setIcon(QIcon.ic('chapters.png'))
layout.addWidget(self.editor_button)
self.setLayout(layout)
@ -466,34 +493,34 @@ class MultipleWidget(QWidget):
return self.editor_button
def update_items_cache(self, values):
self.tags_box.update_items_cache(values)
self.edit_widget.update_items_cache(values)
def clear(self):
self.tags_box.clear()
self.edit_widget.clear()
def setEditText(self):
self.tags_box.setEditText()
self.edit_widget.setEditText()
def addItem(self, itm):
self.tags_box.addItem(itm)
self.edit_widget.addItem(itm)
def set_separator(self, sep):
self.tags_box.set_separator(sep)
self.edit_widget.set_separator(sep)
def set_add_separator(self, sep):
self.tags_box.set_add_separator(sep)
self.edit_widget.set_add_separator(sep)
def set_space_before_sep(self, v):
self.tags_box.set_space_before_sep(v)
self.edit_widget.set_space_before_sep(v)
def setSizePolicy(self, v1, v2):
self.tags_box.setSizePolicy(v1, v2)
self.edit_widget.setSizePolicy(v1, v2)
def setText(self, v):
self.tags_box.setText(v)
self.edit_widget.setText(v)
def text(self):
return self.tags_box.text()
return self.edit_widget.text()
def _save_dialog(parent, title, msg, det_msg=''):
@ -513,20 +540,18 @@ class Text(Base):
self.parent = parent
if self.col_metadata['is_multiple']:
w = MultipleWidget(parent)
w = MultipleWidget(parent, name=self.col_metadata['name'])
w.set_separator(self.sep['ui_to_list'])
if self.sep['ui_to_list'] == '&':
w.set_space_before_sep(True)
w.set_add_separator(tweaks['authors_completer_append_separator'])
w.get_editor_button().clicked.connect(self.edit)
w.setSizePolicy(QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Fixed)
self.set_to_undefined = w.clear
else:
w = EditWithComplete(parent)
w = MultipleWidget(parent, only_manage_items=True, name=self.col_metadata['name'])
w.set_separator(None)
w.setSizeAdjustPolicy(QComboBox.SizeAdjustPolicy.AdjustToMinimumContentsLengthWithIcon)
w.setMinimumContentsLength(25)
self.set_to_undefined = w.clearEditText
w.get_editor_button().clicked.connect(super().edit)
w.setSizePolicy(QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Fixed)
self.set_to_undefined = w.clear
self.widgets = [QLabel(label_string(self.col_metadata['name']), parent)]
self.finish_ui_setup(parent, lambda parent: w)
@ -555,13 +580,12 @@ class Text(Base):
self.editor.setText(self.sep['list_to_ui'].join(val))
def getter(self):
val = str(self.editor.text()).strip()
if self.col_metadata['is_multiple']:
val = str(self.editor.text()).strip()
ans = [x.strip() for x in val.split(self.sep['ui_to_list']) if x.strip()]
if not ans:
ans = None
return ans
val = str(self.editor.currentText()).strip()
if not val:
val = None
return val
@ -592,10 +616,7 @@ class Text(Base):
self.setter(d.tags)
def connect_data_changed(self, slot):
if self.col_metadata['is_multiple']:
s = self.editor.tags_box.currentTextChanged
else:
s = self.editor.currentTextChanged
s = self.editor.edit_widget.currentTextChanged
s.connect(slot)
self.signals_to_disconnect.append(s)
@ -603,14 +624,18 @@ class Text(Base):
class Series(Base):
def setup_ui(self, parent):
w = EditWithComplete(parent, sort_func=title_sort)
self.parent = parent
self.key = self.db.field_metadata.label_to_key(self.col_metadata['label'],
prefer_custom=True)
w = MultipleWidget(parent, only_manage_items=True, name=self.col_metadata['name'])
w.get_editor_button().clicked.connect(self.edit)
w.setSizePolicy(QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Fixed)
self.set_to_undefined = w.clear
w.set_separator(None)
w.setSizeAdjustPolicy(QComboBox.SizeAdjustPolicy.AdjustToMinimumContentsLengthWithIcon)
w.setMinimumContentsLength(25)
self.name_widget = w
self.name_widget = w.edit_widget
self.widgets = [QLabel(label_string(self.col_metadata['name']), parent)]
self.finish_ui_setup(parent, lambda parent: w)
w.editTextChanged.connect(self.series_changed)
self.name_widget.editTextChanged.connect(self.series_changed)
w = QLabel(label_string(self.col_metadata['name'])+_(' index'), parent)
w.setToolTip(get_tooltip(self.col_metadata, add_index=True))
@ -628,6 +653,7 @@ class Series(Base):
self.idx_widget.setValue(1.0)
def initialize(self, book_id):
self.book_id = book_id
values = list(self.db.all_custom(num=self.col_id))
values.sort(key=sort_key)
val = self.db.get_custom(book_id, num=self.col_id, index_is_id=True)
@ -660,6 +686,10 @@ class Series(Base):
num=self.col_id)
self.idx_widget.setValue(s_index)
def values_changed(self):
val, s_index = self.current_val
return val != self.initial_val or s_index != self.initial_index
@property
def current_val(self):
val, s_index = self.gui_val
@ -690,14 +720,23 @@ class Enumeration(Base):
def setup_ui(self, parent):
self.parent = parent
self.key = self.db.field_metadata.label_to_key(self.col_metadata['label'],
prefer_custom=True)
w = MultipleWidget(parent, only_manage_items=True, widget=QComboBox, name=self.col_metadata['name'])
w.get_editor_button().clicked.connect(self.edit)
w.setSizePolicy(QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Fixed)
self.set_to_undefined = w.clear
self.name_widget = w.edit_widget
self.widgets = [QLabel(label_string(self.col_metadata['name']), parent)]
self.finish_ui_setup(parent, QComboBox)
self.finish_ui_setup(parent, lambda parent: w)
self.editor = self.name_widget
vals = self.col_metadata['display']['enum_values']
self.editor.addItem('')
for v in vals:
self.editor.addItem(v)
def initialize(self, book_id):
self.book_id = book_id
val = self.db.get_custom(book_id, num=self.col_id, index_is_id=True)
val = self.normalize_db_val(val)
idx = self.editor.findText(val)
@ -710,7 +749,7 @@ class Enumeration(Base):
idx = 0
self.editor.setCurrentIndex(idx)
self.initial_val = self.current_val
self.initial_val = val
def setter(self, val):
self.editor.setCurrentIndex(self.editor.findText(val))

View File

@ -614,6 +614,7 @@ class SeriesEdit(EditWithComplete, ToMetadataMixin):
LABEL = _('&Series:')
FIELD_NAME = 'series'
data_changed = pyqtSignal()
editor_requested = pyqtSignal()
def __init__(self, parent):
EditWithComplete.__init__(self, parent, sort_func=title_sort)
@ -651,9 +652,40 @@ class SeriesEdit(EditWithComplete, ToMetadataMixin):
if series != self.original_val:
self.books_to_refresh |= db.set_series(id_, series, notify=False, commit=True, allow_case_change=True)
@property
def changed(self):
return self.current_val != self.original_val
def break_cycles(self):
self.dialog = None
def edit(self, db, id_):
if self.changed:
d = save_dialog(self, _('Series changed'),
_('You have changed the series. In order to use the category'
' editor, you must either discard or apply these '
'changes. Apply changes?'))
if d == QMessageBox.StandardButton.Cancel:
return
if d == QMessageBox.StandardButton.Yes:
self.commit(db, id_)
db.commit()
self.original_val = self.current_val
else:
self.current_val = self.original_val
from calibre.gui2.ui import get_gui
get_gui().do_tags_list_edit(self.current_val, 'series')
db = get_gui().current_db
self.update_items_cache(db.new_api.all_field_names('series'))
self.initialize(db, id_)
def keyPressEvent(self, ev):
if ev.key() == Qt.Key.Key_F2:
self.editor_requested.emit()
ev.accept()
return
return EditWithComplete.keyPressEvent(self, ev)
class SeriesIndexEdit(make_undoable(QDoubleSpinBox), ToMetadataMixin):
@ -679,7 +711,6 @@ class SeriesIndexEdit(make_undoable(QDoubleSpinBox), ToMetadataMixin):
@property
def current_val(self):
return self.value()
@current_val.setter
@ -1817,6 +1848,7 @@ class PublisherEdit(EditWithComplete, ToMetadataMixin): # {{{
LABEL = _('&Publisher:')
FIELD_NAME = 'publisher'
data_changed = pyqtSignal()
editor_requested = pyqtSignal()
def __init__(self, parent):
EditWithComplete.__init__(self, parent)
@ -1833,7 +1865,6 @@ class PublisherEdit(EditWithComplete, ToMetadataMixin): # {{{
@property
def current_val(self):
return clean_text(str(self.currentText()))
@current_val.setter
@ -1846,13 +1877,46 @@ class PublisherEdit(EditWithComplete, ToMetadataMixin): # {{{
def initialize(self, db, id_):
self.books_to_refresh = set()
self.update_items_cache(db.new_api.all_field_names('publisher'))
self.original_val = self.current_val = db.new_api.field_for('publisher', id_)
self.current_val = db.new_api.field_for('publisher', id_)
# having this as a separate assignment ensures that original_val is not None
self.original_val = self.current_val
def commit(self, db, id_):
self.books_to_refresh |= db.set_publisher(id_, self.current_val,
notify=False, commit=False, allow_case_change=True)
return True
@property
def changed(self):
return self.original_val != self.current_val
def edit(self, db, id_):
if self.changed:
d = save_dialog(self, _('Publisher changed'),
_('You have changed the publisher. In order to use the category'
' editor, you must either discard or apply these '
'changes. Apply changes?'))
if d == QMessageBox.StandardButton.Cancel:
return
if d == QMessageBox.StandardButton.Yes:
self.commit(db, id_)
db.commit()
self.original_val = self.current_val
else:
self.current_val = self.original_val
from calibre.gui2.ui import get_gui
get_gui().do_tags_list_edit(self.current_val, 'publisher')
db = get_gui().current_db
self.update_items_cache(db.new_api.all_field_names('publisher'))
self.initialize(db, id_)
def keyPressEvent(self, ev):
if ev.key() == Qt.Key.Key_F2:
self.editor_requested.emit()
ev.accept()
return
return EditWithComplete.keyPressEvent(self, ev)
# }}}
# DateEdit {{{

View File

@ -191,14 +191,18 @@ class MetadataSingleDialogBase(QDialog):
self.manage_authors_button = QToolButton(self)
self.manage_authors_button.setIcon(QIcon.ic('user_profile.png'))
self.manage_authors_button.setToolTip('<p>' + _(
'Manage authors. Use to rename authors and correct '
'Open the Authors Category editor. Use to rename authors and correct '
'individual author\'s sort values') + '</p>')
self.manage_authors_button.clicked.connect(self.authors.manage_authors)
self.series_editor_button = QToolButton(self)
self.series_editor_button.setToolTip(_('Open the Series Category editor'))
self.series_editor_button.setIcon(QIcon.ic('chapters.png'))
self.series_editor_button.clicked.connect(self.series_editor)
self.series = SeriesEdit(self)
self.series.editor_requested.connect(self.series_editor)
self.clear_series_button = QToolButton(self)
self.clear_series_button.setToolTip(
_('Clear series'))
self.clear_series_button.setToolTip(_('Clear series'))
self.clear_series_button.clicked.connect(self.series.clear)
self.series_index = SeriesIndexEdit(self, self.series)
self.basic_metadata_widgets.extend([self.series, self.series_index])
@ -229,7 +233,7 @@ class MetadataSingleDialogBase(QDialog):
self.tags = TagsEdit(self)
self.tags_editor_button = QToolButton(self)
self.tags_editor_button.setToolTip(_('Open Tag editor'))
self.tags_editor_button.setToolTip(_('Open the Tag editor. If CTRL or SHIFT is pressed, open the Tags Category editor'))
self.tags_editor_button.setIcon(QIcon.ic('chapters.png'))
self.tags_editor_button.clicked.connect(self.tags_editor)
self.tags.tag_editor_requested.connect(self.tags_editor)
@ -256,7 +260,12 @@ class MetadataSingleDialogBase(QDialog):
b.setMenu(QMenu(b))
self.update_paste_identifiers_menu()
self.publisher_editor_button = QToolButton(self)
self.publisher_editor_button.setToolTip(_('Open the Publishers Category editor'))
self.publisher_editor_button.setIcon(QIcon.ic('chapters.png'))
self.publisher_editor_button.clicked.connect(self.publisher_editor)
self.publisher = PublisherEdit(self)
self.publisher.editor_requested.connect(self.publisher_editor)
self.basic_metadata_widgets.append(self.publisher)
self.timestamp = DateEdit(self)
@ -409,6 +418,12 @@ class MetadataSingleDialogBase(QDialog):
def tags_editor(self, *args):
self.tags.edit(self.db, self.book_id)
def publisher_editor(self, *args):
self.publisher.edit(self.db, self.book_id)
def series_editor(self, *args):
self.series.edit(self.db, self.book_id)
def metadata_from_format(self, *args):
mi, ext = self.formats_manager.get_selected_format_metadata(self.db,
self.book_id)
@ -791,7 +806,9 @@ class MetadataSingleDialog(MetadataSingleDialogBase): # {{{
sto(self.title_sort, self.manage_authors_button)
sto(self.manage_authors_button, self.authors)
create_row(1, self.authors, self.deduce_author_sort_button, self.author_sort)
sto(self.author_sort, self.series)
tl.addWidget(self.series_editor_button, 2, 0, 1, 1)
sto(self.author_sort, self.series_editor_button)
sto(self.series_editor_button, self.series)
create_row(2, self.series, self.clear_series_button,
self.series_index, icon='trash.png')
@ -851,8 +868,9 @@ class MetadataSingleDialog(MetadataSingleDialogBase): # {{{
create_row2(4, self.timestamp, self.timestamp.clear_button)
sto(self.timestamp.clear_button, self.pubdate)
create_row2(5, self.pubdate, self.pubdate.clear_button)
sto(self.pubdate.clear_button, self.publisher)
create_row2(6, self.publisher, self.publisher.clear_button)
sto(self.pubdate.clear_button, self.publisher_editor_button)
sto(self.publisher_editor_button, self.publisher)
create_row2(6, self.publisher, self.publisher.clear_button, front_button=self.publisher_editor_button)
sto(self.publisher.clear_button, self.languages)
create_row2(7, self.languages)
self.tabs[0].spc_two = QSpacerItem(10, 10, QSizePolicy.Policy.Expanding,
@ -957,8 +975,10 @@ class MetadataSingleDialogAlt1(MetadataSingleDialogBase): # {{{
tl.addWidget(self.swap_title_author_button, 0, 0, 2, 1)
tl.addWidget(self.manage_authors_button, 2, 0, 1, 1)
tl.addWidget(self.paste_isbn_button, 12, 0, 1, 1)
tl.addWidget(self.series_editor_button, 6, 0, 1, 1)
tl.addWidget(self.tags_editor_button, 6, 0, 1, 1)
tl.addWidget(self.publisher_editor_button, 9, 0, 1, 1)
tl.addWidget(self.paste_isbn_button, 12, 0, 1, 1)
create_row(0, self.title, self.title_sort,
button=self.deduce_title_sort_button, span=2,
@ -983,8 +1003,10 @@ class MetadataSingleDialogAlt1(MetadataSingleDialogBase): # {{{
button=self.clear_identifiers_button, icon='trash.png')
sto(self.clear_identifiers_button, self.swap_title_author_button)
sto(self.swap_title_author_button, self.manage_authors_button)
sto(self.manage_authors_button, self.tags_editor_button)
sto(self.tags_editor_button, self.paste_isbn_button)
sto(self.manage_authors_button, self.series_editor_button)
sto(self.series_editor_button, self.tags_editor_button)
sto(self.tags_editor_button, self.publisher_editor_button)
sto(self.publisher_editor_button, self.paste_isbn_button)
tl.addItem(QSpacerItem(1, 1, QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Expanding),
13, 1, 1 ,1)
@ -1111,8 +1133,10 @@ class MetadataSingleDialogAlt2(MetadataSingleDialogBase): # {{{
tl.addWidget(self.swap_title_author_button, 0, 0, 2, 1)
tl.addWidget(self.manage_authors_button, 2, 0, 2, 1)
tl.addWidget(self.paste_isbn_button, 12, 0, 1, 1)
tl.addWidget(self.series_editor_button, 4, 0, 1, 1)
tl.addWidget(self.tags_editor_button, 6, 0, 1, 1)
tl.addWidget(self.publisher_editor_button, 9, 0, 1, 1)
tl.addWidget(self.paste_isbn_button, 12, 0, 1, 1)
create_row(0, self.title, self.title_sort,
button=self.deduce_title_sort_button, span=2,
@ -1138,8 +1162,10 @@ class MetadataSingleDialogAlt2(MetadataSingleDialogBase): # {{{
button=self.clear_identifiers_button, icon='trash.png')
sto(self.clear_identifiers_button, self.swap_title_author_button)
sto(self.swap_title_author_button, self.manage_authors_button)
sto(self.manage_authors_button, self.tags_editor_button)
sto(self.tags_editor_button, self.paste_isbn_button)
sto(self.manage_authors_button, self.series_editor_button)
sto(self.series_editor_button, self.tags_editor_button)
sto(self.tags_editor_button, self.publisher_editor_button)
sto(self.publisher_editor_button, self.paste_isbn_button)
tl.addItem(QSpacerItem(1, 1, QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Expanding),
13, 1, 1 ,1)