mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Refactor the search code in preparation for multi-searches
This commit is contained in:
parent
8cb0d7e08b
commit
91a993643e
@ -7,14 +7,14 @@ __license__ = 'GPL v3'
|
||||
__copyright__ = '2013, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||
|
||||
import tempfile, shutil, sys, os
|
||||
from collections import OrderedDict
|
||||
from functools import partial, wraps
|
||||
|
||||
from PyQt4.Qt import (
|
||||
QObject, QApplication, QDialog, QGridLayout, QLabel, QSize, Qt, QCursor,
|
||||
QDialogButtonBox, QIcon, QTimer, QPixmap, QTextBrowser, QVBoxLayout, QInputDialog)
|
||||
QObject, QApplication, QDialog, QGridLayout, QLabel, QSize, Qt,
|
||||
QDialogButtonBox, QIcon, QTimer, QPixmap, QTextBrowser, QVBoxLayout,
|
||||
QInputDialog)
|
||||
|
||||
from calibre import prints, prepare_string_for_xml, isbytestring
|
||||
from calibre import prints, isbytestring
|
||||
from calibre.ptempfile import PersistentTemporaryDirectory, TemporaryDirectory
|
||||
from calibre.ebooks.oeb.base import urlnormalize
|
||||
from calibre.ebooks.oeb.polish.main import SUPPORTED, tweak_polish
|
||||
@ -27,7 +27,6 @@ from calibre.ebooks.oeb.polish.toc import remove_names_from_toc, find_existing_t
|
||||
from calibre.ebooks.oeb.polish.utils import link_stylesheets, setup_cssutils_serialization as scs
|
||||
from calibre.gui2 import error_dialog, choose_files, question_dialog, info_dialog, choose_save_file
|
||||
from calibre.gui2.dialogs.confirm_delete import confirm
|
||||
from calibre.gui2.dialogs.message_box import MessageBox
|
||||
from calibre.gui2.tweak_book import set_current_container, current_container, tprefs, actions, editors
|
||||
from calibre.gui2.tweak_book.undo import GlobalUndoHistory
|
||||
from calibre.gui2.tweak_book.file_list import NewFileDialog
|
||||
@ -37,8 +36,10 @@ from calibre.gui2.tweak_book.toc import TOCEditor
|
||||
from calibre.gui2.tweak_book.editor import editor_from_syntax, syntax_from_mime
|
||||
from calibre.gui2.tweak_book.editor.insert_resource import get_resource_data, NewBook
|
||||
from calibre.gui2.tweak_book.preferences import Preferences
|
||||
from calibre.gui2.tweak_book.search import validate_search_request, run_search
|
||||
from calibre.gui2.tweak_book.widgets import (
|
||||
RationalizeFolders, MultiSplit, ImportForeign, QuickOpen, InsertLink, InsertSemantics)
|
||||
RationalizeFolders, MultiSplit, ImportForeign, QuickOpen, InsertLink,
|
||||
InsertSemantics, BusyCursor)
|
||||
|
||||
_diff_dialogs = []
|
||||
|
||||
@ -58,14 +59,6 @@ def get_container(*args, **kwargs):
|
||||
def setup_cssutils_serialization():
|
||||
scs(tprefs['editor_tab_stop_width'])
|
||||
|
||||
class BusyCursor(object):
|
||||
|
||||
def __enter__(self):
|
||||
QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))
|
||||
|
||||
def __exit__(self, *args):
|
||||
QApplication.restoreOverrideCursor()
|
||||
|
||||
def in_thread_job(func):
|
||||
@wraps(func)
|
||||
def ans(*args, **kwargs):
|
||||
@ -675,7 +668,7 @@ class Boss(QObject):
|
||||
# Ensure the search panel is visible
|
||||
sp.setVisible(True)
|
||||
ed = self.gui.central.current_editor
|
||||
name = editor = None
|
||||
name = None
|
||||
for n, x in editors.iteritems():
|
||||
if x is ed:
|
||||
name = n
|
||||
@ -684,158 +677,11 @@ class Boss(QObject):
|
||||
if overrides:
|
||||
state.update(overrides)
|
||||
searchable_names = self.gui.file_list.searchable_names
|
||||
where = state['where']
|
||||
err = None
|
||||
if name is None and where in {'current', 'selected-text'}:
|
||||
err = _('No file is being edited.')
|
||||
elif where == 'selected' and not searchable_names['selected']:
|
||||
err = _('No files are selected in the Files Browser')
|
||||
elif where == 'selected-text' and not ed.has_marked_text:
|
||||
err = _('No text is marked. First select some text, and then use'
|
||||
' The "Mark selected text" action in the Search menu to mark it.')
|
||||
if not err and not state['find']:
|
||||
err = _('No search query specified')
|
||||
if err:
|
||||
return error_dialog(self.gui, _('Cannot search'), err, show=True)
|
||||
del err
|
||||
if not validate_search_request(name, searchable_names, getattr(ed, 'has_marked_text', False), state, self.gui):
|
||||
return
|
||||
|
||||
files = OrderedDict()
|
||||
do_all = state['wrap'] or action in {'replace-all', 'count'}
|
||||
marked = False
|
||||
if where == 'current':
|
||||
editor = ed
|
||||
elif where in {'styles', 'text', 'selected'}:
|
||||
files = searchable_names[where]
|
||||
if name in files:
|
||||
# Start searching in the current editor
|
||||
editor = ed
|
||||
# Re-order the list of other files so that we search in the same
|
||||
# order every time. Depending on direction, search the files
|
||||
# that come after the current file, or before the current file,
|
||||
# first.
|
||||
lfiles = list(files)
|
||||
idx = lfiles.index(name)
|
||||
before, after = lfiles[:idx], lfiles[idx+1:]
|
||||
if state['direction'] == 'up':
|
||||
lfiles = list(reversed(before))
|
||||
if do_all:
|
||||
lfiles += list(reversed(after)) + [name]
|
||||
else:
|
||||
lfiles = after
|
||||
if do_all:
|
||||
lfiles += before + [name]
|
||||
files = OrderedDict((m, files[m]) for m in lfiles)
|
||||
else:
|
||||
editor = ed
|
||||
marked = True
|
||||
|
||||
def no_match():
|
||||
QApplication.restoreOverrideCursor()
|
||||
msg = '<p>' + _('No matches were found for %s') % ('<pre style="font-style:italic">' + prepare_string_for_xml(state['find']) + '</pre>')
|
||||
if not state['wrap']:
|
||||
msg += '<p>' + _('You have turned off search wrapping, so all text might not have been searched.'
|
||||
' Try the search again, with wrapping enabled. Wrapping is enabled via the'
|
||||
' "Wrap" checkbox at the bottom of the search panel.')
|
||||
return error_dialog(
|
||||
self.gui, _('Not found'), msg, show=True)
|
||||
|
||||
pat = sp.get_regex(state)
|
||||
|
||||
def do_find():
|
||||
if editor is not None:
|
||||
if editor.find(pat, marked=marked, save_match='gui'):
|
||||
return
|
||||
if not files:
|
||||
if not state['wrap']:
|
||||
return no_match()
|
||||
return editor.find(pat, wrap=True, marked=marked, save_match='gui') or no_match()
|
||||
for fname, syntax in files.iteritems():
|
||||
if fname in editors:
|
||||
if not editors[fname].find(pat, complete=True, save_match='gui'):
|
||||
continue
|
||||
return self.show_editor(fname)
|
||||
raw = current_container().raw_data(fname)
|
||||
if pat.search(raw) is not None:
|
||||
self.edit_file(fname, syntax)
|
||||
if editors[fname].find(pat, complete=True, save_match='gui'):
|
||||
return
|
||||
return no_match()
|
||||
|
||||
def no_replace(prefix=''):
|
||||
QApplication.restoreOverrideCursor()
|
||||
if prefix:
|
||||
prefix += ' '
|
||||
error_dialog(
|
||||
self.gui, _('Cannot replace'), prefix + _(
|
||||
'You must first click Find, before trying to replace'), show=True)
|
||||
return False
|
||||
|
||||
def do_replace():
|
||||
if editor is None:
|
||||
return no_replace()
|
||||
if not editor.replace(pat, state['replace'], saved_match='gui'):
|
||||
return no_replace(_(
|
||||
'Currently selected text does not match the search query.'))
|
||||
return True
|
||||
|
||||
def count_message(action, count, show_diff=False):
|
||||
msg = _('%(action)s %(num)s occurrences of %(query)s' % dict(num=count, query=state['find'], action=action))
|
||||
if show_diff and count > 0:
|
||||
d = MessageBox(MessageBox.INFO, _('Searching done'), prepare_string_for_xml(msg), parent=self.gui, show_copy_button=False)
|
||||
d.diffb = b = d.bb.addButton(_('See what &changed'), d.bb.ActionRole)
|
||||
b.setIcon(QIcon(I('diff.png'))), d.set_details(None), b.clicked.connect(d.accept)
|
||||
b.clicked.connect(partial(self.show_current_diff, allow_revert=True))
|
||||
d.exec_()
|
||||
else:
|
||||
info_dialog(self.gui, _('Searching done'), prepare_string_for_xml(msg), show=True)
|
||||
|
||||
def do_all(replace=True):
|
||||
count = 0
|
||||
if not files and editor is None:
|
||||
return 0
|
||||
lfiles = files or {name:editor.syntax}
|
||||
|
||||
for n, syntax in lfiles.iteritems():
|
||||
if n in editors:
|
||||
raw = editors[n].get_raw_data()
|
||||
else:
|
||||
raw = current_container().raw_data(n)
|
||||
if replace:
|
||||
raw, num = pat.subn(state['replace'], raw)
|
||||
else:
|
||||
num = len(pat.findall(raw))
|
||||
count += num
|
||||
if replace and num > 0:
|
||||
if n in editors:
|
||||
editors[n].replace_data(raw)
|
||||
else:
|
||||
with current_container().open(n, 'wb') as f:
|
||||
f.write(raw.encode('utf-8'))
|
||||
QApplication.restoreOverrideCursor()
|
||||
count_message(_('Replaced') if replace else _('Found'), count, show_diff=replace)
|
||||
return count
|
||||
|
||||
with BusyCursor():
|
||||
if action == 'find':
|
||||
return do_find()
|
||||
if action == 'replace':
|
||||
return do_replace()
|
||||
if action == 'replace-find' and do_replace():
|
||||
return do_find()
|
||||
if action == 'replace-all':
|
||||
if marked:
|
||||
return count_message(_('Replaced'), editor.all_in_marked(pat, state['replace']))
|
||||
self.add_savepoint(_('Before: Replace all'))
|
||||
count = do_all()
|
||||
if count == 0:
|
||||
self.rewind_savepoint()
|
||||
else:
|
||||
self.set_modified()
|
||||
return
|
||||
if action == 'count':
|
||||
if marked:
|
||||
return count_message(_('Found'), editor.all_in_marked(pat))
|
||||
return do_all(replace=False)
|
||||
run_search(state, action, ed, name, searchable_names,
|
||||
self.gui, self.show_editor, self.edit_file, self.show_current_diff, self.add_savepoint, self.rewind_savepoint, self.set_modified)
|
||||
|
||||
def create_checkpoint(self):
|
||||
text, ok = QInputDialog.getText(self.gui, _('Choose name'), _(
|
||||
|
@ -21,7 +21,7 @@ from calibre.constants import plugins, cache_dir
|
||||
from calibre.gui2 import NONE
|
||||
from calibre.gui2.widgets2 import HistoryLineEdit2
|
||||
from calibre.gui2.tweak_book import tprefs
|
||||
from calibre.gui2.tweak_book.widgets import Dialog
|
||||
from calibre.gui2.tweak_book.widgets import Dialog, BusyCursor
|
||||
from calibre.utils.icu import safe_chr as chr, icu_unicode_version, character_name_from_code
|
||||
|
||||
ROOT = QModelIndex()
|
||||
@ -765,7 +765,6 @@ class CharSelect(Dialog):
|
||||
self.char_view.setFocus(Qt.OtherFocusReason)
|
||||
|
||||
def do_search(self):
|
||||
from calibre.gui2.tweak_book.boss import BusyCursor
|
||||
text = unicode(self.search.text()).strip()
|
||||
if not text:
|
||||
return self.clear_search()
|
||||
|
@ -15,6 +15,7 @@ from PyQt4.Qt import (
|
||||
from calibre.ebooks.oeb.polish.check.base import WARN, INFO, DEBUG, ERROR, CRITICAL
|
||||
from calibre.ebooks.oeb.polish.check.main import run_checks, fix_errors
|
||||
from calibre.gui2.tweak_book import tprefs
|
||||
from calibre.gui2.tweak_book.widgets import BusyCursor
|
||||
|
||||
def icon_for_level(level):
|
||||
if level > WARN:
|
||||
@ -160,7 +161,6 @@ class Check(QSplitter):
|
||||
template % (err.HELP, ifix, fix_tt, fix_msg, run_tt, run_msg))
|
||||
|
||||
def run_checks(self, container):
|
||||
from calibre.gui2.tweak_book.boss import BusyCursor
|
||||
with BusyCursor():
|
||||
self.show_busy()
|
||||
QApplication.processEvents()
|
||||
@ -179,7 +179,6 @@ class Check(QSplitter):
|
||||
self.clear_help(_('No problems found'))
|
||||
|
||||
def fix_errors(self, container, errors):
|
||||
from calibre.gui2.tweak_book.boss import BusyCursor
|
||||
with BusyCursor():
|
||||
self.show_busy(_('Running fixers, please wait...'))
|
||||
QApplication.processEvents()
|
||||
|
@ -7,6 +7,7 @@ __license__ = 'GPL v3'
|
||||
__copyright__ = '2013, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||
|
||||
from functools import partial
|
||||
from collections import OrderedDict
|
||||
|
||||
from PyQt4.Qt import (
|
||||
QWidget, QToolBar, Qt, QHBoxLayout, QSize, QIcon, QGridLayout, QLabel,
|
||||
@ -16,10 +17,12 @@ from PyQt4.Qt import (
|
||||
|
||||
import regex
|
||||
|
||||
from calibre.gui2 import NONE, error_dialog
|
||||
from calibre import prepare_string_for_xml
|
||||
from calibre.gui2 import NONE, error_dialog, info_dialog
|
||||
from calibre.gui2.dialogs.message_box import MessageBox
|
||||
from calibre.gui2.widgets2 import HistoryLineEdit2
|
||||
from calibre.gui2.tweak_book import tprefs
|
||||
from calibre.gui2.tweak_book.widgets import Dialog
|
||||
from calibre.gui2.tweak_book import tprefs, editors, current_container
|
||||
from calibre.gui2.tweak_book.widgets import Dialog, BusyCursor
|
||||
|
||||
from calibre.utils.icu import primary_contains
|
||||
|
||||
@ -332,22 +335,6 @@ class SearchPanel(QWidget): # {{{
|
||||
def set_where(self, val):
|
||||
self.widget.where = val
|
||||
|
||||
def get_regex(self, state):
|
||||
raw = state['find']
|
||||
if state['mode'] != 'regex':
|
||||
raw = regex.escape(raw, special_only=True)
|
||||
flags = REGEX_FLAGS
|
||||
if not state['case_sensitive']:
|
||||
flags |= regex.IGNORECASE
|
||||
if state['mode'] == 'regex' and state['dot_all']:
|
||||
flags |= regex.DOTALL
|
||||
if state['direction'] == 'up':
|
||||
flags |= regex.REVERSE
|
||||
ans = regex_cache.get((flags, raw), None)
|
||||
if ans is None:
|
||||
ans = regex_cache[(flags, raw)] = regex.compile(raw, flags=flags)
|
||||
return ans
|
||||
|
||||
def keyPressEvent(self, ev):
|
||||
if ev.key() == Qt.Key_Escape:
|
||||
self.hide_panel()
|
||||
@ -544,7 +531,7 @@ class SavedSearches(Dialog):
|
||||
(_('&Replace'), 'replace', _('Run replace using the selected entries.') + mulmsg),
|
||||
(_('Replace a&nd Find'), 'replace-find', _('Run replace and then find using the selected entries.') + mulmsg),
|
||||
(_('Replace &all'), 'replace-all', _('Run Replace All for all selected entries in the order selected')),
|
||||
(_('&Count all'), 'count-all', _('Run Count All for all selected entries')),
|
||||
(_('&Count all'), 'count', _('Run Count All for all selected entries')),
|
||||
]:
|
||||
b = pb(text, tooltip)
|
||||
v.addWidget(b)
|
||||
@ -642,6 +629,7 @@ class SavedSearches(Dialog):
|
||||
search['wrap'] = self.wrap
|
||||
search['direction'] = self.direction
|
||||
search['where'] = self.where
|
||||
search['mode'] = 'regex'
|
||||
searches.append(search)
|
||||
if not searches:
|
||||
return
|
||||
@ -689,6 +677,185 @@ class SavedSearches(Dialog):
|
||||
self.description.setText(_('{2}\nFind: {0}\nReplace: {1}').format(
|
||||
search.get('find', ''), search.get('replace', ''), search.get('name', '')))
|
||||
|
||||
def validate_search_request(name, searchable_names, has_marked_text, state, gui_parent):
|
||||
err = None
|
||||
where = state['where']
|
||||
if name is None and where in {'current', 'selected-text'}:
|
||||
err = _('No file is being edited.')
|
||||
elif where == 'selected' and not searchable_names['selected']:
|
||||
err = _('No files are selected in the Files Browser')
|
||||
elif where == 'selected-text' and not has_marked_text:
|
||||
err = _('No text is marked. First select some text, and then use'
|
||||
' The "Mark selected text" action in the Search menu to mark it.')
|
||||
if not err and not state['find']:
|
||||
err = _('No search query specified')
|
||||
if err:
|
||||
error_dialog(gui_parent, _('Cannot search'), err, show=True)
|
||||
return False
|
||||
return True
|
||||
|
||||
def get_search_regex(state):
|
||||
raw = state['find']
|
||||
if state['mode'] != 'regex':
|
||||
raw = regex.escape(raw, special_only=True)
|
||||
flags = REGEX_FLAGS
|
||||
if not state['case_sensitive']:
|
||||
flags |= regex.IGNORECASE
|
||||
if state['mode'] == 'regex' and state['dot_all']:
|
||||
flags |= regex.DOTALL
|
||||
if state['direction'] == 'up':
|
||||
flags |= regex.REVERSE
|
||||
ans = regex_cache.get((flags, raw), None)
|
||||
if ans is None:
|
||||
ans = regex_cache[(flags, raw)] = regex.compile(raw, flags=flags)
|
||||
return ans
|
||||
|
||||
def initialize_search_request(state, action, current_editor, current_editor_name, searchable_names):
|
||||
editor = None
|
||||
where = state['where']
|
||||
files = OrderedDict()
|
||||
do_all = state['wrap'] or action in {'replace-all', 'count'}
|
||||
marked = False
|
||||
if where == 'current':
|
||||
editor = current_editor
|
||||
elif where in {'styles', 'text', 'selected'}:
|
||||
files = searchable_names[where]
|
||||
if current_editor_name in files:
|
||||
# Start searching in the current editor
|
||||
editor = current_editor
|
||||
# Re-order the list of other files so that we search in the same
|
||||
# order every time. Depending on direction, search the files
|
||||
# that come after the current file, or before the current file,
|
||||
# first.
|
||||
lfiles = list(files)
|
||||
idx = lfiles.index(current_editor_name)
|
||||
before, after = lfiles[:idx], lfiles[idx+1:]
|
||||
if state['direction'] == 'up':
|
||||
lfiles = list(reversed(before))
|
||||
if do_all:
|
||||
lfiles += list(reversed(after)) + [current_editor_name]
|
||||
else:
|
||||
lfiles = after
|
||||
if do_all:
|
||||
lfiles += before + [current_editor_name]
|
||||
files = OrderedDict((m, files[m]) for m in lfiles)
|
||||
else:
|
||||
editor = current_editor
|
||||
marked = True
|
||||
|
||||
return editor, where, files, do_all, marked, get_search_regex(state)
|
||||
|
||||
def run_search(
|
||||
state, action, current_editor, current_editor_name, searchable_names,
|
||||
gui_parent, show_editor, edit_file, show_current_diff, add_savepoint, rewind_savepoint, set_modified):
|
||||
|
||||
editor, where, files, do_all, marked, pat = initialize_search_request(state, action, current_editor, current_editor_name, searchable_names)
|
||||
def no_match():
|
||||
QApplication.restoreOverrideCursor()
|
||||
msg = '<p>' + _('No matches were found for %s') % ('<pre style="font-style:italic">' + prepare_string_for_xml(state['find']) + '</pre>')
|
||||
if not state['wrap']:
|
||||
msg += '<p>' + _('You have turned off search wrapping, so all text might not have been searched.'
|
||||
' Try the search again, with wrapping enabled. Wrapping is enabled via the'
|
||||
' "Wrap" checkbox at the bottom of the search panel.')
|
||||
return error_dialog(
|
||||
gui_parent, _('Not found'), msg, show=True)
|
||||
|
||||
def do_find():
|
||||
if editor is not None:
|
||||
if editor.find(pat, marked=marked, save_match='gui'):
|
||||
return
|
||||
if not files:
|
||||
if not state['wrap']:
|
||||
return no_match()
|
||||
return editor.find(pat, wrap=True, marked=marked, save_match='gui') or no_match()
|
||||
for fname, syntax in files.iteritems():
|
||||
if fname in editors:
|
||||
if not editors[fname].find(pat, complete=True, save_match='gui'):
|
||||
continue
|
||||
return show_editor(fname)
|
||||
raw = current_container().raw_data(fname)
|
||||
if pat.search(raw) is not None:
|
||||
edit_file(fname, syntax)
|
||||
if editors[fname].find(pat, complete=True, save_match='gui'):
|
||||
return
|
||||
return no_match()
|
||||
|
||||
def no_replace(prefix=''):
|
||||
QApplication.restoreOverrideCursor()
|
||||
if prefix:
|
||||
prefix += ' '
|
||||
error_dialog(
|
||||
gui_parent, _('Cannot replace'), prefix + _(
|
||||
'You must first click Find, before trying to replace'), show=True)
|
||||
return False
|
||||
|
||||
def do_replace():
|
||||
if editor is None:
|
||||
return no_replace()
|
||||
if not editor.replace(pat, state['replace'], saved_match='gui'):
|
||||
return no_replace(_(
|
||||
'Currently selected text does not match the search query.'))
|
||||
return True
|
||||
|
||||
def count_message(action, count, show_diff=False):
|
||||
msg = _('%(action)s %(num)s occurrences of %(query)s' % dict(num=count, query=state['find'], action=action))
|
||||
if show_diff and count > 0:
|
||||
d = MessageBox(MessageBox.INFO, _('Searching done'), prepare_string_for_xml(msg), parent=gui_parent, show_copy_button=False)
|
||||
d.diffb = b = d.bb.addButton(_('See what &changed'), d.bb.ActionRole)
|
||||
b.setIcon(QIcon(I('diff.png'))), d.set_details(None), b.clicked.connect(d.accept)
|
||||
b.clicked.connect(partial(show_current_diff, allow_revert=True))
|
||||
d.exec_()
|
||||
else:
|
||||
info_dialog(gui_parent, _('Searching done'), prepare_string_for_xml(msg), show=True)
|
||||
|
||||
def do_all(replace=True):
|
||||
count = 0
|
||||
if not files and editor is None:
|
||||
return 0
|
||||
lfiles = files or {current_editor_name:editor.syntax}
|
||||
|
||||
for n, syntax in lfiles.iteritems():
|
||||
if n in editors:
|
||||
raw = editors[n].get_raw_data()
|
||||
else:
|
||||
raw = current_container().raw_data(n)
|
||||
if replace:
|
||||
raw, num = pat.subn(state['replace'], raw)
|
||||
else:
|
||||
num = len(pat.findall(raw))
|
||||
count += num
|
||||
if replace and num > 0:
|
||||
if n in editors:
|
||||
editors[n].replace_data(raw)
|
||||
else:
|
||||
with current_container().open(n, 'wb') as f:
|
||||
f.write(raw.encode('utf-8'))
|
||||
QApplication.restoreOverrideCursor()
|
||||
count_message(_('Replaced') if replace else _('Found'), count, show_diff=replace)
|
||||
return count
|
||||
|
||||
with BusyCursor():
|
||||
if action == 'find':
|
||||
return do_find()
|
||||
if action == 'replace':
|
||||
return do_replace()
|
||||
if action == 'replace-find' and do_replace():
|
||||
return do_find()
|
||||
if action == 'replace-all':
|
||||
if marked:
|
||||
return count_message(_('Replaced'), editor.all_in_marked(pat, state['replace']))
|
||||
add_savepoint(_('Before: Replace all'))
|
||||
count = do_all()
|
||||
if count == 0:
|
||||
rewind_savepoint()
|
||||
else:
|
||||
set_modified()
|
||||
return
|
||||
if action == 'count':
|
||||
if marked:
|
||||
return count_message(_('Found'), editor.all_in_marked(pat))
|
||||
return do_all(replace=False)
|
||||
|
||||
if __name__ == '__main__':
|
||||
app = QApplication([])
|
||||
d = SavedSearches()
|
||||
|
@ -15,7 +15,7 @@ from PyQt4.Qt import (
|
||||
QFormLayout, QHBoxLayout, QToolButton, QIcon, QApplication, Qt, QWidget,
|
||||
QPoint, QSizePolicy, QPainter, QStaticText, pyqtSignal, QTextOption,
|
||||
QAbstractListModel, QModelIndex, QVariant, QStyledItemDelegate, QStyle,
|
||||
QListView, QTextDocument, QSize, QComboBox, QFrame)
|
||||
QListView, QTextDocument, QSize, QComboBox, QFrame, QCursor)
|
||||
|
||||
from calibre import prepare_string_for_xml
|
||||
from calibre.gui2 import error_dialog, choose_files, choose_save_file, NONE, info_dialog
|
||||
@ -25,6 +25,14 @@ from calibre.utils.matcher import get_char, Matcher
|
||||
|
||||
ROOT = QModelIndex()
|
||||
|
||||
class BusyCursor(object):
|
||||
|
||||
def __enter__(self):
|
||||
QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))
|
||||
|
||||
def __exit__(self, *args):
|
||||
QApplication.restoreOverrideCursor()
|
||||
|
||||
class Dialog(QDialog):
|
||||
|
||||
def __init__(self, title, name, parent=None):
|
||||
|
Loading…
x
Reference in New Issue
Block a user