diff --git a/src/calibre/gui2/dialogs/metadata_single.py b/src/calibre/gui2/dialogs/metadata_single.py index 4a9bb784c8..7c6da7c1f9 100644 --- a/src/calibre/gui2/dialogs/metadata_single.py +++ b/src/calibre/gui2/dialogs/metadata_single.py @@ -23,7 +23,7 @@ from calibre.gui2.dialogs.tag_editor import TagEditor from calibre.gui2.widgets import ProgressIndicator from calibre.ebooks import BOOK_EXTENSIONS from calibre.ebooks.metadata import string_to_authors, \ - authors_to_string, check_isbn + authors_to_string, check_isbn, title_sort from calibre.ebooks.metadata.covers import download_cover from calibre.ebooks.metadata.meta import get_metadata from calibre.ebooks.metadata import MetaInformation @@ -444,13 +444,24 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog): self.cover_fetcher = None self.bc_box.layout().setAlignment(self.cover, Qt.AlignCenter|Qt.AlignHCenter) base = unicode(self.author_sort.toolTip()) - self.ok_aus_tooltip = '

' + textwrap.fill(base+'

'+ + ok_tooltip = '

' + textwrap.fill(base+'

'+ _(' The green color indicates that the current ' 'author sort matches the current author')) - self.bad_aus_tooltip = '

'+textwrap.fill(base + '

'+ + bad_tooltip = '

'+textwrap.fill(base + '

'+ _(' The red color indicates that the current ' - 'author sort does not match the current author')) + 'author sort does not match the current author. ' + 'No action is required if this is what you want.')) + self.aus_tooltips = (ok_tooltip, bad_tooltip) + base = unicode(self.title_sort.toolTip()) + ok_tooltip = '

' + textwrap.fill(base+'

'+ + _(' The green color indicates that the current ' + 'title sort matches the current title')) + bad_tooltip = '

'+textwrap.fill(base + '

'+ + _(' The red color warns that the current ' + 'title sort does not match the current title. ' + 'No action is required if this is what you want.')) + self.ts_tooltips = (ok_tooltip, bad_tooltip) self.row_delta = 0 if prev: self.prev_button = QPushButton(QIcon(I('back.png')), _('Previous'), @@ -506,7 +517,13 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog): self.remove_unused_series) QObject.connect(self.auto_author_sort, SIGNAL('clicked()'), self.deduce_author_sort) + QObject.connect(self.auto_title_sort, SIGNAL('clicked()'), + self.deduce_title_sort) self.trim_cover_button.clicked.connect(self.trim_cover) + self.connect(self.title_sort, SIGNAL('textChanged(const QString&)'), + self.title_sort_box_changed) + self.connect(self.title, SIGNAL('textChanged(const QString&)'), + self.title_box_changed) self.connect(self.author_sort, SIGNAL('textChanged(const QString&)'), self.author_sort_box_changed) self.connect(self.authors, SIGNAL('editTextChanged(const QString&)'), @@ -523,6 +540,7 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog): self.title.setText(db.title(row)) + self.title_sort.setText(db.title_sort(row)) isbn = db.isbn(self.id, index_is_id=True) if not isbn: isbn = '' @@ -610,27 +628,40 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog): for c in range(2, len(ans[i].widgets), 2): w.setTabOrder(ans[i].widgets[c-1], ans[i].widgets[c+1]) + def title_box_changed(self, txt): + ts = unicode(txt) + ts = title_sort(ts) + self.mark_box_as_ok(control = self.title_sort, tt=self.ts_tooltips, + normal=(unicode(self.title_sort.text()) == ts)) + + def title_sort_box_changed(self, txt): + ts = unicode(txt) + self.mark_box_as_ok(control = self.title_sort, tt=self.ts_tooltips, + normal=(title_sort(unicode(self.title.text())) == ts)) + def authors_box_changed(self, txt): aus = unicode(txt) aus = re.sub(r'\s+et al\.$', '', aus) aus = self.db.author_sort_from_authors(string_to_authors(aus)) - self.mark_author_sort(normal=(unicode(self.author_sort.text()) == aus)) + self.mark_box_as_ok(control = self.author_sort, tt=self.aus_tooltips, + normal=(unicode(self.author_sort.text()) == aus)) def author_sort_box_changed(self, txt): au = unicode(self.authors.text()) au = re.sub(r'\s+et al\.$', '', au) au = self.db.author_sort_from_authors(string_to_authors(au)) - self.mark_author_sort(normal=(au == txt)) + self.mark_box_as_ok(control = self.author_sort, tt=self.aus_tooltips, + normal=(au == txt)) - def mark_author_sort(self, normal=True): + def mark_box_as_ok(self, control, tt, normal=True): if normal: col = 'rgb(0, 255, 0, 20%)' else: col = 'rgb(255, 0, 0, 20%)' - self.author_sort.setStyleSheet('QLineEdit { color: black; ' - 'background-color: %s; }'%col) - tt = self.ok_aus_tooltip if normal else self.bad_aus_tooltip - self.author_sort.setToolTip(tt) + control.setStyleSheet('QLineEdit { color: black; ' + 'background-color: %s; }'%col) + tt = tt[0] if normal else tt[1] + control.setToolTip(tt) def validate_isbn(self, isbn): isbn = unicode(isbn).strip() @@ -652,6 +683,10 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog): authors = string_to_authors(au) self.author_sort.setText(self.db.author_sort_from_authors(authors)) + def deduce_title_sort(self): + ts = unicode(self.title.text()) + self.title_sort.setText(title_sort(ts)) + def swap_title_author(self): title = self.title.text() self.title.setText(self.authors.text()) @@ -838,6 +873,10 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog): title = unicode(self.title.text()).strip() if title != self.original_title: self.db.set_title(self.id, title, notify=False) + # This must be after setting the title because of the DB update trigger + ts = unicode(self.title_sort.text()).strip() + if ts: + self.db.set_title_sort(self.id, ts, notify=False, commit=False) au = unicode(self.authors.text()).strip() if au and au != self.original_author: self.db.set_authors(self.id, string_to_authors(au), notify=False) diff --git a/src/calibre/gui2/dialogs/metadata_single.ui b/src/calibre/gui2/dialogs/metadata_single.ui index 2bd85e30bb..02e6eb01e2 100644 --- a/src/calibre/gui2/dialogs/metadata_single.ui +++ b/src/calibre/gui2/dialogs/metadata_single.ui @@ -100,7 +100,7 @@ - + Swap the author and title @@ -121,6 +121,41 @@ + + + Title sort: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + title_sort + + + + + + + Specify how this book should be sorted when by title. For example, The Exorcist might be sorted as Exorcist, The. + + + + + + + Automatically create the title sort entry based on the current title entry. +Using this button to create title sort will change title sort from red to green. + + + ... + + + + :/images/auto_author_sort.png:/images/auto_author_sort.png + + + + &Author(s): @@ -133,7 +168,7 @@ - + Author S&ort: @@ -146,7 +181,7 @@ - + @@ -173,7 +208,7 @@ Using this button to create author sort will change author sort from red to gree - + &Rating: @@ -186,7 +221,7 @@ Using this button to create author sort will change author sort from red to gree - + Rating of this book. 0-5 stars @@ -205,7 +240,7 @@ Using this button to create author sort will change author sort from red to gree - + &Publisher: @@ -218,7 +253,7 @@ Using this button to create author sort will change author sort from red to gree - + Ta&gs: @@ -231,7 +266,7 @@ Using this button to create author sort will change author sort from red to gree - + @@ -256,7 +291,7 @@ Using this button to create author sort will change author sort from red to gree - + &Series: @@ -272,7 +307,7 @@ Using this button to create author sort will change author sort from red to gree - + 5 @@ -309,7 +344,7 @@ Using this button to create author sort will change author sort from red to gree - + IS&BN: @@ -322,10 +357,10 @@ Using this button to create author sort will change author sort from red to gree - + - + Publishe&d: @@ -338,14 +373,14 @@ Using this button to create author sort will change author sort from red to gree - + true - + false @@ -358,7 +393,7 @@ Using this button to create author sort will change author sort from red to gree - + MMM yyyy @@ -368,14 +403,14 @@ Using this button to create author sort will change author sort from red to gree - + true - + dd MMM yyyy @@ -385,7 +420,7 @@ Using this button to create author sort will change author sort from red to gree - + &Date: @@ -744,8 +779,11 @@ Using this button to create author sort will change author sort from red to gree title + title_sort + auto_title_sort swap_button authors + swap_button author_sort auto_author_sort rating diff --git a/src/calibre/library/database2.py b/src/calibre/library/database2.py index 23375995ae..b09c2bfc7c 100644 --- a/src/calibre/library/database2.py +++ b/src/calibre/library/database2.py @@ -1565,6 +1565,20 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): if notify: self.notify('metadata', [id]) + def set_title_sort(self, id, title_sort, notify=True, commit=True): + if not title_sort: + return False + if isbytestring(title_sort): + title_sort = title_sort.decode(preferred_encoding, 'replace') + self.conn.execute('UPDATE books SET sort=? WHERE id=?', (title_sort, id)) + self.data.set(id, self.FIELD_MAP['sort'], title_sort, row_is_id=True) + self.dirtied([id], commit=False) + if commit: + self.conn.commit() + if notify: + self.notify('metadata', [id]) + return True + def _set_title(self, id, title): if not title: return False diff --git a/src/calibre/library/schema_upgrades.py b/src/calibre/library/schema_upgrades.py index 5e581e3f86..4bf7e0ac6a 100644 --- a/src/calibre/library/schema_upgrades.py +++ b/src/calibre/library/schema_upgrades.py @@ -429,3 +429,13 @@ class SchemaUpgrade(object): 'Remove commas from tags' self.conn.execute("UPDATE tags SET name=REPLACE(name, ',', ';')") + def upgrade_version_16(self): + self.conn.executescript(''' + DROP TRIGGER IF EXISTS books_update_trg; + CREATE TRIGGER books_update_trg + AFTER UPDATE ON books + BEGIN + UPDATE books SET sort=title_sort(NEW.title) + WHERE id=NEW.id AND OLD.title <> NEW.title; + END; + ''')