diff --git a/src/calibre/gui2/dialogs/authors_edit.py b/src/calibre/gui2/dialogs/authors_edit.py new file mode 100644 index 0000000000..91b68fdcc0 --- /dev/null +++ b/src/calibre/gui2/dialogs/authors_edit.py @@ -0,0 +1,198 @@ +#!/usr/bin/env python +# vim:fileencoding=utf-8 +from __future__ import (unicode_literals, division, absolute_import, + print_function) + +__license__ = 'GPL v3' +__copyright__ = '2013, Kovid Goyal ' + +from collections import OrderedDict + +from PyQt4.Qt import ( + QDialog, QGridLayout, QDialogButtonBox, QListWidget, QApplication, Qt, + pyqtSignal, QSize, QPushButton, QIcon, QStyledItemDelegate, QLabel) + +from calibre.utils.config_base import tweaks +from calibre.gui2 import gprefs +from calibre.gui2.complete2 import EditWithComplete +from calibre.ebooks.metadata import string_to_authors + +class ItemDelegate(QStyledItemDelegate): + + edited = pyqtSignal(object) + + def __init__(self, all_authors, parent): + QStyledItemDelegate.__init__(self, parent) + self.all_authors = all_authors + + def sizeHint(self, *args): + return QStyledItemDelegate.sizeHint(self, *args) + QSize(0, 15) + + def setEditorData(self, editor, index): + name = unicode(index.data(Qt.DisplayRole).toString()) + editor.setText(name) + editor.lineEdit().selectAll() + + def setModelData(self, editor, model, index): + authors = string_to_authors(unicode(editor.text())) + model.setData(index, authors[0]) + self.edited.emit(index.row()) + + def createEditor(self, parent, option, index): + self.ed = EditWithComplete(parent) + self.ed.setFocusPolicy(Qt.StrongFocus) + init_line_edit(self.ed, self.all_authors) + return self.ed + +class List(QListWidget): + + def __init__(self, all_authors, parent): + QListWidget.__init__(self, parent) + self.setDragEnabled(True) + self.setSelectionMode(self.ExtendedSelection) + self.setDropIndicatorShown(True) + self.setDragDropMode(self.InternalMove) + self.setAlternatingRowColors(True) + self.d = ItemDelegate(all_authors, self) + self.d.edited.connect(self.edited, type=Qt.QueuedConnection) + self.setItemDelegate(self.d) + + def delete_selected(self): + for item in self.selectedItems(): + self.takeItem(self.row(item)) + + def keyPressEvent(self, ev): + if ev.key() == Qt.Key_Delete: + self.delete_selected() + ev.accept() + return + return QListWidget.keyPressEvent(self, ev) + + def addItem(self, *args): + try: + return QListWidget.addItem(self, *args) + finally: + self.mark_as_editable() + + def addItems(self, *args): + try: + return QListWidget.addItems(self, *args) + finally: + self.mark_as_editable() + + def mark_as_editable(self): + for i in xrange(self.count()): + item = self.item(i) + item.setFlags(item.flags() | Qt.ItemIsEditable) + + def edited(self, i): + item = self.item(i) + q = unicode(item.text()) + remove = [] + for j in xrange(self.count()): + if i != j and unicode(self.item(j).text()) == q: + remove.append(j) + for x in sorted(remove, reverse=True): + self.takeItem(x) + +class Edit(EditWithComplete): + + returnPressed = pyqtSignal() + + def keyPressEvent(self, ev): + if ev.key() in (Qt.Key_Return, Qt.Key_Enter): + ev.accept() + self.returnPressed.emit() + return + return EditWithComplete.keyPressEvent(self, ev) + +def init_line_edit(a, all_authors): + a.set_separator('&') + a.set_space_before_sep(True) + a.set_add_separator(tweaks['authors_completer_append_separator']) + a.update_items_cache(all_authors) + +class AuthorsEdit(QDialog): + + def __init__(self, all_authors, current_authors, parent=None): + QDialog.__init__(self, parent) + self.l = l = QGridLayout() + self.setLayout(l) + self.setWindowTitle(_('Edit authors')) + + self.la = QLabel(_( + 'Edit the authors for this book. You can drag and drop to re-arrange authors')) + self.la.setWordWrap(True) + l.addWidget(self.la, 0, 0, 1, 3) + + self.al = al = List(all_authors, self) + al.addItems(current_authors) + l.addWidget(al, 1, 0, 1, 3) + + self.author = a = Edit(self) + init_line_edit(a, all_authors) + a.lineEdit().setPlaceholderText(_('Enter an author to add')) + a.returnPressed.connect(self.add_author) + l.addWidget(a, 2, 0) + + self.ab = b = QPushButton(_('&Add')) + b.setIcon(QIcon(I('plus.png'))) + l.addWidget(b, 2, 1) + b.clicked.connect(self.add_author) + + self.db = b = QPushButton(_('&Remove selected')) + l.addWidget(b, 2, 2) + b.setIcon(QIcon(I('minus.png'))) + b.clicked.connect(self.al.delete_selected) + + self.bb = bb = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) + bb.accepted.connect(self.accept) + bb.rejected.connect(self.reject) + l.addWidget(bb, 3, 0, 1, 3) + + l.setColumnStretch(0, 10) + self.resize(self.sizeHint() + QSize(150, 100)) + geom = gprefs.get('authors-edit-geometry', None) + if geom is not None: + self.restoreGeometry(geom) + self.author.setFocus(Qt.OtherFocusReason) + + def save_geometry(self): + gprefs.set('authors-edit-geometry', bytearray(self.saveGeometry())) + + def accept(self): + self.save_geometry() + return QDialog.accept(self) + + def reject(self): + self.save_geometry() + return QDialog.reject(self) + + @property + def authors(self): + ans = [] + for i in xrange(self.al.count()): + ans.append(unicode(self.al.item(i).text())) + return ans or [_('Unknown')] + + def add_author(self): + text = self.author.text().strip() + authors = OrderedDict((icu_lower(x), (i, x)) for i, x in enumerate(self.authors)) + if text: + for author in string_to_authors(text): + la = icu_lower(author) + if la in authors and authors[la][1] != author: + # Case change + i = authors[la][0] + authors[la] = (i, author) + self.al.item(i).setText(author) + else: + self.al.addItem(author) + authors[la] = author + self.author.setText('') + +if __name__ == '__main__': + app = QApplication([]) + d = AuthorsEdit(['kovid goyal', 'divok layog', 'other author'], ['kovid goyal', 'other author']) + d.exec_() + print (d.authors) diff --git a/src/calibre/gui2/metadata/basic_widgets.py b/src/calibre/gui2/metadata/basic_widgets.py index f46f05cdcb..43c7e66b10 100644 --- a/src/calibre/gui2/metadata/basic_widgets.py +++ b/src/calibre/gui2/metadata/basic_widgets.py @@ -227,6 +227,21 @@ class AuthorsEdit(EditWithComplete): self.setSizeAdjustPolicy(self.AdjustToMinimumContentsLengthWithIcon) self.manage_authors_signal = manage_authors manage_authors.triggered.connect(self.manage_authors) + self.lineEdit().createStandardContextMenu = self.createStandardContextMenu + + def createStandardContextMenu(self): + menu = QLineEdit.createStandardContextMenu(self.lineEdit()) + menu.addSeparator() + menu.addAction(_('&Edit authors'), self.edit_authors) + return menu + + def edit_authors(self): + all_authors = self.lineEdit().all_items + current_authors = self.current_val + from calibre.gui2.dialogs.authors_edit import AuthorsEdit + d = AuthorsEdit(all_authors, current_authors, self) + if d.exec_() == d.Accepted: + self.current_val = d.authors def manage_authors(self): if self.original_val != self.current_val: