blob: 7564f93627485be934cbfcd0a474d434ef1301f2 [file] [log] [blame]
Terry Jan Reedy9946a282013-08-18 18:22:34 -04001'''Define SearchDialogBase used by Search, Replace, and Grep dialogs.'''
Terry Jan Reedyaa608fd2014-06-26 01:40:46 -04002
3from Tkinter import (Toplevel, Frame, Entry, Label, Button,
4 Checkbutton, Radiobutton)
David Scherer7aced172000-08-15 01:13:23 +00005
6class SearchDialogBase:
Terry Jan Reedyaa608fd2014-06-26 01:40:46 -04007 '''Create most of a 3 or 4 row, 3 column search dialog.
Terry Jan Reedy9946a282013-08-18 18:22:34 -04008
Terry Jan Reedyaa608fd2014-06-26 01:40:46 -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 Reedya81e9692013-08-18 18:27:02 -040014
Terry Jan Reedy9946a282013-08-18 18:22:34 -040015 The narrow right column contains command buttons
Terry Jan Reedyaa608fd2014-06-26 01:40:46 -040016 (make_button, create_command_buttons).
Terry Jan Reedy9946a282013-08-18 18:22:34 -040017 These are bound to functions that execute the command.
18
Terry Jan Reedy72eb0752014-06-30 19:59:57 -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 Reedy9946a282013-08-18 18:22:34 -040025 '''
David Scherer7aced172000-08-15 01:13:23 +000026
Terry Jan Reedyaa608fd2014-06-26 01:40:46 -040027 title = "Search Dialog" # replace in subclasses
David Scherer7aced172000-08-15 01:13:23 +000028 icon = "Search"
Terry Jan Reedyaa608fd2014-06-26 01:40:46 -040029 needwrapbutton = 1 # not in Find in Files
David Scherer7aced172000-08-15 01:13:23 +000030
31 def __init__(self, root, engine):
Terry Jan Reedyaa608fd2014-06-26 01:40:46 -040032 '''Initialize root, engine, and top attributes.
33
34 top (level widget): set in create_widgets() called from open().
Terry Jan Reedy72eb0752014-06-30 19:59:57 -040035 text (Text searched): set in open(), only used in subclasses().
Terry Jan Reedyaa608fd2014-06-26 01:40:46 -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 Reedy72eb0752014-06-30 19:59:57 -040038 default_command: set in subclasses, used in create_widgers().
Terry Jan Reedyaa608fd2014-06-26 01:40:46 -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 Tey5a231c82002-11-06 02:18:45 +000047 def open(self, text, searchphrase=None):
Terry Jan Reedyaa608fd2014-06-26 01:40:46 -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 Tey5a231c82002-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 Reedyaa608fd2014-06-26 01:40:46 -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 Reedyaa608fd2014-06-26 01:40:46 -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
82
83 self.row = 0
Chui Tey72a8a3b2002-11-04 23:07:51 +000084 self.top.grid_columnconfigure(0, pad=2, weight=0)
85 self.top.grid_columnconfigure(1, pad=2, minsize=100, weight=100)
David Scherer7aced172000-08-15 01:13:23 +000086
Terry Jan Reedyaa608fd2014-06-26 01:40:46 -040087 self.create_entries() # row 0 (and maybe 1), cols 0, 1
88 self.create_option_buttons() # next row, cols 0, 1
89 self.create_other_buttons() # next row, cols 0, 1
90 self.create_command_buttons() # col 2, all rows
David Scherer7aced172000-08-15 01:13:23 +000091
92 def make_entry(self, label, var):
Terry Jan Reedyaa608fd2014-06-26 01:40:46 -040093 "Return gridded labeled Entry."
David Scherer7aced172000-08-15 01:13:23 +000094 l = Label(self.top, text=label)
Kurt B. Kaiser4fc90472002-11-21 03:02:17 +000095 l.grid(row=self.row, column=0, sticky="nw")
David Scherer7aced172000-08-15 01:13:23 +000096 e = Entry(self.top, textvariable=var, exportselection=0)
Kurt B. Kaiser4fc90472002-11-21 03:02:17 +000097 e.grid(row=self.row, column=1, sticky="nwe")
David Scherer7aced172000-08-15 01:13:23 +000098 self.row = self.row + 1
Terry Jan Reedy72eb0752014-06-30 19:59:57 -040099 return l, e # return label for testing
David Scherer7aced172000-08-15 01:13:23 +0000100
Terry Jan Reedyaa608fd2014-06-26 01:40:46 -0400101 def create_entries(self):
102 "Create one or more entry lines with make_entry."
Terry Jan Reedy72eb0752014-06-30 19:59:57 -0400103 self.ent = self.make_entry("Find:", self.engine.patvar)[1]
Terry Jan Reedyaa608fd2014-06-26 01:40:46 -0400104
Chui Tey72a8a3b2002-11-04 23:07:51 +0000105 def make_frame(self,labeltext=None):
Terry Jan Reedyaa608fd2014-06-26 01:40:46 -0400106 "Return gridded labeled Frame for option or other buttons."
Chui Tey72a8a3b2002-11-04 23:07:51 +0000107 if labeltext:
108 l = Label(self.top, text=labeltext)
Kurt B. Kaiser4fc90472002-11-21 03:02:17 +0000109 l.grid(row=self.row, column=0, sticky="nw")
Terry Jan Reedy72eb0752014-06-30 19:59:57 -0400110 else:
111 l = ''
David Scherer7aced172000-08-15 01:13:23 +0000112 f = Frame(self.top)
Kurt B. Kaiser4fc90472002-11-21 03:02:17 +0000113 f.grid(row=self.row, column=1, columnspan=1, sticky="nwe")
David Scherer7aced172000-08-15 01:13:23 +0000114 self.row = self.row + 1
Terry Jan Reedy72eb0752014-06-30 19:59:57 -0400115 return l, f
David Scherer7aced172000-08-15 01:13:23 +0000116
David Scherer7aced172000-08-15 01:13:23 +0000117 def create_option_buttons(self):
Terry Jan Reedyaa608fd2014-06-26 01:40:46 -0400118 "Fill frame with Checkbuttons bound to SearchEngine booleanvars."
Terry Jan Reedy72eb0752014-06-30 19:59:57 -0400119 f = self.make_frame("Options")[1]
David Scherer7aced172000-08-15 01:13:23 +0000120
121 btn = Checkbutton(f, anchor="w",
122 variable=self.engine.revar,
123 text="Regular expression")
124 btn.pack(side="left", fill="both")
125 if self.engine.isre():
126 btn.select()
127
128 btn = Checkbutton(f, anchor="w",
129 variable=self.engine.casevar,
130 text="Match case")
131 btn.pack(side="left", fill="both")
132 if self.engine.iscase():
133 btn.select()
134
135 btn = Checkbutton(f, anchor="w",
136 variable=self.engine.wordvar,
137 text="Whole word")
138 btn.pack(side="left", fill="both")
139 if self.engine.isword():
140 btn.select()
141
142 if self.needwrapbutton:
143 btn = Checkbutton(f, anchor="w",
144 variable=self.engine.wrapvar,
145 text="Wrap around")
146 btn.pack(side="left", fill="both")
147 if self.engine.iswrap():
148 btn.select()
149
150 def create_other_buttons(self):
Terry Jan Reedyaa608fd2014-06-26 01:40:46 -0400151 "Fill frame with buttons tied to other options."
Terry Jan Reedy72eb0752014-06-30 19:59:57 -0400152 f = self.make_frame("Direction")[1]
David Scherer7aced172000-08-15 01:13:23 +0000153
David Scherer7aced172000-08-15 01:13:23 +0000154 btn = Radiobutton(f, anchor="w",
155 variable=self.engine.backvar, value=1,
156 text="Up")
157 btn.pack(side="left", fill="both")
158 if self.engine.isback():
159 btn.select()
160
161 btn = Radiobutton(f, anchor="w",
162 variable=self.engine.backvar, value=0,
163 text="Down")
164 btn.pack(side="left", fill="both")
165 if not self.engine.isback():
166 btn.select()
167
Terry Jan Reedyaa608fd2014-06-26 01:40:46 -0400168 def make_button(self, label, command, isdef=0):
169 "Return command button gridded in command frame."
170 b = Button(self.buttonframe,
171 text=label, command=command,
172 default=isdef and "active" or "normal")
173 cols,rows=self.buttonframe.grid_size()
174 b.grid(pady=1,row=rows,column=0,sticky="ew")
175 self.buttonframe.grid(rowspan=rows+1)
176 return b
177
David Scherer7aced172000-08-15 01:13:23 +0000178 def create_command_buttons(self):
Terry Jan Reedyaa608fd2014-06-26 01:40:46 -0400179 "Place buttons in vertical command frame gridded on right."
Chui Tey72a8a3b2002-11-04 23:07:51 +0000180 f = self.buttonframe = Frame(self.top)
Kurt B. Kaiser4fc90472002-11-21 03:02:17 +0000181 f.grid(row=0,column=2,padx=2,pady=2,ipadx=2,ipady=2)
Chui Tey72a8a3b2002-11-04 23:07:51 +0000182
183 b = self.make_button("close", self.close)
David Scherer7aced172000-08-15 01:13:23 +0000184 b.lower()
Terry Jan Reedyaa608fd2014-06-26 01:40:46 -0400185
186if __name__ == '__main__':
187 import unittest
188 unittest.main(
189 'idlelib.idle_test.test_searchdialogbase', verbosity=2)