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