mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Finish search and replace.
Fix a bug in database2 that seems to be triggered by interactions with the cover cache.
This commit is contained in:
parent
e721bd44ee
commit
ea44e9053f
@ -11,11 +11,11 @@ from PyQt4 import QtGui
|
|||||||
|
|
||||||
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, \
|
from calibre.ebooks.metadata import string_to_authors, authors_to_string
|
||||||
authors_to_string, MetaInformation
|
|
||||||
from calibre.gui2.custom_column_widgets import populate_metadata_page
|
from calibre.gui2.custom_column_widgets import populate_metadata_page
|
||||||
from calibre.gui2.dialogs.progress import BlockingBusy
|
from calibre.gui2.dialogs.progress import BlockingBusy
|
||||||
from calibre.gui2 import error_dialog, Dispatcher
|
from calibre.gui2 import error_dialog, Dispatcher
|
||||||
|
from calibre.utils.config import dynamic
|
||||||
|
|
||||||
class Worker(Thread):
|
class Worker(Thread):
|
||||||
|
|
||||||
@ -208,26 +208,27 @@ class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog):
|
|||||||
self.book_1_text.setObjectName(name)
|
self.book_1_text.setObjectName(name)
|
||||||
self.testgrid.addWidget(w, i+offset, 2, 1, 1)
|
self.testgrid.addWidget(w, i+offset, 2, 1, 1)
|
||||||
|
|
||||||
self.s_r_heading.setText('<p>'+
|
self.s_r_heading.setText('<p>'+ _(
|
||||||
_('<b>You can destroy your library</b> '
|
'<b>You can destroy your library using this feature.</b> '
|
||||||
'using this feature. Changes are permanent. There '
|
'Changes are permanent. There is no undo function. '
|
||||||
'is no undo function. You are strongly encouraged '
|
' This feature is experimental, and there may be bugs. '
|
||||||
'to back up your library before proceeding.'
|
'You are strongly encouraged to back up your library '
|
||||||
) + '<p>' + _(
|
'before proceeding.'
|
||||||
'Search and replace in text fields using '
|
) + '<p>' + _(
|
||||||
'regular expressions. The search text is an '
|
'Search and replace in text fields using character matching '
|
||||||
'arbitrary python-compatible regular expression. '
|
'or regular expressions. In character mode, search text '
|
||||||
'The replacement text can contain backreferences '
|
'found in the specified field is replaced with replace '
|
||||||
'to parenthesized expressions in the pattern. '
|
'text. In regular expression mode, the search text is an '
|
||||||
'The search is not anchored, and can match and '
|
'arbitrary python-compatible regular expression. The '
|
||||||
'replace multiple times on the same string. See '
|
'replacement text can contain backreferences to parenthesized '
|
||||||
'<a href="http://docs.python.org/library/re.html"> '
|
'expressions in the pattern. The search is not anchored, '
|
||||||
'this reference</a> '
|
'and can match and replace multiple times on the same string. '
|
||||||
'for more information, and in particular the \'sub\' '
|
'See <a href="http://docs.python.org/library/re.html"> '
|
||||||
'function.'
|
'this reference</a> for more information, and in particular '
|
||||||
))
|
'the \'sub\' function.'
|
||||||
|
))
|
||||||
self.search_mode.addItems(self.s_r_match_modes)
|
self.search_mode.addItems(self.s_r_match_modes)
|
||||||
self.search_mode.setCurrentIndex(0)
|
self.search_mode.setCurrentIndex(dynamic.get('s_r_search_mode', 0))
|
||||||
self.replace_mode.addItems(self.s_r_replace_modes)
|
self.replace_mode.addItems(self.s_r_replace_modes)
|
||||||
self.replace_mode.setCurrentIndex(0)
|
self.replace_mode.setCurrentIndex(0)
|
||||||
|
|
||||||
@ -252,7 +253,7 @@ class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog):
|
|||||||
self.search_for.completer().setCaseSensitivity(Qt.CaseSensitive)
|
self.search_for.completer().setCaseSensitivity(Qt.CaseSensitive)
|
||||||
self.replace_with.completer().setCaseSensitivity(Qt.CaseSensitive)
|
self.replace_with.completer().setCaseSensitivity(Qt.CaseSensitive)
|
||||||
|
|
||||||
self.s_r_search_mode_changed(0)
|
self.s_r_search_mode_changed(self.search_mode.currentIndex())
|
||||||
|
|
||||||
def s_r_get_field(self, mi, field):
|
def s_r_get_field(self, mi, field):
|
||||||
if field:
|
if field:
|
||||||
@ -303,6 +304,7 @@ class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog):
|
|||||||
self.replace_mode.setVisible(True)
|
self.replace_mode.setVisible(True)
|
||||||
self.replace_mode_label.setVisible(True)
|
self.replace_mode_label.setVisible(True)
|
||||||
self.comma_separated.setVisible(True)
|
self.comma_separated.setVisible(True)
|
||||||
|
self.s_r_paint_results(None)
|
||||||
|
|
||||||
def s_r_set_colors(self):
|
def s_r_set_colors(self):
|
||||||
if self.s_r_error is not None:
|
if self.s_r_error is not None:
|
||||||
@ -325,8 +327,12 @@ class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog):
|
|||||||
src_field = unicode(self.search_field.currentText())
|
src_field = unicode(self.search_field.currentText())
|
||||||
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())]
|
||||||
for s in src:
|
for s in src:
|
||||||
result.append(self.s_r_obj.sub(self.s_r_func, s))
|
t = self.s_r_obj.sub(self.s_r_func, s)
|
||||||
|
if self.search_mode.currentIndex() == 0:
|
||||||
|
t = rfunc(t)
|
||||||
|
result.append(t)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def s_r_do_destination(self, mi, val):
|
def s_r_do_destination(self, mi, val):
|
||||||
@ -374,7 +380,10 @@ class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog):
|
|||||||
flags = re.I
|
flags = re.I
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.s_r_obj = re.compile(unicode(self.search_for.text()), flags)
|
if self.search_mode.currentIndex() == 0:
|
||||||
|
self.s_r_obj = re.compile(re.escape(unicode(self.search_for.text())), flags)
|
||||||
|
else:
|
||||||
|
self.s_r_obj = re.compile(unicode(self.search_for.text()), flags)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.s_r_obj = None
|
self.s_r_obj = None
|
||||||
self.s_r_error = e
|
self.s_r_error = e
|
||||||
@ -411,7 +420,7 @@ class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog):
|
|||||||
dest = unicode(self.destination_field.currentText())
|
dest = unicode(self.destination_field.currentText())
|
||||||
if not dest:
|
if not dest:
|
||||||
dest = source
|
dest = source
|
||||||
dfm = self.db.field_metadata[source]
|
dfm = self.db.field_metadata[dest]
|
||||||
|
|
||||||
for id in self.ids:
|
for id in self.ids:
|
||||||
mi = self.db.get_metadata(id, index_is_id=True,)
|
mi = self.db.get_metadata(id, index_is_id=True,)
|
||||||
@ -439,6 +448,7 @@ class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog):
|
|||||||
setter = getattr(self.db, 'set_'+dest)
|
setter = getattr(self.db, 'set_'+dest)
|
||||||
setter(id, val, notify=False, commit=False)
|
setter(id, val, notify=False, commit=False)
|
||||||
self.db.commit()
|
self.db.commit()
|
||||||
|
dynamic['s_r_search_mode'] = self.search_mode.currentIndex()
|
||||||
|
|
||||||
def create_custom_column_editors(self):
|
def create_custom_column_editors(self):
|
||||||
w = self.central_widget.widget(1)
|
w = self.central_widget.widget(1)
|
||||||
|
@ -464,11 +464,11 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
|
|||||||
# change case don't cause any changes to the directories in the file
|
# change case don't cause any changes to the directories in the file
|
||||||
# system. This can lead to having the directory names not match the
|
# system. This can lead to having the directory names not match the
|
||||||
# title/author, which leads to trouble when libraries are copied to
|
# title/author, which leads to trouble when libraries are copied to
|
||||||
# a case-sensitive system. The following code fixes this by checking
|
# a case-sensitive system. The following code attempts to fix this
|
||||||
# each segment. If they are different because of case, then rename
|
# by checking each segment. If they are different because of case,
|
||||||
# the segment to some temp file name, then rename it back to the
|
# then rename the segment to some temp file name, then rename it
|
||||||
# correct name. Note that the code above correctly handles files in
|
# back to the correct name. Note that the code above correctly
|
||||||
# the directories, so no need to do them here.
|
# handles files in the directories, so no need to do them here.
|
||||||
for oldseg, newseg in zip(c1, c2):
|
for oldseg, newseg in zip(c1, c2):
|
||||||
if oldseg.lower() == newseg.lower() and oldseg != newseg:
|
if oldseg.lower() == newseg.lower() and oldseg != newseg:
|
||||||
while True:
|
while True:
|
||||||
@ -476,8 +476,17 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
|
|||||||
tempname = os.path.join(curpath, 'TEMP.%f'%time.time())
|
tempname = os.path.join(curpath, 'TEMP.%f'%time.time())
|
||||||
if not os.path.exists(tempname):
|
if not os.path.exists(tempname):
|
||||||
break
|
break
|
||||||
os.rename(os.path.join(curpath, oldseg), tempname)
|
try:
|
||||||
os.rename(tempname, os.path.join(curpath, newseg))
|
os.rename(os.path.join(curpath, oldseg), tempname)
|
||||||
|
except (IOError, OSError):
|
||||||
|
# Windows (at least) sometimes refuses to do the rename
|
||||||
|
# probably because a file such a cover is open in the
|
||||||
|
# hierarchy. Just go on -- nothing is hurt beyond the
|
||||||
|
# case of the filesystem not matching the case in
|
||||||
|
# name stored by calibre
|
||||||
|
print 'rename of library component failed'
|
||||||
|
else:
|
||||||
|
os.rename(tempname, os.path.join(curpath, newseg))
|
||||||
curpath = os.path.join(curpath, newseg)
|
curpath = os.path.join(curpath, newseg)
|
||||||
|
|
||||||
def add_listener(self, listener):
|
def add_listener(self, listener):
|
||||||
|
Loading…
x
Reference in New Issue
Block a user