blob: 6fba0b8e583f2b3d48574201459d3d1c360db6b2 [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 Reedyaff0ada2019-01-02 22:04:06 -05003from tkinter import Toplevel
4from tkinter.ttk import Frame, Entry, Label, Button, Checkbutton, Radiobutton
David Scherer7aced172000-08-15 01:13:23 +00005
Terry Jan Reedybfbaa6b2016-08-31 00:50:55 -04006
David Scherer7aced172000-08-15 01:13:23 +00007class SearchDialogBase:
Terry Jan Reedyede05732014-06-26 01:40:51 -04008 '''Create most of a 3 or 4 row, 3 column search dialog.
Terry Jan Reedyc5507c02013-08-18 18:22:43 -04009
Terry Jan Reedyede05732014-06-26 01:40:51 -040010 The left and wide middle column contain:
11 1 or 2 labeled text entry lines (make_entry, create_entries);
12 a row of standard Checkbuttons (make_frame, create_option_buttons),
13 each of which corresponds to a search engine Variable;
14 a row of dialog-specific Check/Radiobuttons (create_other_buttons).
Terry Jan Reedyba5d8f32013-08-18 18:27:02 -040015
Terry Jan Reedyc5507c02013-08-18 18:22:43 -040016 The narrow right column contains command buttons
Terry Jan Reedyede05732014-06-26 01:40:51 -040017 (make_button, create_command_buttons).
Terry Jan Reedyc5507c02013-08-18 18:22:43 -040018 These are bound to functions that execute the command.
19
Terry Jan Reedy525168b2014-06-30 20:00:03 -040020 Except for command buttons, this base class is not limited to items
21 common to all three subclasses. Rather, it is the Find dialog minus
22 the "Find Next" command, its execution function, and the
23 default_command attribute needed in create_widgets. The other
24 dialogs override attributes and methods, the latter to replace and
25 add widgets.
Terry Jan Reedyc5507c02013-08-18 18:22:43 -040026 '''
David Scherer7aced172000-08-15 01:13:23 +000027
Terry Jan Reedyede05732014-06-26 01:40:51 -040028 title = "Search Dialog" # replace in subclasses
David Scherer7aced172000-08-15 01:13:23 +000029 icon = "Search"
Terry Jan Reedyede05732014-06-26 01:40:51 -040030 needwrapbutton = 1 # not in Find in Files
David Scherer7aced172000-08-15 01:13:23 +000031
32 def __init__(self, root, engine):
Terry Jan Reedyede05732014-06-26 01:40:51 -040033 '''Initialize root, engine, and top attributes.
34
35 top (level widget): set in create_widgets() called from open().
Terry Jan Reedy525168b2014-06-30 20:00:03 -040036 text (Text searched): set in open(), only used in subclasses().
Terry Jan Reedyede05732014-06-26 01:40:51 -040037 ent (ry): created in make_entry() called from create_entry().
38 row (of grid): 0 in create_widgets(), +1 in make_entry/frame().
Xtreakd9677f32019-06-03 09:51:15 +053039 default_command: set in subclasses, used in create_widgets().
Terry Jan Reedyede05732014-06-26 01:40:51 -040040
41 title (of dialog): class attribute, override in subclasses.
42 icon (of dialog): ditto, use unclear if cannot minimize dialog.
43 '''
David Scherer7aced172000-08-15 01:13:23 +000044 self.root = root
Terry Jan Reedyc4656822018-12-28 02:41:35 -050045 self.bell = root.bell
David Scherer7aced172000-08-15 01:13:23 +000046 self.engine = engine
47 self.top = None
48
Chui Tey5a231c8f2002-11-06 02:18:45 +000049 def open(self, text, searchphrase=None):
Terry Jan Reedyede05732014-06-26 01:40:51 -040050 "Make dialog visible on top of others and ready to use."
David Scherer7aced172000-08-15 01:13:23 +000051 self.text = text
52 if not self.top:
53 self.create_widgets()
54 else:
55 self.top.deiconify()
56 self.top.tkraise()
Miss Islington (bot)295fe322019-06-06 23:17:13 -070057 self.top.transient(text.winfo_toplevel())
Chui Tey5a231c8f2002-11-06 02:18:45 +000058 if searchphrase:
59 self.ent.delete(0,"end")
60 self.ent.insert("end",searchphrase)
David Scherer7aced172000-08-15 01:13:23 +000061 self.ent.focus_set()
62 self.ent.selection_range(0, "end")
63 self.ent.icursor(0)
64 self.top.grab_set()
65
66 def close(self, event=None):
Terry Jan Reedyede05732014-06-26 01:40:51 -040067 "Put dialog away for later use."
David Scherer7aced172000-08-15 01:13:23 +000068 if self.top:
69 self.top.grab_release()
Miss Islington (bot)295fe322019-06-06 23:17:13 -070070 self.top.transient('')
David Scherer7aced172000-08-15 01:13:23 +000071 self.top.withdraw()
72
73 def create_widgets(self):
Terry Jan Reedyede05732014-06-26 01:40:51 -040074 '''Create basic 3 row x 3 col search (find) dialog.
75
76 Other dialogs override subsidiary create_x methods as needed.
77 Replace and Find-in-Files add another entry row.
78 '''
David Scherer7aced172000-08-15 01:13:23 +000079 top = Toplevel(self.root)
80 top.bind("<Return>", self.default_command)
81 top.bind("<Escape>", self.close)
82 top.protocol("WM_DELETE_WINDOW", self.close)
83 top.wm_title(self.title)
84 top.wm_iconname(self.icon)
85 self.top = top
86
87 self.row = 0
Chui Tey72a8a3b2002-11-04 23:07:51 +000088 self.top.grid_columnconfigure(0, pad=2, weight=0)
89 self.top.grid_columnconfigure(1, pad=2, minsize=100, weight=100)
David Scherer7aced172000-08-15 01:13:23 +000090
Terry Jan Reedyede05732014-06-26 01:40:51 -040091 self.create_entries() # row 0 (and maybe 1), cols 0, 1
92 self.create_option_buttons() # next row, cols 0, 1
93 self.create_other_buttons() # next row, cols 0, 1
94 self.create_command_buttons() # col 2, all rows
David Scherer7aced172000-08-15 01:13:23 +000095
Terry Jan Reedy5283c4e2014-07-13 17:27:26 -040096 def make_entry(self, label_text, var):
97 '''Return (entry, label), .
98
99 entry - gridded labeled Entry for text entry.
100 label - Label widget, returned for testing.
101 '''
102 label = Label(self.top, text=label_text)
103 label.grid(row=self.row, column=0, sticky="nw")
104 entry = Entry(self.top, textvariable=var, exportselection=0)
105 entry.grid(row=self.row, column=1, sticky="nwe")
David Scherer7aced172000-08-15 01:13:23 +0000106 self.row = self.row + 1
Terry Jan Reedy5283c4e2014-07-13 17:27:26 -0400107 return entry, label
David Scherer7aced172000-08-15 01:13:23 +0000108
Terry Jan Reedyede05732014-06-26 01:40:51 -0400109 def create_entries(self):
110 "Create one or more entry lines with make_entry."
Terry Jan Reedy5283c4e2014-07-13 17:27:26 -0400111 self.ent = self.make_entry("Find:", self.engine.patvar)[0]
Terry Jan Reedyede05732014-06-26 01:40:51 -0400112
Chui Tey72a8a3b2002-11-04 23:07:51 +0000113 def make_frame(self,labeltext=None):
Terry Jan Reedy5283c4e2014-07-13 17:27:26 -0400114 '''Return (frame, label).
115
116 frame - gridded labeled Frame for option or other buttons.
117 label - Label widget, returned for testing.
118 '''
Chui Tey72a8a3b2002-11-04 23:07:51 +0000119 if labeltext:
Terry Jan Reedy8cefd082014-06-30 23:52:20 -0400120 label = Label(self.top, text=labeltext)
121 label.grid(row=self.row, column=0, sticky="nw")
Terry Jan Reedy525168b2014-06-30 20:00:03 -0400122 else:
Terry Jan Reedy8cefd082014-06-30 23:52:20 -0400123 label = ''
124 frame = Frame(self.top)
125 frame.grid(row=self.row, column=1, columnspan=1, sticky="nwe")
David Scherer7aced172000-08-15 01:13:23 +0000126 self.row = self.row + 1
Terry Jan Reedy5283c4e2014-07-13 17:27:26 -0400127 return frame, label
David Scherer7aced172000-08-15 01:13:23 +0000128
David Scherer7aced172000-08-15 01:13:23 +0000129 def create_option_buttons(self):
Terry Jan Reedy5283c4e2014-07-13 17:27:26 -0400130 '''Return (filled frame, options) for testing.
131
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -0400132 Options is a list of searchengine booleanvar, label pairs.
Terry Jan Reedy5283c4e2014-07-13 17:27:26 -0400133 A gridded frame from make_frame is filled with a Checkbutton
134 for each pair, bound to the var, with the corresponding label.
135 '''
Terry Jan Reedy8cefd082014-06-30 23:52:20 -0400136 frame = self.make_frame("Options")[0]
137 engine = self.engine
138 options = [(engine.revar, "Regular expression"),
139 (engine.casevar, "Match case"),
140 (engine.wordvar, "Whole word")]
David Scherer7aced172000-08-15 01:13:23 +0000141 if self.needwrapbutton:
Terry Jan Reedy8cefd082014-06-30 23:52:20 -0400142 options.append((engine.wrapvar, "Wrap around"))
143 for var, label in options:
Terry Jan Reedy6f7b0f52016-07-10 20:21:31 -0400144 btn = Checkbutton(frame, variable=var, text=label)
David Scherer7aced172000-08-15 01:13:23 +0000145 btn.pack(side="left", fill="both")
Terry Jan Reedy5283c4e2014-07-13 17:27:26 -0400146 return frame, options
David Scherer7aced172000-08-15 01:13:23 +0000147
148 def create_other_buttons(self):
Terry Jan Reedy5283c4e2014-07-13 17:27:26 -0400149 '''Return (frame, others) for testing.
150
151 Others is a list of value, label pairs.
152 A gridded frame from make_frame is filled with radio buttons.
153 '''
Terry Jan Reedy8cefd082014-06-30 23:52:20 -0400154 frame = self.make_frame("Direction")[0]
155 var = self.engine.backvar
156 others = [(1, 'Up'), (0, 'Down')]
157 for val, label in others:
Terry Jan Reedy6f7b0f52016-07-10 20:21:31 -0400158 btn = Radiobutton(frame, variable=var, value=val, text=label)
Terry Jan Reedy8cefd082014-06-30 23:52:20 -0400159 btn.pack(side="left", fill="both")
Terry Jan Reedy5283c4e2014-07-13 17:27:26 -0400160 return frame, others
David Scherer7aced172000-08-15 01:13:23 +0000161
Terry Jan Reedyede05732014-06-26 01:40:51 -0400162 def make_button(self, label, command, isdef=0):
163 "Return command button gridded in command frame."
164 b = Button(self.buttonframe,
165 text=label, command=command,
166 default=isdef and "active" or "normal")
167 cols,rows=self.buttonframe.grid_size()
168 b.grid(pady=1,row=rows,column=0,sticky="ew")
169 self.buttonframe.grid(rowspan=rows+1)
170 return b
171
David Scherer7aced172000-08-15 01:13:23 +0000172 def create_command_buttons(self):
Terry Jan Reedyede05732014-06-26 01:40:51 -0400173 "Place buttons in vertical command frame gridded on right."
Chui Tey72a8a3b2002-11-04 23:07:51 +0000174 f = self.buttonframe = Frame(self.top)
Kurt B. Kaiser4fc90472002-11-21 03:02:17 +0000175 f.grid(row=0,column=2,padx=2,pady=2,ipadx=2,ipady=2)
Chui Tey72a8a3b2002-11-04 23:07:51 +0000176
Terry Jan Reedyba043022019-05-31 04:26:35 -0400177 b = self.make_button("Close", self.close)
David Scherer7aced172000-08-15 01:13:23 +0000178 b.lower()
Terry Jan Reedyede05732014-06-26 01:40:51 -0400179
Terry Jan Reedy6f7b0f52016-07-10 20:21:31 -0400180
181class _searchbase(SearchDialogBase): # htest #
182 "Create auto-opening dialog with no text connection."
183
184 def __init__(self, parent):
185 import re
186 from idlelib import searchengine
187
188 self.root = parent
189 self.engine = searchengine.get(parent)
190 self.create_widgets()
191 print(parent.geometry())
192 width,height, x,y = list(map(int, re.split('[x+]', parent.geometry())))
193 self.top.geometry("+%d+%d" % (x + 40, y + 175))
194
Terry Jan Reedy3ff55a82016-08-10 23:44:54 -0400195 def default_command(self, dummy): pass
Terry Jan Reedy6f7b0f52016-07-10 20:21:31 -0400196
Terry Jan Reedy4d921582018-06-19 19:12:52 -0400197
Terry Jan Reedyede05732014-06-26 01:40:51 -0400198if __name__ == '__main__':
Terry Jan Reedy4d921582018-06-19 19:12:52 -0400199 from unittest import main
200 main('idlelib.idle_test.test_searchbase', verbosity=2, exit=False)
Terry Jan Reedy6f7b0f52016-07-10 20:21:31 -0400201
202 from idlelib.idle_test.htest import run
203 run(_searchbase)