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.tag_editor import TagEditor
|
||||
from calibre.ebooks.metadata import string_to_authors, \
|
||||
authors_to_string, MetaInformation
|
||||
from calibre.ebooks.metadata import string_to_authors, authors_to_string
|
||||
from calibre.gui2.custom_column_widgets import populate_metadata_page
|
||||
from calibre.gui2.dialogs.progress import BlockingBusy
|
||||
from calibre.gui2 import error_dialog, Dispatcher
|
||||
from calibre.utils.config import dynamic
|
||||
|
||||
class Worker(Thread):
|
||||
|
||||
@ -208,26 +208,27 @@ class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog):
|
||||
self.book_1_text.setObjectName(name)
|
||||
self.testgrid.addWidget(w, i+offset, 2, 1, 1)
|
||||
|
||||
self.s_r_heading.setText('<p>'+
|
||||
_('<b>You can destroy your library</b> '
|
||||
'using this feature. Changes are permanent. There '
|
||||
'is no undo function. You are strongly encouraged '
|
||||
'to back up your library before proceeding.'
|
||||
) + '<p>' + _(
|
||||
'Search and replace in text fields using '
|
||||
'regular expressions. The search text is an '
|
||||
'arbitrary python-compatible regular expression. '
|
||||
'The replacement text can contain backreferences '
|
||||
'to parenthesized expressions in the pattern. '
|
||||
'The search is not anchored, and can match and '
|
||||
'replace multiple times on the same string. See '
|
||||
'<a href="http://docs.python.org/library/re.html"> '
|
||||
'this reference</a> '
|
||||
'for more information, and in particular the \'sub\' '
|
||||
'function.'
|
||||
))
|
||||
self.s_r_heading.setText('<p>'+ _(
|
||||
'<b>You can destroy your library using this feature.</b> '
|
||||
'Changes are permanent. There is no undo function. '
|
||||
' This feature is experimental, and there may be bugs. '
|
||||
'You are strongly encouraged to back up your library '
|
||||
'before proceeding.'
|
||||
) + '<p>' + _(
|
||||
'Search and replace in text fields using character matching '
|
||||
'or regular expressions. In character mode, search text '
|
||||
'found in the specified field is replaced with replace '
|
||||
'text. In regular expression mode, the search text is an '
|
||||
'arbitrary python-compatible regular expression. The '
|
||||
'replacement text can contain backreferences to parenthesized '
|
||||
'expressions in the pattern. The search is not anchored, '
|
||||
'and can match and replace multiple times on the same string. '
|
||||
'See <a href="http://docs.python.org/library/re.html"> '
|
||||
'this reference</a> for more information, and in particular '
|
||||
'the \'sub\' function.'
|
||||
))
|
||||
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.setCurrentIndex(0)
|
||||
|
||||
@ -252,7 +253,7 @@ class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog):
|
||||
self.search_for.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):
|
||||
if field:
|
||||
@ -303,6 +304,7 @@ class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog):
|
||||
self.replace_mode.setVisible(True)
|
||||
self.replace_mode_label.setVisible(True)
|
||||
self.comma_separated.setVisible(True)
|
||||
self.s_r_paint_results(None)
|
||||
|
||||
def s_r_set_colors(self):
|
||||
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 = self.s_r_get_field(mi, src_field)
|
||||
result = []
|
||||
rfunc = self.s_r_functions[unicode(self.replace_func.currentText())]
|
||||
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
|
||||
|
||||
def s_r_do_destination(self, mi, val):
|
||||
@ -374,7 +380,10 @@ class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog):
|
||||
flags = re.I
|
||||
|
||||
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:
|
||||
self.s_r_obj = None
|
||||
self.s_r_error = e
|
||||
@ -411,7 +420,7 @@ class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog):
|
||||
dest = unicode(self.destination_field.currentText())
|
||||
if not dest:
|
||||
dest = source
|
||||
dfm = self.db.field_metadata[source]
|
||||
dfm = self.db.field_metadata[dest]
|
||||
|
||||
for id in self.ids:
|
||||
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(id, val, notify=False, commit=False)
|
||||
self.db.commit()
|
||||
dynamic['s_r_search_mode'] = self.search_mode.currentIndex()
|
||||
|
||||
def create_custom_column_editors(self):
|
||||
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
|
||||
# system. This can lead to having the directory names not match the
|
||||
# title/author, which leads to trouble when libraries are copied to
|
||||
# a case-sensitive system. The following code fixes this by checking
|
||||
# each segment. If they are different because of case, then rename
|
||||
# the segment to some temp file name, then rename it back to the
|
||||
# correct name. Note that the code above correctly handles files in
|
||||
# the directories, so no need to do them here.
|
||||
# a case-sensitive system. The following code attempts to fix this
|
||||
# by checking each segment. If they are different because of case,
|
||||
# then rename the segment to some temp file name, then rename it
|
||||
# back to the correct name. Note that the code above correctly
|
||||
# handles files in the directories, so no need to do them here.
|
||||
for oldseg, newseg in zip(c1, c2):
|
||||
if oldseg.lower() == newseg.lower() and oldseg != newseg:
|
||||
while True:
|
||||
@ -476,8 +476,17 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
|
||||
tempname = os.path.join(curpath, 'TEMP.%f'%time.time())
|
||||
if not os.path.exists(tempname):
|
||||
break
|
||||
os.rename(os.path.join(curpath, oldseg), tempname)
|
||||
os.rename(tempname, os.path.join(curpath, newseg))
|
||||
try:
|
||||
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)
|
||||
|
||||
def add_listener(self, listener):
|
||||
|
Loading…
x
Reference in New Issue
Block a user