blob: b35f3b59c3d2e8f26ed5c2172e7290f84601371a [file] [log] [blame]
Miss Islington (bot)b34f1aa2019-03-16 16:47:28 -07001"""Search dialog for Find, Find Again, and Find Selection
2 functionality.
3
4 Inherits from SearchDialogBase for GUI and uses searchengine
5 to prepare search pattern.
6"""
Terry Jan Reedy6f7b0f52016-07-10 20:21:31 -04007from tkinter import TclError
David Scherer7aced172000-08-15 01:13:23 +00008
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -04009from idlelib import searchengine
10from idlelib.searchbase import SearchDialogBase
David Scherer7aced172000-08-15 01:13:23 +000011
12def _setup(text):
Miss Islington (bot)b34f1aa2019-03-16 16:47:28 -070013 """Return the new or existing singleton SearchDialog instance.
14
15 The singleton dialog saves user entries and preferences
16 across instances.
17
18 Args:
19 text: Text widget containing the text to be searched.
20 """
David Scherer7aced172000-08-15 01:13:23 +000021 root = text._root()
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -040022 engine = searchengine.get(root)
David Scherer7aced172000-08-15 01:13:23 +000023 if not hasattr(engine, "_searchdialog"):
24 engine._searchdialog = SearchDialog(root, engine)
25 return engine._searchdialog
26
27def find(text):
Miss Islington (bot)b34f1aa2019-03-16 16:47:28 -070028 """Open the search dialog.
29
30 Module-level function to access the singleton SearchDialog
31 instance and open the dialog. If text is selected, it is
32 used as the search phrase; otherwise, the previous entry
33 is used. No search is done with this command.
34 """
Chui Tey5a231c8f2002-11-06 02:18:45 +000035 pat = text.get("sel.first", "sel.last")
Terry Jan Reedyb236fe42016-05-17 18:18:37 -040036 return _setup(text).open(text, pat) # Open is inherited from SDBase.
David Scherer7aced172000-08-15 01:13:23 +000037
38def find_again(text):
Miss Islington (bot)b34f1aa2019-03-16 16:47:28 -070039 """Repeat the search for the last pattern and preferences.
40
41 Module-level function to access the singleton SearchDialog
42 instance to search again using the user entries and preferences
43 from the last dialog. If there was no prior search, open the
44 search dialog; otherwise, perform the search without showing the
45 dialog.
46 """
David Scherer7aced172000-08-15 01:13:23 +000047 return _setup(text).find_again(text)
48
49def find_selection(text):
Miss Islington (bot)b34f1aa2019-03-16 16:47:28 -070050 """Search for the selected pattern in the text.
51
52 Module-level function to access the singleton SearchDialog
53 instance to search using the selected text. With a text
54 selection, perform the search without displaying the dialog.
55 Without a selection, use the prior entry as the search phrase
56 and don't display the dialog. If there has been no prior
57 search, open the search dialog.
58 """
David Scherer7aced172000-08-15 01:13:23 +000059 return _setup(text).find_selection(text)
60
Terry Jan Reedybfbaa6b2016-08-31 00:50:55 -040061
David Scherer7aced172000-08-15 01:13:23 +000062class SearchDialog(SearchDialogBase):
Miss Islington (bot)b34f1aa2019-03-16 16:47:28 -070063 "Dialog for finding a pattern in text."
David Scherer7aced172000-08-15 01:13:23 +000064
65 def create_widgets(self):
Miss Islington (bot)b34f1aa2019-03-16 16:47:28 -070066 "Create the base search dialog and add a button for Find Next."
Terry Jan Reedy27336182015-05-14 18:10:50 -040067 SearchDialogBase.create_widgets(self)
Miss Islington (bot)b34f1aa2019-03-16 16:47:28 -070068 # TODO - why is this here and not in a create_command_buttons?
69 self.make_button("Find Next", self.default_command, isdef=True)
David Scherer7aced172000-08-15 01:13:23 +000070
71 def default_command(self, event=None):
Miss Islington (bot)b34f1aa2019-03-16 16:47:28 -070072 "Handle the Find Next button as the default command."
David Scherer7aced172000-08-15 01:13:23 +000073 if not self.engine.getprog():
74 return
Roger Serwy391f4692013-06-10 23:01:20 -050075 self.find_again(self.text)
David Scherer7aced172000-08-15 01:13:23 +000076
77 def find_again(self, text):
Miss Islington (bot)b34f1aa2019-03-16 16:47:28 -070078 """Repeat the last search.
79
80 If no search was previously run, open a new search dialog. In
81 this case, no search is done.
82
Miss Islington (bot)ab4d42a2019-06-02 21:39:32 -070083 If a search was previously run, the search dialog won't be
Miss Islington (bot)b34f1aa2019-03-16 16:47:28 -070084 shown and the options from the previous search (including the
85 search pattern) will be used to find the next occurrence
86 of the pattern. Next is relative based on direction.
87
88 Position the window to display the located occurrence in the
89 text.
90
91 Return True if the search was successful and False otherwise.
92 """
David Scherer7aced172000-08-15 01:13:23 +000093 if not self.engine.getpat():
94 self.open(text)
Kurt B. Kaiser0f4402d2002-09-18 03:10:10 +000095 return False
David Scherer7aced172000-08-15 01:13:23 +000096 if not self.engine.getprog():
Kurt B. Kaiser0f4402d2002-09-18 03:10:10 +000097 return False
David Scherer7aced172000-08-15 01:13:23 +000098 res = self.engine.search_text(text)
99 if res:
100 line, m = res
101 i, j = m.span()
102 first = "%d.%d" % (line, i)
103 last = "%d.%d" % (line, j)
104 try:
105 selfirst = text.index("sel.first")
106 sellast = text.index("sel.last")
107 if selfirst == first and sellast == last:
Terry Jan Reedy3ff55a82016-08-10 23:44:54 -0400108 self.bell()
Kurt B. Kaiser0f4402d2002-09-18 03:10:10 +0000109 return False
David Scherer7aced172000-08-15 01:13:23 +0000110 except TclError:
111 pass
112 text.tag_remove("sel", "1.0", "end")
113 text.tag_add("sel", first, last)
114 text.mark_set("insert", self.engine.isback() and first or last)
115 text.see("insert")
Kurt B. Kaiser0f4402d2002-09-18 03:10:10 +0000116 return True
David Scherer7aced172000-08-15 01:13:23 +0000117 else:
Terry Jan Reedy3ff55a82016-08-10 23:44:54 -0400118 self.bell()
Kurt B. Kaiser0f4402d2002-09-18 03:10:10 +0000119 return False
David Scherer7aced172000-08-15 01:13:23 +0000120
121 def find_selection(self, text):
Miss Islington (bot)b34f1aa2019-03-16 16:47:28 -0700122 """Search for selected text with previous dialog preferences.
123
124 Instead of using the same pattern for searching (as Find
125 Again does), this first resets the pattern to the currently
126 selected text. If the selected text isn't changed, then use
127 the prior search phrase.
128 """
David Scherer7aced172000-08-15 01:13:23 +0000129 pat = text.get("sel.first", "sel.last")
130 if pat:
131 self.engine.setcookedpat(pat)
132 return self.find_again(text)
Terry Jan Reedy0a4d13e2014-05-27 03:30:54 -0400133
Terry Jan Reedyb236fe42016-05-17 18:18:37 -0400134
135def _search_dialog(parent): # htest #
Terry Jan Reedy6f7b0f52016-07-10 20:21:31 -0400136 "Display search test box."
137 from tkinter import Toplevel, Text
Miss Islington (bot)b364caa2019-01-02 19:22:10 -0800138 from tkinter.ttk import Frame, Button
Terry Jan Reedy6f7b0f52016-07-10 20:21:31 -0400139
Miss Islington (bot)b364caa2019-01-02 19:22:10 -0800140 top = Toplevel(parent)
141 top.title("Test SearchDialog")
Terry Jan Reedya7480322016-07-10 17:28:10 -0400142 x, y = map(int, parent.geometry().split('+')[1:])
Miss Islington (bot)b364caa2019-01-02 19:22:10 -0800143 top.geometry("+%d+%d" % (x, y + 175))
144
145 frame = Frame(top)
146 frame.pack()
147 text = Text(frame, inactiveselectbackground='gray')
Terry Jan Reedy0a4d13e2014-05-27 03:30:54 -0400148 text.pack()
Terry Jan Reedyb236fe42016-05-17 18:18:37 -0400149 text.insert("insert","This is a sample string.\n"*5)
Terry Jan Reedy0a4d13e2014-05-27 03:30:54 -0400150
151 def show_find():
Terry Jan Reedy6f7b0f52016-07-10 20:21:31 -0400152 text.tag_add('sel', '1.0', 'end')
Terry Jan Reedyb236fe42016-05-17 18:18:37 -0400153 _setup(text).open(text)
Terry Jan Reedy6f7b0f52016-07-10 20:21:31 -0400154 text.tag_remove('sel', '1.0', 'end')
Terry Jan Reedy0a4d13e2014-05-27 03:30:54 -0400155
Miss Islington (bot)b364caa2019-01-02 19:22:10 -0800156 button = Button(frame, text="Search (selection ignored)", command=show_find)
Terry Jan Reedy0a4d13e2014-05-27 03:30:54 -0400157 button.pack()
158
159if __name__ == '__main__':
Miss Islington (bot)cbaee6f2018-06-19 17:11:24 -0700160 from unittest import main
161 main('idlelib.idle_test.test_search', verbosity=2, exit=False)
Terry Jan Reedya7480322016-07-10 17:28:10 -0400162
Terry Jan Reedy0a4d13e2014-05-27 03:30:54 -0400163 from idlelib.idle_test.htest import run
164 run(_search_dialog)