jpayne@69: """Search dialog for Find, Find Again, and Find Selection jpayne@69: functionality. jpayne@69: jpayne@69: Inherits from SearchDialogBase for GUI and uses searchengine jpayne@69: to prepare search pattern. jpayne@69: """ jpayne@69: from tkinter import TclError jpayne@69: jpayne@69: from idlelib import searchengine jpayne@69: from idlelib.searchbase import SearchDialogBase jpayne@69: jpayne@69: def _setup(text): jpayne@69: """Return the new or existing singleton SearchDialog instance. jpayne@69: jpayne@69: The singleton dialog saves user entries and preferences jpayne@69: across instances. jpayne@69: jpayne@69: Args: jpayne@69: text: Text widget containing the text to be searched. jpayne@69: """ jpayne@69: root = text._root() jpayne@69: engine = searchengine.get(root) jpayne@69: if not hasattr(engine, "_searchdialog"): jpayne@69: engine._searchdialog = SearchDialog(root, engine) jpayne@69: return engine._searchdialog jpayne@69: jpayne@69: def find(text): jpayne@69: """Open the search dialog. jpayne@69: jpayne@69: Module-level function to access the singleton SearchDialog jpayne@69: instance and open the dialog. If text is selected, it is jpayne@69: used as the search phrase; otherwise, the previous entry jpayne@69: is used. No search is done with this command. jpayne@69: """ jpayne@69: pat = text.get("sel.first", "sel.last") jpayne@69: return _setup(text).open(text, pat) # Open is inherited from SDBase. jpayne@69: jpayne@69: def find_again(text): jpayne@69: """Repeat the search for the last pattern and preferences. jpayne@69: jpayne@69: Module-level function to access the singleton SearchDialog jpayne@69: instance to search again using the user entries and preferences jpayne@69: from the last dialog. If there was no prior search, open the jpayne@69: search dialog; otherwise, perform the search without showing the jpayne@69: dialog. jpayne@69: """ jpayne@69: return _setup(text).find_again(text) jpayne@69: jpayne@69: def find_selection(text): jpayne@69: """Search for the selected pattern in the text. jpayne@69: jpayne@69: Module-level function to access the singleton SearchDialog jpayne@69: instance to search using the selected text. With a text jpayne@69: selection, perform the search without displaying the dialog. jpayne@69: Without a selection, use the prior entry as the search phrase jpayne@69: and don't display the dialog. If there has been no prior jpayne@69: search, open the search dialog. jpayne@69: """ jpayne@69: return _setup(text).find_selection(text) jpayne@69: jpayne@69: jpayne@69: class SearchDialog(SearchDialogBase): jpayne@69: "Dialog for finding a pattern in text." jpayne@69: jpayne@69: def create_widgets(self): jpayne@69: "Create the base search dialog and add a button for Find Next." jpayne@69: SearchDialogBase.create_widgets(self) jpayne@69: # TODO - why is this here and not in a create_command_buttons? jpayne@69: self.make_button("Find Next", self.default_command, isdef=True) jpayne@69: jpayne@69: def default_command(self, event=None): jpayne@69: "Handle the Find Next button as the default command." jpayne@69: if not self.engine.getprog(): jpayne@69: return jpayne@69: self.find_again(self.text) jpayne@69: jpayne@69: def find_again(self, text): jpayne@69: """Repeat the last search. jpayne@69: jpayne@69: If no search was previously run, open a new search dialog. In jpayne@69: this case, no search is done. jpayne@69: jpayne@69: If a search was previously run, the search dialog won't be jpayne@69: shown and the options from the previous search (including the jpayne@69: search pattern) will be used to find the next occurrence jpayne@69: of the pattern. Next is relative based on direction. jpayne@69: jpayne@69: Position the window to display the located occurrence in the jpayne@69: text. jpayne@69: jpayne@69: Return True if the search was successful and False otherwise. jpayne@69: """ jpayne@69: if not self.engine.getpat(): jpayne@69: self.open(text) jpayne@69: return False jpayne@69: if not self.engine.getprog(): jpayne@69: return False jpayne@69: res = self.engine.search_text(text) jpayne@69: if res: jpayne@69: line, m = res jpayne@69: i, j = m.span() jpayne@69: first = "%d.%d" % (line, i) jpayne@69: last = "%d.%d" % (line, j) jpayne@69: try: jpayne@69: selfirst = text.index("sel.first") jpayne@69: sellast = text.index("sel.last") jpayne@69: if selfirst == first and sellast == last: jpayne@69: self.bell() jpayne@69: return False jpayne@69: except TclError: jpayne@69: pass jpayne@69: text.tag_remove("sel", "1.0", "end") jpayne@69: text.tag_add("sel", first, last) jpayne@69: text.mark_set("insert", self.engine.isback() and first or last) jpayne@69: text.see("insert") jpayne@69: return True jpayne@69: else: jpayne@69: self.bell() jpayne@69: return False jpayne@69: jpayne@69: def find_selection(self, text): jpayne@69: """Search for selected text with previous dialog preferences. jpayne@69: jpayne@69: Instead of using the same pattern for searching (as Find jpayne@69: Again does), this first resets the pattern to the currently jpayne@69: selected text. If the selected text isn't changed, then use jpayne@69: the prior search phrase. jpayne@69: """ jpayne@69: pat = text.get("sel.first", "sel.last") jpayne@69: if pat: jpayne@69: self.engine.setcookedpat(pat) jpayne@69: return self.find_again(text) jpayne@69: jpayne@69: jpayne@69: def _search_dialog(parent): # htest # jpayne@69: "Display search test box." jpayne@69: from tkinter import Toplevel, Text jpayne@69: from tkinter.ttk import Frame, Button jpayne@69: jpayne@69: top = Toplevel(parent) jpayne@69: top.title("Test SearchDialog") jpayne@69: x, y = map(int, parent.geometry().split('+')[1:]) jpayne@69: top.geometry("+%d+%d" % (x, y + 175)) jpayne@69: jpayne@69: frame = Frame(top) jpayne@69: frame.pack() jpayne@69: text = Text(frame, inactiveselectbackground='gray') jpayne@69: text.pack() jpayne@69: text.insert("insert","This is a sample string.\n"*5) jpayne@69: jpayne@69: def show_find(): jpayne@69: text.tag_add('sel', '1.0', 'end') jpayne@69: _setup(text).open(text) jpayne@69: text.tag_remove('sel', '1.0', 'end') jpayne@69: jpayne@69: button = Button(frame, text="Search (selection ignored)", command=show_find) jpayne@69: button.pack() jpayne@69: jpayne@69: if __name__ == '__main__': jpayne@69: from unittest import main jpayne@69: main('idlelib.idle_test.test_search', verbosity=2, exit=False) jpayne@69: jpayne@69: from idlelib.idle_test.htest import run jpayne@69: run(_search_dialog)