From 2479e304e90245b5d2f6909f05347dee8d651825 Mon Sep 17 00:00:00 2001 From: Mymei Date: Wed, 10 Aug 2016 19:29:29 +0200 Subject: [PATCH] Configurable suppression of replace result dialog (see 1610658) This is the second part of the proposal filed originally as bug #1609111. The result dialog, shown after replacements, slows down book editing in some cases. For example, when the user repeatedly applies his own regex-function to relatively small pre-marked text fragments. User should have a choice to suppress the result dialog. This implementation allows for the following switch inside the user replace function: replace.suppress_result_dialog = True This way it nicely fits to the first part (see #1609111) and it allows individual configuration from case to case. Actually, it's not dependent on the other two switches ('call_after_last_match' and 'append_final_output_to_marked'), so it can be used in combination with any of them or even individually. This implementation also should not have any impact on other modes of work and it should not change the previous behavior. It also should not break the backwards compatibility, I hope. --- manual/function_mode.rst | 59 +++++++++++++++++++-------- src/calibre/gui2/tweak_book/search.py | 40 +++++++++++------- 2 files changed, 66 insertions(+), 33 deletions(-) diff --git a/manual/function_mode.rst b/manual/function_mode.rst index a721b59691..527a401e55 100644 --- a/manual/function_mode.rst +++ b/manual/function_mode.rst @@ -1,5 +1,5 @@ Function Mode for Search & Replace in the Editor -======================================================================= +================================================ The Search & Replace tool in the editor support a *function mode*. In this mode, you can combine regular expressions (see :doc:`regexp`) with @@ -23,7 +23,7 @@ complex tasks. :align: center Automatically fixing the case of headings in the document -------------------------------------------------------------- +--------------------------------------------------------- Here, we will leverage one of the builtin functions in the editor to automatically change the case of all text inside heading tags to title case:: @@ -37,7 +37,7 @@ the heading tags. Your first custom function - smartening hyphens ------------------------------------------------------------------- +----------------------------------------------- The real power of function mode comes from being able to create your own functions to process text in arbitrary ways. The Smarten Punctuation tool in @@ -71,7 +71,7 @@ inside HTML tag definitions. The power of function mode - using a spelling dictionary to fix mis-hyphenated words ----------------------------------------------------------------------------------------- +------------------------------------------------------------------------------------ Often, ebooks created from scans of printed books contain mis-hyphenated words -- words that were split at the end of the line on the printed page. We will @@ -116,7 +116,7 @@ main language of the book. Auto numbering sections ---------------------------- +----------------------- Now we will see something a little different. Suppose your HTML file has many sections, each with a heading in an :code:`

` tag that looks like @@ -151,7 +151,7 @@ are processed in the order in which they appear in the book. See Auto create a Table of Contents -------------------------------------- +------------------------------- Finally, lets try something a little more ambitious. Suppose your book has headings in ``h1`` and ``h2`` tags that look like @@ -221,7 +221,7 @@ you would be better off using the dedicated Table of Contents tool in :guilabel:`Tools->Table of Contents`. The API for the function mode -------------------------------- +----------------------------- All function mode functions must be python functions named replace, with the following signature:: @@ -236,7 +236,7 @@ the original string. The various arguments to the ``replace()`` function are documented below. The ``match`` argument -^^^^^^^^^^^^^^^^^^^^^^^^^^ +^^^^^^^^^^^^^^^^^^^^^^ The ``match`` argument represents the currently found match. It is a `python Match object `_. @@ -245,14 +245,14 @@ text corresponding to individual capture groups in the search regular expression. The ``number`` argument -^^^^^^^^^^^^^^^^^^^^^^^^^ +^^^^^^^^^^^^^^^^^^^^^^^ The ``number`` argument is the number of the current match. When you run :guilabel:`Replace All`, every successive match will cause ``replace()`` to be called with an increasing number. The first match has number 1. The ``file_name`` argument -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +^^^^^^^^^^^^^^^^^^^^^^^^^^ This is the filename of the file in which the current match was found. When searching inside marked text, the ``file_name`` is empty. The ``file_name`` is @@ -260,7 +260,7 @@ in canonical form, a path relative to the root of the book, using ``/`` as the path separator. The ``metadata`` argument -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +^^^^^^^^^^^^^^^^^^^^^^^^^ This represents the metadata of the current book, such as title, authors, language, etc. It is an object of class :class:`calibre.ebooks.metadata.book.base.Metadata`. @@ -268,7 +268,7 @@ Useful attributes include, ``title``, ``authors`` (a list of authors) and ``language`` (the language code). The ``dictionaries`` argument -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ This represents the collection of dictionaries used for spell checking the current book. It's most useful method is ``dictionaries.recognized(word)`` @@ -276,7 +276,7 @@ which will return ``True`` if the passed in word is recognized by the dictionary for the current book's language. The ``data`` argument -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +^^^^^^^^^^^^^^^^^^^^^ This a simple python ``dict``. When you run :guilabel:`Replace All`, every successive match will cause ``replace()`` to be @@ -285,7 +285,7 @@ data between invocations of ``replace()`` during a :guilabel:`Replace All` operation. The ``functions`` argument -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +^^^^^^^^^^^^^^^^^^^^^^^^^^ The ``functions`` argument gives you access to all other user defined functions. This is useful for code re-use. You can define utility functions in @@ -328,7 +328,7 @@ uses it when it is run afterwards. Consider the following two functions: ... Debugging your functions -^^^^^^^^^^^^^^^^^^^^^^^^^^ +^^^^^^^^^^^^^^^^^^^^^^^^ You can debug the functions you create by using the standard ``print()`` function from python. The output of print will be displayed in a popup window @@ -338,7 +338,7 @@ to output an entire table of contents above. .. _file_order_replace_all: Choose file order when running on multiple HTML files -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ When you run a :guilabel:`Replace All` on multiple HTML files, the order in which the files are processes depends on what files you have open for editing. @@ -357,7 +357,7 @@ the search to process multiple files in the order they appear in the book, either forwards or backwards, respectively. Having your function called an extra time after the last match is found -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Sometimes, as in the auto generate table of contents example above, it is useful to have your function called an extra time after the last match is @@ -373,7 +373,7 @@ function, like this: Appending the output from the function to marked text -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ When running search and replace on marked text, it is sometimes useful to append so text to the end of the marked text. You can do that by setting @@ -388,3 +388,26 @@ also need to set ``call_after_last_match``), like this: replace.call_after_last_match = True replace.append_final_output_to_marked = True + +If you wish, you can also suppress the result dialog by adding yet another +setting ``suppress_result_dialog`` to the previous ones, like this: + +.. code-block:: python + + replace.call_after_last_match = True + replace.append_final_output_to_marked = True + replace.suppress_result_dialog = True + +The setting ``suppress_result_dialog`` is not dependent on the other two, +so you can also use the following combination: + +.. code-block:: python + + replace.call_after_last_match = True + replace.suppress_result_dialog = True + +or even a single setting: + +.. code-block:: python + + replace.suppress_result_dialog = True \ No newline at end of file diff --git a/src/calibre/gui2/tweak_book/search.py b/src/calibre/gui2/tweak_book/search.py index 05e68d86a4..ffec9ed37c 100644 --- a/src/calibre/gui2/tweak_book/search.py +++ b/src/calibre/gui2/tweak_book/search.py @@ -1320,20 +1320,21 @@ def run_search( return no_replace(_( 'Currently selected text does not match the search query.')) - def count_message(replaced, count, show_diff=False): - if replaced: - msg = _('Performed the replacement at {num} occurrences of {query}') - else: - msg = _('Found {num} occurrences of {query}') - msg = msg.format(num=count, query=errfind) - 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), type=Qt.QueuedConnection) - d.exec_() - else: - info_dialog(gui_parent, _('Searching done'), prepare_string_for_xml(msg), show=True) + def count_message(replaced, count, show_diff=False, show_dialog=True): + if show_dialog: + if replaced: + msg = _('Performed the replacement at {num} occurrences of {query}') + else: + msg = _('Found {num} occurrences of {query}') + msg = msg.format(num=count, query=errfind) + 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), type=Qt.QueuedConnection) + d.exec_() + else: + info_dialog(gui_parent, _('Searching done'), prepare_string_for_xml(msg), show=True) def do_all(replace=True): count = 0 @@ -1392,7 +1393,16 @@ def run_search( return do_find() if action == 'replace-all': if marked: - return count_message(True, sum(editor.all_in_marked(p, repl) for p, repl in searches)) + suppress_dialog = False + try: + if hasattr(searches[0][1], 'func'): + user_func = getattr(searches[0][1], 'func') + user_feature = 'suppress_result_dialog' + if hasattr(user_func, user_feature): + suppress_dialog = getattr(user_func, user_feature, False) + except: + pass + return count_message(True, sum(editor.all_in_marked(p, repl) for p, repl in searches), show_dialog = not suppress_dialog) add_savepoint(_('Before: Replace all')) count = do_all() if count == 0: