blob: 5f81785b712c088d6abec74918575ba8e30d88b7 [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
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().
Terry Jan Reedy525168b2014-06-30 20:00:03 -040039 default_command: set in subclasses, used in create_widgers().
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
45 self.engine = engine
46 self.top = None
47
Chui Tey5a231c8f2002-11-06 02:18:45 +000048 def open(self, text, searchphrase=None):
Terry Jan Reedyede05732014-06-26 01:40:51 -040049 "Make dialog visible on top of others and ready to use."
David Scherer7aced172000-08-15 01:13:23 +000050 self.text = text
51 if not self.top:
52 self.create_widgets()
53 else:
54 self.top.deiconify()
55 self.top.tkraise()
Chui Tey5a231c8f2002-11-06 02:18:45 +000056 if searchphrase:
57 self.ent.delete(0,"end")
58 self.ent.insert("end",searchphrase)
David Scherer7aced172000-08-15 01:13:23 +000059 self.ent.focus_set()
60 self.ent.selection_range(0, "end")
61 self.ent.icursor(0)
62 self.top.grab_set()
63
64 def close(self, event=None):
Terry Jan Reedyede05732014-06-26 01:40:51 -040065 "Put dialog away for later use."
David Scherer7aced172000-08-15 01:13:23 +000066 if self.top:
67 self.top.grab_release()
68 self.top.withdraw()
69
70 def create_widgets(self):
Terry Jan Reedyede05732014-06-26 01:40:51 -040071 '''Create basic 3 row x 3 col search (find) dialog.
72
73 Other dialogs override subsidiary create_x methods as needed.
74 Replace and Find-in-Files add another entry row.
75 '''
David Scherer7aced172000-08-15 01:13:23 +000076 top = Toplevel(self.root)
77 top.bind("<Return>", self.default_command)
78 top.bind("<Escape>", self.close)
79 top.protocol("WM_DELETE_WINDOW", self.close)
80 top.wm_title(self.title)
81 top.wm_iconname(self.icon)
82 self.top = top
Terry Jan Reedy3ff55a82016-08-10 23:44:54 -040083 self.bell = top.bell
David Scherer7aced172000-08-15 01:13:23 +000084
85 self.row = 0
Chui Tey72a8a3b2002-11-04 23:07:51 +000086 self.top.grid_columnconfigure(0, pad=2, weight=0)
87 self.top.grid_columnconfigure(1, pad=2, minsize=100, weight=100)
David Scherer7aced172000-08-15 01:13:23 +000088
Terry Jan Reedyede05732014-06-26 01:40:51 -040089 self.create_entries() # row 0 (and maybe 1), cols 0, 1
90 self.create_option_buttons() # next row, cols 0, 1
91 self.create_other_buttons() # next row, cols 0, 1
92 self.create_command_buttons() # col 2, all rows
David Scherer7aced172000-08-15 01:13:23 +000093
Terry Jan Reedy5283c4e2014-07-13 17:27:26 -040094 def make_entry(self, label_text, var):
95 '''Return (entry, label), .
96
97 entry - gridded labeled Entry for text entry.
98 label - Label widget, returned for testing.
99 '''
100 label = Label(self.top, text=label_text)
101 label.grid(row=self.row, column=0, sticky="nw")
102 entry = Entry(self.top, textvariable=var, exportselection=0)
103 entry.grid(row=self.row, column=1, sticky="nwe")
David Scherer7aced172000-08-15 01:13:23 +0000104 self.row = self.row + 1
Terry Jan Reedy5283c4e2014-07-13 17:27:26 -0400105 return entry, label
David Scherer7aced172000-08-15 01:13:23 +0000106
Terry Jan Reedyede05732014-06-26 01:40:51 -0400107 def create_entries(self):
108 "Create one or more entry lines with make_entry."
Terry Jan Reedy5283c4e2014-07-13 17:27:26 -0400109 self.ent = self.make_entry("Find:", self.engine.patvar)[0]
Terry Jan Reedyede05732014-06-26 01:40:51 -0400110
Chui Tey72a8a3b2002-11-04 23:07:51 +0000111 def make_frame(self,labeltext=None):
Terry Jan Reedy5283c4e2014-07-13 17:27:26 -0400112 '''Return (frame, label).
113
114 frame - gridded labeled Frame for option or other buttons.
115 label - Label widget, returned for testing.
116 '''
Chui Tey72a8a3b2002-11-04 23:07:51 +0000117 if labeltext:
Terry Jan Reedy8cefd082014-06-30 23:52:20 -0400118 label = Label(self.top, text=labeltext)
119 label.grid(row=self.row, column=0, sticky="nw")
Terry Jan Reedy525168b2014-06-30 20:00:03 -0400120 else:
Terry Jan Reedy8cefd082014-06-30 23:52:20 -0400121 label = ''
122 frame = Frame(self.top)
123 frame.grid(row=self.row, column=1, columnspan=1, sticky="nwe")
David Scherer7aced172000-08-15 01:13:23 +0000124 self.row = self.row + 1
Terry Jan Reedy5283c4e2014-07-13 17:27:26 -0400125 return frame, label
David Scherer7aced172000-08-15 01:13:23 +0000126
David Scherer7aced172000-08-15 01:13:23 +0000127 def create_option_buttons(self):
Terry Jan Reedy5283c4e2014-07-13 17:27:26 -0400128 '''Return (filled frame, options) for testing.
129
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -0400130 Options is a list of searchengine booleanvar, label pairs.
Terry Jan Reedy5283c4e2014-07-13 17:27:26 -0400131 A gridded frame from make_frame is filled with a Checkbutton
132 for each pair, bound to the var, with the corresponding label.
133 '''
Terry Jan Reedy8cefd082014-06-30 23:52:20 -0400134 frame = self.make_frame("Options")[0]
135 engine = self.engine
136 options = [(engine.revar, "Regular expression"),
137 (engine.casevar, "Match case"),
138 (engine.wordvar, "Whole word")]
David Scherer7aced172000-08-15 01:13:23 +0000139 if self.needwrapbutton:
Terry Jan Reedy8cefd082014-06-30 23:52:20 -0400140 options.append((engine.wrapvar, "Wrap around"))
141 for var, label in options:
Terry Jan Reedy6f7b0f52016-07-10 20:21:31 -0400142 btn = Checkbutton(frame, variable=var, text=label)
David Scherer7aced172000-08-15 01:13:23 +0000143 btn.pack(side="left", fill="both")
Terry Jan Reedy5283c4e2014-07-13 17:27:26 -0400144 return frame, options
David Scherer7aced172000-08-15 01:13:23 +0000145
146 def create_other_buttons(self):
Terry Jan Reedy5283c4e2014-07-13 17:27:26 -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 Reedy8cefd082014-06-30 23:52:20 -0400152 frame = self.make_frame("Direction")[0]
153 var = self.engine.backvar
154 others = [(1, 'Up'), (0, 'Down')]
155 for val, label in others:
Terry Jan Reedy6f7b0f52016-07-10 20:21:31 -0400156 btn = Radiobutton(frame, variable=var, value=val, text=label)
Terry Jan Reedy8cefd082014-06-30 23:52:20 -0400157 btn.pack(side="left", fill="both")
Terry Jan Reedy5283c4e2014-07-13 17:27:26 -0400158 return frame, others
David Scherer7aced172000-08-15 01:13:23 +0000159
Terry Jan Reedyede05732014-06-26 01:40:51 -0400160 def make_button(self, label, command, isdef=0):
161 "Return command button gridded in command frame."
162 b = Button(self.buttonframe,
163 text=label, command=command,
164 default=isdef and "active" or "normal")
165 cols,rows=self.buttonframe.grid_size()
166 b.grid(pady=1,row=rows,column=0,sticky="ew")
167 self.buttonframe.grid(rowspan=rows+1)
168 return b
169
David Scherer7aced172000-08-15 01:13:23 +0000170 def create_command_buttons(self):
Terry Jan Reedyede05732014-06-26 01:40:51 -0400171 "Place buttons in vertical command frame gridded on right."
Chui Tey72a8a3b2002-11-04 23:07:51 +0000172 f = self.buttonframe = Frame(self.top)
Kurt B. Kaiser4fc90472002-11-21 03:02:17 +0000173 f.grid(row=0,column=2,padx=2,pady=2,ipadx=2,ipady=2)
Chui Tey72a8a3b2002-11-04 23:07:51 +0000174
175 b = self.make_button("close", self.close)
David Scherer7aced172000-08-15 01:13:23 +0000176 b.lower()
Terry Jan Reedyede05732014-06-26 01:40:51 -0400177
Terry Jan Reedy6f7b0f52016-07-10 20:21:31 -0400178
179class _searchbase(SearchDialogBase): # htest #
180 "Create auto-opening dialog with no text connection."
181
182 def __init__(self, parent):
183 import re
184 from idlelib import searchengine
185
186 self.root = parent
187 self.engine = searchengine.get(parent)
188 self.create_widgets()
189 print(parent.geometry())
190 width,height, x,y = list(map(int, re.split('[x+]', parent.geometry())))
191 self.top.geometry("+%d+%d" % (x + 40, y + 175))
192
Terry Jan Reedy3ff55a82016-08-10 23:44:54 -0400193 def default_command(self, dummy): pass
Terry Jan Reedy6f7b0f52016-07-10 20:21:31 -0400194
Terry Jan Reedyede05732014-06-26 01:40:51 -0400195if __name__ == '__main__':
196 import unittest
Terry Jan Reedy6f7b0f52016-07-10 20:21:31 -0400197 unittest.main('idlelib.idle_test.test_searchbase', verbosity=2, exit=False)
198
199 from idlelib.idle_test.htest import run
200 run(_searchbase)