17db96d56Sopenharmony_ci"""Search dialog for Find, Find Again, and Find Selection 27db96d56Sopenharmony_ci functionality. 37db96d56Sopenharmony_ci 47db96d56Sopenharmony_ci Inherits from SearchDialogBase for GUI and uses searchengine 57db96d56Sopenharmony_ci to prepare search pattern. 67db96d56Sopenharmony_ci""" 77db96d56Sopenharmony_cifrom tkinter import TclError 87db96d56Sopenharmony_ci 97db96d56Sopenharmony_cifrom idlelib import searchengine 107db96d56Sopenharmony_cifrom idlelib.searchbase import SearchDialogBase 117db96d56Sopenharmony_ci 127db96d56Sopenharmony_cidef _setup(text): 137db96d56Sopenharmony_ci """Return the new or existing singleton SearchDialog instance. 147db96d56Sopenharmony_ci 157db96d56Sopenharmony_ci The singleton dialog saves user entries and preferences 167db96d56Sopenharmony_ci across instances. 177db96d56Sopenharmony_ci 187db96d56Sopenharmony_ci Args: 197db96d56Sopenharmony_ci text: Text widget containing the text to be searched. 207db96d56Sopenharmony_ci """ 217db96d56Sopenharmony_ci root = text._root() 227db96d56Sopenharmony_ci engine = searchengine.get(root) 237db96d56Sopenharmony_ci if not hasattr(engine, "_searchdialog"): 247db96d56Sopenharmony_ci engine._searchdialog = SearchDialog(root, engine) 257db96d56Sopenharmony_ci return engine._searchdialog 267db96d56Sopenharmony_ci 277db96d56Sopenharmony_cidef find(text): 287db96d56Sopenharmony_ci """Open the search dialog. 297db96d56Sopenharmony_ci 307db96d56Sopenharmony_ci Module-level function to access the singleton SearchDialog 317db96d56Sopenharmony_ci instance and open the dialog. If text is selected, it is 327db96d56Sopenharmony_ci used as the search phrase; otherwise, the previous entry 337db96d56Sopenharmony_ci is used. No search is done with this command. 347db96d56Sopenharmony_ci """ 357db96d56Sopenharmony_ci pat = text.get("sel.first", "sel.last") 367db96d56Sopenharmony_ci return _setup(text).open(text, pat) # Open is inherited from SDBase. 377db96d56Sopenharmony_ci 387db96d56Sopenharmony_cidef find_again(text): 397db96d56Sopenharmony_ci """Repeat the search for the last pattern and preferences. 407db96d56Sopenharmony_ci 417db96d56Sopenharmony_ci Module-level function to access the singleton SearchDialog 427db96d56Sopenharmony_ci instance to search again using the user entries and preferences 437db96d56Sopenharmony_ci from the last dialog. If there was no prior search, open the 447db96d56Sopenharmony_ci search dialog; otherwise, perform the search without showing the 457db96d56Sopenharmony_ci dialog. 467db96d56Sopenharmony_ci """ 477db96d56Sopenharmony_ci return _setup(text).find_again(text) 487db96d56Sopenharmony_ci 497db96d56Sopenharmony_cidef find_selection(text): 507db96d56Sopenharmony_ci """Search for the selected pattern in the text. 517db96d56Sopenharmony_ci 527db96d56Sopenharmony_ci Module-level function to access the singleton SearchDialog 537db96d56Sopenharmony_ci instance to search using the selected text. With a text 547db96d56Sopenharmony_ci selection, perform the search without displaying the dialog. 557db96d56Sopenharmony_ci Without a selection, use the prior entry as the search phrase 567db96d56Sopenharmony_ci and don't display the dialog. If there has been no prior 577db96d56Sopenharmony_ci search, open the search dialog. 587db96d56Sopenharmony_ci """ 597db96d56Sopenharmony_ci return _setup(text).find_selection(text) 607db96d56Sopenharmony_ci 617db96d56Sopenharmony_ci 627db96d56Sopenharmony_ciclass SearchDialog(SearchDialogBase): 637db96d56Sopenharmony_ci "Dialog for finding a pattern in text." 647db96d56Sopenharmony_ci 657db96d56Sopenharmony_ci def create_widgets(self): 667db96d56Sopenharmony_ci "Create the base search dialog and add a button for Find Next." 677db96d56Sopenharmony_ci SearchDialogBase.create_widgets(self) 687db96d56Sopenharmony_ci # TODO - why is this here and not in a create_command_buttons? 697db96d56Sopenharmony_ci self.make_button("Find Next", self.default_command, isdef=True) 707db96d56Sopenharmony_ci 717db96d56Sopenharmony_ci def default_command(self, event=None): 727db96d56Sopenharmony_ci "Handle the Find Next button as the default command." 737db96d56Sopenharmony_ci if not self.engine.getprog(): 747db96d56Sopenharmony_ci return 757db96d56Sopenharmony_ci self.find_again(self.text) 767db96d56Sopenharmony_ci 777db96d56Sopenharmony_ci def find_again(self, text): 787db96d56Sopenharmony_ci """Repeat the last search. 797db96d56Sopenharmony_ci 807db96d56Sopenharmony_ci If no search was previously run, open a new search dialog. In 817db96d56Sopenharmony_ci this case, no search is done. 827db96d56Sopenharmony_ci 837db96d56Sopenharmony_ci If a search was previously run, the search dialog won't be 847db96d56Sopenharmony_ci shown and the options from the previous search (including the 857db96d56Sopenharmony_ci search pattern) will be used to find the next occurrence 867db96d56Sopenharmony_ci of the pattern. Next is relative based on direction. 877db96d56Sopenharmony_ci 887db96d56Sopenharmony_ci Position the window to display the located occurrence in the 897db96d56Sopenharmony_ci text. 907db96d56Sopenharmony_ci 917db96d56Sopenharmony_ci Return True if the search was successful and False otherwise. 927db96d56Sopenharmony_ci """ 937db96d56Sopenharmony_ci if not self.engine.getpat(): 947db96d56Sopenharmony_ci self.open(text) 957db96d56Sopenharmony_ci return False 967db96d56Sopenharmony_ci if not self.engine.getprog(): 977db96d56Sopenharmony_ci return False 987db96d56Sopenharmony_ci res = self.engine.search_text(text) 997db96d56Sopenharmony_ci if res: 1007db96d56Sopenharmony_ci line, m = res 1017db96d56Sopenharmony_ci i, j = m.span() 1027db96d56Sopenharmony_ci first = "%d.%d" % (line, i) 1037db96d56Sopenharmony_ci last = "%d.%d" % (line, j) 1047db96d56Sopenharmony_ci try: 1057db96d56Sopenharmony_ci selfirst = text.index("sel.first") 1067db96d56Sopenharmony_ci sellast = text.index("sel.last") 1077db96d56Sopenharmony_ci if selfirst == first and sellast == last: 1087db96d56Sopenharmony_ci self.bell() 1097db96d56Sopenharmony_ci return False 1107db96d56Sopenharmony_ci except TclError: 1117db96d56Sopenharmony_ci pass 1127db96d56Sopenharmony_ci text.tag_remove("sel", "1.0", "end") 1137db96d56Sopenharmony_ci text.tag_add("sel", first, last) 1147db96d56Sopenharmony_ci text.mark_set("insert", self.engine.isback() and first or last) 1157db96d56Sopenharmony_ci text.see("insert") 1167db96d56Sopenharmony_ci return True 1177db96d56Sopenharmony_ci else: 1187db96d56Sopenharmony_ci self.bell() 1197db96d56Sopenharmony_ci return False 1207db96d56Sopenharmony_ci 1217db96d56Sopenharmony_ci def find_selection(self, text): 1227db96d56Sopenharmony_ci """Search for selected text with previous dialog preferences. 1237db96d56Sopenharmony_ci 1247db96d56Sopenharmony_ci Instead of using the same pattern for searching (as Find 1257db96d56Sopenharmony_ci Again does), this first resets the pattern to the currently 1267db96d56Sopenharmony_ci selected text. If the selected text isn't changed, then use 1277db96d56Sopenharmony_ci the prior search phrase. 1287db96d56Sopenharmony_ci """ 1297db96d56Sopenharmony_ci pat = text.get("sel.first", "sel.last") 1307db96d56Sopenharmony_ci if pat: 1317db96d56Sopenharmony_ci self.engine.setcookedpat(pat) 1327db96d56Sopenharmony_ci return self.find_again(text) 1337db96d56Sopenharmony_ci 1347db96d56Sopenharmony_ci 1357db96d56Sopenharmony_cidef _search_dialog(parent): # htest # 1367db96d56Sopenharmony_ci "Display search test box." 1377db96d56Sopenharmony_ci from tkinter import Toplevel, Text 1387db96d56Sopenharmony_ci from tkinter.ttk import Frame, Button 1397db96d56Sopenharmony_ci 1407db96d56Sopenharmony_ci top = Toplevel(parent) 1417db96d56Sopenharmony_ci top.title("Test SearchDialog") 1427db96d56Sopenharmony_ci x, y = map(int, parent.geometry().split('+')[1:]) 1437db96d56Sopenharmony_ci top.geometry("+%d+%d" % (x, y + 175)) 1447db96d56Sopenharmony_ci 1457db96d56Sopenharmony_ci frame = Frame(top) 1467db96d56Sopenharmony_ci frame.pack() 1477db96d56Sopenharmony_ci text = Text(frame, inactiveselectbackground='gray') 1487db96d56Sopenharmony_ci text.pack() 1497db96d56Sopenharmony_ci text.insert("insert","This is a sample string.\n"*5) 1507db96d56Sopenharmony_ci 1517db96d56Sopenharmony_ci def show_find(): 1527db96d56Sopenharmony_ci text.tag_add('sel', '1.0', 'end') 1537db96d56Sopenharmony_ci _setup(text).open(text) 1547db96d56Sopenharmony_ci text.tag_remove('sel', '1.0', 'end') 1557db96d56Sopenharmony_ci 1567db96d56Sopenharmony_ci button = Button(frame, text="Search (selection ignored)", command=show_find) 1577db96d56Sopenharmony_ci button.pack() 1587db96d56Sopenharmony_ci 1597db96d56Sopenharmony_ciif __name__ == '__main__': 1607db96d56Sopenharmony_ci from unittest import main 1617db96d56Sopenharmony_ci main('idlelib.idle_test.test_search', verbosity=2, exit=False) 1627db96d56Sopenharmony_ci 1637db96d56Sopenharmony_ci from idlelib.idle_test.htest import run 1647db96d56Sopenharmony_ci run(_search_dialog) 165