blob: 651e7f4a3fcd90f6530d7515f877001db85f15bb [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
Terry Jan Reedye2c409f2014-07-13 17:27:21 -040092 def make_entry(self, label_text, var):
93 '''Return (entry, label), .
94
95 entry - gridded labeled Entry for text entry.
96 label - Label widget, returned for testing.
97 '''
98 label = Label(self.top, text=label_text)
99 label.grid(row=self.row, column=0, sticky="nw")
100 entry = Entry(self.top, textvariable=var, exportselection=0)
101 entry.grid(row=self.row, column=1, sticky="nwe")
David Scherer7aced172000-08-15 01:13:23 +0000102 self.row = self.row + 1
Terry Jan Reedye2c409f2014-07-13 17:27:21 -0400103 return entry, label
David Scherer7aced172000-08-15 01:13:23 +0000104
Terry Jan Reedyaa608fd2014-06-26 01:40:46 -0400105 def create_entries(self):
106 "Create one or more entry lines with make_entry."
Terry Jan Reedye2c409f2014-07-13 17:27:21 -0400107 self.ent = self.make_entry("Find:", self.engine.patvar)[0]
Terry Jan Reedyaa608fd2014-06-26 01:40:46 -0400108
Chui Tey72a8a3b2002-11-04 23:07:51 +0000109 def make_frame(self,labeltext=None):
Terry Jan Reedye2c409f2014-07-13 17:27:21 -0400110 '''Return (frame, label).
111
112 frame - gridded labeled Frame for option or other buttons.
113 label - Label widget, returned for testing.
114 '''
Chui Tey72a8a3b2002-11-04 23:07:51 +0000115 if labeltext:
Terry Jan Reedy6a0fe8d2014-06-30 23:52:14 -0400116 label = Label(self.top, text=labeltext)
117 label.grid(row=self.row, column=0, sticky="nw")
Terry Jan Reedy72eb0752014-06-30 19:59:57 -0400118 else:
Terry Jan Reedy6a0fe8d2014-06-30 23:52:14 -0400119 label = ''
120 frame = Frame(self.top)
121 frame.grid(row=self.row, column=1, columnspan=1, sticky="nwe")
David Scherer7aced172000-08-15 01:13:23 +0000122 self.row = self.row + 1
Terry Jan Reedye2c409f2014-07-13 17:27:21 -0400123 return frame, label
David Scherer7aced172000-08-15 01:13:23 +0000124
David Scherer7aced172000-08-15 01:13:23 +0000125 def create_option_buttons(self):
Terry Jan Reedye2c409f2014-07-13 17:27:21 -0400126 '''Return (filled frame, options) for testing.
127
128 Options is a list of SearchEngine booleanvar, label pairs.
129 A gridded frame from make_frame is filled with a Checkbutton
130 for each pair, bound to the var, with the corresponding label.
131 '''
Terry Jan Reedy6a0fe8d2014-06-30 23:52:14 -0400132 frame = self.make_frame("Options")[0]
133 engine = self.engine
134 options = [(engine.revar, "Regular expression"),
135 (engine.casevar, "Match case"),
136 (engine.wordvar, "Whole word")]
David Scherer7aced172000-08-15 01:13:23 +0000137 if self.needwrapbutton:
Terry Jan Reedy6a0fe8d2014-06-30 23:52:14 -0400138 options.append((engine.wrapvar, "Wrap around"))
139 for var, label in options:
140 btn = Checkbutton(frame, anchor="w", variable=var, text=label)
David Scherer7aced172000-08-15 01:13:23 +0000141 btn.pack(side="left", fill="both")
Terry Jan Reedy6a0fe8d2014-06-30 23:52:14 -0400142 if var.get():
David Scherer7aced172000-08-15 01:13:23 +0000143 btn.select()
Terry Jan Reedye2c409f2014-07-13 17:27:21 -0400144 return frame, options
David Scherer7aced172000-08-15 01:13:23 +0000145
146 def create_other_buttons(self):
Terry Jan Reedye2c409f2014-07-13 17:27:21 -0400147 '''Return (frame, others) for testing.
148
149 Others is a list of value, label pairs.
150 A gridded frame from make_frame is filled with radio buttons.
151 '''
Terry Jan Reedy6a0fe8d2014-06-30 23:52:14 -0400152 frame = self.make_frame("Direction")[0]
153 var = self.engine.backvar
154 others = [(1, 'Up'), (0, 'Down')]
155 for val, label in others:
156 btn = Radiobutton(frame, anchor="w",
157 variable=var, value=val, text=label)
158 btn.pack(side="left", fill="both")
Terry Jan Reedy6a0fe8d2014-06-30 23:52:14 -0400159 if var.get() == val:
160 btn.select()
Terry Jan Reedye2c409f2014-07-13 17:27:21 -0400161 return frame, others
David Scherer7aced172000-08-15 01:13:23 +0000162
Terry Jan Reedyaa608fd2014-06-26 01:40:46 -0400163 def make_button(self, label, command, isdef=0):
164 "Return command button gridded in command frame."
165 b = Button(self.buttonframe,
166 text=label, command=command,
167 default=isdef and "active" or "normal")
168 cols,rows=self.buttonframe.grid_size()
169 b.grid(pady=1,row=rows,column=0,sticky="ew")
170 self.buttonframe.grid(rowspan=rows+1)
171 return b
172
David Scherer7aced172000-08-15 01:13:23 +0000173 def create_command_buttons(self):
Terry Jan Reedyaa608fd2014-06-26 01:40:46 -0400174 "Place buttons in vertical command frame gridded on right."
Chui Tey72a8a3b2002-11-04 23:07:51 +0000175 f = self.buttonframe = Frame(self.top)
Kurt B. Kaiser4fc90472002-11-21 03:02:17 +0000176 f.grid(row=0,column=2,padx=2,pady=2,ipadx=2,ipady=2)
Chui Tey72a8a3b2002-11-04 23:07:51 +0000177
178 b = self.make_button("close", self.close)
David Scherer7aced172000-08-15 01:13:23 +0000179 b.lower()
Terry Jan Reedyaa608fd2014-06-26 01:40:46 -0400180
181if __name__ == '__main__':
182 import unittest
183 unittest.main(
184 'idlelib.idle_test.test_searchdialogbase', verbosity=2)