mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Improve the manage authors dialog
This commit is contained in:
commit
2dd33e9c25
@ -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 = []
|
||||
|
@ -20,6 +20,50 @@
|
||||
<string>Manage authors</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="">
|
||||
<item>
|
||||
<widget class="QLabel">
|
||||
<property name="text">
|
||||
<string>&Search for:</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>find_box</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="HistoryLineEdit" name="find_box">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>200</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="find_button">
|
||||
<property name="text">
|
||||
<string>F&ind</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTableWidget" name="table">
|
||||
<property name="sizePolicy">
|
||||
@ -143,4 +187,11 @@ after changing Preferences->Advanced->Tweaks->Author sort name algorith
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>HistoryLineEdit</class>
|
||||
<extends>QComboBox</extends>
|
||||
<header>calibre/gui2/widgets.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
</ui>
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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))
|
||||
|
@ -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.
|
||||
|
Loading…
x
Reference in New Issue
Block a user