blob: e1c3923caed43fb2194ff5b5a346fc62ee86ee40 [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
terryjreedy938e7382017-06-26 20:48:39 -0400153 def create_page_font_tab(self):
terryjreedye5bb1122017-07-05 00:54:55 -0400154 """Return frame of widgets for Font/Tabs tab.
155
terryjreedy9a09c662017-07-13 23:53:30 -0400156 Tk Variables:
terryjreedye5bb1122017-07-05 00:54:55 -0400157 font_size: Font size.
158 font_bold: Select font bold or not.
159 font_name: Font face.
terryjreedy9a09c662017-07-13 23:53:30 -0400160 Note: these 3 share var_changed_font callback.
terryjreedye5bb1122017-07-05 00:54:55 -0400161 space_num: Indentation width.
terryjreedy9a09c662017-07-13 23:53:30 -0400162
163 Data Attribute:
terryjreedye5bb1122017-07-05 00:54:55 -0400164 edit_font: Font widget with default font name, size, and weight.
terryjreedy9a09c662017-07-13 23:53:30 -0400165
166 Methods:
167 load_font_cfg: Set vars and fontlist.
168 on_fontlist_select: Bound to fontlist button release
169 or key release.
170 set_font_sample: Command for opt_menu_font_size and
171 check_font_bold.
172 load_tab_cfg: Get current.
173
174 Widget Structure: (*) widgets bound to self
175 frame
176 frame_font: LabelFrame
177 frame_font_name: Frame
178 font_name_title: Label
179 (*)fontlist: ListBox
180 scroll_font: Scrollbar
181 frame_font_param: Frame
182 font_size_title: Label
183 (*)opt_menu_font_size: DynOptionMenu - font_size
184 check_font_bold: Checkbutton - font_bold
185 frame_font_sample: Frame
186 (*)font_sample: Label
187 frame_indent: LabelFrame
188 frame_indent_size: Frame
189 indent_size_title: Label
190 (*)scale_indent_size: Scale - space_num
terryjreedye5bb1122017-07-05 00:54:55 -0400191 """
Terry Jan Reedy22405332014-07-30 19:24:32 -0400192 parent = self.parent
terryjreedy938e7382017-06-26 20:48:39 -0400193 self.font_size = StringVar(parent)
194 self.font_bold = BooleanVar(parent)
195 self.font_name = StringVar(parent)
196 self.space_num = IntVar(parent)
197 self.edit_font = tkFont.Font(parent, ('courier', 10, 'normal'))
Terry Jan Reedy22405332014-07-30 19:24:32 -0400198
terryjreedy7ab33422017-07-09 19:26:32 -0400199 # Create widgets.
200 # body and body section frames.
terryjreedy938e7382017-06-26 20:48:39 -0400201 frame = self.tab_pages.pages['Fonts/Tabs'].frame
terryjreedy938e7382017-06-26 20:48:39 -0400202 frame_font = LabelFrame(
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400203 frame, borderwidth=2, relief=GROOVE, text=' Base Editor Font ')
terryjreedy938e7382017-06-26 20:48:39 -0400204 frame_indent = LabelFrame(
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400205 frame, borderwidth=2, relief=GROOVE, text=' Indentation Width ')
terryjreedy7ab33422017-07-09 19:26:32 -0400206 # frame_font
terryjreedy938e7382017-06-26 20:48:39 -0400207 frame_font_name = Frame(frame_font)
208 frame_font_param = Frame(frame_font)
209 font_name_title = Label(
210 frame_font_name, justify=LEFT, text='Font Face :')
terryjreedy7ab33422017-07-09 19:26:32 -0400211 self.fontlist = Listbox(
terryjreedy938e7382017-06-26 20:48:39 -0400212 frame_font_name, height=5, takefocus=FALSE, exportselection=FALSE)
terryjreedy953e5272017-07-11 02:16:41 -0400213 self.fontlist.bind('<ButtonRelease-1>', self.on_fontlist_select)
214 self.fontlist.bind('<KeyRelease-Up>', self.on_fontlist_select)
215 self.fontlist.bind('<KeyRelease-Down>', self.on_fontlist_select)
terryjreedy938e7382017-06-26 20:48:39 -0400216 scroll_font = Scrollbar(frame_font_name)
terryjreedy7ab33422017-07-09 19:26:32 -0400217 scroll_font.config(command=self.fontlist.yview)
218 self.fontlist.config(yscrollcommand=scroll_font.set)
terryjreedy938e7382017-06-26 20:48:39 -0400219 font_size_title = Label(frame_font_param, text='Size :')
220 self.opt_menu_font_size = DynOptionMenu(
221 frame_font_param, self.font_size, None, command=self.set_font_sample)
222 check_font_bold = Checkbutton(
223 frame_font_param, variable=self.font_bold, onvalue=1,
224 offvalue=0, text='Bold', command=self.set_font_sample)
225 frame_font_sample = Frame(frame_font, relief=SOLID, borderwidth=1)
226 self.font_sample = Label(
227 frame_font_sample, justify=LEFT, font=self.edit_font,
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400228 text='AaBbCcDdEe\nFfGgHhIiJjK\n1234567890\n#:+=(){}[]')
terryjreedy7ab33422017-07-09 19:26:32 -0400229 # frame_indent
terryjreedy938e7382017-06-26 20:48:39 -0400230 frame_indent_size = Frame(frame_indent)
231 indent_size_title = Label(
232 frame_indent_size, justify=LEFT,
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400233 text='Python Standard: 4 Spaces!')
terryjreedy938e7382017-06-26 20:48:39 -0400234 self.scale_indent_size = Scale(
235 frame_indent_size, variable=self.space_num,
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400236 orient='horizontal', tickinterval=2, from_=2, to=16)
Terry Jan Reedy22405332014-07-30 19:24:32 -0400237
terryjreedy7ab33422017-07-09 19:26:32 -0400238 # Pack widgets.
239 # body
terryjreedy938e7382017-06-26 20:48:39 -0400240 frame_font.pack(side=LEFT, padx=5, pady=5, expand=TRUE, fill=BOTH)
241 frame_indent.pack(side=LEFT, padx=5, pady=5, fill=Y)
terryjreedy7ab33422017-07-09 19:26:32 -0400242 # frame_font
terryjreedy938e7382017-06-26 20:48:39 -0400243 frame_font_name.pack(side=TOP, padx=5, pady=5, fill=X)
244 frame_font_param.pack(side=TOP, padx=5, pady=5, fill=X)
245 font_name_title.pack(side=TOP, anchor=W)
terryjreedy7ab33422017-07-09 19:26:32 -0400246 self.fontlist.pack(side=LEFT, expand=TRUE, fill=X)
terryjreedy938e7382017-06-26 20:48:39 -0400247 scroll_font.pack(side=LEFT, fill=Y)
248 font_size_title.pack(side=LEFT, anchor=W)
249 self.opt_menu_font_size.pack(side=LEFT, anchor=W)
250 check_font_bold.pack(side=LEFT, anchor=W, padx=20)
251 frame_font_sample.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH)
252 self.font_sample.pack(expand=TRUE, fill=BOTH)
terryjreedy7ab33422017-07-09 19:26:32 -0400253 # frame_indent
terryjreedy938e7382017-06-26 20:48:39 -0400254 frame_indent_size.pack(side=TOP, fill=X)
255 indent_size_title.pack(side=TOP, anchor=W, padx=5)
256 self.scale_indent_size.pack(side=TOP, padx=5, fill=X)
terryjreedy7ab33422017-07-09 19:26:32 -0400257
Steven M. Gava952d0a52001-08-03 04:43:44 +0000258 return frame
259
terryjreedy938e7382017-06-26 20:48:39 -0400260 def create_page_highlight(self):
terryjreedye5bb1122017-07-05 00:54:55 -0400261 """Return frame of widgets for Highlighting tab.
262
terryjreedy9a09c662017-07-13 23:53:30 -0400263 Tk Variables:
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -0400264 color: Color of selected target.
terryjreedye5bb1122017-07-05 00:54:55 -0400265 builtin_theme: Menu variable for built-in theme.
266 custom_theme: Menu variable for custom theme.
267 fg_bg_toggle: Toggle for foreground/background color.
terryjreedy9a09c662017-07-13 23:53:30 -0400268 Note: this has no callback.
terryjreedye5bb1122017-07-05 00:54:55 -0400269 is_builtin_theme: Selector for built-in or custom theme.
270 highlight_target: Menu variable for the highlight tag target.
terryjreedy9a09c662017-07-13 23:53:30 -0400271
272 Instance Data Attributes:
273 theme_elements: Dictionary of tags for text highlighting.
274 The key is the display name and the value is a tuple of
275 (tag name, display sort order).
276
277 Methods [attachment]:
278 load_theme_cfg: Load current highlight colors.
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -0400279 get_color: Invoke colorchooser [button_set_color].
280 set_color_sample_binding: Call set_color_sample [fg_bg_toggle].
terryjreedy9a09c662017-07-13 23:53:30 -0400281 set_highlight_target: set fg_bg_toggle, set_color_sample().
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -0400282 set_color_sample: Set frame background to target.
283 on_new_color_set: Set new color and add option.
terryjreedy9a09c662017-07-13 23:53:30 -0400284 paint_theme_sample: Recolor sample.
285 get_new_theme_name: Get from popup.
286 create_new_theme: Combine theme with changes and save.
287 save_as_new_theme: Save [button_save_custom_theme].
288 set_theme_type: Command for [is_builtin_theme].
289 delete_custom_theme: Ativate default [button_delete_custom_theme].
290 save_new_theme: Save to userCfg['theme'] (is function).
291
292 Widget Structure: (*) widgets bound to self
293 frame
294 frame_custom: LabelFrame
295 (*)text_highlight_sample: Text
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -0400296 (*)frame_color_set: Frame
297 button_set_color: Button
terryjreedy9a09c662017-07-13 23:53:30 -0400298 (*)opt_menu_highlight_target: DynOptionMenu - highlight_target
299 frame_fg_bg_toggle: Frame
300 (*)radio_fg: Radiobutton - fg_bg_toggle
301 (*)radio_bg: Radiobutton - fg_bg_toggle
302 button_save_custom_theme: Button
303 frame_theme: LabelFrame
304 theme_type_title: Label
305 (*)radio_theme_builtin: Radiobutton - is_builtin_theme
306 (*)radio_theme_custom: Radiobutton - is_builtin_theme
307 (*)opt_menu_theme_builtin: DynOptionMenu - builtin_theme
308 (*)opt_menu_theme_custom: DynOptionMenu - custom_theme
309 (*)button_delete_custom_theme: Button
310 (*)new_custom_theme: Label
terryjreedye5bb1122017-07-05 00:54:55 -0400311 """
terryjreedy9a09c662017-07-13 23:53:30 -0400312 self.theme_elements={
313 'Normal Text': ('normal', '00'),
314 'Python Keywords': ('keyword', '01'),
315 'Python Definitions': ('definition', '02'),
316 'Python Builtins': ('builtin', '03'),
317 'Python Comments': ('comment', '04'),
318 'Python Strings': ('string', '05'),
319 'Selected Text': ('hilite', '06'),
320 'Found Text': ('hit', '07'),
321 'Cursor': ('cursor', '08'),
322 'Editor Breakpoint': ('break', '09'),
323 'Shell Normal Text': ('console', '10'),
324 'Shell Error Text': ('error', '11'),
325 'Shell Stdout Text': ('stdout', '12'),
326 'Shell Stderr Text': ('stderr', '13'),
327 }
Terry Jan Reedy22405332014-07-30 19:24:32 -0400328 parent = self.parent
terryjreedy938e7382017-06-26 20:48:39 -0400329 self.builtin_theme = StringVar(parent)
330 self.custom_theme = StringVar(parent)
331 self.fg_bg_toggle = BooleanVar(parent)
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -0400332 self.color = StringVar(parent)
terryjreedy938e7382017-06-26 20:48:39 -0400333 self.is_builtin_theme = BooleanVar(parent)
334 self.highlight_target = StringVar(parent)
Terry Jan Reedy22405332014-07-30 19:24:32 -0400335
Steven M. Gava952d0a52001-08-03 04:43:44 +0000336 ##widget creation
337 #body frame
terryjreedy938e7382017-06-26 20:48:39 -0400338 frame = self.tab_pages.pages['Highlighting'].frame
Steven M. Gava952d0a52001-08-03 04:43:44 +0000339 #body section frames
terryjreedy938e7382017-06-26 20:48:39 -0400340 frame_custom = LabelFrame(frame, borderwidth=2, relief=GROOVE,
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400341 text=' Custom Highlighting ')
terryjreedy938e7382017-06-26 20:48:39 -0400342 frame_theme = LabelFrame(frame, borderwidth=2, relief=GROOVE,
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400343 text=' Highlighting Theme ')
terryjreedy938e7382017-06-26 20:48:39 -0400344 #frame_custom
345 self.text_highlight_sample=Text(
346 frame_custom, relief=SOLID, borderwidth=1,
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400347 font=('courier', 12, ''), cursor='hand2', width=21, height=11,
348 takefocus=FALSE, highlightthickness=0, wrap=NONE)
terryjreedy938e7382017-06-26 20:48:39 -0400349 text=self.text_highlight_sample
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400350 text.bind('<Double-Button-1>', lambda e: 'break')
351 text.bind('<B1-Motion>', lambda e: 'break')
terryjreedy938e7382017-06-26 20:48:39 -0400352 text_and_tags=(
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400353 ('#you can click here', 'comment'), ('\n', 'normal'),
354 ('#to choose items', 'comment'), ('\n', 'normal'),
355 ('def', 'keyword'), (' ', 'normal'),
356 ('func', 'definition'), ('(param):\n ', 'normal'),
357 ('"""string"""', 'string'), ('\n var0 = ', 'normal'),
358 ("'string'", 'string'), ('\n var1 = ', 'normal'),
359 ("'selected'", 'hilite'), ('\n var2 = ', 'normal'),
360 ("'found'", 'hit'), ('\n var3 = ', 'normal'),
361 ('list', 'builtin'), ('(', 'normal'),
Terry Jan Reedya8aa4d52015-10-02 22:12:17 -0400362 ('None', 'keyword'), (')\n', 'normal'),
363 (' breakpoint("line")', 'break'), ('\n\n', 'normal'),
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400364 (' error ', 'error'), (' ', 'normal'),
365 ('cursor |', 'cursor'), ('\n ', 'normal'),
366 ('shell', 'console'), (' ', 'normal'),
367 ('stdout', 'stdout'), (' ', 'normal'),
368 ('stderr', 'stderr'), ('\n', 'normal'))
terryjreedy938e7382017-06-26 20:48:39 -0400369 for texttag in text_and_tags:
370 text.insert(END, texttag[0], texttag[1])
371 for element in self.theme_elements:
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400372 def tem(event, elem=element):
terryjreedy938e7382017-06-26 20:48:39 -0400373 event.widget.winfo_toplevel().highlight_target.set(elem)
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400374 text.tag_bind(
terryjreedy938e7382017-06-26 20:48:39 -0400375 self.theme_elements[element][0], '<ButtonPress-1>', tem)
Steven M. Gavae16d94b2001-11-03 05:07:28 +0000376 text.config(state=DISABLED)
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -0400377 self.frame_color_set = Frame(frame_custom, relief=SOLID, borderwidth=1)
terryjreedy938e7382017-06-26 20:48:39 -0400378 frame_fg_bg_toggle = Frame(frame_custom)
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -0400379 button_set_color = Button(
380 self.frame_color_set, text='Choose Color for :',
381 command=self.get_color, highlightthickness=0)
terryjreedy938e7382017-06-26 20:48:39 -0400382 self.opt_menu_highlight_target = DynOptionMenu(
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -0400383 self.frame_color_set, self.highlight_target, None,
terryjreedy938e7382017-06-26 20:48:39 -0400384 highlightthickness=0) #, command=self.set_highlight_targetBinding
385 self.radio_fg = Radiobutton(
386 frame_fg_bg_toggle, variable=self.fg_bg_toggle, value=1,
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -0400387 text='Foreground', command=self.set_color_sample_binding)
terryjreedy938e7382017-06-26 20:48:39 -0400388 self.radio_bg=Radiobutton(
389 frame_fg_bg_toggle, variable=self.fg_bg_toggle, value=0,
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -0400390 text='Background', command=self.set_color_sample_binding)
terryjreedy938e7382017-06-26 20:48:39 -0400391 self.fg_bg_toggle.set(1)
392 button_save_custom_theme = Button(
393 frame_custom, text='Save as New Custom Theme',
394 command=self.save_as_new_theme)
395 #frame_theme
396 theme_type_title = Label(frame_theme, text='Select : ')
397 self.radio_theme_builtin = Radiobutton(
398 frame_theme, variable=self.is_builtin_theme, value=1,
399 command=self.set_theme_type, text='a Built-in Theme')
400 self.radio_theme_custom = Radiobutton(
401 frame_theme, variable=self.is_builtin_theme, value=0,
402 command=self.set_theme_type, text='a Custom Theme')
403 self.opt_menu_theme_builtin = DynOptionMenu(
404 frame_theme, self.builtin_theme, None, command=None)
405 self.opt_menu_theme_custom=DynOptionMenu(
406 frame_theme, self.custom_theme, None, command=None)
407 self.button_delete_custom_theme=Button(
408 frame_theme, text='Delete Custom Theme',
409 command=self.delete_custom_theme)
410 self.new_custom_theme = Label(frame_theme, bd=2)
Terry Jan Reedy22405332014-07-30 19:24:32 -0400411
Steven M. Gava952d0a52001-08-03 04:43:44 +0000412 ##widget packing
413 #body
terryjreedy938e7382017-06-26 20:48:39 -0400414 frame_custom.pack(side=LEFT, padx=5, pady=5, expand=TRUE, fill=BOTH)
415 frame_theme.pack(side=LEFT, padx=5, pady=5, fill=Y)
416 #frame_custom
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -0400417 self.frame_color_set.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=X)
terryjreedy938e7382017-06-26 20:48:39 -0400418 frame_fg_bg_toggle.pack(side=TOP, padx=5, pady=0)
419 self.text_highlight_sample.pack(
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400420 side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH)
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -0400421 button_set_color.pack(side=TOP, expand=TRUE, fill=X, padx=8, pady=4)
terryjreedy938e7382017-06-26 20:48:39 -0400422 self.opt_menu_highlight_target.pack(
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400423 side=TOP, expand=TRUE, fill=X, padx=8, pady=3)
terryjreedy938e7382017-06-26 20:48:39 -0400424 self.radio_fg.pack(side=LEFT, anchor=E)
425 self.radio_bg.pack(side=RIGHT, anchor=W)
426 button_save_custom_theme.pack(side=BOTTOM, fill=X, padx=5, pady=5)
427 #frame_theme
428 theme_type_title.pack(side=TOP, anchor=W, padx=5, pady=5)
429 self.radio_theme_builtin.pack(side=TOP, anchor=W, padx=5)
430 self.radio_theme_custom.pack(side=TOP, anchor=W, padx=5, pady=2)
431 self.opt_menu_theme_builtin.pack(side=TOP, fill=X, padx=5, pady=5)
432 self.opt_menu_theme_custom.pack(side=TOP, fill=X, anchor=W, padx=5, pady=5)
433 self.button_delete_custom_theme.pack(side=TOP, fill=X, padx=5, pady=5)
Terry Jan Reedyd0c0f002015-11-12 15:02:57 -0500434 self.new_custom_theme.pack(side=TOP, fill=X, pady=5)
Steven M. Gava952d0a52001-08-03 04:43:44 +0000435 return frame
436
terryjreedy938e7382017-06-26 20:48:39 -0400437 def create_page_keys(self):
terryjreedye5bb1122017-07-05 00:54:55 -0400438 """Return frame of widgets for Keys tab.
439
terryjreedy9a09c662017-07-13 23:53:30 -0400440 Tk Variables:
terryjreedye5bb1122017-07-05 00:54:55 -0400441 builtin_keys: Menu variable for built-in keybindings.
442 custom_keys: Menu variable for custom keybindings.
443 are_keys_builtin: Selector for built-in or custom keybindings.
444 keybinding: Action/key bindings.
terryjreedy9a09c662017-07-13 23:53:30 -0400445
446 Methods:
447 load_key_config: Set table.
448 load_keys_list: Reload active set.
449 keybinding_selected: Bound to list_bindings button release.
450 get_new_keys: Command for button_new_keys.
451 get_new_keys_name: Call popup.
452 create_new_key_set: Combine active keyset and changes.
453 set_keys_type: Command for are_keys_builtin.
454 delete_custom_keys: Command for button_delete_custom_keys.
455 save_as_new_key_set: Command for button_save_custom_keys.
456 save_new_key_set: Save to idleConf.userCfg['keys'] (is function).
457 deactivate_current_config: Remove keys bindings in editors.
458
459 Widget Structure: (*) widgets bound to self
460 frame
461 frame_custom: LabelFrame
462 frame_target: Frame
463 target_title: Label
464 scroll_target_y: Scrollbar
465 scroll_target_x: Scrollbar
466 (*)list_bindings: ListBox
467 (*)button_new_keys: Button
468 frame_key_sets: LabelFrame
469 frames[0]: Frame
470 (*)radio_keys_builtin: Radiobutton - are_keys_builtin
471 (*)radio_keys_custom: Radiobutton - are_keys_builtin
472 (*)opt_menu_keys_builtin: DynOptionMenu - builtin_keys
473 (*)opt_menu_keys_custom: DynOptionMenu - custom_keys
474 (*)new_custom_keys: Label
475 frames[1]: Frame
476 (*)button_delete_custom_keys: Button
477 button_save_custom_keys: Button
terryjreedye5bb1122017-07-05 00:54:55 -0400478 """
Terry Jan Reedy22405332014-07-30 19:24:32 -0400479 parent = self.parent
terryjreedy938e7382017-06-26 20:48:39 -0400480 self.builtin_keys = StringVar(parent)
481 self.custom_keys = StringVar(parent)
482 self.are_keys_builtin = BooleanVar(parent)
483 self.keybinding = StringVar(parent)
Terry Jan Reedy22405332014-07-30 19:24:32 -0400484
Steven M. Gava60fc7072001-08-04 13:58:22 +0000485 ##widget creation
486 #body frame
terryjreedy938e7382017-06-26 20:48:39 -0400487 frame = self.tab_pages.pages['Keys'].frame
Steven M. Gava60fc7072001-08-04 13:58:22 +0000488 #body section frames
terryjreedy938e7382017-06-26 20:48:39 -0400489 frame_custom = LabelFrame(
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400490 frame, borderwidth=2, relief=GROOVE,
491 text=' Custom Key Bindings ')
terryjreedy938e7382017-06-26 20:48:39 -0400492 frame_key_sets = LabelFrame(
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400493 frame, borderwidth=2, relief=GROOVE, text=' Key Set ')
terryjreedy938e7382017-06-26 20:48:39 -0400494 #frame_custom
495 frame_target = Frame(frame_custom)
496 target_title = Label(frame_target, text='Action - Key(s)')
497 scroll_target_y = Scrollbar(frame_target)
498 scroll_target_x = Scrollbar(frame_target, orient=HORIZONTAL)
499 self.list_bindings = Listbox(
500 frame_target, takefocus=FALSE, exportselection=FALSE)
501 self.list_bindings.bind('<ButtonRelease-1>', self.keybinding_selected)
502 scroll_target_y.config(command=self.list_bindings.yview)
503 scroll_target_x.config(command=self.list_bindings.xview)
504 self.list_bindings.config(yscrollcommand=scroll_target_y.set)
505 self.list_bindings.config(xscrollcommand=scroll_target_x.set)
506 self.button_new_keys = Button(
507 frame_custom, text='Get New Keys for Selection',
508 command=self.get_new_keys, state=DISABLED)
509 #frame_key_sets
510 frames = [Frame(frame_key_sets, padx=2, pady=2, borderwidth=0)
Christian Heimes9a371592007-12-28 14:08:13 +0000511 for i in range(2)]
terryjreedy938e7382017-06-26 20:48:39 -0400512 self.radio_keys_builtin = Radiobutton(
513 frames[0], variable=self.are_keys_builtin, value=1,
514 command=self.set_keys_type, text='Use a Built-in Key Set')
515 self.radio_keys_custom = Radiobutton(
516 frames[0], variable=self.are_keys_builtin, value=0,
517 command=self.set_keys_type, text='Use a Custom Key Set')
518 self.opt_menu_keys_builtin = DynOptionMenu(
519 frames[0], self.builtin_keys, None, command=None)
520 self.opt_menu_keys_custom = DynOptionMenu(
521 frames[0], self.custom_keys, None, command=None)
522 self.button_delete_custom_keys = Button(
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400523 frames[1], text='Delete Custom Key Set',
terryjreedy938e7382017-06-26 20:48:39 -0400524 command=self.delete_custom_keys)
525 button_save_custom_keys = Button(
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400526 frames[1], text='Save as New Custom Key Set',
terryjreedy938e7382017-06-26 20:48:39 -0400527 command=self.save_as_new_key_set)
Terry Jan Reedy9bdb1ed2016-07-10 13:46:34 -0400528 self.new_custom_keys = Label(frames[0], bd=2)
Terry Jan Reedy22405332014-07-30 19:24:32 -0400529
Steven M. Gava60fc7072001-08-04 13:58:22 +0000530 ##widget packing
531 #body
terryjreedy938e7382017-06-26 20:48:39 -0400532 frame_custom.pack(side=BOTTOM, padx=5, pady=5, expand=TRUE, fill=BOTH)
533 frame_key_sets.pack(side=BOTTOM, padx=5, pady=5, fill=BOTH)
534 #frame_custom
535 self.button_new_keys.pack(side=BOTTOM, fill=X, padx=5, pady=5)
536 frame_target.pack(side=LEFT, padx=5, pady=5, expand=TRUE, fill=BOTH)
Steven M. Gavafacfc092002-01-19 00:29:54 +0000537 #frame target
terryjreedy938e7382017-06-26 20:48:39 -0400538 frame_target.columnconfigure(0, weight=1)
539 frame_target.rowconfigure(1, weight=1)
540 target_title.grid(row=0, column=0, columnspan=2, sticky=W)
541 self.list_bindings.grid(row=1, column=0, sticky=NSEW)
542 scroll_target_y.grid(row=1, column=1, sticky=NS)
543 scroll_target_x.grid(row=2, column=0, sticky=EW)
544 #frame_key_sets
545 self.radio_keys_builtin.grid(row=0, column=0, sticky=W+NS)
546 self.radio_keys_custom.grid(row=1, column=0, sticky=W+NS)
547 self.opt_menu_keys_builtin.grid(row=0, column=1, sticky=NSEW)
548 self.opt_menu_keys_custom.grid(row=1, column=1, sticky=NSEW)
Terry Jan Reedy9bdb1ed2016-07-10 13:46:34 -0400549 self.new_custom_keys.grid(row=0, column=2, sticky=NSEW, padx=5, pady=5)
terryjreedy938e7382017-06-26 20:48:39 -0400550 self.button_delete_custom_keys.pack(side=LEFT, fill=X, expand=True, padx=2)
551 button_save_custom_keys.pack(side=LEFT, fill=X, expand=True, padx=2)
Christian Heimes9a371592007-12-28 14:08:13 +0000552 frames[0].pack(side=TOP, fill=BOTH, expand=True)
553 frames[1].pack(side=TOP, fill=X, expand=True, pady=2)
Steven M. Gava952d0a52001-08-03 04:43:44 +0000554 return frame
555
terryjreedy938e7382017-06-26 20:48:39 -0400556 def create_page_general(self):
terryjreedye5bb1122017-07-05 00:54:55 -0400557 """Return frame of widgets for General tab.
558
terryjreedy9a09c662017-07-13 23:53:30 -0400559 Tk Variables:
terryjreedye5bb1122017-07-05 00:54:55 -0400560 win_width: Initial window width in characters.
561 win_height: Initial window height in characters.
562 startup_edit: Selector for opening in editor or shell mode.
563 autosave: Selector for save prompt popup when using Run.
terryjreedy9a09c662017-07-13 23:53:30 -0400564
565 Methods:
566 load_general_config:
567 help_source_selected: Bound to list_help button release.
568 set_helplist_button_states: Toggle based on list.
569 helplist_item_edit: Command for button_helplist_edit.
570 helplist_item_add: Command for button_helplist_add.
571 helplist_item_remove: Command for button_helplist_remove.
572 update_user_help_changed_items: Fill in changes.
573
574 Widget Structure: (*) widgets bound to self
575 frame
576 frame_run: LabelFrame
577 startup_title: Label
578 (*)radio_startup_edit: Radiobutton - startup_edit
579 (*)radio_startup_shell: Radiobutton - startup_edit
580 frame_save: LabelFrame
581 run_save_title: Label
582 (*)radio_save_ask: Radiobutton - autosave
583 (*)radio_save_auto: Radiobutton - autosave
584 frame_win_size: LabelFrame
585 win_size_title: Label
586 win_width_title: Label
587 (*)entry_win_width: Entry - win_width
588 win_height_title: Label
589 (*)entry_win_height: Entry - win_height
590 frame_help: LabelFrame
591 frame_helplist: Frame
592 frame_helplist_buttons: Frame
593 (*)button_helplist_edit
594 (*)button_helplist_add
595 (*)button_helplist_remove
596 scroll_helplist: Scrollbar
597 (*)list_help: ListBox
terryjreedye5bb1122017-07-05 00:54:55 -0400598 """
Terry Jan Reedy22405332014-07-30 19:24:32 -0400599 parent = self.parent
terryjreedy938e7382017-06-26 20:48:39 -0400600 self.win_width = StringVar(parent)
601 self.win_height = StringVar(parent)
602 self.startup_edit = IntVar(parent)
603 self.autosave = IntVar(parent)
Terry Jan Reedy22405332014-07-30 19:24:32 -0400604
Steven M. Gava230e5782001-08-07 03:28:25 +0000605 #widget creation
606 #body
terryjreedy938e7382017-06-26 20:48:39 -0400607 frame = self.tab_pages.pages['General'].frame
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000608 #body section frames
terryjreedy938e7382017-06-26 20:48:39 -0400609 frame_run = LabelFrame(frame, borderwidth=2, relief=GROOVE,
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400610 text=' Startup Preferences ')
terryjreedy938e7382017-06-26 20:48:39 -0400611 frame_save = LabelFrame(frame, borderwidth=2, relief=GROOVE,
612 text=' autosave Preferences ')
613 frame_win_size = Frame(frame, borderwidth=2, relief=GROOVE)
614 frame_help = LabelFrame(frame, borderwidth=2, relief=GROOVE,
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400615 text=' Additional Help Sources ')
terryjreedy938e7382017-06-26 20:48:39 -0400616 #frame_run
617 startup_title = Label(frame_run, text='At Startup')
618 self.radio_startup_edit = Radiobutton(
619 frame_run, variable=self.startup_edit, value=1,
Terry Jan Reedyf46b7822016-11-07 17:15:01 -0500620 text="Open Edit Window")
terryjreedy938e7382017-06-26 20:48:39 -0400621 self.radio_startup_shell = Radiobutton(
622 frame_run, variable=self.startup_edit, value=0,
Terry Jan Reedyf46b7822016-11-07 17:15:01 -0500623 text='Open Shell Window')
terryjreedy938e7382017-06-26 20:48:39 -0400624 #frame_save
625 run_save_title = Label(frame_save, text='At Start of Run (F5) ')
626 self.radio_save_ask = Radiobutton(
627 frame_save, variable=self.autosave, value=0,
Terry Jan Reedyf46b7822016-11-07 17:15:01 -0500628 text="Prompt to Save")
terryjreedy938e7382017-06-26 20:48:39 -0400629 self.radio_save_auto = Radiobutton(
630 frame_save, variable=self.autosave, value=1,
Terry Jan Reedyf46b7822016-11-07 17:15:01 -0500631 text='No Prompt')
terryjreedy938e7382017-06-26 20:48:39 -0400632 #frame_win_size
633 win_size_title = Label(
634 frame_win_size, text='Initial Window Size (in characters)')
635 win_width_title = Label(frame_win_size, text='Width')
636 self.entry_win_width = Entry(
637 frame_win_size, textvariable=self.win_width, width=3)
638 win_height_title = Label(frame_win_size, text='Height')
639 self.entry_win_height = Entry(
640 frame_win_size, textvariable=self.win_height, width=3)
641 #frame_help
642 frame_helplist = Frame(frame_help)
643 frame_helplist_buttons = Frame(frame_helplist)
644 scroll_helplist = Scrollbar(frame_helplist)
645 self.list_help = Listbox(
646 frame_helplist, height=5, takefocus=FALSE,
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000647 exportselection=FALSE)
terryjreedy938e7382017-06-26 20:48:39 -0400648 scroll_helplist.config(command=self.list_help.yview)
649 self.list_help.config(yscrollcommand=scroll_helplist.set)
650 self.list_help.bind('<ButtonRelease-1>', self.help_source_selected)
651 self.button_helplist_edit = Button(
652 frame_helplist_buttons, text='Edit', state=DISABLED,
653 width=8, command=self.helplist_item_edit)
654 self.button_helplist_add = Button(
655 frame_helplist_buttons, text='Add',
656 width=8, command=self.helplist_item_add)
657 self.button_helplist_remove = Button(
658 frame_helplist_buttons, text='Remove', state=DISABLED,
659 width=8, command=self.helplist_item_remove)
Terry Jan Reedy22405332014-07-30 19:24:32 -0400660
Steven M. Gava230e5782001-08-07 03:28:25 +0000661 #widget packing
662 #body
terryjreedy938e7382017-06-26 20:48:39 -0400663 frame_run.pack(side=TOP, padx=5, pady=5, fill=X)
664 frame_save.pack(side=TOP, padx=5, pady=5, fill=X)
665 frame_win_size.pack(side=TOP, padx=5, pady=5, fill=X)
666 frame_help.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH)
667 #frame_run
668 startup_title.pack(side=LEFT, anchor=W, padx=5, pady=5)
669 self.radio_startup_shell.pack(side=RIGHT, anchor=W, padx=5, pady=5)
670 self.radio_startup_edit.pack(side=RIGHT, anchor=W, padx=5, pady=5)
671 #frame_save
672 run_save_title.pack(side=LEFT, anchor=W, padx=5, pady=5)
673 self.radio_save_auto.pack(side=RIGHT, anchor=W, padx=5, pady=5)
674 self.radio_save_ask.pack(side=RIGHT, anchor=W, padx=5, pady=5)
675 #frame_win_size
676 win_size_title.pack(side=LEFT, anchor=W, padx=5, pady=5)
677 self.entry_win_height.pack(side=RIGHT, anchor=E, padx=10, pady=5)
678 win_height_title.pack(side=RIGHT, anchor=E, pady=5)
679 self.entry_win_width.pack(side=RIGHT, anchor=E, padx=10, pady=5)
680 win_width_title.pack(side=RIGHT, anchor=E, pady=5)
681 #frame_help
682 frame_helplist_buttons.pack(side=RIGHT, padx=5, pady=5, fill=Y)
683 frame_helplist.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH)
684 scroll_helplist.pack(side=RIGHT, anchor=W, fill=Y)
685 self.list_help.pack(side=LEFT, anchor=E, expand=TRUE, fill=BOTH)
686 self.button_helplist_edit.pack(side=TOP, anchor=W, pady=5)
687 self.button_helplist_add.pack(side=TOP, anchor=W)
688 self.button_helplist_remove.pack(side=TOP, anchor=W, pady=5)
Steven M. Gava952d0a52001-08-03 04:43:44 +0000689 return frame
690
terryjreedy938e7382017-06-26 20:48:39 -0400691 def attach_var_callbacks(self):
terryjreedye5bb1122017-07-05 00:54:55 -0400692 "Attach callbacks to variables that can be changed."
terryjreedy938e7382017-06-26 20:48:39 -0400693 self.font_size.trace_add('write', self.var_changed_font)
694 self.font_name.trace_add('write', self.var_changed_font)
695 self.font_bold.trace_add('write', self.var_changed_font)
696 self.space_num.trace_add('write', self.var_changed_space_num)
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -0400697 self.color.trace_add('write', self.var_changed_color)
terryjreedy938e7382017-06-26 20:48:39 -0400698 self.builtin_theme.trace_add('write', self.var_changed_builtin_theme)
699 self.custom_theme.trace_add('write', self.var_changed_custom_theme)
700 self.is_builtin_theme.trace_add('write', self.var_changed_is_builtin_theme)
701 self.highlight_target.trace_add('write', self.var_changed_highlight_target)
702 self.keybinding.trace_add('write', self.var_changed_keybinding)
703 self.builtin_keys.trace_add('write', self.var_changed_builtin_keys)
704 self.custom_keys.trace_add('write', self.var_changed_custom_keys)
705 self.are_keys_builtin.trace_add('write', self.var_changed_are_keys_builtin)
706 self.win_width.trace_add('write', self.var_changed_win_width)
707 self.win_height.trace_add('write', self.var_changed_win_height)
708 self.startup_edit.trace_add('write', self.var_changed_startup_edit)
709 self.autosave.trace_add('write', self.var_changed_autosave)
Steven M. Gava052937f2002-02-11 02:20:53 +0000710
Terry Jan Reedy6b98ce22016-05-16 22:27:28 -0400711 def remove_var_callbacks(self):
712 "Remove callbacks to prevent memory leaks."
713 for var in (
terryjreedy938e7382017-06-26 20:48:39 -0400714 self.font_size, self.font_name, self.font_bold,
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -0400715 self.space_num, self.color, self.builtin_theme,
terryjreedy938e7382017-06-26 20:48:39 -0400716 self.custom_theme, self.is_builtin_theme, self.highlight_target,
717 self.keybinding, self.builtin_keys, self.custom_keys,
718 self.are_keys_builtin, self.win_width, self.win_height,
terryjreedy8e3f73e2017-07-10 15:11:45 -0400719 self.startup_edit, self.autosave,):
Serhiy Storchaka81221742016-06-26 09:46:57 +0300720 var.trace_remove('write', var.trace_info()[0][1])
Terry Jan Reedy6b98ce22016-05-16 22:27:28 -0400721
terryjreedy938e7382017-06-26 20:48:39 -0400722 def var_changed_font(self, *params):
terryjreedye5bb1122017-07-05 00:54:55 -0400723 """Store changes to font attributes.
724
725 When one font attribute changes, save them all, as they are
Terry Jan Reedyd87d1682015-08-01 18:57:33 -0400726 not independent from each other. In particular, when we are
727 overriding the default font, we need to write out everything.
terryjreedye5bb1122017-07-05 00:54:55 -0400728 """
terryjreedy938e7382017-06-26 20:48:39 -0400729 value = self.font_name.get()
terryjreedyedc03422017-07-07 16:37:39 -0400730 changes.add_option('main', 'EditorWindow', 'font', value)
terryjreedy938e7382017-06-26 20:48:39 -0400731 value = self.font_size.get()
terryjreedyedc03422017-07-07 16:37:39 -0400732 changes.add_option('main', 'EditorWindow', 'font-size', value)
terryjreedy938e7382017-06-26 20:48:39 -0400733 value = self.font_bold.get()
terryjreedyedc03422017-07-07 16:37:39 -0400734 changes.add_option('main', 'EditorWindow', 'font-bold', value)
Steven M. Gavac112cd82002-01-22 05:56:40 +0000735
terryjreedy938e7382017-06-26 20:48:39 -0400736 def var_changed_space_num(self, *params):
terryjreedye5bb1122017-07-05 00:54:55 -0400737 "Store change to indentation size."
terryjreedy938e7382017-06-26 20:48:39 -0400738 value = self.space_num.get()
terryjreedyedc03422017-07-07 16:37:39 -0400739 changes.add_option('main', 'Indent', 'num-spaces', value)
Steven M. Gavac112cd82002-01-22 05:56:40 +0000740
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -0400741 def var_changed_color(self, *params):
terryjreedye5bb1122017-07-05 00:54:55 -0400742 "Process change to color choice."
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -0400743 self.on_new_color_set()
Steven M. Gava052937f2002-02-11 02:20:53 +0000744
terryjreedy938e7382017-06-26 20:48:39 -0400745 def var_changed_builtin_theme(self, *params):
terryjreedye5bb1122017-07-05 00:54:55 -0400746 """Process new builtin theme selection.
747
748 Add the changed theme's name to the changed_items and recreate
749 the sample with the values from the selected theme.
750 """
terryjreedy938e7382017-06-26 20:48:39 -0400751 old_themes = ('IDLE Classic', 'IDLE New')
752 value = self.builtin_theme.get()
753 if value not in old_themes:
754 if idleConf.GetOption('main', 'Theme', 'name') not in old_themes:
terryjreedyedc03422017-07-07 16:37:39 -0400755 changes.add_option('main', 'Theme', 'name', old_themes[0])
756 changes.add_option('main', 'Theme', 'name2', value)
Terry Jan Reedyd0c0f002015-11-12 15:02:57 -0500757 self.new_custom_theme.config(text='New theme, see Help',
758 fg='#500000')
759 else:
terryjreedyedc03422017-07-07 16:37:39 -0400760 changes.add_option('main', 'Theme', 'name', value)
761 changes.add_option('main', 'Theme', 'name2', '')
Terry Jan Reedyd0c0f002015-11-12 15:02:57 -0500762 self.new_custom_theme.config(text='', fg='black')
terryjreedy938e7382017-06-26 20:48:39 -0400763 self.paint_theme_sample()
Steven M. Gava052937f2002-02-11 02:20:53 +0000764
terryjreedy938e7382017-06-26 20:48:39 -0400765 def var_changed_custom_theme(self, *params):
terryjreedye5bb1122017-07-05 00:54:55 -0400766 """Process new custom theme selection.
767
768 If a new custom theme is selected, add the name to the
769 changed_items and apply the theme to the sample.
770 """
terryjreedy938e7382017-06-26 20:48:39 -0400771 value = self.custom_theme.get()
Steven M. Gava49745752002-02-18 01:43:11 +0000772 if value != '- no custom themes -':
terryjreedyedc03422017-07-07 16:37:39 -0400773 changes.add_option('main', 'Theme', 'name', value)
terryjreedy938e7382017-06-26 20:48:39 -0400774 self.paint_theme_sample()
Steven M. Gava052937f2002-02-11 02:20:53 +0000775
terryjreedy938e7382017-06-26 20:48:39 -0400776 def var_changed_is_builtin_theme(self, *params):
terryjreedye5bb1122017-07-05 00:54:55 -0400777 """Process toggle between builtin and custom theme.
778
779 Update the default toggle value and apply the newly
780 selected theme type.
781 """
terryjreedy938e7382017-06-26 20:48:39 -0400782 value = self.is_builtin_theme.get()
terryjreedyedc03422017-07-07 16:37:39 -0400783 changes.add_option('main', 'Theme', 'default', value)
Steven M. Gavaf31eec02002-03-05 00:25:58 +0000784 if value:
terryjreedy938e7382017-06-26 20:48:39 -0400785 self.var_changed_builtin_theme()
Steven M. Gavaf31eec02002-03-05 00:25:58 +0000786 else:
terryjreedy938e7382017-06-26 20:48:39 -0400787 self.var_changed_custom_theme()
Steven M. Gava052937f2002-02-11 02:20:53 +0000788
terryjreedy938e7382017-06-26 20:48:39 -0400789 def var_changed_highlight_target(self, *params):
terryjreedye5bb1122017-07-05 00:54:55 -0400790 "Process selection of new target tag for highlighting."
terryjreedy938e7382017-06-26 20:48:39 -0400791 self.set_highlight_target()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000792
terryjreedy938e7382017-06-26 20:48:39 -0400793 def var_changed_keybinding(self, *params):
terryjreedye5bb1122017-07-05 00:54:55 -0400794 "Store change to a keybinding."
terryjreedy938e7382017-06-26 20:48:39 -0400795 value = self.keybinding.get()
796 key_set = self.custom_keys.get()
797 event = self.list_bindings.get(ANCHOR).split()[0]
Steven M. Gavaa498af22002-02-01 01:33:36 +0000798 if idleConf.IsCoreBinding(event):
terryjreedyedc03422017-07-07 16:37:39 -0400799 changes.add_option('keys', key_set, event, value)
terryjreedye5bb1122017-07-05 00:54:55 -0400800 else: # Event is an extension binding.
terryjreedy938e7382017-06-26 20:48:39 -0400801 ext_name = idleConf.GetExtnNameForEvent(event)
802 ext_keybind_section = ext_name + '_cfgBindings'
terryjreedyedc03422017-07-07 16:37:39 -0400803 changes.add_option('extensions', ext_keybind_section, event, value)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000804
terryjreedy938e7382017-06-26 20:48:39 -0400805 def var_changed_builtin_keys(self, *params):
terryjreedye5bb1122017-07-05 00:54:55 -0400806 "Process selection of builtin key set."
terryjreedy938e7382017-06-26 20:48:39 -0400807 old_keys = (
Terry Jan Reedy9bdb1ed2016-07-10 13:46:34 -0400808 'IDLE Classic Windows',
809 'IDLE Classic Unix',
810 'IDLE Classic Mac',
811 'IDLE Classic OSX',
812 )
terryjreedy938e7382017-06-26 20:48:39 -0400813 value = self.builtin_keys.get()
814 if value not in old_keys:
815 if idleConf.GetOption('main', 'Keys', 'name') not in old_keys:
terryjreedyedc03422017-07-07 16:37:39 -0400816 changes.add_option('main', 'Keys', 'name', old_keys[0])
817 changes.add_option('main', 'Keys', 'name2', value)
Terry Jan Reedy9bdb1ed2016-07-10 13:46:34 -0400818 self.new_custom_keys.config(text='New key set, see Help',
819 fg='#500000')
820 else:
terryjreedyedc03422017-07-07 16:37:39 -0400821 changes.add_option('main', 'Keys', 'name', value)
822 changes.add_option('main', 'Keys', 'name2', '')
Terry Jan Reedy9bdb1ed2016-07-10 13:46:34 -0400823 self.new_custom_keys.config(text='', fg='black')
terryjreedy938e7382017-06-26 20:48:39 -0400824 self.load_keys_list(value)
Steven M. Gava052937f2002-02-11 02:20:53 +0000825
terryjreedy938e7382017-06-26 20:48:39 -0400826 def var_changed_custom_keys(self, *params):
terryjreedye5bb1122017-07-05 00:54:55 -0400827 "Process selection of custom key set."
terryjreedy938e7382017-06-26 20:48:39 -0400828 value = self.custom_keys.get()
Steven M. Gava49745752002-02-18 01:43:11 +0000829 if value != '- no custom keys -':
terryjreedyedc03422017-07-07 16:37:39 -0400830 changes.add_option('main', 'Keys', 'name', value)
terryjreedy938e7382017-06-26 20:48:39 -0400831 self.load_keys_list(value)
Steven M. Gava052937f2002-02-11 02:20:53 +0000832
terryjreedy938e7382017-06-26 20:48:39 -0400833 def var_changed_are_keys_builtin(self, *params):
terryjreedye5bb1122017-07-05 00:54:55 -0400834 "Process toggle between builtin key set and custom key set."
terryjreedy938e7382017-06-26 20:48:39 -0400835 value = self.are_keys_builtin.get()
terryjreedyedc03422017-07-07 16:37:39 -0400836 changes.add_option('main', 'Keys', 'default', value)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000837 if value:
terryjreedy938e7382017-06-26 20:48:39 -0400838 self.var_changed_builtin_keys()
Steven M. Gava052937f2002-02-11 02:20:53 +0000839 else:
terryjreedy938e7382017-06-26 20:48:39 -0400840 self.var_changed_custom_keys()
Steven M. Gava052937f2002-02-11 02:20:53 +0000841
terryjreedy938e7382017-06-26 20:48:39 -0400842 def var_changed_win_width(self, *params):
terryjreedye5bb1122017-07-05 00:54:55 -0400843 "Store change to window width."
terryjreedy938e7382017-06-26 20:48:39 -0400844 value = self.win_width.get()
terryjreedyedc03422017-07-07 16:37:39 -0400845 changes.add_option('main', 'EditorWindow', 'width', value)
Steven M. Gavac112cd82002-01-22 05:56:40 +0000846
terryjreedy938e7382017-06-26 20:48:39 -0400847 def var_changed_win_height(self, *params):
terryjreedye5bb1122017-07-05 00:54:55 -0400848 "Store change to window height."
terryjreedy938e7382017-06-26 20:48:39 -0400849 value = self.win_height.get()
terryjreedyedc03422017-07-07 16:37:39 -0400850 changes.add_option('main', 'EditorWindow', 'height', value)
Steven M. Gavac112cd82002-01-22 05:56:40 +0000851
terryjreedy938e7382017-06-26 20:48:39 -0400852 def var_changed_startup_edit(self, *params):
terryjreedye5bb1122017-07-05 00:54:55 -0400853 "Store change to toggle for starting IDLE in the editor or shell."
terryjreedy938e7382017-06-26 20:48:39 -0400854 value = self.startup_edit.get()
terryjreedyedc03422017-07-07 16:37:39 -0400855 changes.add_option('main', 'General', 'editor-on-startup', value)
Steven M. Gavac112cd82002-01-22 05:56:40 +0000856
terryjreedy938e7382017-06-26 20:48:39 -0400857 def var_changed_autosave(self, *params):
terryjreedye5bb1122017-07-05 00:54:55 -0400858 "Store change to autosave."
terryjreedy938e7382017-06-26 20:48:39 -0400859 value = self.autosave.get()
terryjreedyedc03422017-07-07 16:37:39 -0400860 changes.add_option('main', 'General', 'autosave', value)
Kurt B. Kaiser6c638b62003-05-26 06:23:10 +0000861
terryjreedy938e7382017-06-26 20:48:39 -0400862 def set_theme_type(self):
terryjreedy9a09c662017-07-13 23:53:30 -0400863 """Set available screen options based on builtin or custom theme.
864
865 Attributes accessed:
866 is_builtin_theme
867
868 Attributes updated:
869 opt_menu_theme_builtin
870 opt_menu_theme_custom
871 button_delete_custom_theme
872 radio_theme_custom
873
874 Called from:
875 handler for radio_theme_builtin and radio_theme_custom
876 delete_custom_theme
877 create_new_theme
878 load_theme_cfg
879 """
terryjreedy938e7382017-06-26 20:48:39 -0400880 if self.is_builtin_theme.get():
881 self.opt_menu_theme_builtin.config(state=NORMAL)
882 self.opt_menu_theme_custom.config(state=DISABLED)
883 self.button_delete_custom_theme.config(state=DISABLED)
Steven M. Gava5f28e8f2002-01-21 06:38:21 +0000884 else:
terryjreedy938e7382017-06-26 20:48:39 -0400885 self.opt_menu_theme_builtin.config(state=DISABLED)
886 self.radio_theme_custom.config(state=NORMAL)
887 self.opt_menu_theme_custom.config(state=NORMAL)
888 self.button_delete_custom_theme.config(state=NORMAL)
Steven M. Gava5f28e8f2002-01-21 06:38:21 +0000889
terryjreedy938e7382017-06-26 20:48:39 -0400890 def set_keys_type(self):
terryjreedye5bb1122017-07-05 00:54:55 -0400891 "Set available screen options based on builtin or custom key set."
terryjreedy938e7382017-06-26 20:48:39 -0400892 if self.are_keys_builtin.get():
893 self.opt_menu_keys_builtin.config(state=NORMAL)
894 self.opt_menu_keys_custom.config(state=DISABLED)
895 self.button_delete_custom_keys.config(state=DISABLED)
Steven M. Gava5f28e8f2002-01-21 06:38:21 +0000896 else:
terryjreedy938e7382017-06-26 20:48:39 -0400897 self.opt_menu_keys_builtin.config(state=DISABLED)
898 self.radio_keys_custom.config(state=NORMAL)
899 self.opt_menu_keys_custom.config(state=NORMAL)
900 self.button_delete_custom_keys.config(state=NORMAL)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000901
terryjreedy938e7382017-06-26 20:48:39 -0400902 def get_new_keys(self):
terryjreedye5bb1122017-07-05 00:54:55 -0400903 """Handle event to change key binding for selected line.
904
905 A selection of a key/binding in the list of current
906 bindings pops up a dialog to enter a new binding. If
907 the current key set is builtin and a binding has
908 changed, then a name for a custom key set needs to be
909 entered for the change to be applied.
910 """
terryjreedy938e7382017-06-26 20:48:39 -0400911 list_index = self.list_bindings.index(ANCHOR)
912 binding = self.list_bindings.get(list_index)
terryjreedye5bb1122017-07-05 00:54:55 -0400913 bind_name = binding.split()[0]
terryjreedy938e7382017-06-26 20:48:39 -0400914 if self.are_keys_builtin.get():
915 current_key_set_name = self.builtin_keys.get()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000916 else:
terryjreedy938e7382017-06-26 20:48:39 -0400917 current_key_set_name = self.custom_keys.get()
918 current_bindings = idleConf.GetCurrentKeySet()
terryjreedyedc03422017-07-07 16:37:39 -0400919 if current_key_set_name in changes['keys']: # unsaved changes
920 key_set_changes = changes['keys'][current_key_set_name]
terryjreedy938e7382017-06-26 20:48:39 -0400921 for event in key_set_changes:
922 current_bindings[event] = key_set_changes[event].split()
923 current_key_sequences = list(current_bindings.values())
924 new_keys = GetKeysDialog(self, 'Get New Keys', bind_name,
925 current_key_sequences).result
terryjreedye5bb1122017-07-05 00:54:55 -0400926 if new_keys:
927 if self.are_keys_builtin.get(): # Current key set is a built-in.
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400928 message = ('Your changes will be saved as a new Custom Key Set.'
929 ' Enter a name for your new Custom Key Set below.')
terryjreedy938e7382017-06-26 20:48:39 -0400930 new_keyset = self.get_new_keys_name(message)
terryjreedye5bb1122017-07-05 00:54:55 -0400931 if not new_keyset: # User cancelled custom key set creation.
terryjreedy938e7382017-06-26 20:48:39 -0400932 self.list_bindings.select_set(list_index)
933 self.list_bindings.select_anchor(list_index)
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000934 return
terryjreedye5bb1122017-07-05 00:54:55 -0400935 else: # Create new custom key set based on previously active key set.
terryjreedy938e7382017-06-26 20:48:39 -0400936 self.create_new_key_set(new_keyset)
937 self.list_bindings.delete(list_index)
938 self.list_bindings.insert(list_index, bind_name+' - '+new_keys)
939 self.list_bindings.select_set(list_index)
940 self.list_bindings.select_anchor(list_index)
941 self.keybinding.set(new_keys)
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000942 else:
terryjreedy938e7382017-06-26 20:48:39 -0400943 self.list_bindings.select_set(list_index)
944 self.list_bindings.select_anchor(list_index)
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000945
terryjreedy938e7382017-06-26 20:48:39 -0400946 def get_new_keys_name(self, message):
terryjreedye5bb1122017-07-05 00:54:55 -0400947 "Return new key set name from query popup."
terryjreedy938e7382017-06-26 20:48:39 -0400948 used_names = (idleConf.GetSectionList('user', 'keys') +
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400949 idleConf.GetSectionList('default', 'keys'))
terryjreedy938e7382017-06-26 20:48:39 -0400950 new_keyset = SectionName(
951 self, 'New Custom Key Set', message, used_names).result
952 return new_keyset
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000953
terryjreedy938e7382017-06-26 20:48:39 -0400954 def save_as_new_key_set(self):
terryjreedye5bb1122017-07-05 00:54:55 -0400955 "Prompt for name of new key set and save changes using that name."
terryjreedy938e7382017-06-26 20:48:39 -0400956 new_keys_name = self.get_new_keys_name('New Key Set Name:')
957 if new_keys_name:
958 self.create_new_key_set(new_keys_name)
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000959
terryjreedy938e7382017-06-26 20:48:39 -0400960 def keybinding_selected(self, event):
terryjreedye5bb1122017-07-05 00:54:55 -0400961 "Activate button to assign new keys to selected action."
terryjreedy938e7382017-06-26 20:48:39 -0400962 self.button_new_keys.config(state=NORMAL)
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000963
terryjreedy938e7382017-06-26 20:48:39 -0400964 def create_new_key_set(self, new_key_set_name):
terryjreedye5bb1122017-07-05 00:54:55 -0400965 """Create a new custom key set with the given name.
966
967 Create the new key set based on the previously active set
968 with the current changes applied. Once it is saved, then
969 activate the new key set.
970 """
terryjreedy938e7382017-06-26 20:48:39 -0400971 if self.are_keys_builtin.get():
972 prev_key_set_name = self.builtin_keys.get()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000973 else:
terryjreedy938e7382017-06-26 20:48:39 -0400974 prev_key_set_name = self.custom_keys.get()
975 prev_keys = idleConf.GetCoreKeys(prev_key_set_name)
976 new_keys = {}
terryjreedye5bb1122017-07-05 00:54:55 -0400977 for event in prev_keys: # Add key set to changed items.
978 event_name = event[2:-2] # Trim off the angle brackets.
terryjreedy938e7382017-06-26 20:48:39 -0400979 binding = ' '.join(prev_keys[event])
980 new_keys[event_name] = binding
terryjreedye5bb1122017-07-05 00:54:55 -0400981 # Handle any unsaved changes to prev key set.
terryjreedyedc03422017-07-07 16:37:39 -0400982 if prev_key_set_name in changes['keys']:
983 key_set_changes = changes['keys'][prev_key_set_name]
terryjreedy938e7382017-06-26 20:48:39 -0400984 for event in key_set_changes:
985 new_keys[event] = key_set_changes[event]
terryjreedye5bb1122017-07-05 00:54:55 -0400986 # Save the new key set.
terryjreedy938e7382017-06-26 20:48:39 -0400987 self.save_new_key_set(new_key_set_name, new_keys)
terryjreedye5bb1122017-07-05 00:54:55 -0400988 # Change GUI over to the new key set.
terryjreedy938e7382017-06-26 20:48:39 -0400989 custom_key_list = idleConf.GetSectionList('user', 'keys')
990 custom_key_list.sort()
991 self.opt_menu_keys_custom.SetMenu(custom_key_list, new_key_set_name)
992 self.are_keys_builtin.set(0)
993 self.set_keys_type()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000994
terryjreedy938e7382017-06-26 20:48:39 -0400995 def load_keys_list(self, keyset_name):
terryjreedye5bb1122017-07-05 00:54:55 -0400996 """Reload the list of action/key binding pairs for the active key set.
997
998 An action/key binding can be selected to change the key binding.
999 """
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001000 reselect = 0
terryjreedy938e7382017-06-26 20:48:39 -04001001 if self.list_bindings.curselection():
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001002 reselect = 1
terryjreedy938e7382017-06-26 20:48:39 -04001003 list_index = self.list_bindings.index(ANCHOR)
1004 keyset = idleConf.GetKeySet(keyset_name)
1005 bind_names = list(keyset.keys())
1006 bind_names.sort()
1007 self.list_bindings.delete(0, END)
1008 for bind_name in bind_names:
terryjreedye5bb1122017-07-05 00:54:55 -04001009 key = ' '.join(keyset[bind_name])
1010 bind_name = bind_name[2:-2] # Trim off the angle brackets.
terryjreedyedc03422017-07-07 16:37:39 -04001011 if keyset_name in changes['keys']:
terryjreedye5bb1122017-07-05 00:54:55 -04001012 # Handle any unsaved changes to this key set.
terryjreedyedc03422017-07-07 16:37:39 -04001013 if bind_name in changes['keys'][keyset_name]:
1014 key = changes['keys'][keyset_name][bind_name]
terryjreedy938e7382017-06-26 20:48:39 -04001015 self.list_bindings.insert(END, bind_name+' - '+key)
Steven M. Gava052937f2002-02-11 02:20:53 +00001016 if reselect:
terryjreedy938e7382017-06-26 20:48:39 -04001017 self.list_bindings.see(list_index)
1018 self.list_bindings.select_set(list_index)
1019 self.list_bindings.select_anchor(list_index)
Steven M. Gava052937f2002-02-11 02:20:53 +00001020
terryjreedy938e7382017-06-26 20:48:39 -04001021 def delete_custom_keys(self):
terryjreedye5bb1122017-07-05 00:54:55 -04001022 """Handle event to delete a custom key set.
1023
1024 Applying the delete deactivates the current configuration and
1025 reverts to the default. The custom key set is permanently
1026 deleted from the config file.
1027 """
terryjreedy938e7382017-06-26 20:48:39 -04001028 keyset_name=self.custom_keys.get()
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001029 delmsg = 'Are you sure you wish to delete the key set %r ?'
1030 if not tkMessageBox.askyesno(
terryjreedy938e7382017-06-26 20:48:39 -04001031 'Delete Key Set', delmsg % keyset_name, parent=self):
Steven M. Gava49745752002-02-18 01:43:11 +00001032 return
terryjreedy938e7382017-06-26 20:48:39 -04001033 self.deactivate_current_config()
terryjreedyedc03422017-07-07 16:37:39 -04001034 # Remove key set from changes, config, and file.
terryjreedyc0179482017-07-11 19:50:10 -04001035 changes.delete_section('keys', keyset_name)
terryjreedye5bb1122017-07-05 00:54:55 -04001036 # Reload user key set list.
terryjreedy938e7382017-06-26 20:48:39 -04001037 item_list = idleConf.GetSectionList('user', 'keys')
1038 item_list.sort()
1039 if not item_list:
1040 self.radio_keys_custom.config(state=DISABLED)
1041 self.opt_menu_keys_custom.SetMenu(item_list, '- no custom keys -')
Steven M. Gava49745752002-02-18 01:43:11 +00001042 else:
terryjreedy938e7382017-06-26 20:48:39 -04001043 self.opt_menu_keys_custom.SetMenu(item_list, item_list[0])
terryjreedye5bb1122017-07-05 00:54:55 -04001044 # Revert to default key set.
terryjreedy938e7382017-06-26 20:48:39 -04001045 self.are_keys_builtin.set(idleConf.defaultCfg['main']
Terry Jan Reedy9bdb1ed2016-07-10 13:46:34 -04001046 .Get('Keys', 'default'))
terryjreedy938e7382017-06-26 20:48:39 -04001047 self.builtin_keys.set(idleConf.defaultCfg['main'].Get('Keys', 'name')
Terry Jan Reedy9bdb1ed2016-07-10 13:46:34 -04001048 or idleConf.default_keys())
terryjreedye5bb1122017-07-05 00:54:55 -04001049 # User can't back out of these changes, they must be applied now.
terryjreedyedc03422017-07-07 16:37:39 -04001050 changes.save_all()
1051 self.save_all_changed_extensions()
terryjreedy938e7382017-06-26 20:48:39 -04001052 self.activate_config_changes()
1053 self.set_keys_type()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001054
terryjreedy938e7382017-06-26 20:48:39 -04001055 def delete_custom_theme(self):
terryjreedye5bb1122017-07-05 00:54:55 -04001056 """Handle event to delete custom theme.
1057
1058 The current theme is deactivated and the default theme is
1059 activated. The custom theme is permanently removed from
1060 the config file.
terryjreedy9a09c662017-07-13 23:53:30 -04001061
1062 Attributes accessed:
1063 custom_theme
1064
1065 Attributes updated:
1066 radio_theme_custom
1067 opt_menu_theme_custom
1068 is_builtin_theme
1069 builtin_theme
1070
1071 Methods:
1072 deactivate_current_config
1073 save_all_changed_extensions
1074 activate_config_changes
1075 set_theme_type
terryjreedye5bb1122017-07-05 00:54:55 -04001076 """
terryjreedy938e7382017-06-26 20:48:39 -04001077 theme_name = self.custom_theme.get()
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001078 delmsg = 'Are you sure you wish to delete the theme %r ?'
1079 if not tkMessageBox.askyesno(
terryjreedy938e7382017-06-26 20:48:39 -04001080 'Delete Theme', delmsg % theme_name, parent=self):
Steven M. Gava49745752002-02-18 01:43:11 +00001081 return
terryjreedy938e7382017-06-26 20:48:39 -04001082 self.deactivate_current_config()
terryjreedyedc03422017-07-07 16:37:39 -04001083 # Remove theme from changes, config, and file.
terryjreedyc0179482017-07-11 19:50:10 -04001084 changes.delete_section('highlight', theme_name)
terryjreedye5bb1122017-07-05 00:54:55 -04001085 # Reload user theme list.
terryjreedy938e7382017-06-26 20:48:39 -04001086 item_list = idleConf.GetSectionList('user', 'highlight')
1087 item_list.sort()
1088 if not item_list:
1089 self.radio_theme_custom.config(state=DISABLED)
1090 self.opt_menu_theme_custom.SetMenu(item_list, '- no custom themes -')
Steven M. Gava49745752002-02-18 01:43:11 +00001091 else:
terryjreedy938e7382017-06-26 20:48:39 -04001092 self.opt_menu_theme_custom.SetMenu(item_list, item_list[0])
terryjreedye5bb1122017-07-05 00:54:55 -04001093 # Revert to default theme.
terryjreedy938e7382017-06-26 20:48:39 -04001094 self.is_builtin_theme.set(idleConf.defaultCfg['main'].Get('Theme', 'default'))
1095 self.builtin_theme.set(idleConf.defaultCfg['main'].Get('Theme', 'name'))
terryjreedye5bb1122017-07-05 00:54:55 -04001096 # User can't back out of these changes, they must be applied now.
terryjreedyedc03422017-07-07 16:37:39 -04001097 changes.save_all()
1098 self.save_all_changed_extensions()
terryjreedy938e7382017-06-26 20:48:39 -04001099 self.activate_config_changes()
1100 self.set_theme_type()
Steven M. Gava49745752002-02-18 01:43:11 +00001101
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001102 def get_color(self):
terryjreedye5bb1122017-07-05 00:54:55 -04001103 """Handle button to select a new color for the target tag.
1104
1105 If a new color is selected while using a builtin theme, a
1106 name must be supplied to create a custom theme.
terryjreedy9a09c662017-07-13 23:53:30 -04001107
1108 Attributes accessed:
1109 highlight_target
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001110 frame_color_set
terryjreedy9a09c662017-07-13 23:53:30 -04001111 is_builtin_theme
1112
1113 Attributes updated:
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001114 color
terryjreedy9a09c662017-07-13 23:53:30 -04001115
1116 Methods:
1117 get_new_theme_name
1118 create_new_theme
terryjreedye5bb1122017-07-05 00:54:55 -04001119 """
terryjreedy938e7382017-06-26 20:48:39 -04001120 target = self.highlight_target.get()
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001121 prev_color = self.frame_color_set.cget('bg')
1122 rgbTuplet, color_string = tkColorChooser.askcolor(
1123 parent=self, title='Pick new color for : '+target,
1124 initialcolor=prev_color)
1125 if color_string and (color_string != prev_color):
1126 # User didn't cancel and they chose a new color.
terryjreedye5bb1122017-07-05 00:54:55 -04001127 if self.is_builtin_theme.get(): # Current theme is a built-in.
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001128 message = ('Your changes will be saved as a new Custom Theme. '
1129 'Enter a name for your new Custom Theme below.')
terryjreedy938e7382017-06-26 20:48:39 -04001130 new_theme = self.get_new_theme_name(message)
terryjreedye5bb1122017-07-05 00:54:55 -04001131 if not new_theme: # User cancelled custom theme creation.
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +00001132 return
terryjreedye5bb1122017-07-05 00:54:55 -04001133 else: # Create new custom theme based on previously active theme.
terryjreedy938e7382017-06-26 20:48:39 -04001134 self.create_new_theme(new_theme)
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001135 self.color.set(color_string)
terryjreedye5bb1122017-07-05 00:54:55 -04001136 else: # Current theme is user defined.
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001137 self.color.set(color_string)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001138
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001139 def on_new_color_set(self):
terryjreedye5bb1122017-07-05 00:54:55 -04001140 "Display sample of new color selection on the dialog."
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001141 new_color=self.color.get()
1142 self.frame_color_set.config(bg=new_color) # Set sample.
terryjreedy938e7382017-06-26 20:48:39 -04001143 plane ='foreground' if self.fg_bg_toggle.get() else 'background'
1144 sample_element = self.theme_elements[self.highlight_target.get()][0]
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001145 self.text_highlight_sample.tag_config(sample_element, **{plane:new_color})
terryjreedy938e7382017-06-26 20:48:39 -04001146 theme = self.custom_theme.get()
1147 theme_element = sample_element + '-' + plane
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001148 changes.add_option('highlight', theme, theme_element, new_color)
Steven M. Gava052937f2002-02-11 02:20:53 +00001149
terryjreedy938e7382017-06-26 20:48:39 -04001150 def get_new_theme_name(self, message):
terryjreedye5bb1122017-07-05 00:54:55 -04001151 "Return name of new theme from query popup."
terryjreedy938e7382017-06-26 20:48:39 -04001152 used_names = (idleConf.GetSectionList('user', 'highlight') +
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001153 idleConf.GetSectionList('default', 'highlight'))
terryjreedy938e7382017-06-26 20:48:39 -04001154 new_theme = SectionName(
1155 self, 'New Custom Theme', message, used_names).result
1156 return new_theme
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001157
terryjreedy938e7382017-06-26 20:48:39 -04001158 def save_as_new_theme(self):
terryjreedy9a09c662017-07-13 23:53:30 -04001159 """Prompt for new theme name and create the theme.
1160
1161 Methods:
1162 get_new_theme_name
1163 create_new_theme
1164 """
terryjreedy938e7382017-06-26 20:48:39 -04001165 new_theme_name = self.get_new_theme_name('New Theme Name:')
1166 if new_theme_name:
1167 self.create_new_theme(new_theme_name)
Steven M. Gava085eb1b2002-02-05 04:52:32 +00001168
terryjreedy938e7382017-06-26 20:48:39 -04001169 def create_new_theme(self, new_theme_name):
terryjreedye5bb1122017-07-05 00:54:55 -04001170 """Create a new custom theme with the given name.
1171
1172 Create the new theme based on the previously active theme
1173 with the current changes applied. Once it is saved, then
1174 activate the new theme.
terryjreedy9a09c662017-07-13 23:53:30 -04001175
1176 Attributes accessed:
1177 builtin_theme
1178 custom_theme
1179
1180 Attributes updated:
1181 opt_menu_theme_custom
1182 is_builtin_theme
1183
1184 Method:
1185 save_new_theme
1186 set_theme_type
terryjreedye5bb1122017-07-05 00:54:55 -04001187 """
terryjreedy938e7382017-06-26 20:48:39 -04001188 if self.is_builtin_theme.get():
1189 theme_type = 'default'
1190 theme_name = self.builtin_theme.get()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001191 else:
terryjreedy938e7382017-06-26 20:48:39 -04001192 theme_type = 'user'
1193 theme_name = self.custom_theme.get()
1194 new_theme = idleConf.GetThemeDict(theme_type, theme_name)
terryjreedye5bb1122017-07-05 00:54:55 -04001195 # Apply any of the old theme's unsaved changes to the new theme.
terryjreedyedc03422017-07-07 16:37:39 -04001196 if theme_name in changes['highlight']:
1197 theme_changes = changes['highlight'][theme_name]
terryjreedy938e7382017-06-26 20:48:39 -04001198 for element in theme_changes:
1199 new_theme[element] = theme_changes[element]
terryjreedye5bb1122017-07-05 00:54:55 -04001200 # Save the new theme.
terryjreedy938e7382017-06-26 20:48:39 -04001201 self.save_new_theme(new_theme_name, new_theme)
terryjreedye5bb1122017-07-05 00:54:55 -04001202 # Change GUI over to the new theme.
terryjreedy938e7382017-06-26 20:48:39 -04001203 custom_theme_list = idleConf.GetSectionList('user', 'highlight')
1204 custom_theme_list.sort()
1205 self.opt_menu_theme_custom.SetMenu(custom_theme_list, new_theme_name)
1206 self.is_builtin_theme.set(0)
1207 self.set_theme_type()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001208
terryjreedy7ab33422017-07-09 19:26:32 -04001209 def on_fontlist_select(self, event):
1210 """Handle selecting a font from the list.
terryjreedye5bb1122017-07-05 00:54:55 -04001211
terryjreedy7ab33422017-07-09 19:26:32 -04001212 Event can result from either mouse click or Up or Down key.
1213 Set font_name and example display to selection.
terryjreedy9a09c662017-07-13 23:53:30 -04001214
1215 Attributes updated:
1216 font_name: Set to name selected from fontlist.
1217
1218 Methods:
1219 set_font_sample
terryjreedye5bb1122017-07-05 00:54:55 -04001220 """
terryjreedy953e5272017-07-11 02:16:41 -04001221 font = self.fontlist.get(
1222 ACTIVE if event.type.name == 'KeyRelease' else ANCHOR)
terryjreedy938e7382017-06-26 20:48:39 -04001223 self.font_name.set(font.lower())
1224 self.set_font_sample()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001225
terryjreedy938e7382017-06-26 20:48:39 -04001226 def set_font_sample(self, event=None):
terryjreedy9a09c662017-07-13 23:53:30 -04001227 """Update the screen samples with the font settings from the dialog.
1228
1229 Attributes accessed:
1230 font_name
1231 font_bold
1232 font_size
1233
1234 Attributes updated:
1235 font_sample: Set to selected font name, size, and weight.
1236 text_highlight_sample: Set to selected font name, size, and weight.
1237
1238 Called from:
1239 handler for opt_menu_font_size and check_font_bold
1240 on_fontlist_select
1241 load_font_cfg
1242 """
terryjreedy938e7382017-06-26 20:48:39 -04001243 font_name = self.font_name.get()
1244 font_weight = tkFont.BOLD if self.font_bold.get() else tkFont.NORMAL
1245 new_font = (font_name, self.font_size.get(), font_weight)
1246 self.font_sample.config(font=new_font)
1247 self.text_highlight_sample.configure(font=new_font)
Steven M. Gava5f28e8f2002-01-21 06:38:21 +00001248
terryjreedy938e7382017-06-26 20:48:39 -04001249 def set_highlight_target(self):
terryjreedy9a09c662017-07-13 23:53:30 -04001250 """Set fg/bg toggle and color based on highlight tag target.
1251
1252 Instance variables accessed:
1253 highlight_target
1254
1255 Attributes updated:
1256 radio_fg
1257 radio_bg
1258 fg_bg_toggle
1259
1260 Methods:
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001261 set_color_sample
terryjreedy9a09c662017-07-13 23:53:30 -04001262
1263 Called from:
1264 var_changed_highlight_target
1265 load_theme_cfg
1266 """
terryjreedye5bb1122017-07-05 00:54:55 -04001267 if self.highlight_target.get() == 'Cursor': # bg not possible
terryjreedy938e7382017-06-26 20:48:39 -04001268 self.radio_fg.config(state=DISABLED)
1269 self.radio_bg.config(state=DISABLED)
1270 self.fg_bg_toggle.set(1)
terryjreedye5bb1122017-07-05 00:54:55 -04001271 else: # Both fg and bg can be set.
terryjreedy938e7382017-06-26 20:48:39 -04001272 self.radio_fg.config(state=NORMAL)
1273 self.radio_bg.config(state=NORMAL)
1274 self.fg_bg_toggle.set(1)
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001275 self.set_color_sample()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001276
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001277 def set_color_sample_binding(self, *args):
terryjreedy9a09c662017-07-13 23:53:30 -04001278 """Change color sample based on foreground/background toggle.
1279
1280 Methods:
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001281 set_color_sample
terryjreedy9a09c662017-07-13 23:53:30 -04001282 """
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001283 self.set_color_sample()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001284
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001285 def set_color_sample(self):
terryjreedy9a09c662017-07-13 23:53:30 -04001286 """Set the color of the frame background to reflect the selected target.
1287
1288 Instance variables accessed:
1289 theme_elements
1290 highlight_target
1291 fg_bg_toggle
1292 text_highlight_sample
1293
1294 Attributes updated:
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001295 frame_color_set
terryjreedy9a09c662017-07-13 23:53:30 -04001296 """
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001297 # Set the color sample area.
terryjreedy938e7382017-06-26 20:48:39 -04001298 tag = self.theme_elements[self.highlight_target.get()][0]
1299 plane = 'foreground' if self.fg_bg_toggle.get() else 'background'
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001300 color = self.text_highlight_sample.tag_cget(tag, plane)
1301 self.frame_color_set.config(bg=color)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001302
terryjreedy938e7382017-06-26 20:48:39 -04001303 def paint_theme_sample(self):
terryjreedy9a09c662017-07-13 23:53:30 -04001304 """Apply the theme colors to each element tag in the sample text.
1305
1306 Instance attributes accessed:
1307 theme_elements
1308 is_builtin_theme
1309 builtin_theme
1310 custom_theme
1311
1312 Attributes updated:
1313 text_highlight_sample: Set the tag elements to the theme.
1314
1315 Methods:
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001316 set_color_sample
terryjreedy9a09c662017-07-13 23:53:30 -04001317
1318 Called from:
1319 var_changed_builtin_theme
1320 var_changed_custom_theme
1321 load_theme_cfg
1322 """
terryjreedye5bb1122017-07-05 00:54:55 -04001323 if self.is_builtin_theme.get(): # Default theme
terryjreedy938e7382017-06-26 20:48:39 -04001324 theme = self.builtin_theme.get()
terryjreedye5bb1122017-07-05 00:54:55 -04001325 else: # User theme
terryjreedy938e7382017-06-26 20:48:39 -04001326 theme = self.custom_theme.get()
1327 for element_title in self.theme_elements:
1328 element = self.theme_elements[element_title][0]
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001329 colors = idleConf.GetHighlight(theme, element)
terryjreedye5bb1122017-07-05 00:54:55 -04001330 if element == 'cursor': # Cursor sample needs special painting.
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001331 colors['background'] = idleConf.GetHighlight(
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001332 theme, 'normal', fgBg='bg')
terryjreedye5bb1122017-07-05 00:54:55 -04001333 # Handle any unsaved changes to this theme.
terryjreedyedc03422017-07-07 16:37:39 -04001334 if theme in changes['highlight']:
1335 theme_dict = changes['highlight'][theme]
terryjreedy938e7382017-06-26 20:48:39 -04001336 if element + '-foreground' in theme_dict:
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001337 colors['foreground'] = theme_dict[element + '-foreground']
terryjreedy938e7382017-06-26 20:48:39 -04001338 if element + '-background' in theme_dict:
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001339 colors['background'] = theme_dict[element + '-background']
1340 self.text_highlight_sample.tag_config(element, **colors)
1341 self.set_color_sample()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001342
terryjreedy938e7382017-06-26 20:48:39 -04001343 def help_source_selected(self, event):
terryjreedye5bb1122017-07-05 00:54:55 -04001344 "Handle event for selecting additional help."
terryjreedy938e7382017-06-26 20:48:39 -04001345 self.set_helplist_button_states()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001346
terryjreedy938e7382017-06-26 20:48:39 -04001347 def set_helplist_button_states(self):
terryjreedye5bb1122017-07-05 00:54:55 -04001348 "Toggle the state for the help list buttons based on list entries."
1349 if self.list_help.size() < 1: # No entries in list.
terryjreedy938e7382017-06-26 20:48:39 -04001350 self.button_helplist_edit.config(state=DISABLED)
1351 self.button_helplist_remove.config(state=DISABLED)
terryjreedye5bb1122017-07-05 00:54:55 -04001352 else: # Some entries.
1353 if self.list_help.curselection(): # There currently is a selection.
terryjreedy938e7382017-06-26 20:48:39 -04001354 self.button_helplist_edit.config(state=NORMAL)
1355 self.button_helplist_remove.config(state=NORMAL)
terryjreedye5bb1122017-07-05 00:54:55 -04001356 else: # There currently is not a selection.
terryjreedy938e7382017-06-26 20:48:39 -04001357 self.button_helplist_edit.config(state=DISABLED)
1358 self.button_helplist_remove.config(state=DISABLED)
Steven M. Gava085eb1b2002-02-05 04:52:32 +00001359
terryjreedy938e7382017-06-26 20:48:39 -04001360 def helplist_item_add(self):
terryjreedye5bb1122017-07-05 00:54:55 -04001361 """Handle add button for the help list.
1362
1363 Query for name and location of new help sources and add
1364 them to the list.
1365 """
terryjreedy938e7382017-06-26 20:48:39 -04001366 help_source = HelpSource(self, 'New Help Source',
Terry Jan Reedy8b22c0a2016-07-08 00:22:50 -04001367 ).result
terryjreedy938e7382017-06-26 20:48:39 -04001368 if help_source:
1369 self.user_helplist.append((help_source[0], help_source[1]))
1370 self.list_help.insert(END, help_source[0])
1371 self.update_user_help_changed_items()
1372 self.set_helplist_button_states()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001373
terryjreedy938e7382017-06-26 20:48:39 -04001374 def helplist_item_edit(self):
terryjreedye5bb1122017-07-05 00:54:55 -04001375 """Handle edit button for the help list.
1376
1377 Query with existing help source information and update
1378 config if the values are changed.
1379 """
terryjreedy938e7382017-06-26 20:48:39 -04001380 item_index = self.list_help.index(ANCHOR)
1381 help_source = self.user_helplist[item_index]
1382 new_help_source = HelpSource(
Terry Jan Reedy8b22c0a2016-07-08 00:22:50 -04001383 self, 'Edit Help Source',
terryjreedy938e7382017-06-26 20:48:39 -04001384 menuitem=help_source[0],
1385 filepath=help_source[1],
Terry Jan Reedy8b22c0a2016-07-08 00:22:50 -04001386 ).result
terryjreedy938e7382017-06-26 20:48:39 -04001387 if new_help_source and new_help_source != help_source:
1388 self.user_helplist[item_index] = new_help_source
1389 self.list_help.delete(item_index)
1390 self.list_help.insert(item_index, new_help_source[0])
1391 self.update_user_help_changed_items()
1392 self.set_helplist_button_states()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001393
terryjreedy938e7382017-06-26 20:48:39 -04001394 def helplist_item_remove(self):
terryjreedye5bb1122017-07-05 00:54:55 -04001395 """Handle remove button for the help list.
1396
1397 Delete the help list item from config.
1398 """
terryjreedy938e7382017-06-26 20:48:39 -04001399 item_index = self.list_help.index(ANCHOR)
1400 del(self.user_helplist[item_index])
1401 self.list_help.delete(item_index)
1402 self.update_user_help_changed_items()
1403 self.set_helplist_button_states()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001404
terryjreedy938e7382017-06-26 20:48:39 -04001405 def update_user_help_changed_items(self):
terryjreedyedc03422017-07-07 16:37:39 -04001406 "Clear and rebuild the HelpFiles section in changes"
1407 changes['main']['HelpFiles'] = {}
terryjreedy938e7382017-06-26 20:48:39 -04001408 for num in range(1, len(self.user_helplist) + 1):
terryjreedyedc03422017-07-07 16:37:39 -04001409 changes.add_option(
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001410 'main', 'HelpFiles', str(num),
terryjreedy938e7382017-06-26 20:48:39 -04001411 ';'.join(self.user_helplist[num-1][:2]))
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001412
terryjreedy938e7382017-06-26 20:48:39 -04001413 def load_font_cfg(self):
terryjreedy9a09c662017-07-13 23:53:30 -04001414 """Load current configuration settings for the font options.
1415
1416 Retrieve current font values from idleConf.GetFont to set
1417 as initial values for font widgets.
1418
1419 Attributes updated:
1420 fontlist: Populate with fonts from tkinter.font.
1421 font_name: Set to current font.
1422 opt_menu_font_size: Populate valid options tuple and set
1423 to current size.
1424 font_bold: Set to current font weight.
1425
1426 Methods:
1427 set_font_sample
1428 """
terryjreedye5bb1122017-07-05 00:54:55 -04001429 # Set base editor font selection list.
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001430 fonts = list(tkFont.families(self))
Steven M. Gavac11ccf32001-09-24 09:43:17 +00001431 fonts.sort()
1432 for font in fonts:
terryjreedy7ab33422017-07-09 19:26:32 -04001433 self.fontlist.insert(END, font)
terryjreedy938e7382017-06-26 20:48:39 -04001434 configured_font = idleConf.GetFont(self, 'main', 'EditorWindow')
1435 font_name = configured_font[0].lower()
1436 font_size = configured_font[1]
1437 font_bold = configured_font[2]=='bold'
1438 self.font_name.set(font_name)
Kurt B. Kaiser05391692003-05-26 20:35:53 +00001439 lc_fonts = [s.lower() for s in fonts]
Terry Jan Reedyd87d1682015-08-01 18:57:33 -04001440 try:
terryjreedy938e7382017-06-26 20:48:39 -04001441 current_font_index = lc_fonts.index(font_name)
terryjreedy7ab33422017-07-09 19:26:32 -04001442 self.fontlist.see(current_font_index)
1443 self.fontlist.select_set(current_font_index)
1444 self.fontlist.select_anchor(current_font_index)
1445 self.fontlist.activate(current_font_index)
Terry Jan Reedyd87d1682015-08-01 18:57:33 -04001446 except ValueError:
1447 pass
terryjreedye5bb1122017-07-05 00:54:55 -04001448 # Set font size dropdown.
terryjreedy938e7382017-06-26 20:48:39 -04001449 self.opt_menu_font_size.SetMenu(('7', '8', '9', '10', '11', '12', '13',
Terry Jan Reedyda028872016-08-30 20:19:13 -04001450 '14', '16', '18', '20', '22',
terryjreedy938e7382017-06-26 20:48:39 -04001451 '25', '29', '34', '40'), font_size )
terryjreedye5bb1122017-07-05 00:54:55 -04001452 # Set font weight.
terryjreedy938e7382017-06-26 20:48:39 -04001453 self.font_bold.set(font_bold)
terryjreedye5bb1122017-07-05 00:54:55 -04001454 # Set font sample.
terryjreedy938e7382017-06-26 20:48:39 -04001455 self.set_font_sample()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001456
terryjreedy938e7382017-06-26 20:48:39 -04001457 def load_tab_cfg(self):
terryjreedy9a09c662017-07-13 23:53:30 -04001458 """Load current configuration settings for the tab options.
1459
1460 Attributes updated:
1461 space_num: Set to value from idleConf.
1462 """
terryjreedye5bb1122017-07-05 00:54:55 -04001463 # Set indent sizes.
terryjreedy938e7382017-06-26 20:48:39 -04001464 space_num = idleConf.GetOption(
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001465 'main', 'Indent', 'num-spaces', default=4, type='int')
terryjreedy938e7382017-06-26 20:48:39 -04001466 self.space_num.set(space_num)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001467
terryjreedy938e7382017-06-26 20:48:39 -04001468 def load_theme_cfg(self):
terryjreedy9a09c662017-07-13 23:53:30 -04001469 """Load current configuration settings for the theme options.
1470
1471 Based on the is_builtin_theme toggle, the theme is set as
1472 either builtin or custom and the initial widget values
1473 reflect the current settings from idleConf.
1474
1475 Attributes updated:
1476 is_builtin_theme: Set from idleConf.
1477 opt_menu_theme_builtin: List of default themes from idleConf.
1478 opt_menu_theme_custom: List of custom themes from idleConf.
1479 radio_theme_custom: Disabled if there are no custom themes.
1480 custom_theme: Message with additional information.
1481 opt_menu_highlight_target: Create menu from self.theme_elements.
1482
1483 Methods:
1484 set_theme_type
1485 paint_theme_sample
1486 set_highlight_target
1487 """
terryjreedye5bb1122017-07-05 00:54:55 -04001488 # Set current theme type radiobutton.
terryjreedy938e7382017-06-26 20:48:39 -04001489 self.is_builtin_theme.set(idleConf.GetOption(
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001490 'main', 'Theme', 'default', type='bool', default=1))
terryjreedye5bb1122017-07-05 00:54:55 -04001491 # Set current theme.
terryjreedy938e7382017-06-26 20:48:39 -04001492 current_option = idleConf.CurrentTheme()
terryjreedye5bb1122017-07-05 00:54:55 -04001493 # Load available theme option menus.
1494 if self.is_builtin_theme.get(): # Default theme selected.
terryjreedy938e7382017-06-26 20:48:39 -04001495 item_list = idleConf.GetSectionList('default', 'highlight')
1496 item_list.sort()
1497 self.opt_menu_theme_builtin.SetMenu(item_list, current_option)
1498 item_list = idleConf.GetSectionList('user', 'highlight')
1499 item_list.sort()
1500 if not item_list:
1501 self.radio_theme_custom.config(state=DISABLED)
1502 self.custom_theme.set('- no custom themes -')
Steven M. Gava41a85322001-10-29 08:05:34 +00001503 else:
terryjreedy938e7382017-06-26 20:48:39 -04001504 self.opt_menu_theme_custom.SetMenu(item_list, item_list[0])
terryjreedye5bb1122017-07-05 00:54:55 -04001505 else: # User theme selected.
terryjreedy938e7382017-06-26 20:48:39 -04001506 item_list = idleConf.GetSectionList('user', 'highlight')
1507 item_list.sort()
1508 self.opt_menu_theme_custom.SetMenu(item_list, current_option)
1509 item_list = idleConf.GetSectionList('default', 'highlight')
1510 item_list.sort()
1511 self.opt_menu_theme_builtin.SetMenu(item_list, item_list[0])
1512 self.set_theme_type()
terryjreedye5bb1122017-07-05 00:54:55 -04001513 # Load theme element option menu.
terryjreedy938e7382017-06-26 20:48:39 -04001514 theme_names = list(self.theme_elements.keys())
1515 theme_names.sort(key=lambda x: self.theme_elements[x][1])
1516 self.opt_menu_highlight_target.SetMenu(theme_names, theme_names[0])
1517 self.paint_theme_sample()
1518 self.set_highlight_target()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001519
terryjreedy938e7382017-06-26 20:48:39 -04001520 def load_key_cfg(self):
terryjreedye5bb1122017-07-05 00:54:55 -04001521 "Load current configuration settings for the keybinding options."
1522 # Set current keys type radiobutton.
terryjreedy938e7382017-06-26 20:48:39 -04001523 self.are_keys_builtin.set(idleConf.GetOption(
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001524 'main', 'Keys', 'default', type='bool', default=1))
terryjreedye5bb1122017-07-05 00:54:55 -04001525 # Set current keys.
terryjreedy938e7382017-06-26 20:48:39 -04001526 current_option = idleConf.CurrentKeys()
terryjreedye5bb1122017-07-05 00:54:55 -04001527 # Load available keyset option menus.
1528 if self.are_keys_builtin.get(): # Default theme selected.
terryjreedy938e7382017-06-26 20:48:39 -04001529 item_list = idleConf.GetSectionList('default', 'keys')
1530 item_list.sort()
1531 self.opt_menu_keys_builtin.SetMenu(item_list, current_option)
1532 item_list = idleConf.GetSectionList('user', 'keys')
1533 item_list.sort()
1534 if not item_list:
1535 self.radio_keys_custom.config(state=DISABLED)
1536 self.custom_keys.set('- no custom keys -')
Steven M. Gava41a85322001-10-29 08:05:34 +00001537 else:
terryjreedy938e7382017-06-26 20:48:39 -04001538 self.opt_menu_keys_custom.SetMenu(item_list, item_list[0])
terryjreedye5bb1122017-07-05 00:54:55 -04001539 else: # User key set selected.
terryjreedy938e7382017-06-26 20:48:39 -04001540 item_list = idleConf.GetSectionList('user', 'keys')
1541 item_list.sort()
1542 self.opt_menu_keys_custom.SetMenu(item_list, current_option)
1543 item_list = idleConf.GetSectionList('default', 'keys')
1544 item_list.sort()
1545 self.opt_menu_keys_builtin.SetMenu(item_list, idleConf.default_keys())
1546 self.set_keys_type()
terryjreedye5bb1122017-07-05 00:54:55 -04001547 # Load keyset element list.
terryjreedy938e7382017-06-26 20:48:39 -04001548 keyset_name = idleConf.CurrentKeys()
1549 self.load_keys_list(keyset_name)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001550
terryjreedy938e7382017-06-26 20:48:39 -04001551 def load_general_cfg(self):
terryjreedye5bb1122017-07-05 00:54:55 -04001552 "Load current configuration settings for the general options."
1553 # Set startup state.
terryjreedy938e7382017-06-26 20:48:39 -04001554 self.startup_edit.set(idleConf.GetOption(
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001555 'main', 'General', 'editor-on-startup', default=1, type='bool'))
terryjreedye5bb1122017-07-05 00:54:55 -04001556 # Set autosave state.
terryjreedy938e7382017-06-26 20:48:39 -04001557 self.autosave.set(idleConf.GetOption(
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001558 'main', 'General', 'autosave', default=0, type='bool'))
terryjreedye5bb1122017-07-05 00:54:55 -04001559 # Set initial window size.
terryjreedy938e7382017-06-26 20:48:39 -04001560 self.win_width.set(idleConf.GetOption(
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001561 'main', 'EditorWindow', 'width', type='int'))
terryjreedy938e7382017-06-26 20:48:39 -04001562 self.win_height.set(idleConf.GetOption(
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001563 'main', 'EditorWindow', 'height', type='int'))
terryjreedye5bb1122017-07-05 00:54:55 -04001564 # Set additional help sources.
terryjreedy938e7382017-06-26 20:48:39 -04001565 self.user_helplist = idleConf.GetAllExtraHelpSourcesList()
1566 for help_item in self.user_helplist:
1567 self.list_help.insert(END, help_item[0])
1568 self.set_helplist_button_states()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001569
terryjreedy938e7382017-06-26 20:48:39 -04001570 def load_configs(self):
terryjreedye5bb1122017-07-05 00:54:55 -04001571 """Load configuration for each page.
1572
1573 Load configuration from default and user config files and populate
Steven M. Gava429a86af2001-10-23 10:42:12 +00001574 the widgets on the config dialog pages.
terryjreedy9a09c662017-07-13 23:53:30 -04001575
1576 Methods:
1577 load_font_cfg
1578 load_tab_cfg
1579 load_theme_cfg
1580 load_key_cfg
1581 load_general_cfg
Steven M. Gava429a86af2001-10-23 10:42:12 +00001582 """
terryjreedy938e7382017-06-26 20:48:39 -04001583 self.load_font_cfg()
1584 self.load_tab_cfg()
terryjreedy938e7382017-06-26 20:48:39 -04001585 self.load_theme_cfg()
terryjreedy938e7382017-06-26 20:48:39 -04001586 self.load_key_cfg()
terryjreedy938e7382017-06-26 20:48:39 -04001587 self.load_general_cfg()
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001588 # note: extension page handled separately
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001589
terryjreedy938e7382017-06-26 20:48:39 -04001590 def save_new_key_set(self, keyset_name, keyset):
terryjreedye5bb1122017-07-05 00:54:55 -04001591 """Save a newly created core key set.
1592
terryjreedy938e7382017-06-26 20:48:39 -04001593 keyset_name - string, the name of the new key set
1594 keyset - dictionary containing the new key set
Steven M. Gava052937f2002-02-11 02:20:53 +00001595 """
terryjreedy938e7382017-06-26 20:48:39 -04001596 if not idleConf.userCfg['keys'].has_section(keyset_name):
1597 idleConf.userCfg['keys'].add_section(keyset_name)
1598 for event in keyset:
1599 value = keyset[event]
1600 idleConf.userCfg['keys'].SetOption(keyset_name, event, value)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001601
terryjreedy938e7382017-06-26 20:48:39 -04001602 def save_new_theme(self, theme_name, theme):
terryjreedy9a09c662017-07-13 23:53:30 -04001603 """Save a newly created theme to idleConf.
terryjreedye5bb1122017-07-05 00:54:55 -04001604
terryjreedy938e7382017-06-26 20:48:39 -04001605 theme_name - string, the name of the new theme
Steven M. Gava052937f2002-02-11 02:20:53 +00001606 theme - dictionary containing the new theme
1607 """
terryjreedy938e7382017-06-26 20:48:39 -04001608 if not idleConf.userCfg['highlight'].has_section(theme_name):
1609 idleConf.userCfg['highlight'].add_section(theme_name)
Kurt B. Kaisere0712772007-08-23 05:25:55 +00001610 for element in theme:
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001611 value = theme[element]
terryjreedy938e7382017-06-26 20:48:39 -04001612 idleConf.userCfg['highlight'].SetOption(theme_name, element, value)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001613
terryjreedy938e7382017-06-26 20:48:39 -04001614 def deactivate_current_config(self):
terryjreedy9a09c662017-07-13 23:53:30 -04001615 """Remove current key bindings.
1616
1617 Iterate over window instances defined in parent and remove
1618 the keybindings.
1619 """
terryjreedye5bb1122017-07-05 00:54:55 -04001620 # Before a config is saved, some cleanup of current
1621 # config must be done - remove the previous keybindings.
terryjreedy938e7382017-06-26 20:48:39 -04001622 win_instances = self.parent.instance_dict.keys()
1623 for instance in win_instances:
Kurt B. Kaiserb1754452005-11-18 22:05:48 +00001624 instance.RemoveKeybindings()
1625
terryjreedy938e7382017-06-26 20:48:39 -04001626 def activate_config_changes(self):
terryjreedy9a09c662017-07-13 23:53:30 -04001627 """Apply configuration changes to current windows.
1628
1629 Dynamically update the current parent window instances
1630 with some of the configuration changes.
1631 """
terryjreedy938e7382017-06-26 20:48:39 -04001632 win_instances = self.parent.instance_dict.keys()
1633 for instance in win_instances:
Steven M. Gavab77d3432002-03-02 07:16:21 +00001634 instance.ResetColorizer()
Steven M. Gavab1585412002-03-12 00:21:56 +00001635 instance.ResetFont()
Kurt B. Kaiseracdef852005-01-31 03:34:26 +00001636 instance.set_notabs_indentwidth()
Kurt B. Kaiserb1754452005-11-18 22:05:48 +00001637 instance.ApplyKeybindings()
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +00001638 instance.reset_help_menu_entries()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001639
terryjreedy938e7382017-06-26 20:48:39 -04001640 def cancel(self):
terryjreedy9a09c662017-07-13 23:53:30 -04001641 """Dismiss config dialog.
1642
1643 Methods:
1644 destroy: inherited
1645 """
Steven M. Gava5f28e8f2002-01-21 06:38:21 +00001646 self.destroy()
1647
terryjreedy938e7382017-06-26 20:48:39 -04001648 def ok(self):
terryjreedy9a09c662017-07-13 23:53:30 -04001649 """Apply config changes, then dismiss dialog.
1650
1651 Methods:
1652 apply
1653 destroy: inherited
1654 """
terryjreedy938e7382017-06-26 20:48:39 -04001655 self.apply()
Steven M. Gava5f28e8f2002-01-21 06:38:21 +00001656 self.destroy()
1657
terryjreedy938e7382017-06-26 20:48:39 -04001658 def apply(self):
terryjreedy9a09c662017-07-13 23:53:30 -04001659 """Apply config changes and leave dialog open.
1660
1661 Methods:
1662 deactivate_current_config
1663 save_all_changed_extensions
1664 activate_config_changes
1665 """
terryjreedy938e7382017-06-26 20:48:39 -04001666 self.deactivate_current_config()
terryjreedyedc03422017-07-07 16:37:39 -04001667 changes.save_all()
1668 self.save_all_changed_extensions()
terryjreedy938e7382017-06-26 20:48:39 -04001669 self.activate_config_changes()
Steven M. Gava5f28e8f2002-01-21 06:38:21 +00001670
terryjreedy938e7382017-06-26 20:48:39 -04001671 def help(self):
terryjreedy9a09c662017-07-13 23:53:30 -04001672 """Create textview for config dialog help.
1673
1674 Attrbutes accessed:
1675 tab_pages
1676
1677 Methods:
1678 view_text: Method from textview module.
1679 """
terryjreedy938e7382017-06-26 20:48:39 -04001680 page = self.tab_pages._current_page
Terry Jan Reedyd0cadba2015-10-11 22:07:31 -04001681 view_text(self, title='Help for IDLE preferences',
1682 text=help_common+help_pages.get(page, ''))
1683
terryjreedy938e7382017-06-26 20:48:39 -04001684 def create_page_extensions(self):
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001685 """Part of the config dialog used for configuring IDLE extensions.
1686
1687 This code is generic - it works for any and all IDLE extensions.
1688
1689 IDLE extensions save their configuration options using idleConf.
Terry Jan Reedyb2f87602015-10-13 22:09:06 -04001690 This code reads the current configuration using idleConf, supplies a
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001691 GUI interface to change the configuration values, and saves the
1692 changes using idleConf.
1693
1694 Not all changes take effect immediately - some may require restarting IDLE.
1695 This depends on each extension's implementation.
1696
1697 All values are treated as text, and it is up to the user to supply
1698 reasonable values. The only exception to this are the 'enable*' options,
Serhiy Storchaka6a7b3a72016-04-17 08:32:47 +03001699 which are boolean, and can be toggled with a True/False button.
terryjreedy9a09c662017-07-13 23:53:30 -04001700
1701 Methods:
1702 load_extentions:
1703 extension_selected: Handle selection from list.
1704 create_extension_frame: Hold widgets for one extension.
1705 set_extension_value: Set in userCfg['extensions'].
1706 save_all_changed_extensions: Call extension page Save().
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001707 """
1708 parent = self.parent
terryjreedy938e7382017-06-26 20:48:39 -04001709 frame = self.tab_pages.pages['Extensions'].frame
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001710 self.ext_defaultCfg = idleConf.defaultCfg['extensions']
1711 self.ext_userCfg = idleConf.userCfg['extensions']
1712 self.is_int = self.register(is_int)
1713 self.load_extensions()
terryjreedye5bb1122017-07-05 00:54:55 -04001714 # Create widgets - a listbox shows all available extensions, with the
1715 # controls for the extension selected in the listbox to the right.
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001716 self.extension_names = StringVar(self)
1717 frame.rowconfigure(0, weight=1)
1718 frame.columnconfigure(2, weight=1)
1719 self.extension_list = Listbox(frame, listvariable=self.extension_names,
1720 selectmode='browse')
1721 self.extension_list.bind('<<ListboxSelect>>', self.extension_selected)
1722 scroll = Scrollbar(frame, command=self.extension_list.yview)
1723 self.extension_list.yscrollcommand=scroll.set
1724 self.details_frame = LabelFrame(frame, width=250, height=250)
1725 self.extension_list.grid(column=0, row=0, sticky='nws')
1726 scroll.grid(column=1, row=0, sticky='ns')
1727 self.details_frame.grid(column=2, row=0, sticky='nsew', padx=[10, 0])
1728 frame.configure(padx=10, pady=10)
1729 self.config_frame = {}
1730 self.current_extension = None
1731
1732 self.outerframe = self # TEMPORARY
1733 self.tabbed_page_set = self.extension_list # TEMPORARY
1734
terryjreedye5bb1122017-07-05 00:54:55 -04001735 # Create the frame holding controls for each extension.
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001736 ext_names = ''
1737 for ext_name in sorted(self.extensions):
1738 self.create_extension_frame(ext_name)
1739 ext_names = ext_names + '{' + ext_name + '} '
1740 self.extension_names.set(ext_names)
1741 self.extension_list.selection_set(0)
1742 self.extension_selected(None)
1743
1744 def load_extensions(self):
1745 "Fill self.extensions with data from the default and user configs."
1746 self.extensions = {}
1747 for ext_name in idleConf.GetExtensions(active_only=False):
1748 self.extensions[ext_name] = []
1749
1750 for ext_name in self.extensions:
1751 opt_list = sorted(self.ext_defaultCfg.GetOptionList(ext_name))
1752
terryjreedye5bb1122017-07-05 00:54:55 -04001753 # Bring 'enable' options to the beginning of the list.
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001754 enables = [opt_name for opt_name in opt_list
1755 if opt_name.startswith('enable')]
1756 for opt_name in enables:
1757 opt_list.remove(opt_name)
1758 opt_list = enables + opt_list
1759
1760 for opt_name in opt_list:
1761 def_str = self.ext_defaultCfg.Get(
1762 ext_name, opt_name, raw=True)
1763 try:
1764 def_obj = {'True':True, 'False':False}[def_str]
1765 opt_type = 'bool'
1766 except KeyError:
1767 try:
1768 def_obj = int(def_str)
1769 opt_type = 'int'
1770 except ValueError:
1771 def_obj = def_str
1772 opt_type = None
1773 try:
1774 value = self.ext_userCfg.Get(
1775 ext_name, opt_name, type=opt_type, raw=True,
1776 default=def_obj)
terryjreedye5bb1122017-07-05 00:54:55 -04001777 except ValueError: # Need this until .Get fixed.
1778 value = def_obj # Bad values overwritten by entry.
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001779 var = StringVar(self)
1780 var.set(str(value))
1781
1782 self.extensions[ext_name].append({'name': opt_name,
1783 'type': opt_type,
1784 'default': def_str,
1785 'value': value,
1786 'var': var,
1787 })
1788
1789 def extension_selected(self, event):
terryjreedye5bb1122017-07-05 00:54:55 -04001790 "Handle selection of an extension from the list."
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001791 newsel = self.extension_list.curselection()
1792 if newsel:
1793 newsel = self.extension_list.get(newsel)
1794 if newsel is None or newsel != self.current_extension:
1795 if self.current_extension:
1796 self.details_frame.config(text='')
1797 self.config_frame[self.current_extension].grid_forget()
1798 self.current_extension = None
1799 if newsel:
1800 self.details_frame.config(text=newsel)
1801 self.config_frame[newsel].grid(column=0, row=0, sticky='nsew')
1802 self.current_extension = newsel
1803
1804 def create_extension_frame(self, ext_name):
1805 """Create a frame holding the widgets to configure one extension"""
1806 f = VerticalScrolledFrame(self.details_frame, height=250, width=250)
1807 self.config_frame[ext_name] = f
1808 entry_area = f.interior
terryjreedye5bb1122017-07-05 00:54:55 -04001809 # Create an entry for each configuration option.
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001810 for row, opt in enumerate(self.extensions[ext_name]):
terryjreedye5bb1122017-07-05 00:54:55 -04001811 # Create a row with a label and entry/checkbutton.
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001812 label = Label(entry_area, text=opt['name'])
1813 label.grid(row=row, column=0, sticky=NW)
1814 var = opt['var']
1815 if opt['type'] == 'bool':
1816 Checkbutton(entry_area, textvariable=var, variable=var,
1817 onvalue='True', offvalue='False',
1818 indicatoron=FALSE, selectcolor='', width=8
1819 ).grid(row=row, column=1, sticky=W, padx=7)
1820 elif opt['type'] == 'int':
1821 Entry(entry_area, textvariable=var, validate='key',
1822 validatecommand=(self.is_int, '%P')
1823 ).grid(row=row, column=1, sticky=NSEW, padx=7)
1824
1825 else:
1826 Entry(entry_area, textvariable=var
1827 ).grid(row=row, column=1, sticky=NSEW, padx=7)
1828 return
1829
1830 def set_extension_value(self, section, opt):
terryjreedye5bb1122017-07-05 00:54:55 -04001831 """Return True if the configuration was added or changed.
1832
1833 If the value is the same as the default, then remove it
1834 from user config file.
1835 """
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001836 name = opt['name']
1837 default = opt['default']
1838 value = opt['var'].get().strip() or default
1839 opt['var'].set(value)
1840 # if self.defaultCfg.has_section(section):
terryjreedye5bb1122017-07-05 00:54:55 -04001841 # Currently, always true; if not, indent to return.
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001842 if (value == default):
1843 return self.ext_userCfg.RemoveOption(section, name)
terryjreedye5bb1122017-07-05 00:54:55 -04001844 # Set the option.
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001845 return self.ext_userCfg.SetOption(section, name, value)
1846
1847 def save_all_changed_extensions(self):
terryjreedy9a09c662017-07-13 23:53:30 -04001848 """Save configuration changes to the user config file.
1849
1850 Attributes accessed:
1851 extensions
1852
1853 Methods:
1854 set_extension_value
1855 """
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001856 has_changes = False
1857 for ext_name in self.extensions:
1858 options = self.extensions[ext_name]
1859 for opt in options:
1860 if self.set_extension_value(ext_name, opt):
1861 has_changes = True
1862 if has_changes:
1863 self.ext_userCfg.Save()
1864
1865
Terry Jan Reedyd0cadba2015-10-11 22:07:31 -04001866help_common = '''\
1867When you click either the Apply or Ok buttons, settings in this
1868dialog that are different from IDLE's default are saved in
1869a .idlerc directory in your home directory. Except as noted,
Terry Jan Reedyd0c0f002015-11-12 15:02:57 -05001870these changes apply to all versions of IDLE installed on this
Terry Jan Reedyd0cadba2015-10-11 22:07:31 -04001871machine. Some do not take affect until IDLE is restarted.
1872[Cancel] only cancels changes made since the last save.
1873'''
1874help_pages = {
Terry Jan Reedy9bdb1ed2016-07-10 13:46:34 -04001875 'Highlighting': '''
Terry Jan Reedyd0cadba2015-10-11 22:07:31 -04001876Highlighting:
Terry Jan Reedyd0c0f002015-11-12 15:02:57 -05001877The IDLE Dark color theme is new in October 2015. It can only
Terry Jan Reedyd0cadba2015-10-11 22:07:31 -04001878be used with older IDLE releases if it is saved as a custom
1879theme, with a different name.
Terry Jan Reedy9bdb1ed2016-07-10 13:46:34 -04001880''',
1881 'Keys': '''
1882Keys:
1883The IDLE Modern Unix key set is new in June 2016. It can only
1884be used with older IDLE releases if it is saved as a custom
1885key set, with a different name.
1886''',
terryjreedyaf683822017-06-27 23:02:19 -04001887 'Extensions': '''
1888Extensions:
1889
1890Autocomplete: Popupwait is milleseconds to wait after key char, without
1891cursor movement, before popping up completion box. Key char is '.' after
1892identifier or a '/' (or '\\' on Windows) within a string.
1893
1894FormatParagraph: Max-width is max chars in lines after re-formatting.
1895Use with paragraphs in both strings and comment blocks.
1896
1897ParenMatch: Style indicates what is highlighted when closer is entered:
1898'opener' - opener '({[' corresponding to closer; 'parens' - both chars;
1899'expression' (default) - also everything in between. Flash-delay is how
1900long to highlight if cursor is not moved (0 means forever).
1901'''
Terry Jan Reedyd0cadba2015-10-11 22:07:31 -04001902}
1903
Steven M. Gavac11ccf32001-09-24 09:43:17 +00001904
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001905def is_int(s):
1906 "Return 's is blank or represents an int'"
1907 if not s:
1908 return True
1909 try:
1910 int(s)
1911 return True
1912 except ValueError:
1913 return False
1914
1915
Terry Jan Reedya9421fb2014-10-22 20:15:18 -04001916class VerticalScrolledFrame(Frame):
1917 """A pure Tkinter vertically scrollable frame.
1918
1919 * Use the 'interior' attribute to place widgets inside the scrollable frame
1920 * Construct and pack/place/grid normally
1921 * This frame only allows vertical scrolling
1922 """
1923 def __init__(self, parent, *args, **kw):
1924 Frame.__init__(self, parent, *args, **kw)
1925
terryjreedye5bb1122017-07-05 00:54:55 -04001926 # Create a canvas object and a vertical scrollbar for scrolling it.
Terry Jan Reedya9421fb2014-10-22 20:15:18 -04001927 vscrollbar = Scrollbar(self, orient=VERTICAL)
1928 vscrollbar.pack(fill=Y, side=RIGHT, expand=FALSE)
1929 canvas = Canvas(self, bd=0, highlightthickness=0,
Terry Jan Reedyd0812292015-10-22 03:27:31 -04001930 yscrollcommand=vscrollbar.set, width=240)
Terry Jan Reedya9421fb2014-10-22 20:15:18 -04001931 canvas.pack(side=LEFT, fill=BOTH, expand=TRUE)
1932 vscrollbar.config(command=canvas.yview)
1933
terryjreedye5bb1122017-07-05 00:54:55 -04001934 # Reset the view.
Terry Jan Reedya9421fb2014-10-22 20:15:18 -04001935 canvas.xview_moveto(0)
1936 canvas.yview_moveto(0)
1937
terryjreedye5bb1122017-07-05 00:54:55 -04001938 # Create a frame inside the canvas which will be scrolled with it.
Terry Jan Reedya9421fb2014-10-22 20:15:18 -04001939 self.interior = interior = Frame(canvas)
1940 interior_id = canvas.create_window(0, 0, window=interior, anchor=NW)
1941
terryjreedye5bb1122017-07-05 00:54:55 -04001942 # Track changes to the canvas and frame width and sync them,
1943 # also updating the scrollbar.
Terry Jan Reedya9421fb2014-10-22 20:15:18 -04001944 def _configure_interior(event):
terryjreedye5bb1122017-07-05 00:54:55 -04001945 # Update the scrollbars to match the size of the inner frame.
Terry Jan Reedya9421fb2014-10-22 20:15:18 -04001946 size = (interior.winfo_reqwidth(), interior.winfo_reqheight())
1947 canvas.config(scrollregion="0 0 %s %s" % size)
Terry Jan Reedya9421fb2014-10-22 20:15:18 -04001948 interior.bind('<Configure>', _configure_interior)
1949
1950 def _configure_canvas(event):
1951 if interior.winfo_reqwidth() != canvas.winfo_width():
terryjreedye5bb1122017-07-05 00:54:55 -04001952 # Update the inner frame's width to fill the canvas.
Terry Jan Reedya9421fb2014-10-22 20:15:18 -04001953 canvas.itemconfigure(interior_id, width=canvas.winfo_width())
1954 canvas.bind('<Configure>', _configure_canvas)
1955
1956 return
1957
Terry Jan Reedya9421fb2014-10-22 20:15:18 -04001958
Steven M. Gava44d3d1a2001-07-31 06:59:02 +00001959if __name__ == '__main__':
Terry Jan Reedycfa89502014-07-14 23:07:32 -04001960 import unittest
1961 unittest.main('idlelib.idle_test.test_configdialog',
1962 verbosity=2, exit=False)
Terry Jan Reedy2e8234a2014-05-29 01:46:26 -04001963 from idlelib.idle_test.htest import run
Terry Jan Reedy47304c02015-10-20 02:15:28 -04001964 run(ConfigDialog)