diff --git a/src/calibre/gui2/dialogs/edit_authors_dialog.py b/src/calibre/gui2/dialogs/edit_authors_dialog.py
index eae189e04c..ea16a863e9 100644
--- a/src/calibre/gui2/dialogs/edit_authors_dialog.py
+++ b/src/calibre/gui2/dialogs/edit_authors_dialog.py
@@ -3,7 +3,8 @@ __copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
__docformat__ = 'restructuredtext en'
__license__ = 'GPL v3'
-from PyQt4.Qt import Qt, QDialog, QTableWidgetItem, QAbstractItemView
+from PyQt4.Qt import (Qt, QDialog, QTableWidgetItem, QAbstractItemView, QIcon,
+ QString, QDialogButtonBox, QFrame, QLabel, QTimer)
from calibre.ebooks.metadata import author_to_author_sort
from calibre.gui2 import error_dialog
@@ -19,7 +20,7 @@ class tableItem(QTableWidgetItem):
class EditAuthorsDialog(QDialog, Ui_EditAuthorsDialog):
- def __init__(self, parent, db, id_to_select):
+ def __init__(self, parent, db, id_to_select, select_sort):
QDialog.__init__(self, parent)
Ui_EditAuthorsDialog.__init__(self)
self.setupUi(self)
@@ -30,14 +31,23 @@ class EditAuthorsDialog(QDialog, Ui_EditAuthorsDialog):
self.buttonBox.accepted.connect(self.accepted)
+ # Set up the column headings
self.table.setSelectionMode(QAbstractItemView.SingleSelection)
self.table.setColumnCount(2)
- self.table.setHorizontalHeaderLabels([_('Author'), _('Author sort')])
+ self.down_arrow_icon = QIcon(I('arrow-down.png'))
+ self.up_arrow_icon = QIcon(I('arrow-up.png'))
+ self.blank_icon = QIcon(I('blank.png'))
+ self.auth_col = QTableWidgetItem(_('Author'))
+ self.table.setHorizontalHeaderItem(0, self.auth_col)
+ self.auth_col.setIcon(self.blank_icon)
+ self.aus_col = QTableWidgetItem(_('Author sort'))
+ self.table.setHorizontalHeaderItem(1, self.aus_col)
+ self.aus_col.setIcon(self.up_arrow_icon)
+ # Add the data
self.authors = {}
auts = db.get_authors_with_ids()
self.table.setRowCount(len(auts))
- setattr(self.table, '__lt__', lambda x, y: True if strcmp(x, y) < 0 else False)
select_item = None
for row, (id, author, sort) in enumerate(auts):
author = author.replace('|', ',')
@@ -48,7 +58,10 @@ class EditAuthorsDialog(QDialog, Ui_EditAuthorsDialog):
self.table.setItem(row, 0, aut)
self.table.setItem(row, 1, sort)
if id == id_to_select:
- select_item = sort
+ if select_sort:
+ select_item = sort
+ else:
+ select_item = aut
self.table.resizeColumnsToContents()
# set up the cellChanged signal only after the table is filled
@@ -69,23 +82,83 @@ class EditAuthorsDialog(QDialog, Ui_EditAuthorsDialog):
self.recalc_author_sort.clicked.connect(self.do_recalc_author_sort)
self.auth_sort_to_author.clicked.connect(self.do_auth_sort_to_author)
+ # Position on the desired item
if select_item is not None:
self.table.setCurrentItem(select_item)
self.table.editItem(select_item)
+ self.start_find_pos = select_item.row() * 2 + select_item.column()
else:
self.table.setCurrentCell(0, 0)
+ self.start_find_pos = -1
+
+ # set up the search box
+ self.find_box.initialize('manage_authors_search')
+ self.find_box.lineEdit().returnPressed.connect(self.do_find)
+ self.find_box.editTextChanged.connect(self.find_text_changed)
+ self.find_button.clicked.connect(self.do_find)
+
+ l = QLabel(self.table)
+ self.not_found_label = l
+ l.setFrameStyle(QFrame.StyledPanel)
+ l.setAutoFillBackground(True)
+ l.setText(_('No matches found'))
+ l.setAlignment(Qt.AlignVCenter)
+ l.resize(l.sizeHint())
+ l.move(10,20)
+ l.setVisible(False)
+ self.not_found_label.move(40, 40)
+ self.not_found_label_timer = QTimer()
+ self.not_found_label_timer.setSingleShot(True)
+ self.not_found_label_timer.timeout.connect(
+ self.not_found_label_timer_event, type=Qt.QueuedConnection)
+
+ def not_found_label_timer_event(self):
+ self.not_found_label.setVisible(False)
+
+ def find_text_changed(self):
+ self.start_find_pos = -1
+
+ def do_find(self):
+ self.not_found_label.setVisible(False)
+ # For some reason the button box keeps stealing the RETURN shortcut.
+ # Steal it back
+ self.buttonBox.button(QDialogButtonBox.Ok).setDefault(False)
+ self.buttonBox.button(QDialogButtonBox.Ok).setAutoDefault(False)
+ self.buttonBox.button(QDialogButtonBox.Cancel).setDefault(False)
+ self.buttonBox.button(QDialogButtonBox.Cancel).setAutoDefault(False)
+ st = icu_lower(unicode(self.find_box.currentText()))
+
+ for i in range(0, self.table.rowCount()*2):
+ self.start_find_pos = (self.start_find_pos + 1) % (self.table.rowCount()*2)
+ r = (self.start_find_pos/2)%self.table.rowCount()
+ c = self.start_find_pos % 2
+ item = self.table.item(r, c)
+ text = icu_lower(unicode(item.text()))
+ if st in text:
+ self.table.setCurrentItem(item)
+ self.table.setFocus(True)
+ return
+ # Nothing found. Pop up the little dialog for 1.5 seconds
+ self.not_found_label.setVisible(True)
+ self.not_found_label_timer.start(1500)
def do_sort_by_author(self):
self.author_order = 1 if self.author_order == 0 else 0
self.table.sortByColumn(0, self.author_order)
self.sort_by_author.setChecked(True)
self.sort_by_author_sort.setChecked(False)
+ self.auth_col.setIcon(self.down_arrow_icon if self.author_order
+ else self.up_arrow_icon)
+ self.aus_col.setIcon(self.blank_icon)
def do_sort_by_author_sort(self):
self.author_sort_order = 1 if self.author_sort_order == 0 else 0
self.table.sortByColumn(1, self.author_sort_order)
self.sort_by_author.setChecked(False)
self.sort_by_author_sort.setChecked(True)
+ self.aus_col.setIcon(self.down_arrow_icon if self.author_sort_order
+ else self.up_arrow_icon)
+ self.auth_col.setIcon(self.blank_icon)
def accepted(self):
self.result = []
diff --git a/src/calibre/gui2/dialogs/edit_authors_dialog.ui b/src/calibre/gui2/dialogs/edit_authors_dialog.ui
index 3280245959..35abc5dac5 100644
--- a/src/calibre/gui2/dialogs/edit_authors_dialog.ui
+++ b/src/calibre/gui2/dialogs/edit_authors_dialog.ui
@@ -20,6 +20,50 @@
Manage authors
+ -
+
+
-
+
+
+ &Search for:
+
+
+ find_box
+
+
+
+ -
+
+
+
+ 200
+ 0
+
+
+
+
+ -
+
+
+ F&ind
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+
+
-
@@ -143,4 +187,11 @@ after changing Preferences->Advanced->Tweaks->Author sort name algorith
+
+
+ HistoryLineEdit
+ QComboBox
+
+
+
diff --git a/src/calibre/gui2/metadata/basic_widgets.py b/src/calibre/gui2/metadata/basic_widgets.py
index 50a8e7c0b8..8d5462c7f7 100644
--- a/src/calibre/gui2/metadata/basic_widgets.py
+++ b/src/calibre/gui2/metadata/basic_widgets.py
@@ -9,7 +9,7 @@ __docformat__ = 'restructuredtext en'
import textwrap, re, os
-from PyQt4.Qt import (Qt, QDateEdit, QDate, pyqtSignal,
+from PyQt4.Qt import (Qt, QDateEdit, QDate, pyqtSignal, QMessageBox,
QIcon, QToolButton, QWidget, QLabel, QGridLayout,
QDoubleSpinBox, QListWidgetItem, QSize, QPixmap,
QPushButton, QSpinBox, QLineEdit, QSizePolicy)
@@ -22,7 +22,7 @@ from calibre.ebooks.metadata import (title_sort, authors_to_string,
string_to_authors, check_isbn)
from calibre.ebooks.metadata.meta import get_metadata
from calibre.gui2 import (file_icon_provider, UNDEFINED_QDATE, UNDEFINED_DATE,
- choose_files, error_dialog, choose_images, question_dialog)
+ choose_files, error_dialog, choose_images)
from calibre.utils.date import local_tz, qt_to_dt
from calibre import strftime
from calibre.ebooks import BOOK_EXTENSIONS
@@ -31,6 +31,16 @@ from calibre.utils.date import utcfromtimestamp
from calibre.gui2.comments_editor import Editor
from calibre.library.comments import comments_to_html
from calibre.gui2.dialogs.tag_editor import TagEditor
+from calibre.utils.icu import strcmp
+
+def save_dialog(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_()
+
+
'''
The interface common to all widgets used to set basic metadata
@@ -168,16 +178,22 @@ class AuthorsEdit(MultiCompleteComboBox):
def manage_authors(self):
if self.original_val != self.current_val:
- if (question_dialog(self, _('Authors changed'),
+ d = save_dialog(self, _('Authors changed'),
_('You have changed the authors for this book. You must save '
'these changes before you can use Manage authors. Do you '
- 'want to save these changes?'), show_copy_button=False)):
+ 'want to save these changes?'))
+ if d == QMessageBox.Cancel:
+ return
+ if d == QMessageBox.Yes:
self.commit(self.db, self.id_)
self.db.commit()
self.original_val = self.current_val
else:
self.current_val = self.original_val
- self.dialog.parent().do_author_sort_edit(self, self.id_)
+ first_author = self.current_val[0] if len(self.current_val) else None
+ first_author_id = self.db.get_author_id(first_author) if first_author else None
+ self.dialog.parent().do_author_sort_edit(self, first_author_id,
+ select_sort=False)
self.initialize(self.db, self.id_)
self.dialog.author_sort.initialize(self.db, self.id_)
@@ -256,7 +272,7 @@ class AuthorSortEdit(EnLineEdit):
'No action is required if this is what you want.'))
self.tooltips = (ok_tooltip, bad_tooltip)
- self.authors_edit.editTextChanged.connect(self.update_state)
+ self.authors_edit.editTextChanged.connect(self.update_state_and_val)
self.textChanged.connect(self.update_state)
autogen_button.clicked.connect(self.auto_generate)
@@ -278,12 +294,19 @@ class AuthorSortEdit(EnLineEdit):
return property(fget=fget, fset=fset)
+ def update_state_and_val(self):
+ au = unicode(self.authors_edit.text())
+ # Handle case change if the authors box changed
+ if strcmp(au, self.current_val) == 0:
+ self.current_val = au
+ self.update_state()
+
def update_state(self, *args):
au = unicode(self.authors_edit.text())
au = re.sub(r'\s+et al\.$', '', au)
au = self.db.author_sort_from_authors(string_to_authors(au))
- normal = au == self.current_val
+ normal = strcmp(au, self.current_val) == 0
if normal:
col = 'rgb(0, 255, 0, 20%)'
else:
@@ -316,12 +339,11 @@ class AuthorSortEdit(EnLineEdit):
self.current_val = self.db.author_sort_from_authors(authors)
def initialize(self, db, id_):
- self.current_val = self.original_val = db.author_sort(id_, index_is_id=True)
+ self.current_val = db.author_sort(id_, index_is_id=True)
def commit(self, db, id_):
aus = self.current_val
- if aus != self.original_val:
- db.set_author_sort(id_, aus, notify=False, commit=False)
+ db.set_author_sort(id_, aus, notify=False, commit=False)
return True
# }}}
@@ -919,10 +941,13 @@ class TagsEdit(MultiCompleteLineEdit): # {{{
def edit(self, db, id_):
if self.changed:
- if question_dialog(self, _('Tags changed'),
+ d = save_dialog(self, _('Tags changed'),
_('You have changed the tags. In order to use the tags'
' editor, you must either discard or apply these '
- 'changes. Apply changes?'), show_copy_button=False):
+ 'changes. Apply changes?'))
+ if d == QMessageBox.Cancel:
+ return
+ if d == QMessageBox.Yes:
self.commit(db, id_)
db.commit()
self.original_val = self.current_val
diff --git a/src/calibre/gui2/tag_view.py b/src/calibre/gui2/tag_view.py
index 7b68229da0..a3e39aefd2 100644
--- a/src/calibre/gui2/tag_view.py
+++ b/src/calibre/gui2/tag_view.py
@@ -2048,12 +2048,12 @@ class TagBrowserMixin(object): # {{{
self.library_view.select_rows(ids)
# refreshing the tags view happens at the emit()/call() site
- def do_author_sort_edit(self, parent, id):
+ def do_author_sort_edit(self, parent, id, select_sort=True):
'''
Open the manage authors dialog
'''
db = self.library_view.model().db
- editor = EditAuthorsDialog(parent, db, id)
+ editor = EditAuthorsDialog(parent, db, id, select_sort)
d = editor.exec_()
if d:
for (id, old_author, new_author, new_sort) in editor.result:
diff --git a/src/calibre/library/database2.py b/src/calibre/library/database2.py
index 065f0e8446..ac36335b79 100644
--- a/src/calibre/library/database2.py
+++ b/src/calibre/library/database2.py
@@ -2285,6 +2285,12 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
return []
return result
+ def get_author_id(self, author):
+ author = author.replace(',', '|')
+ result = self.conn.get('SELECT id FROM authors WHERE name=?',
+ (author,), all=False)
+ return result
+
def set_sort_field_for_author(self, old_id, new_sort, commit=True, notify=False):
self.conn.execute('UPDATE authors SET sort=? WHERE id=?', \
(new_sort.strip(), old_id))
diff --git a/src/calibre/manual/metadata.rst b/src/calibre/manual/metadata.rst
index ec3dbb08bf..72ae2b8250 100644
--- a/src/calibre/manual/metadata.rst
+++ b/src/calibre/manual/metadata.rst
@@ -19,7 +19,7 @@ Editing the metadata of one book at a time
Click the book you want to edit and then click the :guilabel:`Edit metadata` button or press the ``E`` key. A dialog opens that allows you to edit all aspects of the metadata. It has various features to make editing faster and more efficient. A list of the commonly used tips:
* You can click the button in between title and authors to swap them automatically.
- * You can click the button next to author sort to automatically to have |app| automatically fill it from the author name.
+ * You can click the button next to author sort to have |app| automatically fill it in using the sort values stored with each author. Use the :guilabel:`Manage authors` dialog to see and change the authors' sort values. This dialog can be opened by clicking and holding the button next to author sort.
* You can click the button next to tags to use the Tag Editor to manage the tags associated with the book.
* The ISBN box will have a red background if you enter an invalid ISBN. It will be green for valid ISBNs
* The author sort box will be red if the author sort value differs from what |app| thinks it should be.