mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-08 10:44:09 -04:00
Add a Tag Editor for tags like custom columns to the edit metadata dialog
This commit is contained in:
commit
ff9af90409
@ -10,12 +10,13 @@ from functools import partial
|
||||
from PyQt4.Qt import QComboBox, QLabel, QSpinBox, QDoubleSpinBox, QDateTimeEdit, \
|
||||
QDateTime, QGroupBox, QVBoxLayout, QSizePolicy, QGridLayout, \
|
||||
QSpacerItem, QIcon, QCheckBox, QWidget, QHBoxLayout, SIGNAL, \
|
||||
QPushButton
|
||||
QPushButton, QMessageBox, QToolButton
|
||||
|
||||
from calibre.utils.date import qt_to_dt, now
|
||||
from calibre.gui2.complete import MultiCompleteLineEdit, MultiCompleteComboBox
|
||||
from calibre.gui2.comments_editor import Editor as CommentsEditor
|
||||
from calibre.gui2 import UNDEFINED_QDATETIME, error_dialog
|
||||
from calibre.gui2.dialogs.tag_editor import TagEditor
|
||||
from calibre.utils.config import tweaks
|
||||
from calibre.utils.icu import sort_key
|
||||
from calibre.library.comments import comments_to_html
|
||||
@ -226,18 +227,71 @@ class Comments(Base):
|
||||
val = None
|
||||
return val
|
||||
|
||||
class MultipleWidget(QWidget):
|
||||
|
||||
def __init__(self, parent):
|
||||
QWidget.__init__(self, parent)
|
||||
layout = QHBoxLayout()
|
||||
layout.setSpacing(5)
|
||||
layout.setContentsMargins(0, 0, 0, 0)
|
||||
|
||||
self.tags_box = MultiCompleteLineEdit(parent)
|
||||
layout.addWidget(self.tags_box, stretch=1000)
|
||||
self.editor_button = QToolButton(self)
|
||||
self.editor_button.setToolTip(_('Open Item Editor'))
|
||||
self.editor_button.setIcon(QIcon(I('chapters.png')))
|
||||
layout.addWidget(self.editor_button)
|
||||
self.setLayout(layout)
|
||||
|
||||
def get_editor_button(self):
|
||||
return self.editor_button
|
||||
|
||||
def update_items_cache(self, values):
|
||||
self.tags_box.update_items_cache(values)
|
||||
|
||||
def clear(self):
|
||||
self.tags_box.clear()
|
||||
|
||||
def setEditText(self):
|
||||
self.tags_box.setEditText()
|
||||
|
||||
def addItem(self, itm):
|
||||
self.tags_box.addItem(itm)
|
||||
|
||||
def set_separator(self, sep):
|
||||
self.tags_box.set_separator(sep)
|
||||
|
||||
def set_add_separator(self, sep):
|
||||
self.tags_box.set_add_separator(sep)
|
||||
|
||||
def set_space_before_sep(self, v):
|
||||
self.tags_box.set_space_before_sep(v)
|
||||
|
||||
def setSizePolicy(self, v1, v2):
|
||||
self.tags_box.setSizePolicy(v1, v2)
|
||||
|
||||
def setText(self, v):
|
||||
self.tags_box.setText(v)
|
||||
|
||||
def text(self):
|
||||
return self.tags_box.text()
|
||||
|
||||
class Text(Base):
|
||||
|
||||
def setup_ui(self, parent):
|
||||
self.sep = self.col_metadata['multiple_seps']
|
||||
self.key = self.db.field_metadata.label_to_key(self.col_metadata['label'],
|
||||
prefer_custom=True)
|
||||
self.parent = parent
|
||||
|
||||
if self.col_metadata['is_multiple']:
|
||||
w = MultiCompleteLineEdit(parent)
|
||||
w = MultipleWidget(parent)
|
||||
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.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Preferred)
|
||||
w.get_editor_button().clicked.connect(self.edit)
|
||||
else:
|
||||
w = MultiCompleteComboBox(parent)
|
||||
w.set_separator(None)
|
||||
@ -248,9 +302,12 @@ class Text(Base):
|
||||
def initialize(self, book_id):
|
||||
values = list(self.db.all_custom(num=self.col_id))
|
||||
values.sort(key=sort_key)
|
||||
self.book_id = book_id
|
||||
self.widgets[1].clear()
|
||||
self.widgets[1].update_items_cache(values)
|
||||
val = self.db.get_custom(book_id, num=self.col_id, index_is_id=True)
|
||||
if isinstance(val, list):
|
||||
val.sort(key=sort_key)
|
||||
self.initial_val = val
|
||||
val = self.normalize_db_val(val)
|
||||
|
||||
@ -284,6 +341,31 @@ class Text(Base):
|
||||
val = None
|
||||
return val
|
||||
|
||||
def _save_dialog(self, parent, title, msg, det_msg=''):
|
||||
d = QMessageBox(parent)
|
||||
d.setWindowTitle(title)
|
||||
d.setText(msg)
|
||||
d.setStandardButtons(QMessageBox.Yes | QMessageBox.No | QMessageBox.Cancel)
|
||||
return d.exec_()
|
||||
|
||||
def edit(self):
|
||||
if self.getter() != self.initial_val:
|
||||
d = self._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.Cancel:
|
||||
return
|
||||
if d == QMessageBox.Yes:
|
||||
self.commit(self.book_id)
|
||||
self.db.commit()
|
||||
self.initial_val = self.getter()
|
||||
else:
|
||||
self.setter(self.initial_val)
|
||||
d = TagEditor(self.parent, self.db, self.book_id, self.key)
|
||||
if d.exec_() == TagEditor.Accepted:
|
||||
self.setter(d.tags)
|
||||
|
||||
class Series(Base):
|
||||
|
||||
def setup_ui(self, parent):
|
||||
|
@ -10,19 +10,26 @@ from calibre.utils.icu import sort_key
|
||||
|
||||
class TagEditor(QDialog, Ui_TagEditor):
|
||||
|
||||
def __init__(self, window, db, id_=None):
|
||||
def __init__(self, window, db, id_=None, key=None):
|
||||
QDialog.__init__(self, window)
|
||||
Ui_TagEditor.__init__(self)
|
||||
self.setupUi(self)
|
||||
|
||||
self.db = db
|
||||
if key:
|
||||
key = db.field_metadata.key_to_label(key)
|
||||
self.key = key
|
||||
self.index = db.row(id_) if id_ is not None else None
|
||||
if self.index is not None:
|
||||
if key is None:
|
||||
tags = self.db.tags(self.index)
|
||||
if tags:
|
||||
tags = [tag.strip() for tag in tags.split(',') if tag.strip()]
|
||||
else:
|
||||
tags = self.db.get_custom(self.index, label=key)
|
||||
else:
|
||||
tags = []
|
||||
if tags:
|
||||
tags = [tag.strip() for tag in tags.split(',') if tag.strip()]
|
||||
tags.sort(key=sort_key)
|
||||
for tag in tags:
|
||||
self.applied_tags.addItem(tag)
|
||||
@ -31,6 +38,9 @@ class TagEditor(QDialog, Ui_TagEditor):
|
||||
|
||||
self.tags = tags
|
||||
|
||||
if key:
|
||||
all_tags = [tag for tag in self.db.all_custom(label=key)]
|
||||
else:
|
||||
all_tags = [tag for tag in self.db.all_tags()]
|
||||
all_tags = list(set(all_tags))
|
||||
all_tags.sort(key=sort_key)
|
||||
@ -61,7 +71,10 @@ class TagEditor(QDialog, Ui_TagEditor):
|
||||
error_dialog(self, 'No tags selected', 'You must select at least one tag from the list of Available tags.').exec_()
|
||||
return
|
||||
for item in items:
|
||||
if self.db.is_tag_used(unicode(item.text())):
|
||||
used = self.db.is_tag_used(unicode(item.text())) \
|
||||
if self.key is None else \
|
||||
self.db.is_item_used_in_multiple(unicode(item.text()), label=self.key)
|
||||
if used:
|
||||
confirms.append(item)
|
||||
else:
|
||||
deletes.append(item)
|
||||
@ -73,7 +86,12 @@ class TagEditor(QDialog, Ui_TagEditor):
|
||||
deletes += confirms
|
||||
|
||||
for item in deletes:
|
||||
if self.key is None:
|
||||
self.db.delete_tag(unicode(item.text()))
|
||||
else:
|
||||
bks = self.db.delete_item_from_multiple(unicode(item.text()),
|
||||
label=self.key)
|
||||
self.db.refresh_ids(bks)
|
||||
self.available_tags.takeItem(self.available_tags.row(item))
|
||||
|
||||
|
||||
|
@ -276,6 +276,37 @@ class CustomColumns(object):
|
||||
self.conn.execute('DELETE FROM %s WHERE value=?'%lt, (id,))
|
||||
self.conn.execute('DELETE FROM %s WHERE id=?'%table, (id,))
|
||||
self.conn.commit()
|
||||
|
||||
def is_item_used_in_multiple(self, item, label=None, num=None):
|
||||
existing_tags = self.all_custom(label=label, num=num)
|
||||
return item.lower() in {t.lower() for t in existing_tags}
|
||||
|
||||
def delete_item_from_multiple(self, item, label=None, num=None):
|
||||
if label is not None:
|
||||
data = self.custom_column_label_map[label]
|
||||
if num is not None:
|
||||
data = self.custom_column_num_map[num]
|
||||
if data['datatype'] != 'text' or not data['is_multiple']:
|
||||
raise ValueError('Column %r is not text/multiple'%data['label'])
|
||||
existing_tags = list(self.all_custom(label=label, num=num))
|
||||
lt = [t.lower() for t in existing_tags]
|
||||
try:
|
||||
idx = lt.index(item.lower())
|
||||
except ValueError:
|
||||
idx = -1
|
||||
books_affected = []
|
||||
if idx > -1:
|
||||
table, lt = self.custom_table_names(data['num'])
|
||||
id_ = self.conn.get('SELECT id FROM %s where value = ?'%table,
|
||||
(existing_tags[idx],), all=False)
|
||||
if id_:
|
||||
books = self.conn.get('SELECT book FROM %s where value = ?'%lt, (id_,))
|
||||
if books:
|
||||
books_affected = [b[0] for b in books]
|
||||
self.conn.execute('DELETE FROM %s WHERE value=?'%lt, (id_,))
|
||||
self.conn.execute('DELETE FROM %s WHERE id=?'%table, (id_,))
|
||||
self.conn.commit()
|
||||
return books_affected
|
||||
# end convenience methods
|
||||
|
||||
def get_next_cc_series_num_for(self, series, label=None, num=None):
|
||||
|
Loading…
x
Reference in New Issue
Block a user