1 '''Define SearchDialogBase used by Search, Replace, and Grep dialogs.'''
2 
3 from Tkinter import (Toplevel, Frame, Entry, Label, Button,
4                      Checkbutton, Radiobutton)
5 
6 class SearchDialogBase:
7     '''Create most of a 3 or 4 row, 3 column search dialog.
8 
9     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).
14 
15     The narrow right column contains command buttons
16     (make_button, create_command_buttons).
17     These are bound to functions that execute the command.
18 
19     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.
25     '''
26 
27     title = "Search Dialog"  # replace in subclasses
28     icon = "Search"
29     needwrapbutton = 1  # not in Find in Files
30 
31     def __init__(self, root, engine):
32         '''Initialize root, engine, and top attributes.
33 
34         top (level widget): set in create_widgets() called from open().
35         text (Text searched): set in open(), only used in subclasses().
36         ent (ry): created in make_entry() called from create_entry().
37         row (of grid): 0 in create_widgets(), +1 in make_entry/frame().
38         default_command: set in subclasses, used in create_widgers().
39 
40         title (of dialog): class attribute, override in subclasses.
41         icon (of dialog): ditto, use unclear if cannot minimize dialog.
42         '''
43         self.root = root
44         self.engine = engine
45         self.top = None
46 
47     def open(self, text, searchphrase=None):
48         "Make dialog visible on top of others and ready to use."
49         self.text = text
50         if not self.top:
51             self.create_widgets()
52         else:
53             self.top.deiconify()
54             self.top.tkraise()
55         if searchphrase:
56             self.ent.delete(0,"end")
57             self.ent.insert("end",searchphrase)
58         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):
64         "Put dialog away for later use."
65         if self.top:
66             self.top.grab_release()
67             self.top.withdraw()
68 
69     def create_widgets(self):
70         '''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         '''
75         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
84         self.top.grid_columnconfigure(0, pad=2, weight=0)
85         self.top.grid_columnconfigure(1, pad=2, minsize=100, weight=100)
86 
87         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
91 
92     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")
102         self.row = self.row + 1
103         return entry, label
104 
105     def create_entries(self):
106         "Create one or more entry lines with make_entry."
107         self.ent = self.make_entry("Find:", self.engine.patvar)[0]
108 
109     def make_frame(self,labeltext=None):
110         '''Return (frame, label).
111 
112         frame - gridded labeled Frame for option or other buttons.
113         label - Label widget, returned for testing.
114         '''
115         if labeltext:
116             label = Label(self.top, text=labeltext)
117             label.grid(row=self.row, column=0, sticky="nw")
118         else:
119             label = ''
120         frame = Frame(self.top)
121         frame.grid(row=self.row, column=1, columnspan=1, sticky="nwe")
122         self.row = self.row + 1
123         return frame, label
124 
125     def create_option_buttons(self):
126         '''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         '''
132         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")]
137         if self.needwrapbutton:
138             options.append((engine.wrapvar, "Wrap around"))
139         for var, label in options:
140             btn = Checkbutton(frame, anchor="w", variable=var, text=label)
141             btn.pack(side="left", fill="both")
142             if var.get():
143                 btn.select()
144         return frame, options
145 
146     def create_other_buttons(self):
147         '''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         '''
152         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")
159             if var.get() == val:
160                 btn.select()
161         return frame, others
162 
163     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 
173     def create_command_buttons(self):
174         "Place buttons in vertical command frame gridded on right."
175         f = self.buttonframe = Frame(self.top)
176         f.grid(row=0,column=2,padx=2,pady=2,ipadx=2,ipady=2)
177 
178         b = self.make_button("close", self.close)
179         b.lower()
180 
181 if __name__ == '__main__':
182     import unittest
183     unittest.main(
184         'idlelib.idle_test.test_searchdialogbase', verbosity=2)
185