diff --git a/src/calibre/ebooks/conversion/cli.py b/src/calibre/ebooks/conversion/cli.py
index a80ddfb839..92f161bb33 100644
--- a/src/calibre/ebooks/conversion/cli.py
+++ b/src/calibre/ebooks/conversion/cli.py
@@ -156,7 +156,10 @@ def add_pipeline_options(parser, plumber):
'SEARCH AND REPLACE' : (
_('Modify the document text and structure using user defined patterns.'),
[
- 'search_replace',
+ 'sr1_search', 'sr1_replace',
+ 'sr2_search', 'sr2_replace',
+ 'sr3_search', 'sr3_replace',
+ 'search_replace',
]
),
diff --git a/src/calibre/ebooks/conversion/plumber.py b/src/calibre/ebooks/conversion/plumber.py
index 62abc30fbd..88e56c4633 100644
--- a/src/calibre/ebooks/conversion/plumber.py
+++ b/src/calibre/ebooks/conversion/plumber.py
@@ -599,6 +599,32 @@ OptionRecommendation(name='renumber_headings',
help=_('Looks for occurrences of sequential
or tags. '
'The tags are renumbered to prevent splitting in the middle '
'of chapter headings.')),
+OptionRecommendation(name='sr1_search',
+ recommended_value='', level=OptionRecommendation.LOW,
+ help=_('Search pattern (regular expression) to be replaced with '
+ 'sr1-replace.')),
+
+OptionRecommendation(name='sr1_replace',
+ recommended_value='', level=OptionRecommendation.LOW,
+ help=_('Replacement to replace the text found with sr1-search.')),
+
+OptionRecommendation(name='sr2_search',
+ recommended_value='', level=OptionRecommendation.LOW,
+ help=_('Search pattern (regular expression) to be replaced with '
+ 'sr2-replace.')),
+
+OptionRecommendation(name='sr2_replace',
+ recommended_value='', level=OptionRecommendation.LOW,
+ help=_('Replacement to replace the text found with sr2-search.')),
+
+OptionRecommendation(name='sr3_search',
+ recommended_value='', level=OptionRecommendation.LOW,
+ help=_('Search pattern (regular expression) to be replaced with '
+ 'sr3-replace.')),
+
+OptionRecommendation(name='sr3_replace',
+ recommended_value='', level=OptionRecommendation.LOW,
+ help=_('Replacement to replace the text found with sr3-search.')),
OptionRecommendation(name='search_replace',
recommended_value='[]', level=OptionRecommendation.LOW,
diff --git a/src/calibre/gui2/convert/search_and_replace.py b/src/calibre/gui2/convert/search_and_replace.py
index d0ad43e1b4..7389482450 100644
--- a/src/calibre/gui2/convert/search_and_replace.py
+++ b/src/calibre/gui2/convert/search_and_replace.py
@@ -22,7 +22,10 @@ class SearchAndReplaceWidget(Widget, Ui_Form):
def __init__(self, parent, get_option, get_help, db=None, book_id=None):
Widget.__init__(self, parent,
- ['search_replace']
+ ['search_replace',
+ 'sr1_search', 'sr1_replace',
+ 'sr2_search', 'sr2_replace',
+ 'sr3_search', 'sr3_replace']
)
self.db, self.book_id = db, book_id
@@ -34,65 +37,70 @@ class SearchAndReplaceWidget(Widget, Ui_Form):
proto = QTableWidgetItem()
proto.setFlags(Qt.ItemFlags(Qt.ItemIsSelectable + Qt.ItemIsEnabled))
- self.opt_search_replace.setItemPrototype(proto)
- self.opt_search_replace.setColumnCount(2)
- self.opt_search_replace.setColumnWidth(0, 300)
- self.opt_search_replace.setColumnWidth(1, 300)
- self.opt_search_replace.setHorizontalHeaderLabels(['Search Expression', 'Replacement'])
+ self.search_replace.setItemPrototype(proto)
+ self.search_replace.setColumnCount(2)
+ self.search_replace.setColumnWidth(0, 300)
+ self.search_replace.setColumnWidth(1, 300)
+ self.search_replace.setHorizontalHeaderLabels(['Search Expression', 'Replacement'])
self.connect(self.sr_add, SIGNAL('clicked()'), self.sr_add_clicked)
self.connect(self.sr_change, SIGNAL('clicked()'), self.sr_change_clicked)
self.connect(self.sr_remove, SIGNAL('clicked()'), self.sr_remove_clicked)
self.connect(self.sr_load, SIGNAL('clicked()'), self.sr_load_clicked)
self.connect(self.sr_save, SIGNAL('clicked()'), self.sr_save_clicked)
- self.connect(self.opt_search_replace, SIGNAL('currentCellChanged(int, int, int, int)'), self.sr_currentCellChanged)
+ self.connect(self.search_replace, SIGNAL('currentCellChanged(int, int, int, int)'), self.sr_currentCellChanged)
self.initialize_options(get_option, get_help, db, book_id)
def sr_add_clicked(self):
if self.sr_search.regex:
- self.opt_search_replace.insertRow(0)
- newItem = self.opt_search_replace.itemPrototype().clone()
- newItem.setText(self.sr_search.regex)
- self.opt_search_replace.setItem(0,0, newItem)
- newItem = self.opt_search_replace.itemPrototype().clone()
- newItem.setText(self.sr_replace.text())
- self.opt_search_replace.setItem(0,1, newItem)
- self.opt_search_replace.setCurrentCell(0, 0)
+ row = self.sr_add_row(self.sr_search.regex, self.sr_replace.text())
+ self.search_replace.setCurrentCell(row, 0)
+
+ def sr_add_row(self, search, replace):
+ row = self.search_replace.rowCount()
+ self.search_replace.setRowCount(row + 1)
+ newItem = self.search_replace.itemPrototype().clone()
+ newItem.setText(search)
+ self.search_replace.setItem(row,0, newItem)
+ newItem = self.search_replace.itemPrototype().clone()
+ newItem.setText(replace)
+ self.search_replace.setItem(row,1, newItem)
+ return row
def sr_change_clicked(self):
- row = self.opt_search_replace.currentRow()
+ row = self.search_replace.currentRow()
if row >= 0:
- self.opt_search_replace.item(row, 0).setText(self.sr_search.regex)
- self.opt_search_replace.item(row, 1).setText(self.sr_replace.text())
- self.opt_search_replace.setCurrentCell(row, 0)
+ self.search_replace.item(row, 0).setText(self.sr_search.regex)
+ self.search_replace.item(row, 1).setText(self.sr_replace.text())
+ self.search_replace.setCurrentCell(row, 0)
def sr_remove_clicked(self):
- row = self.opt_search_replace.currentRow()
+ row = self.search_replace.currentRow()
if row >= 0:
- self.opt_search_replace.removeRow(row)
- self.opt_search_replace.setCurrentCell(row-1, 0)
+ self.search_replace.removeRow(row)
+ self.search_replace.setCurrentCell(row-1, 0)
def sr_load_clicked(self):
filename = QFileDialog.getOpenFileName(self, 'Load Calibre Search-Replace definitions file', '.', 'Calibre Search-Replace definitions file (*.csr)')
if filename:
with open(filename, 'r') as f:
val = f.read()
- self.set_value(self.opt_search_replace, val)
+ self.set_value(self.search_replace, val)
def sr_save_clicked(self):
filename = QFileDialog.getSaveFileName(self, 'Save Calibre Search-Replace definitions file', '.', 'Calibre Search-Replace definitions file (*.csr)')
if filename:
with open(filename, 'w') as f:
- val = self.get_value(self.opt_search_replace)
+ val = self.get_value(self.search_replace)
f.write(val)
def sr_currentCellChanged(self, row, column, previousRow, previousColumn) :
if row >= 0:
self.sr_change.setEnabled(True)
self.sr_remove.setEnabled(True)
- self.sr_search.set_regex(self.opt_search_replace.item(row, 0).text())
- self.sr_replace.setText(self.opt_search_replace.item(row, 1).text())
+ self.sr_search.set_regex(self.search_replace.item(row, 0).text())
+ self.sr_replace.setText(self.search_replace.item(row, 1).text())
else:
self.sr_change.setEnabled(False)
self.sr_remove.setEnabled(False)
@@ -114,9 +122,9 @@ class SearchAndReplaceWidget(Widget, Ui_Form):
self.sr_search.set_doc(doc)
def pre_commit_check(self):
- for row in xrange(0, self.opt_search_replace.rowCount()):
+ for row in xrange(0, self.search_replace.rowCount()):
try:
- pat = unicode(self.opt_search_replace.item(row,0).text())
+ pat = unicode(self.search_replace.item(row,0).text())
re.compile(pat)
except Exception as err:
error_dialog(self, _('Invalid regular expression'),
@@ -124,23 +132,59 @@ class SearchAndReplaceWidget(Widget, Ui_Form):
return False
return True
+ # Options
+ @property
+ def opt_search_replace(self):
+ return 'search_replace'
+
+ @property
+ def opt_sr1_search(self):
+ return 'sr1_search'
+
+ @property
+ def opt_sr1_replace(self):
+ return 'sr1_replace'
+
+ @property
+ def opt_sr2_search(self):
+ return 'sr2_search'
+
+ @property
+ def opt_sr2_replace(self):
+ return 'sr2_replace'
+
+ @property
+ def opt_sr3_search(self):
+ return 'sr3_search'
+
+ @property
+ def opt_sr3_replace(self):
+ return 'sr3_replace'
+
# Options handling
def connect_gui_obj_handler(self, g, slot):
- if isinstance(g, QTableWidget):
- g.cellChanged.connect(slot)
+ if g == self.opt_search_replace:
+ self.search_replace.cellChanged.connect(slot)
def get_value_handler(self, g):
+ if g != self.opt_search_replace:
+ return None
+
ans = []
- for row in xrange(0, g.rowCount()):
+ for row in xrange(0, self.search_replace.rowCount()):
colItems = []
- for col in xrange(0, g.columnCount()):
- colItems.append(unicode(g.item(row, col).text()))
+ for col in xrange(0, self.search_replace.columnCount()):
+ colItems.append(unicode(self.search_replace.item(row, col).text()))
ans.append(colItems)
return json.dumps(ans)
def set_value_handler(self, g, val):
+ if g != self.opt_search_replace:
+ self.handle_legacy(g, val)
+ return True
+
try:
rowItems = json.loads(val)
if not isinstance(rowItems, list):
@@ -148,11 +192,45 @@ class SearchAndReplaceWidget(Widget, Ui_Form):
except:
rowItems = []
- g.setRowCount(len(rowItems))
+ if len(rowItems) == 0:
+ return True
+
+ self.search_replace.setRowCount(len(rowItems))
for row, colItems in enumerate(rowItems):
for col, cellValue in enumerate(colItems):
- newItem = g.itemPrototype().clone()
+ newItem = self.search_replace.itemPrototype().clone()
newItem.setText(cellValue)
- g.setItem(row,col, newItem)
+ self.search_replace.setItem(row,col, newItem)
return True
+
+ def handle_legacy(self, g, val):
+ '''
+ Handles legacy search/replace options sr1_search, sr1_replace,
+ sr2_search, sr2_replace, sr3_search, sr3_replace.
+ Before introducing the search_replace option only three search/replace
+ definitions could be made. These where stored in the options named above.
+ This function is for backward compatibility with saved options and for
+ compatibility with setting sr* options in the CLI.
+ '''
+
+ if not val: return
+
+ row = int(g[2]) - 1 # the row to set in the search_replace table is 0 for sr1_*, 1 for sr2_*, etc
+ col = (0 if g[4] == 's' else 1) # the fourth character in g is 's' for search options and 'r' for replace options
+
+ # add any missing rows
+ while self.search_replace.rowCount() < row+1:
+ self.sr_add_row('', '')
+
+ # set the value
+ self.search_replace.item(row, col).setText(val)
+
+ def setup_help_handler(self, g, help):
+ if g != self.opt_search_replace:
+ return True
+
+ self.search_replace._help = help
+ self.setup_widget_help(self.search_replace)
+ return True
+
diff --git a/src/calibre/gui2/convert/search_and_replace.ui b/src/calibre/gui2/convert/search_and_replace.ui
index af276d7992..47180ed702 100644
--- a/src/calibre/gui2/convert/search_and_replace.ui
+++ b/src/calibre/gui2/convert/search_and_replace.ui
@@ -129,7 +129,7 @@
-
-
+
0