blob: bc4ae6ff60b170f940066d8ae44d4f4ecc2596e8 [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:
Terry Jan Reedy6a0fe8d2014-06-30 23:52:14 -0400108 label = Label(self.top, text=labeltext)
109 label.grid(row=self.row, column=0, sticky="nw")
Terry Jan Reedy72eb0752014-06-30 19:59:57 -0400110 else:
Terry Jan Reedy6a0fe8d2014-06-30 23:52:14 -0400111 label = ''
112 frame = Frame(self.top)
113 frame.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 Reedy6a0fe8d2014-06-30 23:52:14 -0400115 return frame, label # label for test
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 Reedy6a0fe8d2014-06-30 23:52:14 -0400119 frame = self.make_frame("Options")[0]
120 engine = self.engine
121 options = [(engine.revar, "Regular expression"),
122 (engine.casevar, "Match case"),
123 (engine.wordvar, "Whole word")]
David Scherer7aced172000-08-15 01:13:23 +0000124 if self.needwrapbutton:
Terry Jan Reedy6a0fe8d2014-06-30 23:52:14 -0400125 options.append((engine.wrapvar, "Wrap around"))
126 for var, label in options:
127 btn = Checkbutton(frame, anchor="w", variable=var, text=label)
David Scherer7aced172000-08-15 01:13:23 +0000128 btn.pack(side="left", fill="both")
Terry Jan Reedy6a0fe8d2014-06-30 23:52:14 -0400129 if var.get():
David Scherer7aced172000-08-15 01:13:23 +0000130 btn.select()
Terry Jan Reedy6a0fe8d2014-06-30 23:52:14 -0400131 return frame, options # for test
David Scherer7aced172000-08-15 01:13:23 +0000132
133 def create_other_buttons(self):
Terry Jan Reedyaa608fd2014-06-26 01:40:46 -0400134 "Fill frame with buttons tied to other options."
Terry Jan Reedy6a0fe8d2014-06-30 23:52:14 -0400135 frame = self.make_frame("Direction")[0]
136 var = self.engine.backvar
137 others = [(1, 'Up'), (0, 'Down')]
138 for val, label in others:
139 btn = Radiobutton(frame, anchor="w",
140 variable=var, value=val, text=label)
141 btn.pack(side="left", fill="both")
142 #print(var.get(), val, label)
143 if var.get() == val:
144 btn.select()
145 return frame, others # for test
David Scherer7aced172000-08-15 01:13:23 +0000146
Terry Jan Reedyaa608fd2014-06-26 01:40:46 -0400147 def make_button(self, label, command, isdef=0):
148 "Return command button gridded in command frame."
149 b = Button(self.buttonframe,
150 text=label, command=command,
151 default=isdef and "active" or "normal")
152 cols,rows=self.buttonframe.grid_size()
153 b.grid(pady=1,row=rows,column=0,sticky="ew")
154 self.buttonframe.grid(rowspan=rows+1)
155 return b
156
David Scherer7aced172000-08-15 01:13:23 +0000157 def create_command_buttons(self):
Terry Jan Reedyaa608fd2014-06-26 01:40:46 -0400158 "Place buttons in vertical command frame gridded on right."
Chui Tey72a8a3b2002-11-04 23:07:51 +0000159 f = self.buttonframe = Frame(self.top)
Kurt B. Kaiser4fc90472002-11-21 03:02:17 +0000160 f.grid(row=0,column=2,padx=2,pady=2,ipadx=2,ipady=2)
Chui Tey72a8a3b2002-11-04 23:07:51 +0000161
162 b = self.make_button("close", self.close)
David Scherer7aced172000-08-15 01:13:23 +0000163 b.lower()
Terry Jan Reedyaa608fd2014-06-26 01:40:46 -0400164
165if __name__ == '__main__':
166 import unittest
167 unittest.main(
168 'idlelib.idle_test.test_searchdialogbase', verbosity=2)