blob: f98af4600ee805895034d4ae22b5f7caba1fc25d [file] [log] [blame]
Kurt B. Kaisere7a161e2003-01-10 20:13:57 +00001"""IDLE Configuration Dialog: support user customization of IDLE by GUI
2
3Customize font faces, sizes, and colorization attributes. Set indentation
4defaults. Customize keybindings. Colorization and keybindings can be
5saved as user defined sets. Select startup options including shell/editor
6and default window size. Define additional help sources.
7
8Note that tab width in IDLE is currently fixed at eight due to Tk issues.
Kurt B. Kaiseracdef852005-01-31 03:34:26 +00009Refer to comments in EditorWindow autoindent code for details.
Kurt B. Kaisere7a161e2003-01-10 20:13:57 +000010
Steven M. Gava44d3d1a2001-07-31 06:59:02 +000011"""
terryjreedy938e7382017-06-26 20:48:39 -040012from tkinter import (Toplevel, Frame, LabelFrame, Listbox, Label, Button,
13 Entry, Text, Scale, Radiobutton, Checkbutton, Canvas,
14 StringVar, BooleanVar, IntVar, TRUE, FALSE,
15 TOP, BOTTOM, RIGHT, LEFT, SOLID, GROOVE, NORMAL, DISABLED,
16 NONE, BOTH, X, Y, W, E, EW, NS, NSEW, NW,
terryjreedy7ab33422017-07-09 19:26:32 -040017 HORIZONTAL, VERTICAL, ANCHOR, ACTIVE, END)
Terry Jan Reedy01e35752016-06-10 18:19:21 -040018from tkinter.ttk import Scrollbar
Georg Brandl14fc4272008-05-17 18:39:55 +000019import tkinter.colorchooser as tkColorChooser
20import tkinter.font as tkFont
Terry Jan Reedybfbaa6b2016-08-31 00:50:55 -040021import tkinter.messagebox as tkMessageBox
Steven M. Gava44d3d1a2001-07-31 06:59:02 +000022
terryjreedyedc03422017-07-07 16:37:39 -040023from idlelib.config import idleConf, ConfigChanges
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -040024from idlelib.config_key import GetKeysDialog
Terry Jan Reedybfbaa6b2016-08-31 00:50:55 -040025from idlelib.dynoption import DynOptionMenu
26from idlelib import macosx
Terry Jan Reedy8b22c0a2016-07-08 00:22:50 -040027from idlelib.query import SectionName, HelpSource
Terry Jan Reedya9421fb2014-10-22 20:15:18 -040028from idlelib.tabbedpages import TabbedPageSet
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -040029from idlelib.textview import view_text
Terry Jan Reedyd0cadba2015-10-11 22:07:31 -040030
terryjreedyedc03422017-07-07 16:37:39 -040031changes = ConfigChanges()
32
terryjreedye5bb1122017-07-05 00:54:55 -040033
Steven M. Gava44d3d1a2001-07-31 06:59:02 +000034class ConfigDialog(Toplevel):
terryjreedye5bb1122017-07-05 00:54:55 -040035 """Config dialog for IDLE.
36 """
Kurt B. Kaiseracdef852005-01-31 03:34:26 +000037
Terry Jan Reedycd567362014-10-17 01:31:35 -040038 def __init__(self, parent, title='', _htest=False, _utest=False):
terryjreedye5bb1122017-07-05 00:54:55 -040039 """Show the tabbed dialog for user configuration.
40
terryjreedy9a09c662017-07-13 23:53:30 -040041 Args:
42 parent - parent of this dialog
43 title - string which is the title of this popup dialog
44 _htest - bool, change box location when running htest
45 _utest - bool, don't wait_window when running unittest
46
47 Note: Focus set on font page fontlist.
48
49 Methods:
50 create_widgets
51 cancel: Bound to DELETE_WINDOW protocol.
Terry Jan Reedy2e8234a2014-05-29 01:46:26 -040052 """
Steven M. Gavad721c482001-07-31 10:46:53 +000053 Toplevel.__init__(self, parent)
Terry Jan Reedy22405332014-07-30 19:24:32 -040054 self.parent = parent
Terry Jan Reedy4036d872014-08-03 23:02:58 -040055 if _htest:
56 parent.instance_dict = {}
terryjreedy42abf7f2017-07-13 22:24:55 -040057 if not _utest:
58 self.withdraw()
Guido van Rossum8ce8a782007-11-01 19:42:39 +000059
Steven M. Gavad721c482001-07-31 10:46:53 +000060 self.configure(borderwidth=5)
Terry Jan Reedycd567362014-10-17 01:31:35 -040061 self.title(title or 'IDLE Preferences')
terryjreedy938e7382017-06-26 20:48:39 -040062 x = parent.winfo_rootx() + 20
63 y = parent.winfo_rooty() + (30 if not _htest else 150)
64 self.geometry(f'+{x}+{y}')
terryjreedye5bb1122017-07-05 00:54:55 -040065 # Each theme element key is its display name.
66 # The first value of the tuple is the sample area tag name.
67 # The second value is the display name list sort index.
terryjreedy938e7382017-06-26 20:48:39 -040068 self.create_widgets()
Terry Jan Reedy4036d872014-08-03 23:02:58 -040069 self.resizable(height=FALSE, width=FALSE)
Steven M. Gavad721c482001-07-31 10:46:53 +000070 self.transient(parent)
terryjreedy938e7382017-06-26 20:48:39 -040071 self.protocol("WM_DELETE_WINDOW", self.cancel)
terryjreedy7ab33422017-07-09 19:26:32 -040072 self.fontlist.focus_set()
terryjreedye5bb1122017-07-05 00:54:55 -040073 # XXX Decide whether to keep or delete these key bindings.
74 # Key bindings for this dialog.
75 # self.bind('<Escape>', self.Cancel) #dismiss dialog, no save
76 # self.bind('<Alt-a>', self.Apply) #apply changes, save
77 # self.bind('<F1>', self.Help) #context help
terryjreedy938e7382017-06-26 20:48:39 -040078 self.load_configs()
terryjreedye5bb1122017-07-05 00:54:55 -040079 self.attach_var_callbacks() # Avoid callbacks during load_configs.
Guido van Rossum8ce8a782007-11-01 19:42:39 +000080
Terry Jan Reedycfa89502014-07-14 23:07:32 -040081 if not _utest:
terryjreedy42abf7f2017-07-13 22:24:55 -040082 self.grab_set()
Terry Jan Reedycfa89502014-07-14 23:07:32 -040083 self.wm_deiconify()
84 self.wait_window()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000085
terryjreedy938e7382017-06-26 20:48:39 -040086 def create_widgets(self):
terryjreedy9a09c662017-07-13 23:53:30 -040087 """Create and place widgets for tabbed dialog.
88
89 Widgets Bound to self:
90 tab_pages: TabbedPageSet
91
92 Methods:
93 create_page_font_tab
94 create_page_highlight
95 create_page_keys
96 create_page_general
97 create_page_extensions
98 create_action_buttons
99 load_configs: Load pages except for extensions.
100 attach_var_callbacks
101 remove_var_callbacks
102 activate_config_changes: Tell editors to reload.
103 """
terryjreedy938e7382017-06-26 20:48:39 -0400104 self.tab_pages = TabbedPageSet(self,
Terry Jan Reedy93f35422015-10-13 22:03:51 -0400105 page_names=['Fonts/Tabs', 'Highlighting', 'Keys', 'General',
106 'Extensions'])
terryjreedy938e7382017-06-26 20:48:39 -0400107 self.tab_pages.pack(side=TOP, expand=TRUE, fill=BOTH)
108 self.create_page_font_tab()
109 self.create_page_highlight()
110 self.create_page_keys()
111 self.create_page_general()
112 self.create_page_extensions()
Terry Jan Reedy92cb0a32014-10-08 20:29:13 -0400113 self.create_action_buttons().pack(side=BOTTOM)
Terry Jan Reedyd0cadba2015-10-11 22:07:31 -0400114
Terry Jan Reedy92cb0a32014-10-08 20:29:13 -0400115 def create_action_buttons(self):
terryjreedy9a09c662017-07-13 23:53:30 -0400116 """Return frame of action buttons for dialog.
117
118 Methods:
119 ok
120 apply
121 cancel
122 help
123
124 Widget Structure:
125 outer: Frame
126 buttons: Frame
127 (no assignment): Button (ok)
128 (no assignment): Button (apply)
129 (no assignment): Button (cancel)
130 (no assignment): Button (help)
131 (no assignment): Frame
132 """
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -0400133 if macosx.isAquaTk():
Terry Jan Reedye3416e62014-07-26 19:40:16 -0400134 # Changing the default padding on OSX results in unreadable
terryjreedye5bb1122017-07-05 00:54:55 -0400135 # text in the buttons.
terryjreedy938e7382017-06-26 20:48:39 -0400136 padding_args = {}
Ronald Oussoren9e350042009-02-12 16:02:11 +0000137 else:
terryjreedy938e7382017-06-26 20:48:39 -0400138 padding_args = {'padx':6, 'pady':3}
Terry Jan Reedya9421fb2014-10-22 20:15:18 -0400139 outer = Frame(self, pady=2)
140 buttons = Frame(outer, pady=2)
Terry Jan Reedyd0cadba2015-10-11 22:07:31 -0400141 for txt, cmd in (
terryjreedy938e7382017-06-26 20:48:39 -0400142 ('Ok', self.ok),
143 ('Apply', self.apply),
144 ('Cancel', self.cancel),
145 ('Help', self.help)):
Terry Jan Reedyd0cadba2015-10-11 22:07:31 -0400146 Button(buttons, text=txt, command=cmd, takefocus=FALSE,
terryjreedy938e7382017-06-26 20:48:39 -0400147 **padding_args).pack(side=LEFT, padx=5)
terryjreedye5bb1122017-07-05 00:54:55 -0400148 # Add space above buttons.
Terry Jan Reedya9421fb2014-10-22 20:15:18 -0400149 Frame(outer, height=2, borderwidth=0).pack(side=TOP)
150 buttons.pack(side=BOTTOM)
151 return outer
Terry Jan Reedyd0cadba2015-10-11 22:07:31 -0400152
Terry Jan Reedy1daeb252017-07-24 02:50:28 -0400153
terryjreedy938e7382017-06-26 20:48:39 -0400154 def create_page_font_tab(self):
terryjreedye5bb1122017-07-05 00:54:55 -0400155 """Return frame of widgets for Font/Tabs tab.
156
Terry Jan Reedy5aa3bf02017-07-23 14:18:27 -0400157 Fonts: Enable users to provisionally change font face, size, or
Terry Jan Reedy04864b42017-07-22 00:56:18 -0400158 boldness and to see the consequence of proposed choices. Each
159 action set 3 options in changes structuree and changes the
160 corresponding aspect of the font sample on this page and
161 highlight sample on highlight page.
162
Terry Jan Reedy1daeb252017-07-24 02:50:28 -0400163 Load_font_cfg initializes font vars and widgets from
164 idleConf entries and tk.
165
Terry Jan Reedy5aa3bf02017-07-23 14:18:27 -0400166 Fontlist: mouse button 1 click or up or down key invoke
Terry Jan Reedy1daeb252017-07-24 02:50:28 -0400167 on_fontlist_select(), which sets var font_name.
Terry Jan Reedy04864b42017-07-22 00:56:18 -0400168
Terry Jan Reedy5aa3bf02017-07-23 14:18:27 -0400169 Sizelist: clicking the menubutton opens the dropdown menu. A
Terry Jan Reedy1daeb252017-07-24 02:50:28 -0400170 mouse button 1 click or return key sets var font_size.
terryjreedy9a09c662017-07-13 23:53:30 -0400171
Terry Jan Reedy1daeb252017-07-24 02:50:28 -0400172 Bold_toggle: clicking the box toggles var font_bold.
terryjreedy9a09c662017-07-13 23:53:30 -0400173
Terry Jan Reedy1daeb252017-07-24 02:50:28 -0400174 Changing any of the font vars invokes var_changed_font, which
175 adds all 3 font options to changes and calls set_samples.
176 Set_samples applies a new font constructed from the font vars to
177 font_sample and to highlight_sample on the hightlight page.
Terry Jan Reedy5aa3bf02017-07-23 14:18:27 -0400178
179 Tabs: Enable users to change spaces entered for indent tabs.
180 Changing indent_scale value with the mouse sets Var space_num,
181 which invokes var_changed_space_num, which adds an entry to
Terry Jan Reedy1daeb252017-07-24 02:50:28 -0400182 changes. Load_tab_cfg initializes space_num to default.
terryjreedy9a09c662017-07-13 23:53:30 -0400183
184 Widget Structure: (*) widgets bound to self
Terry Jan Reedy5aa3bf02017-07-23 14:18:27 -0400185 frame (of tab_pages)
terryjreedy9a09c662017-07-13 23:53:30 -0400186 frame_font: LabelFrame
187 frame_font_name: Frame
188 font_name_title: Label
Terry Jan Reedy5aa3bf02017-07-23 14:18:27 -0400189 (*)fontlist: ListBox - font_name
terryjreedy9a09c662017-07-13 23:53:30 -0400190 scroll_font: Scrollbar
191 frame_font_param: Frame
192 font_size_title: Label
Terry Jan Reedy5aa3bf02017-07-23 14:18:27 -0400193 (*)sizelist: DynOptionMenu - font_size
Terry Jan Reedy04864b42017-07-22 00:56:18 -0400194 (*)bold_toggle: Checkbutton - font_bold
terryjreedy9a09c662017-07-13 23:53:30 -0400195 frame_font_sample: Frame
196 (*)font_sample: Label
197 frame_indent: LabelFrame
Terry Jan Reedy5aa3bf02017-07-23 14:18:27 -0400198 indent_title: Label
199 (*)indent_scale: Scale - space_num
terryjreedye5bb1122017-07-05 00:54:55 -0400200 """
Terry Jan Reedy22405332014-07-30 19:24:32 -0400201 parent = self.parent
Terry Jan Reedy04864b42017-07-22 00:56:18 -0400202 self.font_name = StringVar(parent)
terryjreedy938e7382017-06-26 20:48:39 -0400203 self.font_size = StringVar(parent)
204 self.font_bold = BooleanVar(parent)
terryjreedy938e7382017-06-26 20:48:39 -0400205 self.space_num = IntVar(parent)
Terry Jan Reedy22405332014-07-30 19:24:32 -0400206
Terry Jan Reedy5aa3bf02017-07-23 14:18:27 -0400207 # Create widgets:
terryjreedy7ab33422017-07-09 19:26:32 -0400208 # body and body section frames.
terryjreedy938e7382017-06-26 20:48:39 -0400209 frame = self.tab_pages.pages['Fonts/Tabs'].frame
terryjreedy938e7382017-06-26 20:48:39 -0400210 frame_font = LabelFrame(
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400211 frame, borderwidth=2, relief=GROOVE, text=' Base Editor Font ')
terryjreedy938e7382017-06-26 20:48:39 -0400212 frame_indent = LabelFrame(
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400213 frame, borderwidth=2, relief=GROOVE, text=' Indentation Width ')
Terry Jan Reedy5aa3bf02017-07-23 14:18:27 -0400214 # frame_font.
terryjreedy938e7382017-06-26 20:48:39 -0400215 frame_font_name = Frame(frame_font)
216 frame_font_param = Frame(frame_font)
217 font_name_title = Label(
218 frame_font_name, justify=LEFT, text='Font Face :')
Terry Jan Reedy5aa3bf02017-07-23 14:18:27 -0400219 self.fontlist = Listbox(frame_font_name, height=5,
220 takefocus=FALSE, exportselection=FALSE)
terryjreedy953e5272017-07-11 02:16:41 -0400221 self.fontlist.bind('<ButtonRelease-1>', self.on_fontlist_select)
222 self.fontlist.bind('<KeyRelease-Up>', self.on_fontlist_select)
223 self.fontlist.bind('<KeyRelease-Down>', self.on_fontlist_select)
terryjreedy938e7382017-06-26 20:48:39 -0400224 scroll_font = Scrollbar(frame_font_name)
terryjreedy7ab33422017-07-09 19:26:32 -0400225 scroll_font.config(command=self.fontlist.yview)
226 self.fontlist.config(yscrollcommand=scroll_font.set)
terryjreedy938e7382017-06-26 20:48:39 -0400227 font_size_title = Label(frame_font_param, text='Size :')
Terry Jan Reedy1daeb252017-07-24 02:50:28 -0400228 self.sizelist = DynOptionMenu(frame_font_param, self.font_size, None)
Terry Jan Reedy04864b42017-07-22 00:56:18 -0400229 self.bold_toggle = Checkbutton(
Terry Jan Reedy1daeb252017-07-24 02:50:28 -0400230 frame_font_param, variable=self.font_bold,
231 onvalue=1, offvalue=0, text='Bold')
terryjreedy938e7382017-06-26 20:48:39 -0400232 frame_font_sample = Frame(frame_font, relief=SOLID, borderwidth=1)
Terry Jan Reedy5aa3bf02017-07-23 14:18:27 -0400233 temp_font = tkFont.Font(parent, ('courier', 10, 'normal'))
terryjreedy938e7382017-06-26 20:48:39 -0400234 self.font_sample = Label(
Terry Jan Reedy5aa3bf02017-07-23 14:18:27 -0400235 frame_font_sample, justify=LEFT, font=temp_font,
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400236 text='AaBbCcDdEe\nFfGgHhIiJjK\n1234567890\n#:+=(){}[]')
Terry Jan Reedy5aa3bf02017-07-23 14:18:27 -0400237 # frame_indent.
238 indent_title = Label(
239 frame_indent, justify=LEFT,
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400240 text='Python Standard: 4 Spaces!')
Terry Jan Reedy5aa3bf02017-07-23 14:18:27 -0400241 self.indent_scale = Scale(
242 frame_indent, variable=self.space_num,
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400243 orient='horizontal', tickinterval=2, from_=2, to=16)
Terry Jan Reedy22405332014-07-30 19:24:32 -0400244
Terry Jan Reedy5aa3bf02017-07-23 14:18:27 -0400245 # Pack widgets:
246 # body.
terryjreedy938e7382017-06-26 20:48:39 -0400247 frame_font.pack(side=LEFT, padx=5, pady=5, expand=TRUE, fill=BOTH)
248 frame_indent.pack(side=LEFT, padx=5, pady=5, fill=Y)
Terry Jan Reedy5aa3bf02017-07-23 14:18:27 -0400249 # frame_font.
terryjreedy938e7382017-06-26 20:48:39 -0400250 frame_font_name.pack(side=TOP, padx=5, pady=5, fill=X)
251 frame_font_param.pack(side=TOP, padx=5, pady=5, fill=X)
252 font_name_title.pack(side=TOP, anchor=W)
terryjreedy7ab33422017-07-09 19:26:32 -0400253 self.fontlist.pack(side=LEFT, expand=TRUE, fill=X)
terryjreedy938e7382017-06-26 20:48:39 -0400254 scroll_font.pack(side=LEFT, fill=Y)
255 font_size_title.pack(side=LEFT, anchor=W)
Terry Jan Reedy5aa3bf02017-07-23 14:18:27 -0400256 self.sizelist.pack(side=LEFT, anchor=W)
Terry Jan Reedy04864b42017-07-22 00:56:18 -0400257 self.bold_toggle.pack(side=LEFT, anchor=W, padx=20)
terryjreedy938e7382017-06-26 20:48:39 -0400258 frame_font_sample.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH)
259 self.font_sample.pack(expand=TRUE, fill=BOTH)
Terry Jan Reedy5aa3bf02017-07-23 14:18:27 -0400260 # frame_indent.
261 frame_indent.pack(side=TOP, fill=X)
262 indent_title.pack(side=TOP, anchor=W, padx=5)
263 self.indent_scale.pack(side=TOP, padx=5, fill=X)
terryjreedy7ab33422017-07-09 19:26:32 -0400264
Steven M. Gava952d0a52001-08-03 04:43:44 +0000265 return frame
266
Terry Jan Reedy1daeb252017-07-24 02:50:28 -0400267 def load_font_cfg(self):
268 """Load current configuration settings for the font options.
269
270 Retrieve current font with idleConf.GetFont and font families
271 from tk. Setup fontlist and set font_name. Setup sizelist,
272 which sets font_size. Set font_bold. Setting font variables
273 calls set_samples (thrice).
274 """
275 configured_font = idleConf.GetFont(self, 'main', 'EditorWindow')
276 font_name = configured_font[0].lower()
277 font_size = configured_font[1]
278 font_bold = configured_font[2]=='bold'
279
280 # Set editor font selection list and font_name.
281 fonts = list(tkFont.families(self))
282 fonts.sort()
283 for font in fonts:
284 self.fontlist.insert(END, font)
285 self.font_name.set(font_name)
286 lc_fonts = [s.lower() for s in fonts]
287 try:
288 current_font_index = lc_fonts.index(font_name)
289 self.fontlist.see(current_font_index)
290 self.fontlist.select_set(current_font_index)
291 self.fontlist.select_anchor(current_font_index)
292 self.fontlist.activate(current_font_index)
293 except ValueError:
294 pass
295 # Set font size dropdown.
296 self.sizelist.SetMenu(('7', '8', '9', '10', '11', '12', '13', '14',
297 '16', '18', '20', '22', '25', '29', '34', '40'),
298 font_size)
299 # Set font weight.
300 self.font_bold.set(font_bold)
301
302 def on_fontlist_select(self, event):
303 """Handle selecting a font from the list.
304
305 Event can result from either mouse click or Up or Down key.
306 Set font_name and example displays to selection.
307 """
308 font = self.fontlist.get(
309 ACTIVE if event.type.name == 'KeyRelease' else ANCHOR)
310 self.font_name.set(font.lower())
311
312 def var_changed_font(self, *params):
313 """Store changes to font attributes.
314
315 When one font attribute changes, save them all, as they are
316 not independent from each other. In particular, when we are
317 overriding the default font, we need to write out everything.
318 """
319 value = self.font_name.get()
320 changes.add_option('main', 'EditorWindow', 'font', value)
321 value = self.font_size.get()
322 changes.add_option('main', 'EditorWindow', 'font-size', value)
323 value = self.font_bold.get()
324 changes.add_option('main', 'EditorWindow', 'font-bold', value)
325 self.set_samples()
326
327 def set_samples(self, event=None):
328 """Update update both screen samples with the font settings.
329
330 Called on font initialization and change events.
331 Accesses font_name, font_size, and font_bold Variables.
332 Updates font_sample and hightlight page highlight_sample.
333 """
334 font_name = self.font_name.get()
335 font_weight = tkFont.BOLD if self.font_bold.get() else tkFont.NORMAL
336 new_font = (font_name, self.font_size.get(), font_weight)
337 self.font_sample['font'] = new_font
338 self.highlight_sample['font'] = new_font
339
340 def load_tab_cfg(self):
341 """Load current configuration settings for the tab options.
342
343 Attributes updated:
344 space_num: Set to value from idleConf.
345 """
346 # Set indent sizes.
347 space_num = idleConf.GetOption(
348 'main', 'Indent', 'num-spaces', default=4, type='int')
349 self.space_num.set(space_num)
350
351 def var_changed_space_num(self, *params):
352 "Store change to indentation size."
353 value = self.space_num.get()
354 changes.add_option('main', 'Indent', 'num-spaces', value)
355
356
terryjreedy938e7382017-06-26 20:48:39 -0400357 def create_page_highlight(self):
terryjreedye5bb1122017-07-05 00:54:55 -0400358 """Return frame of widgets for Highlighting tab.
359
terryjreedy9a09c662017-07-13 23:53:30 -0400360 Tk Variables:
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -0400361 color: Color of selected target.
terryjreedye5bb1122017-07-05 00:54:55 -0400362 builtin_theme: Menu variable for built-in theme.
363 custom_theme: Menu variable for custom theme.
364 fg_bg_toggle: Toggle for foreground/background color.
terryjreedy9a09c662017-07-13 23:53:30 -0400365 Note: this has no callback.
terryjreedye5bb1122017-07-05 00:54:55 -0400366 is_builtin_theme: Selector for built-in or custom theme.
367 highlight_target: Menu variable for the highlight tag target.
terryjreedy9a09c662017-07-13 23:53:30 -0400368
369 Instance Data Attributes:
370 theme_elements: Dictionary of tags for text highlighting.
371 The key is the display name and the value is a tuple of
372 (tag name, display sort order).
373
374 Methods [attachment]:
375 load_theme_cfg: Load current highlight colors.
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -0400376 get_color: Invoke colorchooser [button_set_color].
377 set_color_sample_binding: Call set_color_sample [fg_bg_toggle].
terryjreedy9a09c662017-07-13 23:53:30 -0400378 set_highlight_target: set fg_bg_toggle, set_color_sample().
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -0400379 set_color_sample: Set frame background to target.
380 on_new_color_set: Set new color and add option.
terryjreedy9a09c662017-07-13 23:53:30 -0400381 paint_theme_sample: Recolor sample.
382 get_new_theme_name: Get from popup.
383 create_new_theme: Combine theme with changes and save.
384 save_as_new_theme: Save [button_save_custom_theme].
385 set_theme_type: Command for [is_builtin_theme].
386 delete_custom_theme: Ativate default [button_delete_custom_theme].
387 save_new_theme: Save to userCfg['theme'] (is function).
388
389 Widget Structure: (*) widgets bound to self
390 frame
391 frame_custom: LabelFrame
Terry Jan Reedy04864b42017-07-22 00:56:18 -0400392 (*)highlight_sample: Text
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -0400393 (*)frame_color_set: Frame
394 button_set_color: Button
terryjreedy9a09c662017-07-13 23:53:30 -0400395 (*)opt_menu_highlight_target: DynOptionMenu - highlight_target
396 frame_fg_bg_toggle: Frame
397 (*)radio_fg: Radiobutton - fg_bg_toggle
398 (*)radio_bg: Radiobutton - fg_bg_toggle
399 button_save_custom_theme: Button
400 frame_theme: LabelFrame
401 theme_type_title: Label
402 (*)radio_theme_builtin: Radiobutton - is_builtin_theme
403 (*)radio_theme_custom: Radiobutton - is_builtin_theme
404 (*)opt_menu_theme_builtin: DynOptionMenu - builtin_theme
405 (*)opt_menu_theme_custom: DynOptionMenu - custom_theme
406 (*)button_delete_custom_theme: Button
407 (*)new_custom_theme: Label
terryjreedye5bb1122017-07-05 00:54:55 -0400408 """
terryjreedy9a09c662017-07-13 23:53:30 -0400409 self.theme_elements={
410 'Normal Text': ('normal', '00'),
411 'Python Keywords': ('keyword', '01'),
412 'Python Definitions': ('definition', '02'),
413 'Python Builtins': ('builtin', '03'),
414 'Python Comments': ('comment', '04'),
415 'Python Strings': ('string', '05'),
416 'Selected Text': ('hilite', '06'),
417 'Found Text': ('hit', '07'),
418 'Cursor': ('cursor', '08'),
419 'Editor Breakpoint': ('break', '09'),
420 'Shell Normal Text': ('console', '10'),
421 'Shell Error Text': ('error', '11'),
422 'Shell Stdout Text': ('stdout', '12'),
423 'Shell Stderr Text': ('stderr', '13'),
424 }
Terry Jan Reedy22405332014-07-30 19:24:32 -0400425 parent = self.parent
terryjreedy938e7382017-06-26 20:48:39 -0400426 self.builtin_theme = StringVar(parent)
427 self.custom_theme = StringVar(parent)
428 self.fg_bg_toggle = BooleanVar(parent)
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -0400429 self.color = StringVar(parent)
terryjreedy938e7382017-06-26 20:48:39 -0400430 self.is_builtin_theme = BooleanVar(parent)
431 self.highlight_target = StringVar(parent)
Terry Jan Reedy22405332014-07-30 19:24:32 -0400432
Steven M. Gava952d0a52001-08-03 04:43:44 +0000433 ##widget creation
434 #body frame
terryjreedy938e7382017-06-26 20:48:39 -0400435 frame = self.tab_pages.pages['Highlighting'].frame
Steven M. Gava952d0a52001-08-03 04:43:44 +0000436 #body section frames
terryjreedy938e7382017-06-26 20:48:39 -0400437 frame_custom = LabelFrame(frame, borderwidth=2, relief=GROOVE,
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400438 text=' Custom Highlighting ')
terryjreedy938e7382017-06-26 20:48:39 -0400439 frame_theme = LabelFrame(frame, borderwidth=2, relief=GROOVE,
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400440 text=' Highlighting Theme ')
terryjreedy938e7382017-06-26 20:48:39 -0400441 #frame_custom
Terry Jan Reedy04864b42017-07-22 00:56:18 -0400442 self.highlight_sample=Text(
terryjreedy938e7382017-06-26 20:48:39 -0400443 frame_custom, relief=SOLID, borderwidth=1,
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400444 font=('courier', 12, ''), cursor='hand2', width=21, height=11,
445 takefocus=FALSE, highlightthickness=0, wrap=NONE)
Terry Jan Reedy04864b42017-07-22 00:56:18 -0400446 text=self.highlight_sample
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400447 text.bind('<Double-Button-1>', lambda e: 'break')
448 text.bind('<B1-Motion>', lambda e: 'break')
terryjreedy938e7382017-06-26 20:48:39 -0400449 text_and_tags=(
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400450 ('#you can click here', 'comment'), ('\n', 'normal'),
451 ('#to choose items', 'comment'), ('\n', 'normal'),
452 ('def', 'keyword'), (' ', 'normal'),
453 ('func', 'definition'), ('(param):\n ', 'normal'),
454 ('"""string"""', 'string'), ('\n var0 = ', 'normal'),
455 ("'string'", 'string'), ('\n var1 = ', 'normal'),
456 ("'selected'", 'hilite'), ('\n var2 = ', 'normal'),
457 ("'found'", 'hit'), ('\n var3 = ', 'normal'),
458 ('list', 'builtin'), ('(', 'normal'),
Terry Jan Reedya8aa4d52015-10-02 22:12:17 -0400459 ('None', 'keyword'), (')\n', 'normal'),
460 (' breakpoint("line")', 'break'), ('\n\n', 'normal'),
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400461 (' error ', 'error'), (' ', 'normal'),
462 ('cursor |', 'cursor'), ('\n ', 'normal'),
463 ('shell', 'console'), (' ', 'normal'),
464 ('stdout', 'stdout'), (' ', 'normal'),
465 ('stderr', 'stderr'), ('\n', 'normal'))
terryjreedy938e7382017-06-26 20:48:39 -0400466 for texttag in text_and_tags:
467 text.insert(END, texttag[0], texttag[1])
468 for element in self.theme_elements:
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400469 def tem(event, elem=element):
terryjreedy938e7382017-06-26 20:48:39 -0400470 event.widget.winfo_toplevel().highlight_target.set(elem)
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400471 text.tag_bind(
terryjreedy938e7382017-06-26 20:48:39 -0400472 self.theme_elements[element][0], '<ButtonPress-1>', tem)
Steven M. Gavae16d94b2001-11-03 05:07:28 +0000473 text.config(state=DISABLED)
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -0400474 self.frame_color_set = Frame(frame_custom, relief=SOLID, borderwidth=1)
terryjreedy938e7382017-06-26 20:48:39 -0400475 frame_fg_bg_toggle = Frame(frame_custom)
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -0400476 button_set_color = Button(
477 self.frame_color_set, text='Choose Color for :',
478 command=self.get_color, highlightthickness=0)
terryjreedy938e7382017-06-26 20:48:39 -0400479 self.opt_menu_highlight_target = DynOptionMenu(
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -0400480 self.frame_color_set, self.highlight_target, None,
terryjreedy938e7382017-06-26 20:48:39 -0400481 highlightthickness=0) #, command=self.set_highlight_targetBinding
482 self.radio_fg = Radiobutton(
483 frame_fg_bg_toggle, variable=self.fg_bg_toggle, value=1,
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -0400484 text='Foreground', command=self.set_color_sample_binding)
terryjreedy938e7382017-06-26 20:48:39 -0400485 self.radio_bg=Radiobutton(
486 frame_fg_bg_toggle, variable=self.fg_bg_toggle, value=0,
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -0400487 text='Background', command=self.set_color_sample_binding)
terryjreedy938e7382017-06-26 20:48:39 -0400488 self.fg_bg_toggle.set(1)
489 button_save_custom_theme = Button(
490 frame_custom, text='Save as New Custom Theme',
491 command=self.save_as_new_theme)
492 #frame_theme
493 theme_type_title = Label(frame_theme, text='Select : ')
494 self.radio_theme_builtin = Radiobutton(
495 frame_theme, variable=self.is_builtin_theme, value=1,
496 command=self.set_theme_type, text='a Built-in Theme')
497 self.radio_theme_custom = Radiobutton(
498 frame_theme, variable=self.is_builtin_theme, value=0,
499 command=self.set_theme_type, text='a Custom Theme')
500 self.opt_menu_theme_builtin = DynOptionMenu(
501 frame_theme, self.builtin_theme, None, command=None)
502 self.opt_menu_theme_custom=DynOptionMenu(
503 frame_theme, self.custom_theme, None, command=None)
504 self.button_delete_custom_theme=Button(
505 frame_theme, text='Delete Custom Theme',
506 command=self.delete_custom_theme)
507 self.new_custom_theme = Label(frame_theme, bd=2)
Terry Jan Reedy22405332014-07-30 19:24:32 -0400508
Steven M. Gava952d0a52001-08-03 04:43:44 +0000509 ##widget packing
510 #body
terryjreedy938e7382017-06-26 20:48:39 -0400511 frame_custom.pack(side=LEFT, padx=5, pady=5, expand=TRUE, fill=BOTH)
512 frame_theme.pack(side=LEFT, padx=5, pady=5, fill=Y)
513 #frame_custom
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -0400514 self.frame_color_set.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=X)
terryjreedy938e7382017-06-26 20:48:39 -0400515 frame_fg_bg_toggle.pack(side=TOP, padx=5, pady=0)
Terry Jan Reedy04864b42017-07-22 00:56:18 -0400516 self.highlight_sample.pack(
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400517 side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH)
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -0400518 button_set_color.pack(side=TOP, expand=TRUE, fill=X, padx=8, pady=4)
terryjreedy938e7382017-06-26 20:48:39 -0400519 self.opt_menu_highlight_target.pack(
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400520 side=TOP, expand=TRUE, fill=X, padx=8, pady=3)
terryjreedy938e7382017-06-26 20:48:39 -0400521 self.radio_fg.pack(side=LEFT, anchor=E)
522 self.radio_bg.pack(side=RIGHT, anchor=W)
523 button_save_custom_theme.pack(side=BOTTOM, fill=X, padx=5, pady=5)
524 #frame_theme
525 theme_type_title.pack(side=TOP, anchor=W, padx=5, pady=5)
526 self.radio_theme_builtin.pack(side=TOP, anchor=W, padx=5)
527 self.radio_theme_custom.pack(side=TOP, anchor=W, padx=5, pady=2)
528 self.opt_menu_theme_builtin.pack(side=TOP, fill=X, padx=5, pady=5)
529 self.opt_menu_theme_custom.pack(side=TOP, fill=X, anchor=W, padx=5, pady=5)
530 self.button_delete_custom_theme.pack(side=TOP, fill=X, padx=5, pady=5)
Terry Jan Reedyd0c0f002015-11-12 15:02:57 -0500531 self.new_custom_theme.pack(side=TOP, fill=X, pady=5)
Steven M. Gava952d0a52001-08-03 04:43:44 +0000532 return frame
533
terryjreedy938e7382017-06-26 20:48:39 -0400534 def create_page_keys(self):
terryjreedye5bb1122017-07-05 00:54:55 -0400535 """Return frame of widgets for Keys tab.
536
terryjreedy9a09c662017-07-13 23:53:30 -0400537 Tk Variables:
terryjreedye5bb1122017-07-05 00:54:55 -0400538 builtin_keys: Menu variable for built-in keybindings.
539 custom_keys: Menu variable for custom keybindings.
540 are_keys_builtin: Selector for built-in or custom keybindings.
541 keybinding: Action/key bindings.
terryjreedy9a09c662017-07-13 23:53:30 -0400542
543 Methods:
544 load_key_config: Set table.
545 load_keys_list: Reload active set.
546 keybinding_selected: Bound to list_bindings button release.
547 get_new_keys: Command for button_new_keys.
548 get_new_keys_name: Call popup.
549 create_new_key_set: Combine active keyset and changes.
550 set_keys_type: Command for are_keys_builtin.
551 delete_custom_keys: Command for button_delete_custom_keys.
552 save_as_new_key_set: Command for button_save_custom_keys.
553 save_new_key_set: Save to idleConf.userCfg['keys'] (is function).
554 deactivate_current_config: Remove keys bindings in editors.
555
556 Widget Structure: (*) widgets bound to self
557 frame
558 frame_custom: LabelFrame
559 frame_target: Frame
560 target_title: Label
561 scroll_target_y: Scrollbar
562 scroll_target_x: Scrollbar
563 (*)list_bindings: ListBox
564 (*)button_new_keys: Button
565 frame_key_sets: LabelFrame
566 frames[0]: Frame
567 (*)radio_keys_builtin: Radiobutton - are_keys_builtin
568 (*)radio_keys_custom: Radiobutton - are_keys_builtin
569 (*)opt_menu_keys_builtin: DynOptionMenu - builtin_keys
570 (*)opt_menu_keys_custom: DynOptionMenu - custom_keys
571 (*)new_custom_keys: Label
572 frames[1]: Frame
573 (*)button_delete_custom_keys: Button
574 button_save_custom_keys: Button
terryjreedye5bb1122017-07-05 00:54:55 -0400575 """
Terry Jan Reedy22405332014-07-30 19:24:32 -0400576 parent = self.parent
terryjreedy938e7382017-06-26 20:48:39 -0400577 self.builtin_keys = StringVar(parent)
578 self.custom_keys = StringVar(parent)
579 self.are_keys_builtin = BooleanVar(parent)
580 self.keybinding = StringVar(parent)
Terry Jan Reedy22405332014-07-30 19:24:32 -0400581
Steven M. Gava60fc7072001-08-04 13:58:22 +0000582 ##widget creation
583 #body frame
terryjreedy938e7382017-06-26 20:48:39 -0400584 frame = self.tab_pages.pages['Keys'].frame
Steven M. Gava60fc7072001-08-04 13:58:22 +0000585 #body section frames
terryjreedy938e7382017-06-26 20:48:39 -0400586 frame_custom = LabelFrame(
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400587 frame, borderwidth=2, relief=GROOVE,
588 text=' Custom Key Bindings ')
terryjreedy938e7382017-06-26 20:48:39 -0400589 frame_key_sets = LabelFrame(
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400590 frame, borderwidth=2, relief=GROOVE, text=' Key Set ')
terryjreedy938e7382017-06-26 20:48:39 -0400591 #frame_custom
592 frame_target = Frame(frame_custom)
593 target_title = Label(frame_target, text='Action - Key(s)')
594 scroll_target_y = Scrollbar(frame_target)
595 scroll_target_x = Scrollbar(frame_target, orient=HORIZONTAL)
596 self.list_bindings = Listbox(
597 frame_target, takefocus=FALSE, exportselection=FALSE)
598 self.list_bindings.bind('<ButtonRelease-1>', self.keybinding_selected)
599 scroll_target_y.config(command=self.list_bindings.yview)
600 scroll_target_x.config(command=self.list_bindings.xview)
601 self.list_bindings.config(yscrollcommand=scroll_target_y.set)
602 self.list_bindings.config(xscrollcommand=scroll_target_x.set)
603 self.button_new_keys = Button(
604 frame_custom, text='Get New Keys for Selection',
605 command=self.get_new_keys, state=DISABLED)
606 #frame_key_sets
607 frames = [Frame(frame_key_sets, padx=2, pady=2, borderwidth=0)
Christian Heimes9a371592007-12-28 14:08:13 +0000608 for i in range(2)]
terryjreedy938e7382017-06-26 20:48:39 -0400609 self.radio_keys_builtin = Radiobutton(
610 frames[0], variable=self.are_keys_builtin, value=1,
611 command=self.set_keys_type, text='Use a Built-in Key Set')
612 self.radio_keys_custom = Radiobutton(
613 frames[0], variable=self.are_keys_builtin, value=0,
614 command=self.set_keys_type, text='Use a Custom Key Set')
615 self.opt_menu_keys_builtin = DynOptionMenu(
616 frames[0], self.builtin_keys, None, command=None)
617 self.opt_menu_keys_custom = DynOptionMenu(
618 frames[0], self.custom_keys, None, command=None)
619 self.button_delete_custom_keys = Button(
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400620 frames[1], text='Delete Custom Key Set',
terryjreedy938e7382017-06-26 20:48:39 -0400621 command=self.delete_custom_keys)
622 button_save_custom_keys = Button(
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400623 frames[1], text='Save as New Custom Key Set',
terryjreedy938e7382017-06-26 20:48:39 -0400624 command=self.save_as_new_key_set)
Terry Jan Reedy9bdb1ed2016-07-10 13:46:34 -0400625 self.new_custom_keys = Label(frames[0], bd=2)
Terry Jan Reedy22405332014-07-30 19:24:32 -0400626
Steven M. Gava60fc7072001-08-04 13:58:22 +0000627 ##widget packing
628 #body
terryjreedy938e7382017-06-26 20:48:39 -0400629 frame_custom.pack(side=BOTTOM, padx=5, pady=5, expand=TRUE, fill=BOTH)
630 frame_key_sets.pack(side=BOTTOM, padx=5, pady=5, fill=BOTH)
631 #frame_custom
632 self.button_new_keys.pack(side=BOTTOM, fill=X, padx=5, pady=5)
633 frame_target.pack(side=LEFT, padx=5, pady=5, expand=TRUE, fill=BOTH)
Steven M. Gavafacfc092002-01-19 00:29:54 +0000634 #frame target
terryjreedy938e7382017-06-26 20:48:39 -0400635 frame_target.columnconfigure(0, weight=1)
636 frame_target.rowconfigure(1, weight=1)
637 target_title.grid(row=0, column=0, columnspan=2, sticky=W)
638 self.list_bindings.grid(row=1, column=0, sticky=NSEW)
639 scroll_target_y.grid(row=1, column=1, sticky=NS)
640 scroll_target_x.grid(row=2, column=0, sticky=EW)
641 #frame_key_sets
642 self.radio_keys_builtin.grid(row=0, column=0, sticky=W+NS)
643 self.radio_keys_custom.grid(row=1, column=0, sticky=W+NS)
644 self.opt_menu_keys_builtin.grid(row=0, column=1, sticky=NSEW)
645 self.opt_menu_keys_custom.grid(row=1, column=1, sticky=NSEW)
Terry Jan Reedy9bdb1ed2016-07-10 13:46:34 -0400646 self.new_custom_keys.grid(row=0, column=2, sticky=NSEW, padx=5, pady=5)
terryjreedy938e7382017-06-26 20:48:39 -0400647 self.button_delete_custom_keys.pack(side=LEFT, fill=X, expand=True, padx=2)
648 button_save_custom_keys.pack(side=LEFT, fill=X, expand=True, padx=2)
Christian Heimes9a371592007-12-28 14:08:13 +0000649 frames[0].pack(side=TOP, fill=BOTH, expand=True)
650 frames[1].pack(side=TOP, fill=X, expand=True, pady=2)
Steven M. Gava952d0a52001-08-03 04:43:44 +0000651 return frame
652
terryjreedy938e7382017-06-26 20:48:39 -0400653 def create_page_general(self):
terryjreedye5bb1122017-07-05 00:54:55 -0400654 """Return frame of widgets for General tab.
655
terryjreedy9a09c662017-07-13 23:53:30 -0400656 Tk Variables:
terryjreedye5bb1122017-07-05 00:54:55 -0400657 win_width: Initial window width in characters.
658 win_height: Initial window height in characters.
659 startup_edit: Selector for opening in editor or shell mode.
660 autosave: Selector for save prompt popup when using Run.
terryjreedy9a09c662017-07-13 23:53:30 -0400661
662 Methods:
663 load_general_config:
664 help_source_selected: Bound to list_help button release.
665 set_helplist_button_states: Toggle based on list.
666 helplist_item_edit: Command for button_helplist_edit.
667 helplist_item_add: Command for button_helplist_add.
668 helplist_item_remove: Command for button_helplist_remove.
669 update_user_help_changed_items: Fill in changes.
670
671 Widget Structure: (*) widgets bound to self
672 frame
673 frame_run: LabelFrame
674 startup_title: Label
675 (*)radio_startup_edit: Radiobutton - startup_edit
676 (*)radio_startup_shell: Radiobutton - startup_edit
677 frame_save: LabelFrame
678 run_save_title: Label
679 (*)radio_save_ask: Radiobutton - autosave
680 (*)radio_save_auto: Radiobutton - autosave
681 frame_win_size: LabelFrame
682 win_size_title: Label
683 win_width_title: Label
684 (*)entry_win_width: Entry - win_width
685 win_height_title: Label
686 (*)entry_win_height: Entry - win_height
687 frame_help: LabelFrame
688 frame_helplist: Frame
689 frame_helplist_buttons: Frame
690 (*)button_helplist_edit
691 (*)button_helplist_add
692 (*)button_helplist_remove
693 scroll_helplist: Scrollbar
694 (*)list_help: ListBox
terryjreedye5bb1122017-07-05 00:54:55 -0400695 """
Terry Jan Reedy22405332014-07-30 19:24:32 -0400696 parent = self.parent
terryjreedy938e7382017-06-26 20:48:39 -0400697 self.win_width = StringVar(parent)
698 self.win_height = StringVar(parent)
699 self.startup_edit = IntVar(parent)
700 self.autosave = IntVar(parent)
Terry Jan Reedy22405332014-07-30 19:24:32 -0400701
Steven M. Gava230e5782001-08-07 03:28:25 +0000702 #widget creation
703 #body
terryjreedy938e7382017-06-26 20:48:39 -0400704 frame = self.tab_pages.pages['General'].frame
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000705 #body section frames
terryjreedy938e7382017-06-26 20:48:39 -0400706 frame_run = LabelFrame(frame, borderwidth=2, relief=GROOVE,
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400707 text=' Startup Preferences ')
terryjreedy938e7382017-06-26 20:48:39 -0400708 frame_save = LabelFrame(frame, borderwidth=2, relief=GROOVE,
709 text=' autosave Preferences ')
710 frame_win_size = Frame(frame, borderwidth=2, relief=GROOVE)
711 frame_help = LabelFrame(frame, borderwidth=2, relief=GROOVE,
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400712 text=' Additional Help Sources ')
terryjreedy938e7382017-06-26 20:48:39 -0400713 #frame_run
714 startup_title = Label(frame_run, text='At Startup')
715 self.radio_startup_edit = Radiobutton(
716 frame_run, variable=self.startup_edit, value=1,
Terry Jan Reedyf46b7822016-11-07 17:15:01 -0500717 text="Open Edit Window")
terryjreedy938e7382017-06-26 20:48:39 -0400718 self.radio_startup_shell = Radiobutton(
719 frame_run, variable=self.startup_edit, value=0,
Terry Jan Reedyf46b7822016-11-07 17:15:01 -0500720 text='Open Shell Window')
terryjreedy938e7382017-06-26 20:48:39 -0400721 #frame_save
722 run_save_title = Label(frame_save, text='At Start of Run (F5) ')
723 self.radio_save_ask = Radiobutton(
724 frame_save, variable=self.autosave, value=0,
Terry Jan Reedyf46b7822016-11-07 17:15:01 -0500725 text="Prompt to Save")
terryjreedy938e7382017-06-26 20:48:39 -0400726 self.radio_save_auto = Radiobutton(
727 frame_save, variable=self.autosave, value=1,
Terry Jan Reedyf46b7822016-11-07 17:15:01 -0500728 text='No Prompt')
terryjreedy938e7382017-06-26 20:48:39 -0400729 #frame_win_size
730 win_size_title = Label(
731 frame_win_size, text='Initial Window Size (in characters)')
732 win_width_title = Label(frame_win_size, text='Width')
733 self.entry_win_width = Entry(
734 frame_win_size, textvariable=self.win_width, width=3)
735 win_height_title = Label(frame_win_size, text='Height')
736 self.entry_win_height = Entry(
737 frame_win_size, textvariable=self.win_height, width=3)
738 #frame_help
739 frame_helplist = Frame(frame_help)
740 frame_helplist_buttons = Frame(frame_helplist)
741 scroll_helplist = Scrollbar(frame_helplist)
742 self.list_help = Listbox(
743 frame_helplist, height=5, takefocus=FALSE,
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000744 exportselection=FALSE)
terryjreedy938e7382017-06-26 20:48:39 -0400745 scroll_helplist.config(command=self.list_help.yview)
746 self.list_help.config(yscrollcommand=scroll_helplist.set)
747 self.list_help.bind('<ButtonRelease-1>', self.help_source_selected)
748 self.button_helplist_edit = Button(
749 frame_helplist_buttons, text='Edit', state=DISABLED,
750 width=8, command=self.helplist_item_edit)
751 self.button_helplist_add = Button(
752 frame_helplist_buttons, text='Add',
753 width=8, command=self.helplist_item_add)
754 self.button_helplist_remove = Button(
755 frame_helplist_buttons, text='Remove', state=DISABLED,
756 width=8, command=self.helplist_item_remove)
Terry Jan Reedy22405332014-07-30 19:24:32 -0400757
Steven M. Gava230e5782001-08-07 03:28:25 +0000758 #widget packing
759 #body
terryjreedy938e7382017-06-26 20:48:39 -0400760 frame_run.pack(side=TOP, padx=5, pady=5, fill=X)
761 frame_save.pack(side=TOP, padx=5, pady=5, fill=X)
762 frame_win_size.pack(side=TOP, padx=5, pady=5, fill=X)
763 frame_help.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH)
764 #frame_run
765 startup_title.pack(side=LEFT, anchor=W, padx=5, pady=5)
766 self.radio_startup_shell.pack(side=RIGHT, anchor=W, padx=5, pady=5)
767 self.radio_startup_edit.pack(side=RIGHT, anchor=W, padx=5, pady=5)
768 #frame_save
769 run_save_title.pack(side=LEFT, anchor=W, padx=5, pady=5)
770 self.radio_save_auto.pack(side=RIGHT, anchor=W, padx=5, pady=5)
771 self.radio_save_ask.pack(side=RIGHT, anchor=W, padx=5, pady=5)
772 #frame_win_size
773 win_size_title.pack(side=LEFT, anchor=W, padx=5, pady=5)
774 self.entry_win_height.pack(side=RIGHT, anchor=E, padx=10, pady=5)
775 win_height_title.pack(side=RIGHT, anchor=E, pady=5)
776 self.entry_win_width.pack(side=RIGHT, anchor=E, padx=10, pady=5)
777 win_width_title.pack(side=RIGHT, anchor=E, pady=5)
778 #frame_help
779 frame_helplist_buttons.pack(side=RIGHT, padx=5, pady=5, fill=Y)
780 frame_helplist.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH)
781 scroll_helplist.pack(side=RIGHT, anchor=W, fill=Y)
782 self.list_help.pack(side=LEFT, anchor=E, expand=TRUE, fill=BOTH)
783 self.button_helplist_edit.pack(side=TOP, anchor=W, pady=5)
784 self.button_helplist_add.pack(side=TOP, anchor=W)
785 self.button_helplist_remove.pack(side=TOP, anchor=W, pady=5)
Steven M. Gava952d0a52001-08-03 04:43:44 +0000786 return frame
787
terryjreedy938e7382017-06-26 20:48:39 -0400788 def attach_var_callbacks(self):
terryjreedye5bb1122017-07-05 00:54:55 -0400789 "Attach callbacks to variables that can be changed."
terryjreedy938e7382017-06-26 20:48:39 -0400790 self.font_size.trace_add('write', self.var_changed_font)
791 self.font_name.trace_add('write', self.var_changed_font)
792 self.font_bold.trace_add('write', self.var_changed_font)
793 self.space_num.trace_add('write', self.var_changed_space_num)
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -0400794 self.color.trace_add('write', self.var_changed_color)
terryjreedy938e7382017-06-26 20:48:39 -0400795 self.builtin_theme.trace_add('write', self.var_changed_builtin_theme)
796 self.custom_theme.trace_add('write', self.var_changed_custom_theme)
797 self.is_builtin_theme.trace_add('write', self.var_changed_is_builtin_theme)
798 self.highlight_target.trace_add('write', self.var_changed_highlight_target)
799 self.keybinding.trace_add('write', self.var_changed_keybinding)
800 self.builtin_keys.trace_add('write', self.var_changed_builtin_keys)
801 self.custom_keys.trace_add('write', self.var_changed_custom_keys)
802 self.are_keys_builtin.trace_add('write', self.var_changed_are_keys_builtin)
803 self.win_width.trace_add('write', self.var_changed_win_width)
804 self.win_height.trace_add('write', self.var_changed_win_height)
805 self.startup_edit.trace_add('write', self.var_changed_startup_edit)
806 self.autosave.trace_add('write', self.var_changed_autosave)
Steven M. Gava052937f2002-02-11 02:20:53 +0000807
Terry Jan Reedy6b98ce22016-05-16 22:27:28 -0400808 def remove_var_callbacks(self):
809 "Remove callbacks to prevent memory leaks."
810 for var in (
terryjreedy938e7382017-06-26 20:48:39 -0400811 self.font_size, self.font_name, self.font_bold,
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -0400812 self.space_num, self.color, self.builtin_theme,
terryjreedy938e7382017-06-26 20:48:39 -0400813 self.custom_theme, self.is_builtin_theme, self.highlight_target,
814 self.keybinding, self.builtin_keys, self.custom_keys,
815 self.are_keys_builtin, self.win_width, self.win_height,
terryjreedy8e3f73e2017-07-10 15:11:45 -0400816 self.startup_edit, self.autosave,):
Serhiy Storchaka81221742016-06-26 09:46:57 +0300817 var.trace_remove('write', var.trace_info()[0][1])
Terry Jan Reedy6b98ce22016-05-16 22:27:28 -0400818
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -0400819 def var_changed_color(self, *params):
terryjreedye5bb1122017-07-05 00:54:55 -0400820 "Process change to color choice."
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -0400821 self.on_new_color_set()
Steven M. Gava052937f2002-02-11 02:20:53 +0000822
terryjreedy938e7382017-06-26 20:48:39 -0400823 def var_changed_builtin_theme(self, *params):
terryjreedye5bb1122017-07-05 00:54:55 -0400824 """Process new builtin theme selection.
825
826 Add the changed theme's name to the changed_items and recreate
827 the sample with the values from the selected theme.
828 """
terryjreedy938e7382017-06-26 20:48:39 -0400829 old_themes = ('IDLE Classic', 'IDLE New')
830 value = self.builtin_theme.get()
831 if value not in old_themes:
832 if idleConf.GetOption('main', 'Theme', 'name') not in old_themes:
terryjreedyedc03422017-07-07 16:37:39 -0400833 changes.add_option('main', 'Theme', 'name', old_themes[0])
834 changes.add_option('main', 'Theme', 'name2', value)
Terry Jan Reedyd0c0f002015-11-12 15:02:57 -0500835 self.new_custom_theme.config(text='New theme, see Help',
836 fg='#500000')
837 else:
terryjreedyedc03422017-07-07 16:37:39 -0400838 changes.add_option('main', 'Theme', 'name', value)
839 changes.add_option('main', 'Theme', 'name2', '')
Terry Jan Reedyd0c0f002015-11-12 15:02:57 -0500840 self.new_custom_theme.config(text='', fg='black')
terryjreedy938e7382017-06-26 20:48:39 -0400841 self.paint_theme_sample()
Steven M. Gava052937f2002-02-11 02:20:53 +0000842
terryjreedy938e7382017-06-26 20:48:39 -0400843 def var_changed_custom_theme(self, *params):
terryjreedye5bb1122017-07-05 00:54:55 -0400844 """Process new custom theme selection.
845
846 If a new custom theme is selected, add the name to the
847 changed_items and apply the theme to the sample.
848 """
terryjreedy938e7382017-06-26 20:48:39 -0400849 value = self.custom_theme.get()
Steven M. Gava49745752002-02-18 01:43:11 +0000850 if value != '- no custom themes -':
terryjreedyedc03422017-07-07 16:37:39 -0400851 changes.add_option('main', 'Theme', 'name', value)
terryjreedy938e7382017-06-26 20:48:39 -0400852 self.paint_theme_sample()
Steven M. Gava052937f2002-02-11 02:20:53 +0000853
terryjreedy938e7382017-06-26 20:48:39 -0400854 def var_changed_is_builtin_theme(self, *params):
terryjreedye5bb1122017-07-05 00:54:55 -0400855 """Process toggle between builtin and custom theme.
856
857 Update the default toggle value and apply the newly
858 selected theme type.
859 """
terryjreedy938e7382017-06-26 20:48:39 -0400860 value = self.is_builtin_theme.get()
terryjreedyedc03422017-07-07 16:37:39 -0400861 changes.add_option('main', 'Theme', 'default', value)
Steven M. Gavaf31eec02002-03-05 00:25:58 +0000862 if value:
terryjreedy938e7382017-06-26 20:48:39 -0400863 self.var_changed_builtin_theme()
Steven M. Gavaf31eec02002-03-05 00:25:58 +0000864 else:
terryjreedy938e7382017-06-26 20:48:39 -0400865 self.var_changed_custom_theme()
Steven M. Gava052937f2002-02-11 02:20:53 +0000866
terryjreedy938e7382017-06-26 20:48:39 -0400867 def var_changed_highlight_target(self, *params):
terryjreedye5bb1122017-07-05 00:54:55 -0400868 "Process selection of new target tag for highlighting."
terryjreedy938e7382017-06-26 20:48:39 -0400869 self.set_highlight_target()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000870
terryjreedy938e7382017-06-26 20:48:39 -0400871 def var_changed_keybinding(self, *params):
terryjreedye5bb1122017-07-05 00:54:55 -0400872 "Store change to a keybinding."
terryjreedy938e7382017-06-26 20:48:39 -0400873 value = self.keybinding.get()
874 key_set = self.custom_keys.get()
875 event = self.list_bindings.get(ANCHOR).split()[0]
Steven M. Gavaa498af22002-02-01 01:33:36 +0000876 if idleConf.IsCoreBinding(event):
terryjreedyedc03422017-07-07 16:37:39 -0400877 changes.add_option('keys', key_set, event, value)
terryjreedye5bb1122017-07-05 00:54:55 -0400878 else: # Event is an extension binding.
terryjreedy938e7382017-06-26 20:48:39 -0400879 ext_name = idleConf.GetExtnNameForEvent(event)
880 ext_keybind_section = ext_name + '_cfgBindings'
terryjreedyedc03422017-07-07 16:37:39 -0400881 changes.add_option('extensions', ext_keybind_section, event, value)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000882
terryjreedy938e7382017-06-26 20:48:39 -0400883 def var_changed_builtin_keys(self, *params):
terryjreedye5bb1122017-07-05 00:54:55 -0400884 "Process selection of builtin key set."
terryjreedy938e7382017-06-26 20:48:39 -0400885 old_keys = (
Terry Jan Reedy9bdb1ed2016-07-10 13:46:34 -0400886 'IDLE Classic Windows',
887 'IDLE Classic Unix',
888 'IDLE Classic Mac',
889 'IDLE Classic OSX',
890 )
terryjreedy938e7382017-06-26 20:48:39 -0400891 value = self.builtin_keys.get()
892 if value not in old_keys:
893 if idleConf.GetOption('main', 'Keys', 'name') not in old_keys:
terryjreedyedc03422017-07-07 16:37:39 -0400894 changes.add_option('main', 'Keys', 'name', old_keys[0])
895 changes.add_option('main', 'Keys', 'name2', value)
Terry Jan Reedy9bdb1ed2016-07-10 13:46:34 -0400896 self.new_custom_keys.config(text='New key set, see Help',
897 fg='#500000')
898 else:
terryjreedyedc03422017-07-07 16:37:39 -0400899 changes.add_option('main', 'Keys', 'name', value)
900 changes.add_option('main', 'Keys', 'name2', '')
Terry Jan Reedy9bdb1ed2016-07-10 13:46:34 -0400901 self.new_custom_keys.config(text='', fg='black')
terryjreedy938e7382017-06-26 20:48:39 -0400902 self.load_keys_list(value)
Steven M. Gava052937f2002-02-11 02:20:53 +0000903
terryjreedy938e7382017-06-26 20:48:39 -0400904 def var_changed_custom_keys(self, *params):
terryjreedye5bb1122017-07-05 00:54:55 -0400905 "Process selection of custom key set."
terryjreedy938e7382017-06-26 20:48:39 -0400906 value = self.custom_keys.get()
Steven M. Gava49745752002-02-18 01:43:11 +0000907 if value != '- no custom keys -':
terryjreedyedc03422017-07-07 16:37:39 -0400908 changes.add_option('main', 'Keys', 'name', value)
terryjreedy938e7382017-06-26 20:48:39 -0400909 self.load_keys_list(value)
Steven M. Gava052937f2002-02-11 02:20:53 +0000910
terryjreedy938e7382017-06-26 20:48:39 -0400911 def var_changed_are_keys_builtin(self, *params):
terryjreedye5bb1122017-07-05 00:54:55 -0400912 "Process toggle between builtin key set and custom key set."
terryjreedy938e7382017-06-26 20:48:39 -0400913 value = self.are_keys_builtin.get()
terryjreedyedc03422017-07-07 16:37:39 -0400914 changes.add_option('main', 'Keys', 'default', value)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000915 if value:
terryjreedy938e7382017-06-26 20:48:39 -0400916 self.var_changed_builtin_keys()
Steven M. Gava052937f2002-02-11 02:20:53 +0000917 else:
terryjreedy938e7382017-06-26 20:48:39 -0400918 self.var_changed_custom_keys()
Steven M. Gava052937f2002-02-11 02:20:53 +0000919
terryjreedy938e7382017-06-26 20:48:39 -0400920 def var_changed_win_width(self, *params):
terryjreedye5bb1122017-07-05 00:54:55 -0400921 "Store change to window width."
terryjreedy938e7382017-06-26 20:48:39 -0400922 value = self.win_width.get()
terryjreedyedc03422017-07-07 16:37:39 -0400923 changes.add_option('main', 'EditorWindow', 'width', value)
Steven M. Gavac112cd82002-01-22 05:56:40 +0000924
terryjreedy938e7382017-06-26 20:48:39 -0400925 def var_changed_win_height(self, *params):
terryjreedye5bb1122017-07-05 00:54:55 -0400926 "Store change to window height."
terryjreedy938e7382017-06-26 20:48:39 -0400927 value = self.win_height.get()
terryjreedyedc03422017-07-07 16:37:39 -0400928 changes.add_option('main', 'EditorWindow', 'height', value)
Steven M. Gavac112cd82002-01-22 05:56:40 +0000929
terryjreedy938e7382017-06-26 20:48:39 -0400930 def var_changed_startup_edit(self, *params):
terryjreedye5bb1122017-07-05 00:54:55 -0400931 "Store change to toggle for starting IDLE in the editor or shell."
terryjreedy938e7382017-06-26 20:48:39 -0400932 value = self.startup_edit.get()
terryjreedyedc03422017-07-07 16:37:39 -0400933 changes.add_option('main', 'General', 'editor-on-startup', value)
Steven M. Gavac112cd82002-01-22 05:56:40 +0000934
terryjreedy938e7382017-06-26 20:48:39 -0400935 def var_changed_autosave(self, *params):
terryjreedye5bb1122017-07-05 00:54:55 -0400936 "Store change to autosave."
terryjreedy938e7382017-06-26 20:48:39 -0400937 value = self.autosave.get()
terryjreedyedc03422017-07-07 16:37:39 -0400938 changes.add_option('main', 'General', 'autosave', value)
Kurt B. Kaiser6c638b62003-05-26 06:23:10 +0000939
terryjreedy938e7382017-06-26 20:48:39 -0400940 def set_theme_type(self):
terryjreedy9a09c662017-07-13 23:53:30 -0400941 """Set available screen options based on builtin or custom theme.
942
943 Attributes accessed:
944 is_builtin_theme
945
946 Attributes updated:
947 opt_menu_theme_builtin
948 opt_menu_theme_custom
949 button_delete_custom_theme
950 radio_theme_custom
951
952 Called from:
953 handler for radio_theme_builtin and radio_theme_custom
954 delete_custom_theme
955 create_new_theme
956 load_theme_cfg
957 """
terryjreedy938e7382017-06-26 20:48:39 -0400958 if self.is_builtin_theme.get():
959 self.opt_menu_theme_builtin.config(state=NORMAL)
960 self.opt_menu_theme_custom.config(state=DISABLED)
961 self.button_delete_custom_theme.config(state=DISABLED)
Steven M. Gava5f28e8f2002-01-21 06:38:21 +0000962 else:
terryjreedy938e7382017-06-26 20:48:39 -0400963 self.opt_menu_theme_builtin.config(state=DISABLED)
964 self.radio_theme_custom.config(state=NORMAL)
965 self.opt_menu_theme_custom.config(state=NORMAL)
966 self.button_delete_custom_theme.config(state=NORMAL)
Steven M. Gava5f28e8f2002-01-21 06:38:21 +0000967
terryjreedy938e7382017-06-26 20:48:39 -0400968 def set_keys_type(self):
terryjreedye5bb1122017-07-05 00:54:55 -0400969 "Set available screen options based on builtin or custom key set."
terryjreedy938e7382017-06-26 20:48:39 -0400970 if self.are_keys_builtin.get():
971 self.opt_menu_keys_builtin.config(state=NORMAL)
972 self.opt_menu_keys_custom.config(state=DISABLED)
973 self.button_delete_custom_keys.config(state=DISABLED)
Steven M. Gava5f28e8f2002-01-21 06:38:21 +0000974 else:
terryjreedy938e7382017-06-26 20:48:39 -0400975 self.opt_menu_keys_builtin.config(state=DISABLED)
976 self.radio_keys_custom.config(state=NORMAL)
977 self.opt_menu_keys_custom.config(state=NORMAL)
978 self.button_delete_custom_keys.config(state=NORMAL)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000979
terryjreedy938e7382017-06-26 20:48:39 -0400980 def get_new_keys(self):
terryjreedye5bb1122017-07-05 00:54:55 -0400981 """Handle event to change key binding for selected line.
982
983 A selection of a key/binding in the list of current
984 bindings pops up a dialog to enter a new binding. If
985 the current key set is builtin and a binding has
986 changed, then a name for a custom key set needs to be
987 entered for the change to be applied.
988 """
terryjreedy938e7382017-06-26 20:48:39 -0400989 list_index = self.list_bindings.index(ANCHOR)
990 binding = self.list_bindings.get(list_index)
terryjreedye5bb1122017-07-05 00:54:55 -0400991 bind_name = binding.split()[0]
terryjreedy938e7382017-06-26 20:48:39 -0400992 if self.are_keys_builtin.get():
993 current_key_set_name = self.builtin_keys.get()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000994 else:
terryjreedy938e7382017-06-26 20:48:39 -0400995 current_key_set_name = self.custom_keys.get()
996 current_bindings = idleConf.GetCurrentKeySet()
terryjreedyedc03422017-07-07 16:37:39 -0400997 if current_key_set_name in changes['keys']: # unsaved changes
998 key_set_changes = changes['keys'][current_key_set_name]
terryjreedy938e7382017-06-26 20:48:39 -0400999 for event in key_set_changes:
1000 current_bindings[event] = key_set_changes[event].split()
1001 current_key_sequences = list(current_bindings.values())
1002 new_keys = GetKeysDialog(self, 'Get New Keys', bind_name,
1003 current_key_sequences).result
terryjreedye5bb1122017-07-05 00:54:55 -04001004 if new_keys:
1005 if self.are_keys_builtin.get(): # Current key set is a built-in.
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001006 message = ('Your changes will be saved as a new Custom Key Set.'
1007 ' Enter a name for your new Custom Key Set below.')
terryjreedy938e7382017-06-26 20:48:39 -04001008 new_keyset = self.get_new_keys_name(message)
terryjreedye5bb1122017-07-05 00:54:55 -04001009 if not new_keyset: # User cancelled custom key set creation.
terryjreedy938e7382017-06-26 20:48:39 -04001010 self.list_bindings.select_set(list_index)
1011 self.list_bindings.select_anchor(list_index)
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +00001012 return
terryjreedye5bb1122017-07-05 00:54:55 -04001013 else: # Create new custom key set based on previously active key set.
terryjreedy938e7382017-06-26 20:48:39 -04001014 self.create_new_key_set(new_keyset)
1015 self.list_bindings.delete(list_index)
1016 self.list_bindings.insert(list_index, bind_name+' - '+new_keys)
1017 self.list_bindings.select_set(list_index)
1018 self.list_bindings.select_anchor(list_index)
1019 self.keybinding.set(new_keys)
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +00001020 else:
terryjreedy938e7382017-06-26 20:48:39 -04001021 self.list_bindings.select_set(list_index)
1022 self.list_bindings.select_anchor(list_index)
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +00001023
terryjreedy938e7382017-06-26 20:48:39 -04001024 def get_new_keys_name(self, message):
terryjreedye5bb1122017-07-05 00:54:55 -04001025 "Return new key set name from query popup."
terryjreedy938e7382017-06-26 20:48:39 -04001026 used_names = (idleConf.GetSectionList('user', 'keys') +
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001027 idleConf.GetSectionList('default', 'keys'))
terryjreedy938e7382017-06-26 20:48:39 -04001028 new_keyset = SectionName(
1029 self, 'New Custom Key Set', message, used_names).result
1030 return new_keyset
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001031
terryjreedy938e7382017-06-26 20:48:39 -04001032 def save_as_new_key_set(self):
terryjreedye5bb1122017-07-05 00:54:55 -04001033 "Prompt for name of new key set and save changes using that name."
terryjreedy938e7382017-06-26 20:48:39 -04001034 new_keys_name = self.get_new_keys_name('New Key Set Name:')
1035 if new_keys_name:
1036 self.create_new_key_set(new_keys_name)
Steven M. Gava085eb1b2002-02-05 04:52:32 +00001037
terryjreedy938e7382017-06-26 20:48:39 -04001038 def keybinding_selected(self, event):
terryjreedye5bb1122017-07-05 00:54:55 -04001039 "Activate button to assign new keys to selected action."
terryjreedy938e7382017-06-26 20:48:39 -04001040 self.button_new_keys.config(state=NORMAL)
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +00001041
terryjreedy938e7382017-06-26 20:48:39 -04001042 def create_new_key_set(self, new_key_set_name):
terryjreedye5bb1122017-07-05 00:54:55 -04001043 """Create a new custom key set with the given name.
1044
1045 Create the new key set based on the previously active set
1046 with the current changes applied. Once it is saved, then
1047 activate the new key set.
1048 """
terryjreedy938e7382017-06-26 20:48:39 -04001049 if self.are_keys_builtin.get():
1050 prev_key_set_name = self.builtin_keys.get()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001051 else:
terryjreedy938e7382017-06-26 20:48:39 -04001052 prev_key_set_name = self.custom_keys.get()
1053 prev_keys = idleConf.GetCoreKeys(prev_key_set_name)
1054 new_keys = {}
terryjreedye5bb1122017-07-05 00:54:55 -04001055 for event in prev_keys: # Add key set to changed items.
1056 event_name = event[2:-2] # Trim off the angle brackets.
terryjreedy938e7382017-06-26 20:48:39 -04001057 binding = ' '.join(prev_keys[event])
1058 new_keys[event_name] = binding
terryjreedye5bb1122017-07-05 00:54:55 -04001059 # Handle any unsaved changes to prev key set.
terryjreedyedc03422017-07-07 16:37:39 -04001060 if prev_key_set_name in changes['keys']:
1061 key_set_changes = changes['keys'][prev_key_set_name]
terryjreedy938e7382017-06-26 20:48:39 -04001062 for event in key_set_changes:
1063 new_keys[event] = key_set_changes[event]
terryjreedye5bb1122017-07-05 00:54:55 -04001064 # Save the new key set.
terryjreedy938e7382017-06-26 20:48:39 -04001065 self.save_new_key_set(new_key_set_name, new_keys)
terryjreedye5bb1122017-07-05 00:54:55 -04001066 # Change GUI over to the new key set.
terryjreedy938e7382017-06-26 20:48:39 -04001067 custom_key_list = idleConf.GetSectionList('user', 'keys')
1068 custom_key_list.sort()
1069 self.opt_menu_keys_custom.SetMenu(custom_key_list, new_key_set_name)
1070 self.are_keys_builtin.set(0)
1071 self.set_keys_type()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001072
terryjreedy938e7382017-06-26 20:48:39 -04001073 def load_keys_list(self, keyset_name):
terryjreedye5bb1122017-07-05 00:54:55 -04001074 """Reload the list of action/key binding pairs for the active key set.
1075
1076 An action/key binding can be selected to change the key binding.
1077 """
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001078 reselect = 0
terryjreedy938e7382017-06-26 20:48:39 -04001079 if self.list_bindings.curselection():
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001080 reselect = 1
terryjreedy938e7382017-06-26 20:48:39 -04001081 list_index = self.list_bindings.index(ANCHOR)
1082 keyset = idleConf.GetKeySet(keyset_name)
1083 bind_names = list(keyset.keys())
1084 bind_names.sort()
1085 self.list_bindings.delete(0, END)
1086 for bind_name in bind_names:
terryjreedye5bb1122017-07-05 00:54:55 -04001087 key = ' '.join(keyset[bind_name])
1088 bind_name = bind_name[2:-2] # Trim off the angle brackets.
terryjreedyedc03422017-07-07 16:37:39 -04001089 if keyset_name in changes['keys']:
terryjreedye5bb1122017-07-05 00:54:55 -04001090 # Handle any unsaved changes to this key set.
terryjreedyedc03422017-07-07 16:37:39 -04001091 if bind_name in changes['keys'][keyset_name]:
1092 key = changes['keys'][keyset_name][bind_name]
terryjreedy938e7382017-06-26 20:48:39 -04001093 self.list_bindings.insert(END, bind_name+' - '+key)
Steven M. Gava052937f2002-02-11 02:20:53 +00001094 if reselect:
terryjreedy938e7382017-06-26 20:48:39 -04001095 self.list_bindings.see(list_index)
1096 self.list_bindings.select_set(list_index)
1097 self.list_bindings.select_anchor(list_index)
Steven M. Gava052937f2002-02-11 02:20:53 +00001098
terryjreedy938e7382017-06-26 20:48:39 -04001099 def delete_custom_keys(self):
terryjreedye5bb1122017-07-05 00:54:55 -04001100 """Handle event to delete a custom key set.
1101
1102 Applying the delete deactivates the current configuration and
1103 reverts to the default. The custom key set is permanently
1104 deleted from the config file.
1105 """
terryjreedy938e7382017-06-26 20:48:39 -04001106 keyset_name=self.custom_keys.get()
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001107 delmsg = 'Are you sure you wish to delete the key set %r ?'
1108 if not tkMessageBox.askyesno(
terryjreedy938e7382017-06-26 20:48:39 -04001109 'Delete Key Set', delmsg % keyset_name, parent=self):
Steven M. Gava49745752002-02-18 01:43:11 +00001110 return
terryjreedy938e7382017-06-26 20:48:39 -04001111 self.deactivate_current_config()
terryjreedyedc03422017-07-07 16:37:39 -04001112 # Remove key set from changes, config, and file.
terryjreedyc0179482017-07-11 19:50:10 -04001113 changes.delete_section('keys', keyset_name)
terryjreedye5bb1122017-07-05 00:54:55 -04001114 # Reload user key set list.
terryjreedy938e7382017-06-26 20:48:39 -04001115 item_list = idleConf.GetSectionList('user', 'keys')
1116 item_list.sort()
1117 if not item_list:
1118 self.radio_keys_custom.config(state=DISABLED)
1119 self.opt_menu_keys_custom.SetMenu(item_list, '- no custom keys -')
Steven M. Gava49745752002-02-18 01:43:11 +00001120 else:
terryjreedy938e7382017-06-26 20:48:39 -04001121 self.opt_menu_keys_custom.SetMenu(item_list, item_list[0])
terryjreedye5bb1122017-07-05 00:54:55 -04001122 # Revert to default key set.
terryjreedy938e7382017-06-26 20:48:39 -04001123 self.are_keys_builtin.set(idleConf.defaultCfg['main']
Terry Jan Reedy9bdb1ed2016-07-10 13:46:34 -04001124 .Get('Keys', 'default'))
terryjreedy938e7382017-06-26 20:48:39 -04001125 self.builtin_keys.set(idleConf.defaultCfg['main'].Get('Keys', 'name')
Terry Jan Reedy9bdb1ed2016-07-10 13:46:34 -04001126 or idleConf.default_keys())
terryjreedye5bb1122017-07-05 00:54:55 -04001127 # User can't back out of these changes, they must be applied now.
terryjreedyedc03422017-07-07 16:37:39 -04001128 changes.save_all()
1129 self.save_all_changed_extensions()
terryjreedy938e7382017-06-26 20:48:39 -04001130 self.activate_config_changes()
1131 self.set_keys_type()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001132
terryjreedy938e7382017-06-26 20:48:39 -04001133 def delete_custom_theme(self):
terryjreedye5bb1122017-07-05 00:54:55 -04001134 """Handle event to delete custom theme.
1135
1136 The current theme is deactivated and the default theme is
1137 activated. The custom theme is permanently removed from
1138 the config file.
terryjreedy9a09c662017-07-13 23:53:30 -04001139
1140 Attributes accessed:
1141 custom_theme
1142
1143 Attributes updated:
1144 radio_theme_custom
1145 opt_menu_theme_custom
1146 is_builtin_theme
1147 builtin_theme
1148
1149 Methods:
1150 deactivate_current_config
1151 save_all_changed_extensions
1152 activate_config_changes
1153 set_theme_type
terryjreedye5bb1122017-07-05 00:54:55 -04001154 """
terryjreedy938e7382017-06-26 20:48:39 -04001155 theme_name = self.custom_theme.get()
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001156 delmsg = 'Are you sure you wish to delete the theme %r ?'
1157 if not tkMessageBox.askyesno(
terryjreedy938e7382017-06-26 20:48:39 -04001158 'Delete Theme', delmsg % theme_name, parent=self):
Steven M. Gava49745752002-02-18 01:43:11 +00001159 return
terryjreedy938e7382017-06-26 20:48:39 -04001160 self.deactivate_current_config()
terryjreedyedc03422017-07-07 16:37:39 -04001161 # Remove theme from changes, config, and file.
terryjreedyc0179482017-07-11 19:50:10 -04001162 changes.delete_section('highlight', theme_name)
terryjreedye5bb1122017-07-05 00:54:55 -04001163 # Reload user theme list.
terryjreedy938e7382017-06-26 20:48:39 -04001164 item_list = idleConf.GetSectionList('user', 'highlight')
1165 item_list.sort()
1166 if not item_list:
1167 self.radio_theme_custom.config(state=DISABLED)
1168 self.opt_menu_theme_custom.SetMenu(item_list, '- no custom themes -')
Steven M. Gava49745752002-02-18 01:43:11 +00001169 else:
terryjreedy938e7382017-06-26 20:48:39 -04001170 self.opt_menu_theme_custom.SetMenu(item_list, item_list[0])
terryjreedye5bb1122017-07-05 00:54:55 -04001171 # Revert to default theme.
terryjreedy938e7382017-06-26 20:48:39 -04001172 self.is_builtin_theme.set(idleConf.defaultCfg['main'].Get('Theme', 'default'))
1173 self.builtin_theme.set(idleConf.defaultCfg['main'].Get('Theme', 'name'))
terryjreedye5bb1122017-07-05 00:54:55 -04001174 # User can't back out of these changes, they must be applied now.
terryjreedyedc03422017-07-07 16:37:39 -04001175 changes.save_all()
1176 self.save_all_changed_extensions()
terryjreedy938e7382017-06-26 20:48:39 -04001177 self.activate_config_changes()
1178 self.set_theme_type()
Steven M. Gava49745752002-02-18 01:43:11 +00001179
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001180 def get_color(self):
terryjreedye5bb1122017-07-05 00:54:55 -04001181 """Handle button to select a new color for the target tag.
1182
1183 If a new color is selected while using a builtin theme, a
1184 name must be supplied to create a custom theme.
terryjreedy9a09c662017-07-13 23:53:30 -04001185
1186 Attributes accessed:
1187 highlight_target
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001188 frame_color_set
terryjreedy9a09c662017-07-13 23:53:30 -04001189 is_builtin_theme
1190
1191 Attributes updated:
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001192 color
terryjreedy9a09c662017-07-13 23:53:30 -04001193
1194 Methods:
1195 get_new_theme_name
1196 create_new_theme
terryjreedye5bb1122017-07-05 00:54:55 -04001197 """
terryjreedy938e7382017-06-26 20:48:39 -04001198 target = self.highlight_target.get()
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001199 prev_color = self.frame_color_set.cget('bg')
1200 rgbTuplet, color_string = tkColorChooser.askcolor(
1201 parent=self, title='Pick new color for : '+target,
1202 initialcolor=prev_color)
1203 if color_string and (color_string != prev_color):
1204 # User didn't cancel and they chose a new color.
terryjreedye5bb1122017-07-05 00:54:55 -04001205 if self.is_builtin_theme.get(): # Current theme is a built-in.
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001206 message = ('Your changes will be saved as a new Custom Theme. '
1207 'Enter a name for your new Custom Theme below.')
terryjreedy938e7382017-06-26 20:48:39 -04001208 new_theme = self.get_new_theme_name(message)
terryjreedye5bb1122017-07-05 00:54:55 -04001209 if not new_theme: # User cancelled custom theme creation.
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +00001210 return
terryjreedye5bb1122017-07-05 00:54:55 -04001211 else: # Create new custom theme based on previously active theme.
terryjreedy938e7382017-06-26 20:48:39 -04001212 self.create_new_theme(new_theme)
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001213 self.color.set(color_string)
terryjreedye5bb1122017-07-05 00:54:55 -04001214 else: # Current theme is user defined.
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001215 self.color.set(color_string)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001216
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001217 def on_new_color_set(self):
terryjreedye5bb1122017-07-05 00:54:55 -04001218 "Display sample of new color selection on the dialog."
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001219 new_color=self.color.get()
1220 self.frame_color_set.config(bg=new_color) # Set sample.
terryjreedy938e7382017-06-26 20:48:39 -04001221 plane ='foreground' if self.fg_bg_toggle.get() else 'background'
1222 sample_element = self.theme_elements[self.highlight_target.get()][0]
Terry Jan Reedy04864b42017-07-22 00:56:18 -04001223 self.highlight_sample.tag_config(sample_element, **{plane:new_color})
terryjreedy938e7382017-06-26 20:48:39 -04001224 theme = self.custom_theme.get()
1225 theme_element = sample_element + '-' + plane
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001226 changes.add_option('highlight', theme, theme_element, new_color)
Steven M. Gava052937f2002-02-11 02:20:53 +00001227
terryjreedy938e7382017-06-26 20:48:39 -04001228 def get_new_theme_name(self, message):
terryjreedye5bb1122017-07-05 00:54:55 -04001229 "Return name of new theme from query popup."
terryjreedy938e7382017-06-26 20:48:39 -04001230 used_names = (idleConf.GetSectionList('user', 'highlight') +
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001231 idleConf.GetSectionList('default', 'highlight'))
terryjreedy938e7382017-06-26 20:48:39 -04001232 new_theme = SectionName(
1233 self, 'New Custom Theme', message, used_names).result
1234 return new_theme
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001235
terryjreedy938e7382017-06-26 20:48:39 -04001236 def save_as_new_theme(self):
terryjreedy9a09c662017-07-13 23:53:30 -04001237 """Prompt for new theme name and create the theme.
1238
1239 Methods:
1240 get_new_theme_name
1241 create_new_theme
1242 """
terryjreedy938e7382017-06-26 20:48:39 -04001243 new_theme_name = self.get_new_theme_name('New Theme Name:')
1244 if new_theme_name:
1245 self.create_new_theme(new_theme_name)
Steven M. Gava085eb1b2002-02-05 04:52:32 +00001246
terryjreedy938e7382017-06-26 20:48:39 -04001247 def create_new_theme(self, new_theme_name):
terryjreedye5bb1122017-07-05 00:54:55 -04001248 """Create a new custom theme with the given name.
1249
1250 Create the new theme based on the previously active theme
1251 with the current changes applied. Once it is saved, then
1252 activate the new theme.
terryjreedy9a09c662017-07-13 23:53:30 -04001253
1254 Attributes accessed:
1255 builtin_theme
1256 custom_theme
1257
1258 Attributes updated:
1259 opt_menu_theme_custom
1260 is_builtin_theme
1261
1262 Method:
1263 save_new_theme
1264 set_theme_type
terryjreedye5bb1122017-07-05 00:54:55 -04001265 """
terryjreedy938e7382017-06-26 20:48:39 -04001266 if self.is_builtin_theme.get():
1267 theme_type = 'default'
1268 theme_name = self.builtin_theme.get()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001269 else:
terryjreedy938e7382017-06-26 20:48:39 -04001270 theme_type = 'user'
1271 theme_name = self.custom_theme.get()
1272 new_theme = idleConf.GetThemeDict(theme_type, theme_name)
terryjreedye5bb1122017-07-05 00:54:55 -04001273 # Apply any of the old theme's unsaved changes to the new theme.
terryjreedyedc03422017-07-07 16:37:39 -04001274 if theme_name in changes['highlight']:
1275 theme_changes = changes['highlight'][theme_name]
terryjreedy938e7382017-06-26 20:48:39 -04001276 for element in theme_changes:
1277 new_theme[element] = theme_changes[element]
terryjreedye5bb1122017-07-05 00:54:55 -04001278 # Save the new theme.
terryjreedy938e7382017-06-26 20:48:39 -04001279 self.save_new_theme(new_theme_name, new_theme)
terryjreedye5bb1122017-07-05 00:54:55 -04001280 # Change GUI over to the new theme.
terryjreedy938e7382017-06-26 20:48:39 -04001281 custom_theme_list = idleConf.GetSectionList('user', 'highlight')
1282 custom_theme_list.sort()
1283 self.opt_menu_theme_custom.SetMenu(custom_theme_list, new_theme_name)
1284 self.is_builtin_theme.set(0)
1285 self.set_theme_type()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001286
terryjreedy938e7382017-06-26 20:48:39 -04001287 def set_highlight_target(self):
terryjreedy9a09c662017-07-13 23:53:30 -04001288 """Set fg/bg toggle and color based on highlight tag target.
1289
1290 Instance variables accessed:
1291 highlight_target
1292
1293 Attributes updated:
1294 radio_fg
1295 radio_bg
1296 fg_bg_toggle
1297
1298 Methods:
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001299 set_color_sample
terryjreedy9a09c662017-07-13 23:53:30 -04001300
1301 Called from:
1302 var_changed_highlight_target
1303 load_theme_cfg
1304 """
terryjreedye5bb1122017-07-05 00:54:55 -04001305 if self.highlight_target.get() == 'Cursor': # bg not possible
terryjreedy938e7382017-06-26 20:48:39 -04001306 self.radio_fg.config(state=DISABLED)
1307 self.radio_bg.config(state=DISABLED)
1308 self.fg_bg_toggle.set(1)
terryjreedye5bb1122017-07-05 00:54:55 -04001309 else: # Both fg and bg can be set.
terryjreedy938e7382017-06-26 20:48:39 -04001310 self.radio_fg.config(state=NORMAL)
1311 self.radio_bg.config(state=NORMAL)
1312 self.fg_bg_toggle.set(1)
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001313 self.set_color_sample()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001314
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001315 def set_color_sample_binding(self, *args):
terryjreedy9a09c662017-07-13 23:53:30 -04001316 """Change color sample based on foreground/background toggle.
1317
1318 Methods:
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001319 set_color_sample
terryjreedy9a09c662017-07-13 23:53:30 -04001320 """
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001321 self.set_color_sample()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001322
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001323 def set_color_sample(self):
terryjreedy9a09c662017-07-13 23:53:30 -04001324 """Set the color of the frame background to reflect the selected target.
1325
1326 Instance variables accessed:
1327 theme_elements
1328 highlight_target
1329 fg_bg_toggle
Terry Jan Reedy04864b42017-07-22 00:56:18 -04001330 highlight_sample
terryjreedy9a09c662017-07-13 23:53:30 -04001331
1332 Attributes updated:
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001333 frame_color_set
terryjreedy9a09c662017-07-13 23:53:30 -04001334 """
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001335 # Set the color sample area.
terryjreedy938e7382017-06-26 20:48:39 -04001336 tag = self.theme_elements[self.highlight_target.get()][0]
1337 plane = 'foreground' if self.fg_bg_toggle.get() else 'background'
Terry Jan Reedy04864b42017-07-22 00:56:18 -04001338 color = self.highlight_sample.tag_cget(tag, plane)
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001339 self.frame_color_set.config(bg=color)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001340
terryjreedy938e7382017-06-26 20:48:39 -04001341 def paint_theme_sample(self):
terryjreedy9a09c662017-07-13 23:53:30 -04001342 """Apply the theme colors to each element tag in the sample text.
1343
1344 Instance attributes accessed:
1345 theme_elements
1346 is_builtin_theme
1347 builtin_theme
1348 custom_theme
1349
1350 Attributes updated:
Terry Jan Reedy04864b42017-07-22 00:56:18 -04001351 highlight_sample: Set the tag elements to the theme.
terryjreedy9a09c662017-07-13 23:53:30 -04001352
1353 Methods:
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001354 set_color_sample
terryjreedy9a09c662017-07-13 23:53:30 -04001355
1356 Called from:
1357 var_changed_builtin_theme
1358 var_changed_custom_theme
1359 load_theme_cfg
1360 """
terryjreedye5bb1122017-07-05 00:54:55 -04001361 if self.is_builtin_theme.get(): # Default theme
terryjreedy938e7382017-06-26 20:48:39 -04001362 theme = self.builtin_theme.get()
terryjreedye5bb1122017-07-05 00:54:55 -04001363 else: # User theme
terryjreedy938e7382017-06-26 20:48:39 -04001364 theme = self.custom_theme.get()
1365 for element_title in self.theme_elements:
1366 element = self.theme_elements[element_title][0]
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001367 colors = idleConf.GetHighlight(theme, element)
terryjreedye5bb1122017-07-05 00:54:55 -04001368 if element == 'cursor': # Cursor sample needs special painting.
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001369 colors['background'] = idleConf.GetHighlight(
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001370 theme, 'normal', fgBg='bg')
terryjreedye5bb1122017-07-05 00:54:55 -04001371 # Handle any unsaved changes to this theme.
terryjreedyedc03422017-07-07 16:37:39 -04001372 if theme in changes['highlight']:
1373 theme_dict = changes['highlight'][theme]
terryjreedy938e7382017-06-26 20:48:39 -04001374 if element + '-foreground' in theme_dict:
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001375 colors['foreground'] = theme_dict[element + '-foreground']
terryjreedy938e7382017-06-26 20:48:39 -04001376 if element + '-background' in theme_dict:
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001377 colors['background'] = theme_dict[element + '-background']
Terry Jan Reedy04864b42017-07-22 00:56:18 -04001378 self.highlight_sample.tag_config(element, **colors)
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001379 self.set_color_sample()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001380
terryjreedy938e7382017-06-26 20:48:39 -04001381 def help_source_selected(self, event):
terryjreedye5bb1122017-07-05 00:54:55 -04001382 "Handle event for selecting additional help."
terryjreedy938e7382017-06-26 20:48:39 -04001383 self.set_helplist_button_states()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001384
terryjreedy938e7382017-06-26 20:48:39 -04001385 def set_helplist_button_states(self):
terryjreedye5bb1122017-07-05 00:54:55 -04001386 "Toggle the state for the help list buttons based on list entries."
1387 if self.list_help.size() < 1: # No entries in list.
terryjreedy938e7382017-06-26 20:48:39 -04001388 self.button_helplist_edit.config(state=DISABLED)
1389 self.button_helplist_remove.config(state=DISABLED)
terryjreedye5bb1122017-07-05 00:54:55 -04001390 else: # Some entries.
1391 if self.list_help.curselection(): # There currently is a selection.
terryjreedy938e7382017-06-26 20:48:39 -04001392 self.button_helplist_edit.config(state=NORMAL)
1393 self.button_helplist_remove.config(state=NORMAL)
terryjreedye5bb1122017-07-05 00:54:55 -04001394 else: # There currently is not a selection.
terryjreedy938e7382017-06-26 20:48:39 -04001395 self.button_helplist_edit.config(state=DISABLED)
1396 self.button_helplist_remove.config(state=DISABLED)
Steven M. Gava085eb1b2002-02-05 04:52:32 +00001397
terryjreedy938e7382017-06-26 20:48:39 -04001398 def helplist_item_add(self):
terryjreedye5bb1122017-07-05 00:54:55 -04001399 """Handle add button for the help list.
1400
1401 Query for name and location of new help sources and add
1402 them to the list.
1403 """
terryjreedy938e7382017-06-26 20:48:39 -04001404 help_source = HelpSource(self, 'New Help Source',
Terry Jan Reedy8b22c0a2016-07-08 00:22:50 -04001405 ).result
terryjreedy938e7382017-06-26 20:48:39 -04001406 if help_source:
1407 self.user_helplist.append((help_source[0], help_source[1]))
1408 self.list_help.insert(END, help_source[0])
1409 self.update_user_help_changed_items()
1410 self.set_helplist_button_states()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001411
terryjreedy938e7382017-06-26 20:48:39 -04001412 def helplist_item_edit(self):
terryjreedye5bb1122017-07-05 00:54:55 -04001413 """Handle edit button for the help list.
1414
1415 Query with existing help source information and update
1416 config if the values are changed.
1417 """
terryjreedy938e7382017-06-26 20:48:39 -04001418 item_index = self.list_help.index(ANCHOR)
1419 help_source = self.user_helplist[item_index]
1420 new_help_source = HelpSource(
Terry Jan Reedy8b22c0a2016-07-08 00:22:50 -04001421 self, 'Edit Help Source',
terryjreedy938e7382017-06-26 20:48:39 -04001422 menuitem=help_source[0],
1423 filepath=help_source[1],
Terry Jan Reedy8b22c0a2016-07-08 00:22:50 -04001424 ).result
terryjreedy938e7382017-06-26 20:48:39 -04001425 if new_help_source and new_help_source != help_source:
1426 self.user_helplist[item_index] = new_help_source
1427 self.list_help.delete(item_index)
1428 self.list_help.insert(item_index, new_help_source[0])
1429 self.update_user_help_changed_items()
1430 self.set_helplist_button_states()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001431
terryjreedy938e7382017-06-26 20:48:39 -04001432 def helplist_item_remove(self):
terryjreedye5bb1122017-07-05 00:54:55 -04001433 """Handle remove button for the help list.
1434
1435 Delete the help list item from config.
1436 """
terryjreedy938e7382017-06-26 20:48:39 -04001437 item_index = self.list_help.index(ANCHOR)
1438 del(self.user_helplist[item_index])
1439 self.list_help.delete(item_index)
1440 self.update_user_help_changed_items()
1441 self.set_helplist_button_states()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001442
terryjreedy938e7382017-06-26 20:48:39 -04001443 def update_user_help_changed_items(self):
terryjreedyedc03422017-07-07 16:37:39 -04001444 "Clear and rebuild the HelpFiles section in changes"
1445 changes['main']['HelpFiles'] = {}
terryjreedy938e7382017-06-26 20:48:39 -04001446 for num in range(1, len(self.user_helplist) + 1):
terryjreedyedc03422017-07-07 16:37:39 -04001447 changes.add_option(
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001448 'main', 'HelpFiles', str(num),
terryjreedy938e7382017-06-26 20:48:39 -04001449 ';'.join(self.user_helplist[num-1][:2]))
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001450
terryjreedy938e7382017-06-26 20:48:39 -04001451 def load_theme_cfg(self):
terryjreedy9a09c662017-07-13 23:53:30 -04001452 """Load current configuration settings for the theme options.
1453
1454 Based on the is_builtin_theme toggle, the theme is set as
1455 either builtin or custom and the initial widget values
1456 reflect the current settings from idleConf.
1457
1458 Attributes updated:
1459 is_builtin_theme: Set from idleConf.
1460 opt_menu_theme_builtin: List of default themes from idleConf.
1461 opt_menu_theme_custom: List of custom themes from idleConf.
1462 radio_theme_custom: Disabled if there are no custom themes.
1463 custom_theme: Message with additional information.
1464 opt_menu_highlight_target: Create menu from self.theme_elements.
1465
1466 Methods:
1467 set_theme_type
1468 paint_theme_sample
1469 set_highlight_target
1470 """
terryjreedye5bb1122017-07-05 00:54:55 -04001471 # Set current theme type radiobutton.
terryjreedy938e7382017-06-26 20:48:39 -04001472 self.is_builtin_theme.set(idleConf.GetOption(
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001473 'main', 'Theme', 'default', type='bool', default=1))
terryjreedye5bb1122017-07-05 00:54:55 -04001474 # Set current theme.
terryjreedy938e7382017-06-26 20:48:39 -04001475 current_option = idleConf.CurrentTheme()
terryjreedye5bb1122017-07-05 00:54:55 -04001476 # Load available theme option menus.
1477 if self.is_builtin_theme.get(): # Default theme selected.
terryjreedy938e7382017-06-26 20:48:39 -04001478 item_list = idleConf.GetSectionList('default', 'highlight')
1479 item_list.sort()
1480 self.opt_menu_theme_builtin.SetMenu(item_list, current_option)
1481 item_list = idleConf.GetSectionList('user', 'highlight')
1482 item_list.sort()
1483 if not item_list:
1484 self.radio_theme_custom.config(state=DISABLED)
1485 self.custom_theme.set('- no custom themes -')
Steven M. Gava41a85322001-10-29 08:05:34 +00001486 else:
terryjreedy938e7382017-06-26 20:48:39 -04001487 self.opt_menu_theme_custom.SetMenu(item_list, item_list[0])
terryjreedye5bb1122017-07-05 00:54:55 -04001488 else: # User theme selected.
terryjreedy938e7382017-06-26 20:48:39 -04001489 item_list = idleConf.GetSectionList('user', 'highlight')
1490 item_list.sort()
1491 self.opt_menu_theme_custom.SetMenu(item_list, current_option)
1492 item_list = idleConf.GetSectionList('default', 'highlight')
1493 item_list.sort()
1494 self.opt_menu_theme_builtin.SetMenu(item_list, item_list[0])
1495 self.set_theme_type()
terryjreedye5bb1122017-07-05 00:54:55 -04001496 # Load theme element option menu.
terryjreedy938e7382017-06-26 20:48:39 -04001497 theme_names = list(self.theme_elements.keys())
1498 theme_names.sort(key=lambda x: self.theme_elements[x][1])
1499 self.opt_menu_highlight_target.SetMenu(theme_names, theme_names[0])
1500 self.paint_theme_sample()
1501 self.set_highlight_target()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001502
terryjreedy938e7382017-06-26 20:48:39 -04001503 def load_key_cfg(self):
terryjreedye5bb1122017-07-05 00:54:55 -04001504 "Load current configuration settings for the keybinding options."
1505 # Set current keys type radiobutton.
terryjreedy938e7382017-06-26 20:48:39 -04001506 self.are_keys_builtin.set(idleConf.GetOption(
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001507 'main', 'Keys', 'default', type='bool', default=1))
terryjreedye5bb1122017-07-05 00:54:55 -04001508 # Set current keys.
terryjreedy938e7382017-06-26 20:48:39 -04001509 current_option = idleConf.CurrentKeys()
terryjreedye5bb1122017-07-05 00:54:55 -04001510 # Load available keyset option menus.
1511 if self.are_keys_builtin.get(): # Default theme selected.
terryjreedy938e7382017-06-26 20:48:39 -04001512 item_list = idleConf.GetSectionList('default', 'keys')
1513 item_list.sort()
1514 self.opt_menu_keys_builtin.SetMenu(item_list, current_option)
1515 item_list = idleConf.GetSectionList('user', 'keys')
1516 item_list.sort()
1517 if not item_list:
1518 self.radio_keys_custom.config(state=DISABLED)
1519 self.custom_keys.set('- no custom keys -')
Steven M. Gava41a85322001-10-29 08:05:34 +00001520 else:
terryjreedy938e7382017-06-26 20:48:39 -04001521 self.opt_menu_keys_custom.SetMenu(item_list, item_list[0])
terryjreedye5bb1122017-07-05 00:54:55 -04001522 else: # User key set selected.
terryjreedy938e7382017-06-26 20:48:39 -04001523 item_list = idleConf.GetSectionList('user', 'keys')
1524 item_list.sort()
1525 self.opt_menu_keys_custom.SetMenu(item_list, current_option)
1526 item_list = idleConf.GetSectionList('default', 'keys')
1527 item_list.sort()
1528 self.opt_menu_keys_builtin.SetMenu(item_list, idleConf.default_keys())
1529 self.set_keys_type()
terryjreedye5bb1122017-07-05 00:54:55 -04001530 # Load keyset element list.
terryjreedy938e7382017-06-26 20:48:39 -04001531 keyset_name = idleConf.CurrentKeys()
1532 self.load_keys_list(keyset_name)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001533
terryjreedy938e7382017-06-26 20:48:39 -04001534 def load_general_cfg(self):
terryjreedye5bb1122017-07-05 00:54:55 -04001535 "Load current configuration settings for the general options."
1536 # Set startup state.
terryjreedy938e7382017-06-26 20:48:39 -04001537 self.startup_edit.set(idleConf.GetOption(
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001538 'main', 'General', 'editor-on-startup', default=1, type='bool'))
terryjreedye5bb1122017-07-05 00:54:55 -04001539 # Set autosave state.
terryjreedy938e7382017-06-26 20:48:39 -04001540 self.autosave.set(idleConf.GetOption(
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001541 'main', 'General', 'autosave', default=0, type='bool'))
terryjreedye5bb1122017-07-05 00:54:55 -04001542 # Set initial window size.
terryjreedy938e7382017-06-26 20:48:39 -04001543 self.win_width.set(idleConf.GetOption(
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001544 'main', 'EditorWindow', 'width', type='int'))
terryjreedy938e7382017-06-26 20:48:39 -04001545 self.win_height.set(idleConf.GetOption(
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001546 'main', 'EditorWindow', 'height', type='int'))
terryjreedye5bb1122017-07-05 00:54:55 -04001547 # Set additional help sources.
terryjreedy938e7382017-06-26 20:48:39 -04001548 self.user_helplist = idleConf.GetAllExtraHelpSourcesList()
1549 for help_item in self.user_helplist:
1550 self.list_help.insert(END, help_item[0])
1551 self.set_helplist_button_states()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001552
terryjreedy938e7382017-06-26 20:48:39 -04001553 def load_configs(self):
terryjreedye5bb1122017-07-05 00:54:55 -04001554 """Load configuration for each page.
1555
1556 Load configuration from default and user config files and populate
Steven M. Gava429a86af2001-10-23 10:42:12 +00001557 the widgets on the config dialog pages.
terryjreedy9a09c662017-07-13 23:53:30 -04001558
1559 Methods:
1560 load_font_cfg
1561 load_tab_cfg
1562 load_theme_cfg
1563 load_key_cfg
1564 load_general_cfg
Steven M. Gava429a86af2001-10-23 10:42:12 +00001565 """
terryjreedy938e7382017-06-26 20:48:39 -04001566 self.load_font_cfg()
1567 self.load_tab_cfg()
terryjreedy938e7382017-06-26 20:48:39 -04001568 self.load_theme_cfg()
terryjreedy938e7382017-06-26 20:48:39 -04001569 self.load_key_cfg()
terryjreedy938e7382017-06-26 20:48:39 -04001570 self.load_general_cfg()
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001571 # note: extension page handled separately
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001572
terryjreedy938e7382017-06-26 20:48:39 -04001573 def save_new_key_set(self, keyset_name, keyset):
terryjreedye5bb1122017-07-05 00:54:55 -04001574 """Save a newly created core key set.
1575
terryjreedy938e7382017-06-26 20:48:39 -04001576 keyset_name - string, the name of the new key set
1577 keyset - dictionary containing the new key set
Steven M. Gava052937f2002-02-11 02:20:53 +00001578 """
terryjreedy938e7382017-06-26 20:48:39 -04001579 if not idleConf.userCfg['keys'].has_section(keyset_name):
1580 idleConf.userCfg['keys'].add_section(keyset_name)
1581 for event in keyset:
1582 value = keyset[event]
1583 idleConf.userCfg['keys'].SetOption(keyset_name, event, value)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001584
terryjreedy938e7382017-06-26 20:48:39 -04001585 def save_new_theme(self, theme_name, theme):
terryjreedy9a09c662017-07-13 23:53:30 -04001586 """Save a newly created theme to idleConf.
terryjreedye5bb1122017-07-05 00:54:55 -04001587
terryjreedy938e7382017-06-26 20:48:39 -04001588 theme_name - string, the name of the new theme
Steven M. Gava052937f2002-02-11 02:20:53 +00001589 theme - dictionary containing the new theme
1590 """
terryjreedy938e7382017-06-26 20:48:39 -04001591 if not idleConf.userCfg['highlight'].has_section(theme_name):
1592 idleConf.userCfg['highlight'].add_section(theme_name)
Kurt B. Kaisere0712772007-08-23 05:25:55 +00001593 for element in theme:
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001594 value = theme[element]
terryjreedy938e7382017-06-26 20:48:39 -04001595 idleConf.userCfg['highlight'].SetOption(theme_name, element, value)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001596
terryjreedy938e7382017-06-26 20:48:39 -04001597 def deactivate_current_config(self):
terryjreedy9a09c662017-07-13 23:53:30 -04001598 """Remove current key bindings.
1599
1600 Iterate over window instances defined in parent and remove
1601 the keybindings.
1602 """
terryjreedye5bb1122017-07-05 00:54:55 -04001603 # Before a config is saved, some cleanup of current
1604 # config must be done - remove the previous keybindings.
terryjreedy938e7382017-06-26 20:48:39 -04001605 win_instances = self.parent.instance_dict.keys()
1606 for instance in win_instances:
Kurt B. Kaiserb1754452005-11-18 22:05:48 +00001607 instance.RemoveKeybindings()
1608
terryjreedy938e7382017-06-26 20:48:39 -04001609 def activate_config_changes(self):
terryjreedy9a09c662017-07-13 23:53:30 -04001610 """Apply configuration changes to current windows.
1611
1612 Dynamically update the current parent window instances
1613 with some of the configuration changes.
1614 """
terryjreedy938e7382017-06-26 20:48:39 -04001615 win_instances = self.parent.instance_dict.keys()
1616 for instance in win_instances:
Steven M. Gavab77d3432002-03-02 07:16:21 +00001617 instance.ResetColorizer()
Steven M. Gavab1585412002-03-12 00:21:56 +00001618 instance.ResetFont()
Kurt B. Kaiseracdef852005-01-31 03:34:26 +00001619 instance.set_notabs_indentwidth()
Kurt B. Kaiserb1754452005-11-18 22:05:48 +00001620 instance.ApplyKeybindings()
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +00001621 instance.reset_help_menu_entries()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001622
terryjreedy938e7382017-06-26 20:48:39 -04001623 def cancel(self):
terryjreedy9a09c662017-07-13 23:53:30 -04001624 """Dismiss config dialog.
1625
1626 Methods:
1627 destroy: inherited
1628 """
Steven M. Gava5f28e8f2002-01-21 06:38:21 +00001629 self.destroy()
1630
terryjreedy938e7382017-06-26 20:48:39 -04001631 def ok(self):
terryjreedy9a09c662017-07-13 23:53:30 -04001632 """Apply config changes, then dismiss dialog.
1633
1634 Methods:
1635 apply
1636 destroy: inherited
1637 """
terryjreedy938e7382017-06-26 20:48:39 -04001638 self.apply()
Steven M. Gava5f28e8f2002-01-21 06:38:21 +00001639 self.destroy()
1640
terryjreedy938e7382017-06-26 20:48:39 -04001641 def apply(self):
terryjreedy9a09c662017-07-13 23:53:30 -04001642 """Apply config changes and leave dialog open.
1643
1644 Methods:
1645 deactivate_current_config
1646 save_all_changed_extensions
1647 activate_config_changes
1648 """
terryjreedy938e7382017-06-26 20:48:39 -04001649 self.deactivate_current_config()
terryjreedyedc03422017-07-07 16:37:39 -04001650 changes.save_all()
1651 self.save_all_changed_extensions()
terryjreedy938e7382017-06-26 20:48:39 -04001652 self.activate_config_changes()
Steven M. Gava5f28e8f2002-01-21 06:38:21 +00001653
terryjreedy938e7382017-06-26 20:48:39 -04001654 def help(self):
terryjreedy9a09c662017-07-13 23:53:30 -04001655 """Create textview for config dialog help.
1656
1657 Attrbutes accessed:
1658 tab_pages
1659
1660 Methods:
1661 view_text: Method from textview module.
1662 """
terryjreedy938e7382017-06-26 20:48:39 -04001663 page = self.tab_pages._current_page
Terry Jan Reedyd0cadba2015-10-11 22:07:31 -04001664 view_text(self, title='Help for IDLE preferences',
1665 text=help_common+help_pages.get(page, ''))
1666
terryjreedy938e7382017-06-26 20:48:39 -04001667 def create_page_extensions(self):
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001668 """Part of the config dialog used for configuring IDLE extensions.
1669
1670 This code is generic - it works for any and all IDLE extensions.
1671
1672 IDLE extensions save their configuration options using idleConf.
Terry Jan Reedyb2f87602015-10-13 22:09:06 -04001673 This code reads the current configuration using idleConf, supplies a
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001674 GUI interface to change the configuration values, and saves the
1675 changes using idleConf.
1676
1677 Not all changes take effect immediately - some may require restarting IDLE.
1678 This depends on each extension's implementation.
1679
1680 All values are treated as text, and it is up to the user to supply
1681 reasonable values. The only exception to this are the 'enable*' options,
Serhiy Storchaka6a7b3a72016-04-17 08:32:47 +03001682 which are boolean, and can be toggled with a True/False button.
terryjreedy9a09c662017-07-13 23:53:30 -04001683
1684 Methods:
1685 load_extentions:
1686 extension_selected: Handle selection from list.
1687 create_extension_frame: Hold widgets for one extension.
1688 set_extension_value: Set in userCfg['extensions'].
1689 save_all_changed_extensions: Call extension page Save().
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001690 """
1691 parent = self.parent
terryjreedy938e7382017-06-26 20:48:39 -04001692 frame = self.tab_pages.pages['Extensions'].frame
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001693 self.ext_defaultCfg = idleConf.defaultCfg['extensions']
1694 self.ext_userCfg = idleConf.userCfg['extensions']
1695 self.is_int = self.register(is_int)
1696 self.load_extensions()
terryjreedye5bb1122017-07-05 00:54:55 -04001697 # Create widgets - a listbox shows all available extensions, with the
1698 # controls for the extension selected in the listbox to the right.
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001699 self.extension_names = StringVar(self)
1700 frame.rowconfigure(0, weight=1)
1701 frame.columnconfigure(2, weight=1)
1702 self.extension_list = Listbox(frame, listvariable=self.extension_names,
1703 selectmode='browse')
1704 self.extension_list.bind('<<ListboxSelect>>', self.extension_selected)
1705 scroll = Scrollbar(frame, command=self.extension_list.yview)
1706 self.extension_list.yscrollcommand=scroll.set
1707 self.details_frame = LabelFrame(frame, width=250, height=250)
1708 self.extension_list.grid(column=0, row=0, sticky='nws')
1709 scroll.grid(column=1, row=0, sticky='ns')
1710 self.details_frame.grid(column=2, row=0, sticky='nsew', padx=[10, 0])
1711 frame.configure(padx=10, pady=10)
1712 self.config_frame = {}
1713 self.current_extension = None
1714
1715 self.outerframe = self # TEMPORARY
1716 self.tabbed_page_set = self.extension_list # TEMPORARY
1717
terryjreedye5bb1122017-07-05 00:54:55 -04001718 # Create the frame holding controls for each extension.
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001719 ext_names = ''
1720 for ext_name in sorted(self.extensions):
1721 self.create_extension_frame(ext_name)
1722 ext_names = ext_names + '{' + ext_name + '} '
1723 self.extension_names.set(ext_names)
1724 self.extension_list.selection_set(0)
1725 self.extension_selected(None)
1726
1727 def load_extensions(self):
1728 "Fill self.extensions with data from the default and user configs."
1729 self.extensions = {}
1730 for ext_name in idleConf.GetExtensions(active_only=False):
1731 self.extensions[ext_name] = []
1732
1733 for ext_name in self.extensions:
1734 opt_list = sorted(self.ext_defaultCfg.GetOptionList(ext_name))
1735
terryjreedye5bb1122017-07-05 00:54:55 -04001736 # Bring 'enable' options to the beginning of the list.
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001737 enables = [opt_name for opt_name in opt_list
1738 if opt_name.startswith('enable')]
1739 for opt_name in enables:
1740 opt_list.remove(opt_name)
1741 opt_list = enables + opt_list
1742
1743 for opt_name in opt_list:
1744 def_str = self.ext_defaultCfg.Get(
1745 ext_name, opt_name, raw=True)
1746 try:
1747 def_obj = {'True':True, 'False':False}[def_str]
1748 opt_type = 'bool'
1749 except KeyError:
1750 try:
1751 def_obj = int(def_str)
1752 opt_type = 'int'
1753 except ValueError:
1754 def_obj = def_str
1755 opt_type = None
1756 try:
1757 value = self.ext_userCfg.Get(
1758 ext_name, opt_name, type=opt_type, raw=True,
1759 default=def_obj)
terryjreedye5bb1122017-07-05 00:54:55 -04001760 except ValueError: # Need this until .Get fixed.
1761 value = def_obj # Bad values overwritten by entry.
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001762 var = StringVar(self)
1763 var.set(str(value))
1764
1765 self.extensions[ext_name].append({'name': opt_name,
1766 'type': opt_type,
1767 'default': def_str,
1768 'value': value,
1769 'var': var,
1770 })
1771
1772 def extension_selected(self, event):
terryjreedye5bb1122017-07-05 00:54:55 -04001773 "Handle selection of an extension from the list."
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001774 newsel = self.extension_list.curselection()
1775 if newsel:
1776 newsel = self.extension_list.get(newsel)
1777 if newsel is None or newsel != self.current_extension:
1778 if self.current_extension:
1779 self.details_frame.config(text='')
1780 self.config_frame[self.current_extension].grid_forget()
1781 self.current_extension = None
1782 if newsel:
1783 self.details_frame.config(text=newsel)
1784 self.config_frame[newsel].grid(column=0, row=0, sticky='nsew')
1785 self.current_extension = newsel
1786
1787 def create_extension_frame(self, ext_name):
1788 """Create a frame holding the widgets to configure one extension"""
1789 f = VerticalScrolledFrame(self.details_frame, height=250, width=250)
1790 self.config_frame[ext_name] = f
1791 entry_area = f.interior
terryjreedye5bb1122017-07-05 00:54:55 -04001792 # Create an entry for each configuration option.
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001793 for row, opt in enumerate(self.extensions[ext_name]):
terryjreedye5bb1122017-07-05 00:54:55 -04001794 # Create a row with a label and entry/checkbutton.
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001795 label = Label(entry_area, text=opt['name'])
1796 label.grid(row=row, column=0, sticky=NW)
1797 var = opt['var']
1798 if opt['type'] == 'bool':
1799 Checkbutton(entry_area, textvariable=var, variable=var,
1800 onvalue='True', offvalue='False',
1801 indicatoron=FALSE, selectcolor='', width=8
1802 ).grid(row=row, column=1, sticky=W, padx=7)
1803 elif opt['type'] == 'int':
1804 Entry(entry_area, textvariable=var, validate='key',
1805 validatecommand=(self.is_int, '%P')
1806 ).grid(row=row, column=1, sticky=NSEW, padx=7)
1807
1808 else:
1809 Entry(entry_area, textvariable=var
1810 ).grid(row=row, column=1, sticky=NSEW, padx=7)
1811 return
1812
1813 def set_extension_value(self, section, opt):
terryjreedye5bb1122017-07-05 00:54:55 -04001814 """Return True if the configuration was added or changed.
1815
1816 If the value is the same as the default, then remove it
1817 from user config file.
1818 """
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001819 name = opt['name']
1820 default = opt['default']
1821 value = opt['var'].get().strip() or default
1822 opt['var'].set(value)
1823 # if self.defaultCfg.has_section(section):
terryjreedye5bb1122017-07-05 00:54:55 -04001824 # Currently, always true; if not, indent to return.
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001825 if (value == default):
1826 return self.ext_userCfg.RemoveOption(section, name)
terryjreedye5bb1122017-07-05 00:54:55 -04001827 # Set the option.
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001828 return self.ext_userCfg.SetOption(section, name, value)
1829
1830 def save_all_changed_extensions(self):
terryjreedy9a09c662017-07-13 23:53:30 -04001831 """Save configuration changes to the user config file.
1832
1833 Attributes accessed:
1834 extensions
1835
1836 Methods:
1837 set_extension_value
1838 """
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001839 has_changes = False
1840 for ext_name in self.extensions:
1841 options = self.extensions[ext_name]
1842 for opt in options:
1843 if self.set_extension_value(ext_name, opt):
1844 has_changes = True
1845 if has_changes:
1846 self.ext_userCfg.Save()
1847
1848
Terry Jan Reedy0243bea2017-07-26 20:53:13 -04001849class VarTrace:
1850 """Maintain Tk variables trace state."""
1851
1852 def __init__(self):
1853 """Store Tk variables and callbacks.
1854
1855 untraced: List of tuples (var, callback)
1856 that do not have the callback attached
1857 to the Tk var.
1858 traced: List of tuples (var, callback) where
1859 that callback has been attached to the var.
1860 """
1861 self.untraced = []
1862 self.traced = []
1863
1864 def add(self, var, callback):
1865 """Add (var, callback) tuple to untraced list.
1866
1867 Args:
1868 var: Tk variable instance.
1869 callback: Function to be used as a callback or
1870 a tuple with IdleConf values for default
1871 callback.
1872
1873 Return:
1874 Tk variable instance.
1875 """
1876 if isinstance(callback, tuple):
1877 callback = self.make_callback(var, callback)
1878 self.untraced.append((var, callback))
1879 return var
1880
1881 @staticmethod
1882 def make_callback(var, config):
1883 "Return default callback function to add values to changes instance."
1884 def default_callback(*params):
1885 "Add config values to changes instance."
1886 changes.add_option(*config, var.get())
1887 return default_callback
1888
1889 def attach(self):
1890 "Attach callback to all vars that are not traced."
1891 while self.untraced:
1892 var, callback = self.untraced.pop()
1893 var.trace_add('write', callback)
1894 self.traced.append((var, callback))
1895
1896 def detach(self):
1897 "Remove callback from traced vars."
1898 while self.traced:
1899 var, callback = self.traced.pop()
1900 var.trace_remove('write', var.trace_info()[0][1])
1901 self.untraced.append((var, callback))
1902
1903
Terry Jan Reedyd0cadba2015-10-11 22:07:31 -04001904help_common = '''\
1905When you click either the Apply or Ok buttons, settings in this
1906dialog that are different from IDLE's default are saved in
1907a .idlerc directory in your home directory. Except as noted,
Terry Jan Reedyd0c0f002015-11-12 15:02:57 -05001908these changes apply to all versions of IDLE installed on this
Terry Jan Reedyd0cadba2015-10-11 22:07:31 -04001909machine. Some do not take affect until IDLE is restarted.
1910[Cancel] only cancels changes made since the last save.
1911'''
1912help_pages = {
Terry Jan Reedy9bdb1ed2016-07-10 13:46:34 -04001913 'Highlighting': '''
Terry Jan Reedyd0cadba2015-10-11 22:07:31 -04001914Highlighting:
Terry Jan Reedyd0c0f002015-11-12 15:02:57 -05001915The IDLE Dark color theme is new in October 2015. It can only
Terry Jan Reedyd0cadba2015-10-11 22:07:31 -04001916be used with older IDLE releases if it is saved as a custom
1917theme, with a different name.
Terry Jan Reedy9bdb1ed2016-07-10 13:46:34 -04001918''',
1919 'Keys': '''
1920Keys:
1921The IDLE Modern Unix key set is new in June 2016. It can only
1922be used with older IDLE releases if it is saved as a custom
1923key set, with a different name.
1924''',
terryjreedyaf683822017-06-27 23:02:19 -04001925 'Extensions': '''
1926Extensions:
1927
1928Autocomplete: Popupwait is milleseconds to wait after key char, without
1929cursor movement, before popping up completion box. Key char is '.' after
1930identifier or a '/' (or '\\' on Windows) within a string.
1931
1932FormatParagraph: Max-width is max chars in lines after re-formatting.
1933Use with paragraphs in both strings and comment blocks.
1934
1935ParenMatch: Style indicates what is highlighted when closer is entered:
1936'opener' - opener '({[' corresponding to closer; 'parens' - both chars;
1937'expression' (default) - also everything in between. Flash-delay is how
1938long to highlight if cursor is not moved (0 means forever).
1939'''
Terry Jan Reedyd0cadba2015-10-11 22:07:31 -04001940}
1941
Steven M. Gavac11ccf32001-09-24 09:43:17 +00001942
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001943def is_int(s):
1944 "Return 's is blank or represents an int'"
1945 if not s:
1946 return True
1947 try:
1948 int(s)
1949 return True
1950 except ValueError:
1951 return False
1952
1953
Terry Jan Reedya9421fb2014-10-22 20:15:18 -04001954class VerticalScrolledFrame(Frame):
1955 """A pure Tkinter vertically scrollable frame.
1956
1957 * Use the 'interior' attribute to place widgets inside the scrollable frame
1958 * Construct and pack/place/grid normally
1959 * This frame only allows vertical scrolling
1960 """
1961 def __init__(self, parent, *args, **kw):
1962 Frame.__init__(self, parent, *args, **kw)
1963
terryjreedye5bb1122017-07-05 00:54:55 -04001964 # Create a canvas object and a vertical scrollbar for scrolling it.
Terry Jan Reedya9421fb2014-10-22 20:15:18 -04001965 vscrollbar = Scrollbar(self, orient=VERTICAL)
1966 vscrollbar.pack(fill=Y, side=RIGHT, expand=FALSE)
1967 canvas = Canvas(self, bd=0, highlightthickness=0,
Terry Jan Reedyd0812292015-10-22 03:27:31 -04001968 yscrollcommand=vscrollbar.set, width=240)
Terry Jan Reedya9421fb2014-10-22 20:15:18 -04001969 canvas.pack(side=LEFT, fill=BOTH, expand=TRUE)
1970 vscrollbar.config(command=canvas.yview)
1971
terryjreedye5bb1122017-07-05 00:54:55 -04001972 # Reset the view.
Terry Jan Reedya9421fb2014-10-22 20:15:18 -04001973 canvas.xview_moveto(0)
1974 canvas.yview_moveto(0)
1975
terryjreedye5bb1122017-07-05 00:54:55 -04001976 # Create a frame inside the canvas which will be scrolled with it.
Terry Jan Reedya9421fb2014-10-22 20:15:18 -04001977 self.interior = interior = Frame(canvas)
1978 interior_id = canvas.create_window(0, 0, window=interior, anchor=NW)
1979
terryjreedye5bb1122017-07-05 00:54:55 -04001980 # Track changes to the canvas and frame width and sync them,
1981 # also updating the scrollbar.
Terry Jan Reedya9421fb2014-10-22 20:15:18 -04001982 def _configure_interior(event):
terryjreedye5bb1122017-07-05 00:54:55 -04001983 # Update the scrollbars to match the size of the inner frame.
Terry Jan Reedya9421fb2014-10-22 20:15:18 -04001984 size = (interior.winfo_reqwidth(), interior.winfo_reqheight())
1985 canvas.config(scrollregion="0 0 %s %s" % size)
Terry Jan Reedya9421fb2014-10-22 20:15:18 -04001986 interior.bind('<Configure>', _configure_interior)
1987
1988 def _configure_canvas(event):
1989 if interior.winfo_reqwidth() != canvas.winfo_width():
terryjreedye5bb1122017-07-05 00:54:55 -04001990 # Update the inner frame's width to fill the canvas.
Terry Jan Reedya9421fb2014-10-22 20:15:18 -04001991 canvas.itemconfigure(interior_id, width=canvas.winfo_width())
1992 canvas.bind('<Configure>', _configure_canvas)
1993
1994 return
1995
Terry Jan Reedya9421fb2014-10-22 20:15:18 -04001996
Steven M. Gava44d3d1a2001-07-31 06:59:02 +00001997if __name__ == '__main__':
Terry Jan Reedycfa89502014-07-14 23:07:32 -04001998 import unittest
1999 unittest.main('idlelib.idle_test.test_configdialog',
2000 verbosity=2, exit=False)
Terry Jan Reedy2e8234a2014-05-29 01:46:26 -04002001 from idlelib.idle_test.htest import run
Terry Jan Reedy47304c02015-10-20 02:15:28 -04002002 run(ConfigDialog)