mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Ticket #8732: Search and replace title sort
This commit is contained in:
parent
38b418de1b
commit
0c9dbec71f
@ -11,7 +11,7 @@ from PyQt4.Qt import Qt, QDialog, QGridLayout, QVBoxLayout, QFont, QLabel, \
|
|||||||
|
|
||||||
from calibre.gui2.dialogs.metadata_bulk_ui import Ui_MetadataBulkDialog
|
from calibre.gui2.dialogs.metadata_bulk_ui import Ui_MetadataBulkDialog
|
||||||
from calibre.gui2.dialogs.tag_editor import TagEditor
|
from calibre.gui2.dialogs.tag_editor import TagEditor
|
||||||
from calibre.ebooks.metadata import string_to_authors, authors_to_string
|
from calibre.ebooks.metadata import string_to_authors, authors_to_string, title_sort
|
||||||
from calibre.ebooks.metadata.book.base import composite_formatter
|
from calibre.ebooks.metadata.book.base import composite_formatter
|
||||||
from calibre.ebooks.metadata.meta import get_metadata
|
from calibre.ebooks.metadata.meta import get_metadata
|
||||||
from calibre.gui2.custom_column_widgets import populate_metadata_page
|
from calibre.gui2.custom_column_widgets import populate_metadata_page
|
||||||
@ -134,7 +134,7 @@ class MyBlockingBusy(QDialog): # {{{
|
|||||||
do_autonumber, do_remove_format, remove_format, do_swap_ta, \
|
do_autonumber, do_remove_format, remove_format, do_swap_ta, \
|
||||||
do_remove_conv, do_auto_author, series, do_series_restart, \
|
do_remove_conv, do_auto_author, series, do_series_restart, \
|
||||||
series_start_value, do_title_case, cover_action, clear_series, \
|
series_start_value, do_title_case, cover_action, clear_series, \
|
||||||
pubdate, adddate = self.args
|
pubdate, adddate, do_title_sort = self.args
|
||||||
|
|
||||||
|
|
||||||
# first loop: do author and title. These will commit at the end of each
|
# first loop: do author and title. These will commit at the end of each
|
||||||
@ -159,6 +159,9 @@ class MyBlockingBusy(QDialog): # {{{
|
|||||||
if do_title_case and not title_set:
|
if do_title_case and not title_set:
|
||||||
title = self.db.title(id, index_is_id=True)
|
title = self.db.title(id, index_is_id=True)
|
||||||
self.db.set_title(id, titlecase(title), notify=False)
|
self.db.set_title(id, titlecase(title), notify=False)
|
||||||
|
if do_title_sort:
|
||||||
|
title = self.db.title(id, index_is_id=True)
|
||||||
|
self.db.set_title_sort(id, title_sort(title), notify=False)
|
||||||
if au:
|
if au:
|
||||||
self.db.set_authors(id, string_to_authors(au), notify=False)
|
self.db.set_authors(id, string_to_authors(au), notify=False)
|
||||||
if cover_action == 'remove':
|
if cover_action == 'remove':
|
||||||
@ -360,11 +363,11 @@ class MetadataBulkDialog(ResizableDialog, Ui_MetadataBulkDialog):
|
|||||||
if (f in ['author_sort'] or
|
if (f in ['author_sort'] or
|
||||||
(fm[f]['datatype'] in ['text', 'series', 'enumeration']
|
(fm[f]['datatype'] in ['text', 'series', 'enumeration']
|
||||||
and fm[f].get('search_terms', None)
|
and fm[f].get('search_terms', None)
|
||||||
and f not in ['formats', 'ondevice', 'sort']) or
|
and f not in ['formats', 'ondevice']) or
|
||||||
fm[f]['datatype'] in ['int', 'float', 'bool'] ):
|
fm[f]['datatype'] in ['int', 'float', 'bool'] ):
|
||||||
self.all_fields.append(f)
|
self.all_fields.append(f)
|
||||||
self.writable_fields.append(f)
|
self.writable_fields.append(f)
|
||||||
if f in ['sort'] or fm[f]['datatype'] == 'composite':
|
if fm[f]['datatype'] == 'composite':
|
||||||
self.all_fields.append(f)
|
self.all_fields.append(f)
|
||||||
self.all_fields.sort()
|
self.all_fields.sort()
|
||||||
self.all_fields.insert(1, '{template}')
|
self.all_fields.insert(1, '{template}')
|
||||||
@ -437,7 +440,7 @@ class MetadataBulkDialog(ResizableDialog, Ui_MetadataBulkDialog):
|
|||||||
self.replace_func.addItems(sorted(self.s_r_functions.keys()))
|
self.replace_func.addItems(sorted(self.s_r_functions.keys()))
|
||||||
self.search_mode.currentIndexChanged[int].connect(self.s_r_search_mode_changed)
|
self.search_mode.currentIndexChanged[int].connect(self.s_r_search_mode_changed)
|
||||||
self.search_field.currentIndexChanged[int].connect(self.s_r_search_field_changed)
|
self.search_field.currentIndexChanged[int].connect(self.s_r_search_field_changed)
|
||||||
self.destination_field.currentIndexChanged[str].connect(self.s_r_destination_field_changed)
|
self.destination_field.currentIndexChanged[int].connect(self.s_r_destination_field_changed)
|
||||||
|
|
||||||
self.replace_mode.currentIndexChanged[int].connect(self.s_r_paint_results)
|
self.replace_mode.currentIndexChanged[int].connect(self.s_r_paint_results)
|
||||||
self.replace_func.currentIndexChanged[str].connect(self.s_r_paint_results)
|
self.replace_func.currentIndexChanged[str].connect(self.s_r_paint_results)
|
||||||
@ -469,6 +472,16 @@ class MetadataBulkDialog(ResizableDialog, Ui_MetadataBulkDialog):
|
|||||||
self.query_field.currentIndexChanged[str].connect(self.s_r_query_change)
|
self.query_field.currentIndexChanged[str].connect(self.s_r_query_change)
|
||||||
self.query_field.setCurrentIndex(0)
|
self.query_field.setCurrentIndex(0)
|
||||||
|
|
||||||
|
def s_r_sf_itemdata(self, idx):
|
||||||
|
if idx is None:
|
||||||
|
idx = self.search_field.currentIndex()
|
||||||
|
return unicode(self.search_field.itemData(idx).toString())
|
||||||
|
|
||||||
|
def s_r_df_itemdata(self, idx):
|
||||||
|
if idx is None:
|
||||||
|
idx = self.destination_field.currentIndex()
|
||||||
|
return unicode(self.destination_field.itemData(idx).toString())
|
||||||
|
|
||||||
def s_r_get_field(self, mi, field):
|
def s_r_get_field(self, mi, field):
|
||||||
if field:
|
if field:
|
||||||
if field == '{template}':
|
if field == '{template}':
|
||||||
@ -508,7 +521,7 @@ class MetadataBulkDialog(ResizableDialog, Ui_MetadataBulkDialog):
|
|||||||
for i in range(0, self.s_r_number_of_books):
|
for i in range(0, self.s_r_number_of_books):
|
||||||
w = getattr(self, 'book_%d_text'%(i+1))
|
w = getattr(self, 'book_%d_text'%(i+1))
|
||||||
mi = self.db.get_metadata(self.ids[i], index_is_id=True)
|
mi = self.db.get_metadata(self.ids[i], index_is_id=True)
|
||||||
src = unicode(self.search_field.currentText())
|
src = self.s_r_sf_itemdata(idx)
|
||||||
t = self.s_r_get_field(mi, src)
|
t = self.s_r_get_field(mi, src)
|
||||||
if len(t) > 1:
|
if len(t) > 1:
|
||||||
t = t[self.starting_from.value()-1:
|
t = t[self.starting_from.value()-1:
|
||||||
@ -518,13 +531,13 @@ class MetadataBulkDialog(ResizableDialog, Ui_MetadataBulkDialog):
|
|||||||
if self.search_mode.currentIndex() == 0:
|
if self.search_mode.currentIndex() == 0:
|
||||||
self.destination_field.setCurrentIndex(idx)
|
self.destination_field.setCurrentIndex(idx)
|
||||||
else:
|
else:
|
||||||
self.s_r_destination_field_changed(self.destination_field.currentText())
|
self.s_r_destination_field_changed(self.destination_field.currentIndex())
|
||||||
self.s_r_paint_results(None)
|
self.s_r_paint_results(None)
|
||||||
|
|
||||||
def s_r_destination_field_changed(self, txt):
|
def s_r_destination_field_changed(self, idx):
|
||||||
txt = unicode(txt)
|
txt = self.s_r_df_itemdata(idx)
|
||||||
if not txt:
|
if not txt:
|
||||||
txt = unicode(self.search_field.currentText())
|
txt = self.s_r_sf_itemdata(None)
|
||||||
if txt and txt in self.writable_fields:
|
if txt and txt in self.writable_fields:
|
||||||
self.destination_field_fm = self.db.metadata_for_field(txt)
|
self.destination_field_fm = self.db.metadata_for_field(txt)
|
||||||
self.s_r_paint_results(None)
|
self.s_r_paint_results(None)
|
||||||
@ -533,8 +546,9 @@ class MetadataBulkDialog(ResizableDialog, Ui_MetadataBulkDialog):
|
|||||||
self.search_field.clear()
|
self.search_field.clear()
|
||||||
self.destination_field.clear()
|
self.destination_field.clear()
|
||||||
if val == 0:
|
if val == 0:
|
||||||
self.search_field.addItems(self.writable_fields)
|
for f in self.writable_fields:
|
||||||
self.destination_field.addItems(self.writable_fields)
|
self.search_field.addItem(f if f != 'sort' else 'title_sort', f)
|
||||||
|
self.destination_field.addItem(f if f != 'sort' else 'title_sort', f)
|
||||||
self.destination_field.setCurrentIndex(0)
|
self.destination_field.setCurrentIndex(0)
|
||||||
self.destination_field.setVisible(False)
|
self.destination_field.setVisible(False)
|
||||||
self.destination_field_label.setVisible(False)
|
self.destination_field_label.setVisible(False)
|
||||||
@ -544,8 +558,16 @@ class MetadataBulkDialog(ResizableDialog, Ui_MetadataBulkDialog):
|
|||||||
self.comma_separated.setVisible(False)
|
self.comma_separated.setVisible(False)
|
||||||
self.s_r_heading.setText('<p>'+self.main_heading + self.character_heading)
|
self.s_r_heading.setText('<p>'+self.main_heading + self.character_heading)
|
||||||
else:
|
else:
|
||||||
self.search_field.addItems(self.all_fields)
|
self.search_field.blockSignals(True)
|
||||||
self.destination_field.addItems(self.writable_fields)
|
self.destination_field.blockSignals(True)
|
||||||
|
for f in self.all_fields:
|
||||||
|
self.search_field.addItem(f if f != 'sort' else 'title_sort', f)
|
||||||
|
for f in self.writable_fields:
|
||||||
|
self.destination_field.addItem(f if f != 'sort' else 'title_sort', f)
|
||||||
|
self.search_field.blockSignals(False)
|
||||||
|
self.destination_field.blockSignals(False)
|
||||||
|
for i in range(0, len(self.all_fields)):
|
||||||
|
print unicode(self.search_field.itemData(i).toString())
|
||||||
self.destination_field.setVisible(True)
|
self.destination_field.setVisible(True)
|
||||||
self.destination_field_label.setVisible(True)
|
self.destination_field_label.setVisible(True)
|
||||||
self.replace_mode.setVisible(True)
|
self.replace_mode.setVisible(True)
|
||||||
@ -575,7 +597,7 @@ class MetadataBulkDialog(ResizableDialog, Ui_MetadataBulkDialog):
|
|||||||
return rfunc(rtext)
|
return rfunc(rtext)
|
||||||
|
|
||||||
def s_r_do_regexp(self, mi):
|
def s_r_do_regexp(self, mi):
|
||||||
src_field = unicode(self.search_field.currentText())
|
src_field = self.s_r_sf_itemdata(None)
|
||||||
src = self.s_r_get_field(mi, src_field)
|
src = self.s_r_get_field(mi, src_field)
|
||||||
result = []
|
result = []
|
||||||
rfunc = self.s_r_functions[unicode(self.replace_func.currentText())]
|
rfunc = self.s_r_functions[unicode(self.replace_func.currentText())]
|
||||||
@ -587,10 +609,10 @@ class MetadataBulkDialog(ResizableDialog, Ui_MetadataBulkDialog):
|
|||||||
return result
|
return result
|
||||||
|
|
||||||
def s_r_do_destination(self, mi, val):
|
def s_r_do_destination(self, mi, val):
|
||||||
src = unicode(self.search_field.currentText())
|
src = self.s_r_sf_itemdata(None)
|
||||||
if src == '':
|
if src == '':
|
||||||
return ''
|
return ''
|
||||||
dest = unicode(self.destination_field.currentText())
|
dest = self.s_r_df_itemdata(None)
|
||||||
if dest == '':
|
if dest == '':
|
||||||
if self.db.metadata_for_field(src)['datatype'] == 'composite':
|
if self.db.metadata_for_field(src)['datatype'] == 'composite':
|
||||||
raise Exception(_('You must specify a destination when source is a composite field'))
|
raise Exception(_('You must specify a destination when source is a composite field'))
|
||||||
@ -680,10 +702,10 @@ class MetadataBulkDialog(ResizableDialog, Ui_MetadataBulkDialog):
|
|||||||
break
|
break
|
||||||
|
|
||||||
def do_search_replace(self, id):
|
def do_search_replace(self, id):
|
||||||
source = unicode(self.search_field.currentText())
|
source = self.s_r_sf_itemdata(None)
|
||||||
if not source or not self.s_r_obj:
|
if not source or not self.s_r_obj:
|
||||||
return
|
return
|
||||||
dest = unicode(self.destination_field.currentText())
|
dest = self.s_r_df_itemdata(None)
|
||||||
if not dest:
|
if not dest:
|
||||||
dest = source
|
dest = source
|
||||||
dfm = self.db.field_metadata[dest]
|
dfm = self.db.field_metadata[dest]
|
||||||
@ -717,6 +739,8 @@ class MetadataBulkDialog(ResizableDialog, Ui_MetadataBulkDialog):
|
|||||||
else:
|
else:
|
||||||
if dest == 'comments':
|
if dest == 'comments':
|
||||||
setter = self.db.set_comment
|
setter = self.db.set_comment
|
||||||
|
elif dest == 'sort':
|
||||||
|
setter = self.db.set_title_sort
|
||||||
else:
|
else:
|
||||||
setter = getattr(self.db, 'set_'+dest)
|
setter = getattr(self.db, 'set_'+dest)
|
||||||
if dest in ['title', 'authors']:
|
if dest in ['title', 'authors']:
|
||||||
@ -844,6 +868,7 @@ class MetadataBulkDialog(ResizableDialog, Ui_MetadataBulkDialog):
|
|||||||
do_remove_conv = self.remove_conversion_settings.isChecked()
|
do_remove_conv = self.remove_conversion_settings.isChecked()
|
||||||
do_auto_author = self.auto_author_sort.isChecked()
|
do_auto_author = self.auto_author_sort.isChecked()
|
||||||
do_title_case = self.change_title_to_title_case.isChecked()
|
do_title_case = self.change_title_to_title_case.isChecked()
|
||||||
|
do_title_sort = self.update_title_sort.isChecked()
|
||||||
pubdate = adddate = None
|
pubdate = adddate = None
|
||||||
if self.apply_pubdate.isChecked():
|
if self.apply_pubdate.isChecked():
|
||||||
pubdate = qt_to_dt(self.pubdate.date())
|
pubdate = qt_to_dt(self.pubdate.date())
|
||||||
@ -862,7 +887,7 @@ class MetadataBulkDialog(ResizableDialog, Ui_MetadataBulkDialog):
|
|||||||
do_autonumber, do_remove_format, remove_format, do_swap_ta,
|
do_autonumber, do_remove_format, remove_format, do_swap_ta,
|
||||||
do_remove_conv, do_auto_author, series, do_series_restart,
|
do_remove_conv, do_auto_author, series, do_series_restart,
|
||||||
series_start_value, do_title_case, cover_action, clear_series,
|
series_start_value, do_title_case, cover_action, clear_series,
|
||||||
pubdate, adddate)
|
pubdate, adddate, do_title_sort)
|
||||||
|
|
||||||
bb = MyBlockingBusy(_('Applying changes to %d books.\nPhase {0} {1}%%.')
|
bb = MyBlockingBusy(_('Applying changes to %d books.\nPhase {0} {1}%%.')
|
||||||
%len(self.ids), args, self.db, self.ids,
|
%len(self.ids), args, self.db, self.ids,
|
||||||
|
@ -489,6 +489,17 @@ title and author are swapped before the title case is set</string>
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="update_title_sort">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Recompute the title sort value and store it in title sort.
|
||||||
|
This will happen after any title case changes</string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Update &title sort</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<spacer name="horizontalSpacer">
|
<spacer name="horizontalSpacer">
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
|
@ -1718,10 +1718,6 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
|
|||||||
title = title.decode(preferred_encoding, 'replace')
|
title = title.decode(preferred_encoding, 'replace')
|
||||||
self.conn.execute('UPDATE books SET title=? WHERE id=?', (title, id))
|
self.conn.execute('UPDATE books SET title=? WHERE id=?', (title, id))
|
||||||
self.data.set(id, self.FIELD_MAP['title'], title, row_is_id=True)
|
self.data.set(id, self.FIELD_MAP['title'], title, row_is_id=True)
|
||||||
if tweaks['title_series_sorting'] == 'library_order':
|
|
||||||
self.data.set(id, self.FIELD_MAP['sort'], title_sort(title), row_is_id=True)
|
|
||||||
else:
|
|
||||||
self.data.set(id, self.FIELD_MAP['sort'], title, row_is_id=True)
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def set_title(self, id, title, notify=True, commit=True):
|
def set_title(self, id, title, notify=True, commit=True):
|
||||||
|
Loading…
x
Reference in New Issue
Block a user