blob: b326a1c6aae35f1b5a48645b3d855e7bba595d61 [file] [log] [blame]
Terry Jan Reedyc5507c02013-08-18 18:22:43 -04001'''Define SearchDialogBase used by Search, Replace, and Grep dialogs.'''
Terry Jan Reedyede05732014-06-26 01:40:51 -04002
Terry Jan Reedy6f7b0f52016-07-10 20:21:31 -04003from tkinter import Toplevel, Frame
4from tkinter.ttk import Entry, Label, Button, Checkbutton, Radiobutton
David Scherer7aced172000-08-15 01:13:23 +00005
6class SearchDialogBase:
Terry Jan Reedyede05732014-06-26 01:40:51 -04007 '''Create most of a 3 or 4 row, 3 column search dialog.
Terry Jan Reedyc5507c02013-08-18 18:22:43 -04008
Terry Jan Reedyede05732014-06-26 01:40:51 -04009 The left and wide middle column contain:
10 1 or 2 labeled text entry lines (make_entry, create_entries);
11 a row of standard Checkbuttons (make_frame, create_option_buttons),
12 each of which corresponds to a search engine Variable;
13 a row of dialog-specific Check/Radiobuttons (create_other_buttons).
Terry Jan Reedyba5d8f32013-08-18 18:27:02 -040014
Terry Jan Reedyc5507c02013-08-18 18:22:43 -040015 The narrow right column contains command buttons
Terry Jan Reedyede05732014-06-26 01:40:51 -040016 (make_button, create_command_buttons).
Terry Jan Reedyc5507c02013-08-18 18:22:43 -040017 These are bound to functions that execute the command.
18
Terry Jan Reedy525168b2014-06-30 20:00:03 -040019 Except for command buttons, this base class is not limited to items
20 common to all three subclasses. Rather, it is the Find dialog minus
21 the "Find Next" command, its execution function, and the
22 default_command attribute needed in create_widgets. The other
23 dialogs override attributes and methods, the latter to replace and
24 add widgets.
Terry Jan Reedyc5507c02013-08-18 18:22:43 -040025 '''
David Scherer7aced172000-08-15 01:13:23 +000026
Terry Jan Reedyede05732014-06-26 01:40:51 -040027 title = "Search Dialog" # replace in subclasses
David Scherer7aced172000-08-15 01:13:23 +000028 icon = "Search"
Terry Jan Reedyede05732014-06-26 01:40:51 -040029 needwrapbutton = 1 # not in Find in Files
David Scherer7aced172000-08-15 01:13:23 +000030
31 def __init__(self, root, engine):
Terry Jan Reedyede05732014-06-26 01:40:51 -040032 '''Initialize root, engine, and top attributes.
33
34 top (level widget): set in create_widgets() called from open().
Terry Jan Reedy525168b2014-06-30 20:00:03 -040035 text (Text searched): set in open(), only used in subclasses().
Terry Jan Reedyede05732014-06-26 01:40:51 -040036 ent (ry): created in make_entry() called from create_entry().
37 row (of grid): 0 in create_widgets(), +1 in make_entry/frame().
Terry Jan Reedy525168b2014-06-30 20:00:03 -040038 default_command: set in subclasses, used in create_widgers().
Terry Jan Reedyede05732014-06-26 01:40:51 -040039
40 title (of dialog): class attribute, override in subclasses.
41 icon (of dialog): ditto, use unclear if cannot minimize dialog.
42 '''
David Scherer7aced172000-08-15 01:13:23 +000043 self.root = root
44 self.engine = engine
45 self.top = None
46
Chui Tey5a231c8f2002-11-06 02:18:45 +000047 def open(self, text, searchphrase=None):
Terry Jan Reedyede05732014-06-26 01:40:51 -040048 "Make dialog visible on top of others and ready to use."
David Scherer7aced172000-08-15 01:13:23 +000049 self.text = text
50 if not self.top:
51 self.create_widgets()
52 else:
53 self.top.deiconify()
54 self.top.tkraise()
Chui Tey5a231c8f2002-11-06 02:18:45 +000055 if searchphrase:
56 self.ent.delete(0,"end")
57 self.ent.insert("end",searchphrase)
David Scherer7aced172000-08-15 01:13:23 +000058 self.ent.focus_set()
59 self.ent.selection_range(0, "end")
60 self.ent.icursor(0)
61 self.top.grab_set()
62
63 def close(self, event=None):
Terry Jan Reedyede05732014-06-26 01:40:51 -040064 "Put dialog away for later use."
David Scherer7aced172000-08-15 01:13:23 +000065 if self.top:
66 self.top.grab_release()
67 self.top.withdraw()
68
69 def create_widgets(self):
Terry Jan Reedyede05732014-06-26 01:40:51 -040070 '''Create basic 3 row x 3 col search (find) dialog.
71
72 Other dialogs override subsidiary create_x methods as needed.
73 Replace and Find-in-Files add another entry row.
74 '''
David Scherer7aced172000-08-15 01:13:23 +000075 top = Toplevel(self.root)
76 top.bind("<Return>", self.default_command)
77 top.bind("<Escape>", self.close)
78 top.protocol("WM_DELETE_WINDOW", self.close)
79 top.wm_title(self.title)
80 top.wm_iconname(self.icon)
81 self.top = top
Terry Jan Reedy3ff55a82016-08-10 23:44:54 -040082 self.bell = top.bell
David Scherer7aced172000-08-15 01:13:23 +000083
84 self.row = 0
Chui Tey72a8a3b2002-11-04 23:07:51 +000085 self.top.grid_columnconfigure(0, pad=2, weight=0)
86 self.top.grid_columnconfigure(1, pad=2, minsize=100, weight=100)
David Scherer7aced172000-08-15 01:13:23 +000087
Terry Jan Reedyede05732014-06-26 01:40:51 -040088 self.create_entries() # row 0 (and maybe 1), cols 0, 1
89 self.create_option_buttons() # next row, cols 0, 1
90 self.create_other_buttons() # next row, cols 0, 1
91 self.create_command_buttons() # col 2, all rows
David Scherer7aced172000-08-15 01:13:23 +000092
Terry Jan Reedy5283c4e2014-07-13 17:27:26 -040093 def make_entry(self, label_text, var):
94 '''Return (entry, label), .
95
96 entry - gridded labeled Entry for text entry.
97 label - Label widget, returned for testing.
98 '''
99 label = Label(self.top, text=label_text)
100 label.grid(row=self.row, column=0, sticky="nw")
101 entry = Entry(self.top, textvariable=var, exportselection=0)
102 entry.grid(row=self.row, column=1, sticky="nwe")
David Scherer7aced172000-08-15 01:13:23 +0000103 self.row = self.row + 1
Terry Jan Reedy5283c4e2014-07-13 17:27:26 -0400104 return entry, label
David Scherer7aced172000-08-15 01:13:23 +0000105
Terry Jan Reedyede05732014-06-26 01:40:51 -0400106 def create_entries(self):
107 "Create one or more entry lines with make_entry."
Terry Jan Reedy5283c4e2014-07-13 17:27:26 -0400108 self.ent = self.make_entry("Find:", self.engine.patvar)[0]
Terry Jan Reedyede05732014-06-26 01:40:51 -0400109
Chui Tey72a8a3b2002-11-04 23:07:51 +0000110 def make_frame(self,labeltext=None):
Terry Jan Reedy5283c4e2014-07-13 17:27:26 -0400111 '''Return (frame, label).
112
113 frame - gridded labeled Frame for option or other buttons.
114 label - Label widget, returned for testing.
115 '''
Chui Tey72a8a3b2002-11-04 23:07:51 +0000116 if labeltext:
Terry Jan Reedy8cefd082014-06-30 23:52:20 -0400117 label = Label(self.top, text=labeltext)
118 label.grid(row=self.row, column=0, sticky="nw")
Terry Jan Reedy525168b2014-06-30 20:00:03 -0400119 else:
Terry Jan Reedy8cefd082014-06-30 23:52:20 -0400120 label = ''
121 frame = Frame(self.top)
122 frame.grid(row=self.row, column=1, columnspan=1, sticky="nwe")
David Scherer7aced172000-08-15 01:13:23 +0000123 self.row = self.row + 1
Terry Jan Reedy5283c4e2014-07-13 17:27:26 -0400124 return frame, label
David Scherer7aced172000-08-15 01:13:23 +0000125
David Scherer7aced172000-08-15 01:13:23 +0000126 def create_option_buttons(self):
Terry Jan Reedy5283c4e2014-07-13 17:27:26 -0400127 '''Return (filled frame, options) for testing.
128
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -0400129 Options is a list of searchengine booleanvar, label pairs.
Terry Jan Reedy5283c4e2014-07-13 17:27:26 -0400130 A gridded frame from make_frame is filled with a Checkbutton
131 for each pair, bound to the var, with the corresponding label.
132 '''
Terry Jan Reedy8cefd082014-06-30 23:52:20 -0400133 frame = self.make_frame("Options")[0]
134 engine = self.engine
135 options = [(engine.revar, "Regular expression"),
136 (engine.casevar, "Match case"),
137 (engine.wordvar, "Whole word")]
David Scherer7aced172000-08-15 01:13:23 +0000138 if self.needwrapbutton:
Terry Jan Reedy8cefd082014-06-30 23:52:20 -0400139 options.append((engine.wrapvar, "Wrap around"))
140 for var, label in options:
Terry Jan Reedy6f7b0f52016-07-10 20:21:31 -0400141 btn = Checkbutton(frame, variable=var, text=label)
David Scherer7aced172000-08-15 01:13:23 +0000142 btn.pack(side="left", fill="both")
Terry Jan Reedy5283c4e2014-07-13 17:27:26 -0400143 return frame, options
David Scherer7aced172000-08-15 01:13:23 +0000144
145 def create_other_buttons(self):
Terry Jan Reedy5283c4e2014-07-13 17:27:26 -0400146 '''Return (frame, others) for testing.
147
148 Others is a list of value, label pairs.
149 A gridded frame from make_frame is filled with radio buttons.
150 '''
Terry Jan Reedy8cefd082014-06-30 23:52:20 -0400151 frame = self.make_frame("Direction")[0]
152 var = self.engine.backvar
153 others = [(1, 'Up'), (0, 'Down')]
154 for val, label in others:
Terry Jan Reedy6f7b0f52016-07-10 20:21:31 -0400155 btn = Radiobutton(frame, variable=var, value=val, text=label)
Terry Jan Reedy8cefd082014-06-30 23:52:20 -0400156 btn.pack(side="left", fill="both")
Terry Jan Reedy5283c4e2014-07-13 17:27:26 -0400157 return frame, others
David Scherer7aced172000-08-15 01:13:23 +0000158
Terry Jan Reedyede05732014-06-26 01:40:51 -0400159 def make_button(self, label, command, isdef=0):
160 "Return command button gridded in command frame."
161 b = Button(self.buttonframe,
162 text=label, command=command,
163 default=isdef and "active" or "normal")
164 cols,rows=self.buttonframe.grid_size()
165 b.grid(pady=1,row=rows,column=0,sticky="ew")
166 self.buttonframe.grid(rowspan=rows+1)
167 return b
168
David Scherer7aced172000-08-15 01:13:23 +0000169 def create_command_buttons(self):
Terry Jan Reedyede05732014-06-26 01:40:51 -0400170 "Place buttons in vertical command frame gridded on right."
Chui Tey72a8a3b2002-11-04 23:07:51 +0000171 f = self.buttonframe = Frame(self.top)
Kurt B. Kaiser4fc90472002-11-21 03:02:17 +0000172 f.grid(row=0,column=2,padx=2,pady=2,ipadx=2,ipady=2)
Chui Tey72a8a3b2002-11-04 23:07:51 +0000173
174 b = self.make_button("close", self.close)
David Scherer7aced172000-08-15 01:13:23 +0000175 b.lower()
Terry Jan Reedyede05732014-06-26 01:40:51 -0400176
Terry Jan Reedy6f7b0f52016-07-10 20:21:31 -0400177
178class _searchbase(SearchDialogBase): # htest #
179 "Create auto-opening dialog with no text connection."
180
181 def __init__(self, parent):
182 import re
183 from idlelib import searchengine
184
185 self.root = parent
186 self.engine = searchengine.get(parent)
187 self.create_widgets()
188 print(parent.geometry())
189 width,height, x,y = list(map(int, re.split('[x+]', parent.geometry())))
190 self.top.geometry("+%d+%d" % (x + 40, y + 175))
191
Terry Jan Reedy3ff55a82016-08-10 23:44:54 -0400192 def default_command(self, dummy): pass
Terry Jan Reedy6f7b0f52016-07-10 20:21:31 -0400193
Terry Jan Reedyede05732014-06-26 01:40:51 -0400194if __name__ == '__main__':
195 import unittest
Terry Jan Reedy6f7b0f52016-07-10 20:21:31 -0400196 unittest.main('idlelib.idle_test.test_searchbase', verbosity=2, exit=False)
197
198 from idlelib.idle_test.htest import run
199 run(_searchbase)