blob: c1db76817c17c718e3d657494fc33db35012682e [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
Terry Jan Reedy04864b42017-07-22 00:56:18 -0400156 Enable users to provisionally change font face, size, or
157 boldness and to see the consequence of proposed choices. Each
158 action set 3 options in changes structuree and changes the
159 corresponding aspect of the font sample on this page and
160 highlight sample on highlight page.
161
162 Enable users to change spaces entered for indent tabs.
163
terryjreedy9a09c662017-07-13 23:53:30 -0400164 Tk Variables:
Terry Jan Reedy04864b42017-07-22 00:56:18 -0400165 font_name: Font face.
terryjreedye5bb1122017-07-05 00:54:55 -0400166 font_size: Font size.
167 font_bold: Select font bold or not.
terryjreedy9a09c662017-07-13 23:53:30 -0400168 Note: these 3 share var_changed_font callback.
terryjreedye5bb1122017-07-05 00:54:55 -0400169 space_num: Indentation width.
terryjreedy9a09c662017-07-13 23:53:30 -0400170
171 Data Attribute:
Terry Jan Reedy04864b42017-07-22 00:56:18 -0400172 edit_font: Font with default font name, size, and weight.
terryjreedy9a09c662017-07-13 23:53:30 -0400173
174 Methods:
175 load_font_cfg: Set vars and fontlist.
176 on_fontlist_select: Bound to fontlist button release
177 or key release.
Terry Jan Reedy04864b42017-07-22 00:56:18 -0400178 set_samples: Notify both samples of any font change.
terryjreedy9a09c662017-07-13 23:53:30 -0400179 load_tab_cfg: Get current.
180
181 Widget Structure: (*) widgets bound to self
182 frame
183 frame_font: LabelFrame
184 frame_font_name: Frame
185 font_name_title: Label
186 (*)fontlist: ListBox
187 scroll_font: Scrollbar
188 frame_font_param: Frame
189 font_size_title: Label
190 (*)opt_menu_font_size: DynOptionMenu - font_size
Terry Jan Reedy04864b42017-07-22 00:56:18 -0400191 (*)bold_toggle: Checkbutton - font_bold
terryjreedy9a09c662017-07-13 23:53:30 -0400192 frame_font_sample: Frame
193 (*)font_sample: Label
194 frame_indent: LabelFrame
195 frame_indent_size: Frame
196 indent_size_title: Label
197 (*)scale_indent_size: Scale - space_num
terryjreedye5bb1122017-07-05 00:54:55 -0400198 """
Terry Jan Reedy22405332014-07-30 19:24:32 -0400199 parent = self.parent
Terry Jan Reedy04864b42017-07-22 00:56:18 -0400200 self.font_name = StringVar(parent)
terryjreedy938e7382017-06-26 20:48:39 -0400201 self.font_size = StringVar(parent)
202 self.font_bold = BooleanVar(parent)
terryjreedy938e7382017-06-26 20:48:39 -0400203 self.space_num = IntVar(parent)
204 self.edit_font = tkFont.Font(parent, ('courier', 10, 'normal'))
Terry Jan Reedy22405332014-07-30 19:24:32 -0400205
terryjreedy7ab33422017-07-09 19:26:32 -0400206 # Create widgets.
207 # body and body section frames.
terryjreedy938e7382017-06-26 20:48:39 -0400208 frame = self.tab_pages.pages['Fonts/Tabs'].frame
terryjreedy938e7382017-06-26 20:48:39 -0400209 frame_font = LabelFrame(
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400210 frame, borderwidth=2, relief=GROOVE, text=' Base Editor Font ')
terryjreedy938e7382017-06-26 20:48:39 -0400211 frame_indent = LabelFrame(
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400212 frame, borderwidth=2, relief=GROOVE, text=' Indentation Width ')
terryjreedy7ab33422017-07-09 19:26:32 -0400213 # frame_font
terryjreedy938e7382017-06-26 20:48:39 -0400214 frame_font_name = Frame(frame_font)
215 frame_font_param = Frame(frame_font)
216 font_name_title = Label(
217 frame_font_name, justify=LEFT, text='Font Face :')
terryjreedy7ab33422017-07-09 19:26:32 -0400218 self.fontlist = Listbox(
terryjreedy938e7382017-06-26 20:48:39 -0400219 frame_font_name, height=5, takefocus=FALSE, exportselection=FALSE)
terryjreedy953e5272017-07-11 02:16:41 -0400220 self.fontlist.bind('<ButtonRelease-1>', self.on_fontlist_select)
221 self.fontlist.bind('<KeyRelease-Up>', self.on_fontlist_select)
222 self.fontlist.bind('<KeyRelease-Down>', self.on_fontlist_select)
terryjreedy938e7382017-06-26 20:48:39 -0400223 scroll_font = Scrollbar(frame_font_name)
terryjreedy7ab33422017-07-09 19:26:32 -0400224 scroll_font.config(command=self.fontlist.yview)
225 self.fontlist.config(yscrollcommand=scroll_font.set)
terryjreedy938e7382017-06-26 20:48:39 -0400226 font_size_title = Label(frame_font_param, text='Size :')
227 self.opt_menu_font_size = DynOptionMenu(
Terry Jan Reedy04864b42017-07-22 00:56:18 -0400228 frame_font_param, self.font_size, None, command=self.set_samples)
229 self.bold_toggle = Checkbutton(
terryjreedy938e7382017-06-26 20:48:39 -0400230 frame_font_param, variable=self.font_bold, onvalue=1,
Terry Jan Reedy04864b42017-07-22 00:56:18 -0400231 offvalue=0, text='Bold', command=self.set_samples)
terryjreedy938e7382017-06-26 20:48:39 -0400232 frame_font_sample = Frame(frame_font, relief=SOLID, borderwidth=1)
233 self.font_sample = Label(
234 frame_font_sample, justify=LEFT, font=self.edit_font,
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400235 text='AaBbCcDdEe\nFfGgHhIiJjK\n1234567890\n#:+=(){}[]')
terryjreedy7ab33422017-07-09 19:26:32 -0400236 # frame_indent
terryjreedy938e7382017-06-26 20:48:39 -0400237 frame_indent_size = Frame(frame_indent)
238 indent_size_title = Label(
239 frame_indent_size, justify=LEFT,
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400240 text='Python Standard: 4 Spaces!')
terryjreedy938e7382017-06-26 20:48:39 -0400241 self.scale_indent_size = Scale(
242 frame_indent_size, variable=self.space_num,
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400243 orient='horizontal', tickinterval=2, from_=2, to=16)
Terry Jan Reedy22405332014-07-30 19:24:32 -0400244
terryjreedy7ab33422017-07-09 19:26:32 -0400245 # Pack widgets.
246 # body
terryjreedy938e7382017-06-26 20:48:39 -0400247 frame_font.pack(side=LEFT, padx=5, pady=5, expand=TRUE, fill=BOTH)
248 frame_indent.pack(side=LEFT, padx=5, pady=5, fill=Y)
terryjreedy7ab33422017-07-09 19:26:32 -0400249 # frame_font
terryjreedy938e7382017-06-26 20:48:39 -0400250 frame_font_name.pack(side=TOP, padx=5, pady=5, fill=X)
251 frame_font_param.pack(side=TOP, padx=5, pady=5, fill=X)
252 font_name_title.pack(side=TOP, anchor=W)
terryjreedy7ab33422017-07-09 19:26:32 -0400253 self.fontlist.pack(side=LEFT, expand=TRUE, fill=X)
terryjreedy938e7382017-06-26 20:48:39 -0400254 scroll_font.pack(side=LEFT, fill=Y)
255 font_size_title.pack(side=LEFT, anchor=W)
256 self.opt_menu_font_size.pack(side=LEFT, anchor=W)
Terry Jan Reedy04864b42017-07-22 00:56:18 -0400257 self.bold_toggle.pack(side=LEFT, anchor=W, padx=20)
terryjreedy938e7382017-06-26 20:48:39 -0400258 frame_font_sample.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH)
259 self.font_sample.pack(expand=TRUE, fill=BOTH)
terryjreedy7ab33422017-07-09 19:26:32 -0400260 # frame_indent
terryjreedy938e7382017-06-26 20:48:39 -0400261 frame_indent_size.pack(side=TOP, fill=X)
262 indent_size_title.pack(side=TOP, anchor=W, padx=5)
263 self.scale_indent_size.pack(side=TOP, padx=5, fill=X)
terryjreedy7ab33422017-07-09 19:26:32 -0400264
Steven M. Gava952d0a52001-08-03 04:43:44 +0000265 return frame
266
terryjreedy938e7382017-06-26 20:48:39 -0400267 def create_page_highlight(self):
terryjreedye5bb1122017-07-05 00:54:55 -0400268 """Return frame of widgets for Highlighting tab.
269
terryjreedy9a09c662017-07-13 23:53:30 -0400270 Tk Variables:
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -0400271 color: Color of selected target.
terryjreedye5bb1122017-07-05 00:54:55 -0400272 builtin_theme: Menu variable for built-in theme.
273 custom_theme: Menu variable for custom theme.
274 fg_bg_toggle: Toggle for foreground/background color.
terryjreedy9a09c662017-07-13 23:53:30 -0400275 Note: this has no callback.
terryjreedye5bb1122017-07-05 00:54:55 -0400276 is_builtin_theme: Selector for built-in or custom theme.
277 highlight_target: Menu variable for the highlight tag target.
terryjreedy9a09c662017-07-13 23:53:30 -0400278
279 Instance Data Attributes:
280 theme_elements: Dictionary of tags for text highlighting.
281 The key is the display name and the value is a tuple of
282 (tag name, display sort order).
283
284 Methods [attachment]:
285 load_theme_cfg: Load current highlight colors.
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -0400286 get_color: Invoke colorchooser [button_set_color].
287 set_color_sample_binding: Call set_color_sample [fg_bg_toggle].
terryjreedy9a09c662017-07-13 23:53:30 -0400288 set_highlight_target: set fg_bg_toggle, set_color_sample().
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -0400289 set_color_sample: Set frame background to target.
290 on_new_color_set: Set new color and add option.
terryjreedy9a09c662017-07-13 23:53:30 -0400291 paint_theme_sample: Recolor sample.
292 get_new_theme_name: Get from popup.
293 create_new_theme: Combine theme with changes and save.
294 save_as_new_theme: Save [button_save_custom_theme].
295 set_theme_type: Command for [is_builtin_theme].
296 delete_custom_theme: Ativate default [button_delete_custom_theme].
297 save_new_theme: Save to userCfg['theme'] (is function).
298
299 Widget Structure: (*) widgets bound to self
300 frame
301 frame_custom: LabelFrame
Terry Jan Reedy04864b42017-07-22 00:56:18 -0400302 (*)highlight_sample: Text
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -0400303 (*)frame_color_set: Frame
304 button_set_color: Button
terryjreedy9a09c662017-07-13 23:53:30 -0400305 (*)opt_menu_highlight_target: DynOptionMenu - highlight_target
306 frame_fg_bg_toggle: Frame
307 (*)radio_fg: Radiobutton - fg_bg_toggle
308 (*)radio_bg: Radiobutton - fg_bg_toggle
309 button_save_custom_theme: Button
310 frame_theme: LabelFrame
311 theme_type_title: Label
312 (*)radio_theme_builtin: Radiobutton - is_builtin_theme
313 (*)radio_theme_custom: Radiobutton - is_builtin_theme
314 (*)opt_menu_theme_builtin: DynOptionMenu - builtin_theme
315 (*)opt_menu_theme_custom: DynOptionMenu - custom_theme
316 (*)button_delete_custom_theme: Button
317 (*)new_custom_theme: Label
terryjreedye5bb1122017-07-05 00:54:55 -0400318 """
terryjreedy9a09c662017-07-13 23:53:30 -0400319 self.theme_elements={
320 'Normal Text': ('normal', '00'),
321 'Python Keywords': ('keyword', '01'),
322 'Python Definitions': ('definition', '02'),
323 'Python Builtins': ('builtin', '03'),
324 'Python Comments': ('comment', '04'),
325 'Python Strings': ('string', '05'),
326 'Selected Text': ('hilite', '06'),
327 'Found Text': ('hit', '07'),
328 'Cursor': ('cursor', '08'),
329 'Editor Breakpoint': ('break', '09'),
330 'Shell Normal Text': ('console', '10'),
331 'Shell Error Text': ('error', '11'),
332 'Shell Stdout Text': ('stdout', '12'),
333 'Shell Stderr Text': ('stderr', '13'),
334 }
Terry Jan Reedy22405332014-07-30 19:24:32 -0400335 parent = self.parent
terryjreedy938e7382017-06-26 20:48:39 -0400336 self.builtin_theme = StringVar(parent)
337 self.custom_theme = StringVar(parent)
338 self.fg_bg_toggle = BooleanVar(parent)
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -0400339 self.color = StringVar(parent)
terryjreedy938e7382017-06-26 20:48:39 -0400340 self.is_builtin_theme = BooleanVar(parent)
341 self.highlight_target = StringVar(parent)
Terry Jan Reedy22405332014-07-30 19:24:32 -0400342
Steven M. Gava952d0a52001-08-03 04:43:44 +0000343 ##widget creation
344 #body frame
terryjreedy938e7382017-06-26 20:48:39 -0400345 frame = self.tab_pages.pages['Highlighting'].frame
Steven M. Gava952d0a52001-08-03 04:43:44 +0000346 #body section frames
terryjreedy938e7382017-06-26 20:48:39 -0400347 frame_custom = LabelFrame(frame, borderwidth=2, relief=GROOVE,
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400348 text=' Custom Highlighting ')
terryjreedy938e7382017-06-26 20:48:39 -0400349 frame_theme = LabelFrame(frame, borderwidth=2, relief=GROOVE,
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400350 text=' Highlighting Theme ')
terryjreedy938e7382017-06-26 20:48:39 -0400351 #frame_custom
Terry Jan Reedy04864b42017-07-22 00:56:18 -0400352 self.highlight_sample=Text(
terryjreedy938e7382017-06-26 20:48:39 -0400353 frame_custom, relief=SOLID, borderwidth=1,
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400354 font=('courier', 12, ''), cursor='hand2', width=21, height=11,
355 takefocus=FALSE, highlightthickness=0, wrap=NONE)
Terry Jan Reedy04864b42017-07-22 00:56:18 -0400356 text=self.highlight_sample
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400357 text.bind('<Double-Button-1>', lambda e: 'break')
358 text.bind('<B1-Motion>', lambda e: 'break')
terryjreedy938e7382017-06-26 20:48:39 -0400359 text_and_tags=(
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400360 ('#you can click here', 'comment'), ('\n', 'normal'),
361 ('#to choose items', 'comment'), ('\n', 'normal'),
362 ('def', 'keyword'), (' ', 'normal'),
363 ('func', 'definition'), ('(param):\n ', 'normal'),
364 ('"""string"""', 'string'), ('\n var0 = ', 'normal'),
365 ("'string'", 'string'), ('\n var1 = ', 'normal'),
366 ("'selected'", 'hilite'), ('\n var2 = ', 'normal'),
367 ("'found'", 'hit'), ('\n var3 = ', 'normal'),
368 ('list', 'builtin'), ('(', 'normal'),
Terry Jan Reedya8aa4d52015-10-02 22:12:17 -0400369 ('None', 'keyword'), (')\n', 'normal'),
370 (' breakpoint("line")', 'break'), ('\n\n', 'normal'),
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400371 (' error ', 'error'), (' ', 'normal'),
372 ('cursor |', 'cursor'), ('\n ', 'normal'),
373 ('shell', 'console'), (' ', 'normal'),
374 ('stdout', 'stdout'), (' ', 'normal'),
375 ('stderr', 'stderr'), ('\n', 'normal'))
terryjreedy938e7382017-06-26 20:48:39 -0400376 for texttag in text_and_tags:
377 text.insert(END, texttag[0], texttag[1])
378 for element in self.theme_elements:
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400379 def tem(event, elem=element):
terryjreedy938e7382017-06-26 20:48:39 -0400380 event.widget.winfo_toplevel().highlight_target.set(elem)
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400381 text.tag_bind(
terryjreedy938e7382017-06-26 20:48:39 -0400382 self.theme_elements[element][0], '<ButtonPress-1>', tem)
Steven M. Gavae16d94b2001-11-03 05:07:28 +0000383 text.config(state=DISABLED)
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -0400384 self.frame_color_set = Frame(frame_custom, relief=SOLID, borderwidth=1)
terryjreedy938e7382017-06-26 20:48:39 -0400385 frame_fg_bg_toggle = Frame(frame_custom)
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -0400386 button_set_color = Button(
387 self.frame_color_set, text='Choose Color for :',
388 command=self.get_color, highlightthickness=0)
terryjreedy938e7382017-06-26 20:48:39 -0400389 self.opt_menu_highlight_target = DynOptionMenu(
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -0400390 self.frame_color_set, self.highlight_target, None,
terryjreedy938e7382017-06-26 20:48:39 -0400391 highlightthickness=0) #, command=self.set_highlight_targetBinding
392 self.radio_fg = Radiobutton(
393 frame_fg_bg_toggle, variable=self.fg_bg_toggle, value=1,
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -0400394 text='Foreground', command=self.set_color_sample_binding)
terryjreedy938e7382017-06-26 20:48:39 -0400395 self.radio_bg=Radiobutton(
396 frame_fg_bg_toggle, variable=self.fg_bg_toggle, value=0,
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -0400397 text='Background', command=self.set_color_sample_binding)
terryjreedy938e7382017-06-26 20:48:39 -0400398 self.fg_bg_toggle.set(1)
399 button_save_custom_theme = Button(
400 frame_custom, text='Save as New Custom Theme',
401 command=self.save_as_new_theme)
402 #frame_theme
403 theme_type_title = Label(frame_theme, text='Select : ')
404 self.radio_theme_builtin = Radiobutton(
405 frame_theme, variable=self.is_builtin_theme, value=1,
406 command=self.set_theme_type, text='a Built-in Theme')
407 self.radio_theme_custom = Radiobutton(
408 frame_theme, variable=self.is_builtin_theme, value=0,
409 command=self.set_theme_type, text='a Custom Theme')
410 self.opt_menu_theme_builtin = DynOptionMenu(
411 frame_theme, self.builtin_theme, None, command=None)
412 self.opt_menu_theme_custom=DynOptionMenu(
413 frame_theme, self.custom_theme, None, command=None)
414 self.button_delete_custom_theme=Button(
415 frame_theme, text='Delete Custom Theme',
416 command=self.delete_custom_theme)
417 self.new_custom_theme = Label(frame_theme, bd=2)
Terry Jan Reedy22405332014-07-30 19:24:32 -0400418
Steven M. Gava952d0a52001-08-03 04:43:44 +0000419 ##widget packing
420 #body
terryjreedy938e7382017-06-26 20:48:39 -0400421 frame_custom.pack(side=LEFT, padx=5, pady=5, expand=TRUE, fill=BOTH)
422 frame_theme.pack(side=LEFT, padx=5, pady=5, fill=Y)
423 #frame_custom
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -0400424 self.frame_color_set.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=X)
terryjreedy938e7382017-06-26 20:48:39 -0400425 frame_fg_bg_toggle.pack(side=TOP, padx=5, pady=0)
Terry Jan Reedy04864b42017-07-22 00:56:18 -0400426 self.highlight_sample.pack(
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400427 side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH)
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -0400428 button_set_color.pack(side=TOP, expand=TRUE, fill=X, padx=8, pady=4)
terryjreedy938e7382017-06-26 20:48:39 -0400429 self.opt_menu_highlight_target.pack(
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400430 side=TOP, expand=TRUE, fill=X, padx=8, pady=3)
terryjreedy938e7382017-06-26 20:48:39 -0400431 self.radio_fg.pack(side=LEFT, anchor=E)
432 self.radio_bg.pack(side=RIGHT, anchor=W)
433 button_save_custom_theme.pack(side=BOTTOM, fill=X, padx=5, pady=5)
434 #frame_theme
435 theme_type_title.pack(side=TOP, anchor=W, padx=5, pady=5)
436 self.radio_theme_builtin.pack(side=TOP, anchor=W, padx=5)
437 self.radio_theme_custom.pack(side=TOP, anchor=W, padx=5, pady=2)
438 self.opt_menu_theme_builtin.pack(side=TOP, fill=X, padx=5, pady=5)
439 self.opt_menu_theme_custom.pack(side=TOP, fill=X, anchor=W, padx=5, pady=5)
440 self.button_delete_custom_theme.pack(side=TOP, fill=X, padx=5, pady=5)
Terry Jan Reedyd0c0f002015-11-12 15:02:57 -0500441 self.new_custom_theme.pack(side=TOP, fill=X, pady=5)
Steven M. Gava952d0a52001-08-03 04:43:44 +0000442 return frame
443
terryjreedy938e7382017-06-26 20:48:39 -0400444 def create_page_keys(self):
terryjreedye5bb1122017-07-05 00:54:55 -0400445 """Return frame of widgets for Keys tab.
446
terryjreedy9a09c662017-07-13 23:53:30 -0400447 Tk Variables:
terryjreedye5bb1122017-07-05 00:54:55 -0400448 builtin_keys: Menu variable for built-in keybindings.
449 custom_keys: Menu variable for custom keybindings.
450 are_keys_builtin: Selector for built-in or custom keybindings.
451 keybinding: Action/key bindings.
terryjreedy9a09c662017-07-13 23:53:30 -0400452
453 Methods:
454 load_key_config: Set table.
455 load_keys_list: Reload active set.
456 keybinding_selected: Bound to list_bindings button release.
457 get_new_keys: Command for button_new_keys.
458 get_new_keys_name: Call popup.
459 create_new_key_set: Combine active keyset and changes.
460 set_keys_type: Command for are_keys_builtin.
461 delete_custom_keys: Command for button_delete_custom_keys.
462 save_as_new_key_set: Command for button_save_custom_keys.
463 save_new_key_set: Save to idleConf.userCfg['keys'] (is function).
464 deactivate_current_config: Remove keys bindings in editors.
465
466 Widget Structure: (*) widgets bound to self
467 frame
468 frame_custom: LabelFrame
469 frame_target: Frame
470 target_title: Label
471 scroll_target_y: Scrollbar
472 scroll_target_x: Scrollbar
473 (*)list_bindings: ListBox
474 (*)button_new_keys: Button
475 frame_key_sets: LabelFrame
476 frames[0]: Frame
477 (*)radio_keys_builtin: Radiobutton - are_keys_builtin
478 (*)radio_keys_custom: Radiobutton - are_keys_builtin
479 (*)opt_menu_keys_builtin: DynOptionMenu - builtin_keys
480 (*)opt_menu_keys_custom: DynOptionMenu - custom_keys
481 (*)new_custom_keys: Label
482 frames[1]: Frame
483 (*)button_delete_custom_keys: Button
484 button_save_custom_keys: Button
terryjreedye5bb1122017-07-05 00:54:55 -0400485 """
Terry Jan Reedy22405332014-07-30 19:24:32 -0400486 parent = self.parent
terryjreedy938e7382017-06-26 20:48:39 -0400487 self.builtin_keys = StringVar(parent)
488 self.custom_keys = StringVar(parent)
489 self.are_keys_builtin = BooleanVar(parent)
490 self.keybinding = StringVar(parent)
Terry Jan Reedy22405332014-07-30 19:24:32 -0400491
Steven M. Gava60fc7072001-08-04 13:58:22 +0000492 ##widget creation
493 #body frame
terryjreedy938e7382017-06-26 20:48:39 -0400494 frame = self.tab_pages.pages['Keys'].frame
Steven M. Gava60fc7072001-08-04 13:58:22 +0000495 #body section frames
terryjreedy938e7382017-06-26 20:48:39 -0400496 frame_custom = LabelFrame(
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400497 frame, borderwidth=2, relief=GROOVE,
498 text=' Custom Key Bindings ')
terryjreedy938e7382017-06-26 20:48:39 -0400499 frame_key_sets = LabelFrame(
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400500 frame, borderwidth=2, relief=GROOVE, text=' Key Set ')
terryjreedy938e7382017-06-26 20:48:39 -0400501 #frame_custom
502 frame_target = Frame(frame_custom)
503 target_title = Label(frame_target, text='Action - Key(s)')
504 scroll_target_y = Scrollbar(frame_target)
505 scroll_target_x = Scrollbar(frame_target, orient=HORIZONTAL)
506 self.list_bindings = Listbox(
507 frame_target, takefocus=FALSE, exportselection=FALSE)
508 self.list_bindings.bind('<ButtonRelease-1>', self.keybinding_selected)
509 scroll_target_y.config(command=self.list_bindings.yview)
510 scroll_target_x.config(command=self.list_bindings.xview)
511 self.list_bindings.config(yscrollcommand=scroll_target_y.set)
512 self.list_bindings.config(xscrollcommand=scroll_target_x.set)
513 self.button_new_keys = Button(
514 frame_custom, text='Get New Keys for Selection',
515 command=self.get_new_keys, state=DISABLED)
516 #frame_key_sets
517 frames = [Frame(frame_key_sets, padx=2, pady=2, borderwidth=0)
Christian Heimes9a371592007-12-28 14:08:13 +0000518 for i in range(2)]
terryjreedy938e7382017-06-26 20:48:39 -0400519 self.radio_keys_builtin = Radiobutton(
520 frames[0], variable=self.are_keys_builtin, value=1,
521 command=self.set_keys_type, text='Use a Built-in Key Set')
522 self.radio_keys_custom = Radiobutton(
523 frames[0], variable=self.are_keys_builtin, value=0,
524 command=self.set_keys_type, text='Use a Custom Key Set')
525 self.opt_menu_keys_builtin = DynOptionMenu(
526 frames[0], self.builtin_keys, None, command=None)
527 self.opt_menu_keys_custom = DynOptionMenu(
528 frames[0], self.custom_keys, None, command=None)
529 self.button_delete_custom_keys = Button(
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400530 frames[1], text='Delete Custom Key Set',
terryjreedy938e7382017-06-26 20:48:39 -0400531 command=self.delete_custom_keys)
532 button_save_custom_keys = Button(
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400533 frames[1], text='Save as New Custom Key Set',
terryjreedy938e7382017-06-26 20:48:39 -0400534 command=self.save_as_new_key_set)
Terry Jan Reedy9bdb1ed2016-07-10 13:46:34 -0400535 self.new_custom_keys = Label(frames[0], bd=2)
Terry Jan Reedy22405332014-07-30 19:24:32 -0400536
Steven M. Gava60fc7072001-08-04 13:58:22 +0000537 ##widget packing
538 #body
terryjreedy938e7382017-06-26 20:48:39 -0400539 frame_custom.pack(side=BOTTOM, padx=5, pady=5, expand=TRUE, fill=BOTH)
540 frame_key_sets.pack(side=BOTTOM, padx=5, pady=5, fill=BOTH)
541 #frame_custom
542 self.button_new_keys.pack(side=BOTTOM, fill=X, padx=5, pady=5)
543 frame_target.pack(side=LEFT, padx=5, pady=5, expand=TRUE, fill=BOTH)
Steven M. Gavafacfc092002-01-19 00:29:54 +0000544 #frame target
terryjreedy938e7382017-06-26 20:48:39 -0400545 frame_target.columnconfigure(0, weight=1)
546 frame_target.rowconfigure(1, weight=1)
547 target_title.grid(row=0, column=0, columnspan=2, sticky=W)
548 self.list_bindings.grid(row=1, column=0, sticky=NSEW)
549 scroll_target_y.grid(row=1, column=1, sticky=NS)
550 scroll_target_x.grid(row=2, column=0, sticky=EW)
551 #frame_key_sets
552 self.radio_keys_builtin.grid(row=0, column=0, sticky=W+NS)
553 self.radio_keys_custom.grid(row=1, column=0, sticky=W+NS)
554 self.opt_menu_keys_builtin.grid(row=0, column=1, sticky=NSEW)
555 self.opt_menu_keys_custom.grid(row=1, column=1, sticky=NSEW)
Terry Jan Reedy9bdb1ed2016-07-10 13:46:34 -0400556 self.new_custom_keys.grid(row=0, column=2, sticky=NSEW, padx=5, pady=5)
terryjreedy938e7382017-06-26 20:48:39 -0400557 self.button_delete_custom_keys.pack(side=LEFT, fill=X, expand=True, padx=2)
558 button_save_custom_keys.pack(side=LEFT, fill=X, expand=True, padx=2)
Christian Heimes9a371592007-12-28 14:08:13 +0000559 frames[0].pack(side=TOP, fill=BOTH, expand=True)
560 frames[1].pack(side=TOP, fill=X, expand=True, pady=2)
Steven M. Gava952d0a52001-08-03 04:43:44 +0000561 return frame
562
terryjreedy938e7382017-06-26 20:48:39 -0400563 def create_page_general(self):
terryjreedye5bb1122017-07-05 00:54:55 -0400564 """Return frame of widgets for General tab.
565
terryjreedy9a09c662017-07-13 23:53:30 -0400566 Tk Variables:
terryjreedye5bb1122017-07-05 00:54:55 -0400567 win_width: Initial window width in characters.
568 win_height: Initial window height in characters.
569 startup_edit: Selector for opening in editor or shell mode.
570 autosave: Selector for save prompt popup when using Run.
terryjreedy9a09c662017-07-13 23:53:30 -0400571
572 Methods:
573 load_general_config:
574 help_source_selected: Bound to list_help button release.
575 set_helplist_button_states: Toggle based on list.
576 helplist_item_edit: Command for button_helplist_edit.
577 helplist_item_add: Command for button_helplist_add.
578 helplist_item_remove: Command for button_helplist_remove.
579 update_user_help_changed_items: Fill in changes.
580
581 Widget Structure: (*) widgets bound to self
582 frame
583 frame_run: LabelFrame
584 startup_title: Label
585 (*)radio_startup_edit: Radiobutton - startup_edit
586 (*)radio_startup_shell: Radiobutton - startup_edit
587 frame_save: LabelFrame
588 run_save_title: Label
589 (*)radio_save_ask: Radiobutton - autosave
590 (*)radio_save_auto: Radiobutton - autosave
591 frame_win_size: LabelFrame
592 win_size_title: Label
593 win_width_title: Label
594 (*)entry_win_width: Entry - win_width
595 win_height_title: Label
596 (*)entry_win_height: Entry - win_height
597 frame_help: LabelFrame
598 frame_helplist: Frame
599 frame_helplist_buttons: Frame
600 (*)button_helplist_edit
601 (*)button_helplist_add
602 (*)button_helplist_remove
603 scroll_helplist: Scrollbar
604 (*)list_help: ListBox
terryjreedye5bb1122017-07-05 00:54:55 -0400605 """
Terry Jan Reedy22405332014-07-30 19:24:32 -0400606 parent = self.parent
terryjreedy938e7382017-06-26 20:48:39 -0400607 self.win_width = StringVar(parent)
608 self.win_height = StringVar(parent)
609 self.startup_edit = IntVar(parent)
610 self.autosave = IntVar(parent)
Terry Jan Reedy22405332014-07-30 19:24:32 -0400611
Steven M. Gava230e5782001-08-07 03:28:25 +0000612 #widget creation
613 #body
terryjreedy938e7382017-06-26 20:48:39 -0400614 frame = self.tab_pages.pages['General'].frame
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000615 #body section frames
terryjreedy938e7382017-06-26 20:48:39 -0400616 frame_run = LabelFrame(frame, borderwidth=2, relief=GROOVE,
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400617 text=' Startup Preferences ')
terryjreedy938e7382017-06-26 20:48:39 -0400618 frame_save = LabelFrame(frame, borderwidth=2, relief=GROOVE,
619 text=' autosave Preferences ')
620 frame_win_size = Frame(frame, borderwidth=2, relief=GROOVE)
621 frame_help = LabelFrame(frame, borderwidth=2, relief=GROOVE,
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400622 text=' Additional Help Sources ')
terryjreedy938e7382017-06-26 20:48:39 -0400623 #frame_run
624 startup_title = Label(frame_run, text='At Startup')
625 self.radio_startup_edit = Radiobutton(
626 frame_run, variable=self.startup_edit, value=1,
Terry Jan Reedyf46b7822016-11-07 17:15:01 -0500627 text="Open Edit Window")
terryjreedy938e7382017-06-26 20:48:39 -0400628 self.radio_startup_shell = Radiobutton(
629 frame_run, variable=self.startup_edit, value=0,
Terry Jan Reedyf46b7822016-11-07 17:15:01 -0500630 text='Open Shell Window')
terryjreedy938e7382017-06-26 20:48:39 -0400631 #frame_save
632 run_save_title = Label(frame_save, text='At Start of Run (F5) ')
633 self.radio_save_ask = Radiobutton(
634 frame_save, variable=self.autosave, value=0,
Terry Jan Reedyf46b7822016-11-07 17:15:01 -0500635 text="Prompt to Save")
terryjreedy938e7382017-06-26 20:48:39 -0400636 self.radio_save_auto = Radiobutton(
637 frame_save, variable=self.autosave, value=1,
Terry Jan Reedyf46b7822016-11-07 17:15:01 -0500638 text='No Prompt')
terryjreedy938e7382017-06-26 20:48:39 -0400639 #frame_win_size
640 win_size_title = Label(
641 frame_win_size, text='Initial Window Size (in characters)')
642 win_width_title = Label(frame_win_size, text='Width')
643 self.entry_win_width = Entry(
644 frame_win_size, textvariable=self.win_width, width=3)
645 win_height_title = Label(frame_win_size, text='Height')
646 self.entry_win_height = Entry(
647 frame_win_size, textvariable=self.win_height, width=3)
648 #frame_help
649 frame_helplist = Frame(frame_help)
650 frame_helplist_buttons = Frame(frame_helplist)
651 scroll_helplist = Scrollbar(frame_helplist)
652 self.list_help = Listbox(
653 frame_helplist, height=5, takefocus=FALSE,
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000654 exportselection=FALSE)
terryjreedy938e7382017-06-26 20:48:39 -0400655 scroll_helplist.config(command=self.list_help.yview)
656 self.list_help.config(yscrollcommand=scroll_helplist.set)
657 self.list_help.bind('<ButtonRelease-1>', self.help_source_selected)
658 self.button_helplist_edit = Button(
659 frame_helplist_buttons, text='Edit', state=DISABLED,
660 width=8, command=self.helplist_item_edit)
661 self.button_helplist_add = Button(
662 frame_helplist_buttons, text='Add',
663 width=8, command=self.helplist_item_add)
664 self.button_helplist_remove = Button(
665 frame_helplist_buttons, text='Remove', state=DISABLED,
666 width=8, command=self.helplist_item_remove)
Terry Jan Reedy22405332014-07-30 19:24:32 -0400667
Steven M. Gava230e5782001-08-07 03:28:25 +0000668 #widget packing
669 #body
terryjreedy938e7382017-06-26 20:48:39 -0400670 frame_run.pack(side=TOP, padx=5, pady=5, fill=X)
671 frame_save.pack(side=TOP, padx=5, pady=5, fill=X)
672 frame_win_size.pack(side=TOP, padx=5, pady=5, fill=X)
673 frame_help.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH)
674 #frame_run
675 startup_title.pack(side=LEFT, anchor=W, padx=5, pady=5)
676 self.radio_startup_shell.pack(side=RIGHT, anchor=W, padx=5, pady=5)
677 self.radio_startup_edit.pack(side=RIGHT, anchor=W, padx=5, pady=5)
678 #frame_save
679 run_save_title.pack(side=LEFT, anchor=W, padx=5, pady=5)
680 self.radio_save_auto.pack(side=RIGHT, anchor=W, padx=5, pady=5)
681 self.radio_save_ask.pack(side=RIGHT, anchor=W, padx=5, pady=5)
682 #frame_win_size
683 win_size_title.pack(side=LEFT, anchor=W, padx=5, pady=5)
684 self.entry_win_height.pack(side=RIGHT, anchor=E, padx=10, pady=5)
685 win_height_title.pack(side=RIGHT, anchor=E, pady=5)
686 self.entry_win_width.pack(side=RIGHT, anchor=E, padx=10, pady=5)
687 win_width_title.pack(side=RIGHT, anchor=E, pady=5)
688 #frame_help
689 frame_helplist_buttons.pack(side=RIGHT, padx=5, pady=5, fill=Y)
690 frame_helplist.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH)
691 scroll_helplist.pack(side=RIGHT, anchor=W, fill=Y)
692 self.list_help.pack(side=LEFT, anchor=E, expand=TRUE, fill=BOTH)
693 self.button_helplist_edit.pack(side=TOP, anchor=W, pady=5)
694 self.button_helplist_add.pack(side=TOP, anchor=W)
695 self.button_helplist_remove.pack(side=TOP, anchor=W, pady=5)
Steven M. Gava952d0a52001-08-03 04:43:44 +0000696 return frame
697
terryjreedy938e7382017-06-26 20:48:39 -0400698 def attach_var_callbacks(self):
terryjreedye5bb1122017-07-05 00:54:55 -0400699 "Attach callbacks to variables that can be changed."
terryjreedy938e7382017-06-26 20:48:39 -0400700 self.font_size.trace_add('write', self.var_changed_font)
701 self.font_name.trace_add('write', self.var_changed_font)
702 self.font_bold.trace_add('write', self.var_changed_font)
703 self.space_num.trace_add('write', self.var_changed_space_num)
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -0400704 self.color.trace_add('write', self.var_changed_color)
terryjreedy938e7382017-06-26 20:48:39 -0400705 self.builtin_theme.trace_add('write', self.var_changed_builtin_theme)
706 self.custom_theme.trace_add('write', self.var_changed_custom_theme)
707 self.is_builtin_theme.trace_add('write', self.var_changed_is_builtin_theme)
708 self.highlight_target.trace_add('write', self.var_changed_highlight_target)
709 self.keybinding.trace_add('write', self.var_changed_keybinding)
710 self.builtin_keys.trace_add('write', self.var_changed_builtin_keys)
711 self.custom_keys.trace_add('write', self.var_changed_custom_keys)
712 self.are_keys_builtin.trace_add('write', self.var_changed_are_keys_builtin)
713 self.win_width.trace_add('write', self.var_changed_win_width)
714 self.win_height.trace_add('write', self.var_changed_win_height)
715 self.startup_edit.trace_add('write', self.var_changed_startup_edit)
716 self.autosave.trace_add('write', self.var_changed_autosave)
Steven M. Gava052937f2002-02-11 02:20:53 +0000717
Terry Jan Reedy6b98ce22016-05-16 22:27:28 -0400718 def remove_var_callbacks(self):
719 "Remove callbacks to prevent memory leaks."
720 for var in (
terryjreedy938e7382017-06-26 20:48:39 -0400721 self.font_size, self.font_name, self.font_bold,
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -0400722 self.space_num, self.color, self.builtin_theme,
terryjreedy938e7382017-06-26 20:48:39 -0400723 self.custom_theme, self.is_builtin_theme, self.highlight_target,
724 self.keybinding, self.builtin_keys, self.custom_keys,
725 self.are_keys_builtin, self.win_width, self.win_height,
terryjreedy8e3f73e2017-07-10 15:11:45 -0400726 self.startup_edit, self.autosave,):
Serhiy Storchaka81221742016-06-26 09:46:57 +0300727 var.trace_remove('write', var.trace_info()[0][1])
Terry Jan Reedy6b98ce22016-05-16 22:27:28 -0400728
terryjreedy938e7382017-06-26 20:48:39 -0400729 def var_changed_font(self, *params):
terryjreedye5bb1122017-07-05 00:54:55 -0400730 """Store changes to font attributes.
731
732 When one font attribute changes, save them all, as they are
Terry Jan Reedyd87d1682015-08-01 18:57:33 -0400733 not independent from each other. In particular, when we are
734 overriding the default font, we need to write out everything.
terryjreedye5bb1122017-07-05 00:54:55 -0400735 """
terryjreedy938e7382017-06-26 20:48:39 -0400736 value = self.font_name.get()
terryjreedyedc03422017-07-07 16:37:39 -0400737 changes.add_option('main', 'EditorWindow', 'font', value)
terryjreedy938e7382017-06-26 20:48:39 -0400738 value = self.font_size.get()
terryjreedyedc03422017-07-07 16:37:39 -0400739 changes.add_option('main', 'EditorWindow', 'font-size', value)
terryjreedy938e7382017-06-26 20:48:39 -0400740 value = self.font_bold.get()
terryjreedyedc03422017-07-07 16:37:39 -0400741 changes.add_option('main', 'EditorWindow', 'font-bold', value)
Steven M. Gavac112cd82002-01-22 05:56:40 +0000742
terryjreedy938e7382017-06-26 20:48:39 -0400743 def var_changed_space_num(self, *params):
terryjreedye5bb1122017-07-05 00:54:55 -0400744 "Store change to indentation size."
terryjreedy938e7382017-06-26 20:48:39 -0400745 value = self.space_num.get()
terryjreedyedc03422017-07-07 16:37:39 -0400746 changes.add_option('main', 'Indent', 'num-spaces', value)
Steven M. Gavac112cd82002-01-22 05:56:40 +0000747
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -0400748 def var_changed_color(self, *params):
terryjreedye5bb1122017-07-05 00:54:55 -0400749 "Process change to color choice."
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -0400750 self.on_new_color_set()
Steven M. Gava052937f2002-02-11 02:20:53 +0000751
terryjreedy938e7382017-06-26 20:48:39 -0400752 def var_changed_builtin_theme(self, *params):
terryjreedye5bb1122017-07-05 00:54:55 -0400753 """Process new builtin theme selection.
754
755 Add the changed theme's name to the changed_items and recreate
756 the sample with the values from the selected theme.
757 """
terryjreedy938e7382017-06-26 20:48:39 -0400758 old_themes = ('IDLE Classic', 'IDLE New')
759 value = self.builtin_theme.get()
760 if value not in old_themes:
761 if idleConf.GetOption('main', 'Theme', 'name') not in old_themes:
terryjreedyedc03422017-07-07 16:37:39 -0400762 changes.add_option('main', 'Theme', 'name', old_themes[0])
763 changes.add_option('main', 'Theme', 'name2', value)
Terry Jan Reedyd0c0f002015-11-12 15:02:57 -0500764 self.new_custom_theme.config(text='New theme, see Help',
765 fg='#500000')
766 else:
terryjreedyedc03422017-07-07 16:37:39 -0400767 changes.add_option('main', 'Theme', 'name', value)
768 changes.add_option('main', 'Theme', 'name2', '')
Terry Jan Reedyd0c0f002015-11-12 15:02:57 -0500769 self.new_custom_theme.config(text='', fg='black')
terryjreedy938e7382017-06-26 20:48:39 -0400770 self.paint_theme_sample()
Steven M. Gava052937f2002-02-11 02:20:53 +0000771
terryjreedy938e7382017-06-26 20:48:39 -0400772 def var_changed_custom_theme(self, *params):
terryjreedye5bb1122017-07-05 00:54:55 -0400773 """Process new custom theme selection.
774
775 If a new custom theme is selected, add the name to the
776 changed_items and apply the theme to the sample.
777 """
terryjreedy938e7382017-06-26 20:48:39 -0400778 value = self.custom_theme.get()
Steven M. Gava49745752002-02-18 01:43:11 +0000779 if value != '- no custom themes -':
terryjreedyedc03422017-07-07 16:37:39 -0400780 changes.add_option('main', 'Theme', 'name', value)
terryjreedy938e7382017-06-26 20:48:39 -0400781 self.paint_theme_sample()
Steven M. Gava052937f2002-02-11 02:20:53 +0000782
terryjreedy938e7382017-06-26 20:48:39 -0400783 def var_changed_is_builtin_theme(self, *params):
terryjreedye5bb1122017-07-05 00:54:55 -0400784 """Process toggle between builtin and custom theme.
785
786 Update the default toggle value and apply the newly
787 selected theme type.
788 """
terryjreedy938e7382017-06-26 20:48:39 -0400789 value = self.is_builtin_theme.get()
terryjreedyedc03422017-07-07 16:37:39 -0400790 changes.add_option('main', 'Theme', 'default', value)
Steven M. Gavaf31eec02002-03-05 00:25:58 +0000791 if value:
terryjreedy938e7382017-06-26 20:48:39 -0400792 self.var_changed_builtin_theme()
Steven M. Gavaf31eec02002-03-05 00:25:58 +0000793 else:
terryjreedy938e7382017-06-26 20:48:39 -0400794 self.var_changed_custom_theme()
Steven M. Gava052937f2002-02-11 02:20:53 +0000795
terryjreedy938e7382017-06-26 20:48:39 -0400796 def var_changed_highlight_target(self, *params):
terryjreedye5bb1122017-07-05 00:54:55 -0400797 "Process selection of new target tag for highlighting."
terryjreedy938e7382017-06-26 20:48:39 -0400798 self.set_highlight_target()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000799
terryjreedy938e7382017-06-26 20:48:39 -0400800 def var_changed_keybinding(self, *params):
terryjreedye5bb1122017-07-05 00:54:55 -0400801 "Store change to a keybinding."
terryjreedy938e7382017-06-26 20:48:39 -0400802 value = self.keybinding.get()
803 key_set = self.custom_keys.get()
804 event = self.list_bindings.get(ANCHOR).split()[0]
Steven M. Gavaa498af22002-02-01 01:33:36 +0000805 if idleConf.IsCoreBinding(event):
terryjreedyedc03422017-07-07 16:37:39 -0400806 changes.add_option('keys', key_set, event, value)
terryjreedye5bb1122017-07-05 00:54:55 -0400807 else: # Event is an extension binding.
terryjreedy938e7382017-06-26 20:48:39 -0400808 ext_name = idleConf.GetExtnNameForEvent(event)
809 ext_keybind_section = ext_name + '_cfgBindings'
terryjreedyedc03422017-07-07 16:37:39 -0400810 changes.add_option('extensions', ext_keybind_section, event, value)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000811
terryjreedy938e7382017-06-26 20:48:39 -0400812 def var_changed_builtin_keys(self, *params):
terryjreedye5bb1122017-07-05 00:54:55 -0400813 "Process selection of builtin key set."
terryjreedy938e7382017-06-26 20:48:39 -0400814 old_keys = (
Terry Jan Reedy9bdb1ed2016-07-10 13:46:34 -0400815 'IDLE Classic Windows',
816 'IDLE Classic Unix',
817 'IDLE Classic Mac',
818 'IDLE Classic OSX',
819 )
terryjreedy938e7382017-06-26 20:48:39 -0400820 value = self.builtin_keys.get()
821 if value not in old_keys:
822 if idleConf.GetOption('main', 'Keys', 'name') not in old_keys:
terryjreedyedc03422017-07-07 16:37:39 -0400823 changes.add_option('main', 'Keys', 'name', old_keys[0])
824 changes.add_option('main', 'Keys', 'name2', value)
Terry Jan Reedy9bdb1ed2016-07-10 13:46:34 -0400825 self.new_custom_keys.config(text='New key set, see Help',
826 fg='#500000')
827 else:
terryjreedyedc03422017-07-07 16:37:39 -0400828 changes.add_option('main', 'Keys', 'name', value)
829 changes.add_option('main', 'Keys', 'name2', '')
Terry Jan Reedy9bdb1ed2016-07-10 13:46:34 -0400830 self.new_custom_keys.config(text='', fg='black')
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_custom_keys(self, *params):
terryjreedye5bb1122017-07-05 00:54:55 -0400834 "Process selection of custom key set."
terryjreedy938e7382017-06-26 20:48:39 -0400835 value = self.custom_keys.get()
Steven M. Gava49745752002-02-18 01:43:11 +0000836 if value != '- no custom keys -':
terryjreedyedc03422017-07-07 16:37:39 -0400837 changes.add_option('main', 'Keys', 'name', value)
terryjreedy938e7382017-06-26 20:48:39 -0400838 self.load_keys_list(value)
Steven M. Gava052937f2002-02-11 02:20:53 +0000839
terryjreedy938e7382017-06-26 20:48:39 -0400840 def var_changed_are_keys_builtin(self, *params):
terryjreedye5bb1122017-07-05 00:54:55 -0400841 "Process toggle between builtin key set and custom key set."
terryjreedy938e7382017-06-26 20:48:39 -0400842 value = self.are_keys_builtin.get()
terryjreedyedc03422017-07-07 16:37:39 -0400843 changes.add_option('main', 'Keys', 'default', value)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000844 if value:
terryjreedy938e7382017-06-26 20:48:39 -0400845 self.var_changed_builtin_keys()
Steven M. Gava052937f2002-02-11 02:20:53 +0000846 else:
terryjreedy938e7382017-06-26 20:48:39 -0400847 self.var_changed_custom_keys()
Steven M. Gava052937f2002-02-11 02:20:53 +0000848
terryjreedy938e7382017-06-26 20:48:39 -0400849 def var_changed_win_width(self, *params):
terryjreedye5bb1122017-07-05 00:54:55 -0400850 "Store change to window width."
terryjreedy938e7382017-06-26 20:48:39 -0400851 value = self.win_width.get()
terryjreedyedc03422017-07-07 16:37:39 -0400852 changes.add_option('main', 'EditorWindow', 'width', value)
Steven M. Gavac112cd82002-01-22 05:56:40 +0000853
terryjreedy938e7382017-06-26 20:48:39 -0400854 def var_changed_win_height(self, *params):
terryjreedye5bb1122017-07-05 00:54:55 -0400855 "Store change to window height."
terryjreedy938e7382017-06-26 20:48:39 -0400856 value = self.win_height.get()
terryjreedyedc03422017-07-07 16:37:39 -0400857 changes.add_option('main', 'EditorWindow', 'height', value)
Steven M. Gavac112cd82002-01-22 05:56:40 +0000858
terryjreedy938e7382017-06-26 20:48:39 -0400859 def var_changed_startup_edit(self, *params):
terryjreedye5bb1122017-07-05 00:54:55 -0400860 "Store change to toggle for starting IDLE in the editor or shell."
terryjreedy938e7382017-06-26 20:48:39 -0400861 value = self.startup_edit.get()
terryjreedyedc03422017-07-07 16:37:39 -0400862 changes.add_option('main', 'General', 'editor-on-startup', value)
Steven M. Gavac112cd82002-01-22 05:56:40 +0000863
terryjreedy938e7382017-06-26 20:48:39 -0400864 def var_changed_autosave(self, *params):
terryjreedye5bb1122017-07-05 00:54:55 -0400865 "Store change to autosave."
terryjreedy938e7382017-06-26 20:48:39 -0400866 value = self.autosave.get()
terryjreedyedc03422017-07-07 16:37:39 -0400867 changes.add_option('main', 'General', 'autosave', value)
Kurt B. Kaiser6c638b62003-05-26 06:23:10 +0000868
terryjreedy938e7382017-06-26 20:48:39 -0400869 def set_theme_type(self):
terryjreedy9a09c662017-07-13 23:53:30 -0400870 """Set available screen options based on builtin or custom theme.
871
872 Attributes accessed:
873 is_builtin_theme
874
875 Attributes updated:
876 opt_menu_theme_builtin
877 opt_menu_theme_custom
878 button_delete_custom_theme
879 radio_theme_custom
880
881 Called from:
882 handler for radio_theme_builtin and radio_theme_custom
883 delete_custom_theme
884 create_new_theme
885 load_theme_cfg
886 """
terryjreedy938e7382017-06-26 20:48:39 -0400887 if self.is_builtin_theme.get():
888 self.opt_menu_theme_builtin.config(state=NORMAL)
889 self.opt_menu_theme_custom.config(state=DISABLED)
890 self.button_delete_custom_theme.config(state=DISABLED)
Steven M. Gava5f28e8f2002-01-21 06:38:21 +0000891 else:
terryjreedy938e7382017-06-26 20:48:39 -0400892 self.opt_menu_theme_builtin.config(state=DISABLED)
893 self.radio_theme_custom.config(state=NORMAL)
894 self.opt_menu_theme_custom.config(state=NORMAL)
895 self.button_delete_custom_theme.config(state=NORMAL)
Steven M. Gava5f28e8f2002-01-21 06:38:21 +0000896
terryjreedy938e7382017-06-26 20:48:39 -0400897 def set_keys_type(self):
terryjreedye5bb1122017-07-05 00:54:55 -0400898 "Set available screen options based on builtin or custom key set."
terryjreedy938e7382017-06-26 20:48:39 -0400899 if self.are_keys_builtin.get():
900 self.opt_menu_keys_builtin.config(state=NORMAL)
901 self.opt_menu_keys_custom.config(state=DISABLED)
902 self.button_delete_custom_keys.config(state=DISABLED)
Steven M. Gava5f28e8f2002-01-21 06:38:21 +0000903 else:
terryjreedy938e7382017-06-26 20:48:39 -0400904 self.opt_menu_keys_builtin.config(state=DISABLED)
905 self.radio_keys_custom.config(state=NORMAL)
906 self.opt_menu_keys_custom.config(state=NORMAL)
907 self.button_delete_custom_keys.config(state=NORMAL)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000908
terryjreedy938e7382017-06-26 20:48:39 -0400909 def get_new_keys(self):
terryjreedye5bb1122017-07-05 00:54:55 -0400910 """Handle event to change key binding for selected line.
911
912 A selection of a key/binding in the list of current
913 bindings pops up a dialog to enter a new binding. If
914 the current key set is builtin and a binding has
915 changed, then a name for a custom key set needs to be
916 entered for the change to be applied.
917 """
terryjreedy938e7382017-06-26 20:48:39 -0400918 list_index = self.list_bindings.index(ANCHOR)
919 binding = self.list_bindings.get(list_index)
terryjreedye5bb1122017-07-05 00:54:55 -0400920 bind_name = binding.split()[0]
terryjreedy938e7382017-06-26 20:48:39 -0400921 if self.are_keys_builtin.get():
922 current_key_set_name = self.builtin_keys.get()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000923 else:
terryjreedy938e7382017-06-26 20:48:39 -0400924 current_key_set_name = self.custom_keys.get()
925 current_bindings = idleConf.GetCurrentKeySet()
terryjreedyedc03422017-07-07 16:37:39 -0400926 if current_key_set_name in changes['keys']: # unsaved changes
927 key_set_changes = changes['keys'][current_key_set_name]
terryjreedy938e7382017-06-26 20:48:39 -0400928 for event in key_set_changes:
929 current_bindings[event] = key_set_changes[event].split()
930 current_key_sequences = list(current_bindings.values())
931 new_keys = GetKeysDialog(self, 'Get New Keys', bind_name,
932 current_key_sequences).result
terryjreedye5bb1122017-07-05 00:54:55 -0400933 if new_keys:
934 if self.are_keys_builtin.get(): # Current key set is a built-in.
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400935 message = ('Your changes will be saved as a new Custom Key Set.'
936 ' Enter a name for your new Custom Key Set below.')
terryjreedy938e7382017-06-26 20:48:39 -0400937 new_keyset = self.get_new_keys_name(message)
terryjreedye5bb1122017-07-05 00:54:55 -0400938 if not new_keyset: # User cancelled custom key set creation.
terryjreedy938e7382017-06-26 20:48:39 -0400939 self.list_bindings.select_set(list_index)
940 self.list_bindings.select_anchor(list_index)
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000941 return
terryjreedye5bb1122017-07-05 00:54:55 -0400942 else: # Create new custom key set based on previously active key set.
terryjreedy938e7382017-06-26 20:48:39 -0400943 self.create_new_key_set(new_keyset)
944 self.list_bindings.delete(list_index)
945 self.list_bindings.insert(list_index, bind_name+' - '+new_keys)
946 self.list_bindings.select_set(list_index)
947 self.list_bindings.select_anchor(list_index)
948 self.keybinding.set(new_keys)
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000949 else:
terryjreedy938e7382017-06-26 20:48:39 -0400950 self.list_bindings.select_set(list_index)
951 self.list_bindings.select_anchor(list_index)
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000952
terryjreedy938e7382017-06-26 20:48:39 -0400953 def get_new_keys_name(self, message):
terryjreedye5bb1122017-07-05 00:54:55 -0400954 "Return new key set name from query popup."
terryjreedy938e7382017-06-26 20:48:39 -0400955 used_names = (idleConf.GetSectionList('user', 'keys') +
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400956 idleConf.GetSectionList('default', 'keys'))
terryjreedy938e7382017-06-26 20:48:39 -0400957 new_keyset = SectionName(
958 self, 'New Custom Key Set', message, used_names).result
959 return new_keyset
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000960
terryjreedy938e7382017-06-26 20:48:39 -0400961 def save_as_new_key_set(self):
terryjreedye5bb1122017-07-05 00:54:55 -0400962 "Prompt for name of new key set and save changes using that name."
terryjreedy938e7382017-06-26 20:48:39 -0400963 new_keys_name = self.get_new_keys_name('New Key Set Name:')
964 if new_keys_name:
965 self.create_new_key_set(new_keys_name)
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000966
terryjreedy938e7382017-06-26 20:48:39 -0400967 def keybinding_selected(self, event):
terryjreedye5bb1122017-07-05 00:54:55 -0400968 "Activate button to assign new keys to selected action."
terryjreedy938e7382017-06-26 20:48:39 -0400969 self.button_new_keys.config(state=NORMAL)
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000970
terryjreedy938e7382017-06-26 20:48:39 -0400971 def create_new_key_set(self, new_key_set_name):
terryjreedye5bb1122017-07-05 00:54:55 -0400972 """Create a new custom key set with the given name.
973
974 Create the new key set based on the previously active set
975 with the current changes applied. Once it is saved, then
976 activate the new key set.
977 """
terryjreedy938e7382017-06-26 20:48:39 -0400978 if self.are_keys_builtin.get():
979 prev_key_set_name = self.builtin_keys.get()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000980 else:
terryjreedy938e7382017-06-26 20:48:39 -0400981 prev_key_set_name = self.custom_keys.get()
982 prev_keys = idleConf.GetCoreKeys(prev_key_set_name)
983 new_keys = {}
terryjreedye5bb1122017-07-05 00:54:55 -0400984 for event in prev_keys: # Add key set to changed items.
985 event_name = event[2:-2] # Trim off the angle brackets.
terryjreedy938e7382017-06-26 20:48:39 -0400986 binding = ' '.join(prev_keys[event])
987 new_keys[event_name] = binding
terryjreedye5bb1122017-07-05 00:54:55 -0400988 # Handle any unsaved changes to prev key set.
terryjreedyedc03422017-07-07 16:37:39 -0400989 if prev_key_set_name in changes['keys']:
990 key_set_changes = changes['keys'][prev_key_set_name]
terryjreedy938e7382017-06-26 20:48:39 -0400991 for event in key_set_changes:
992 new_keys[event] = key_set_changes[event]
terryjreedye5bb1122017-07-05 00:54:55 -0400993 # Save the new key set.
terryjreedy938e7382017-06-26 20:48:39 -0400994 self.save_new_key_set(new_key_set_name, new_keys)
terryjreedye5bb1122017-07-05 00:54:55 -0400995 # Change GUI over to the new key set.
terryjreedy938e7382017-06-26 20:48:39 -0400996 custom_key_list = idleConf.GetSectionList('user', 'keys')
997 custom_key_list.sort()
998 self.opt_menu_keys_custom.SetMenu(custom_key_list, new_key_set_name)
999 self.are_keys_builtin.set(0)
1000 self.set_keys_type()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001001
terryjreedy938e7382017-06-26 20:48:39 -04001002 def load_keys_list(self, keyset_name):
terryjreedye5bb1122017-07-05 00:54:55 -04001003 """Reload the list of action/key binding pairs for the active key set.
1004
1005 An action/key binding can be selected to change the key binding.
1006 """
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001007 reselect = 0
terryjreedy938e7382017-06-26 20:48:39 -04001008 if self.list_bindings.curselection():
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001009 reselect = 1
terryjreedy938e7382017-06-26 20:48:39 -04001010 list_index = self.list_bindings.index(ANCHOR)
1011 keyset = idleConf.GetKeySet(keyset_name)
1012 bind_names = list(keyset.keys())
1013 bind_names.sort()
1014 self.list_bindings.delete(0, END)
1015 for bind_name in bind_names:
terryjreedye5bb1122017-07-05 00:54:55 -04001016 key = ' '.join(keyset[bind_name])
1017 bind_name = bind_name[2:-2] # Trim off the angle brackets.
terryjreedyedc03422017-07-07 16:37:39 -04001018 if keyset_name in changes['keys']:
terryjreedye5bb1122017-07-05 00:54:55 -04001019 # Handle any unsaved changes to this key set.
terryjreedyedc03422017-07-07 16:37:39 -04001020 if bind_name in changes['keys'][keyset_name]:
1021 key = changes['keys'][keyset_name][bind_name]
terryjreedy938e7382017-06-26 20:48:39 -04001022 self.list_bindings.insert(END, bind_name+' - '+key)
Steven M. Gava052937f2002-02-11 02:20:53 +00001023 if reselect:
terryjreedy938e7382017-06-26 20:48:39 -04001024 self.list_bindings.see(list_index)
1025 self.list_bindings.select_set(list_index)
1026 self.list_bindings.select_anchor(list_index)
Steven M. Gava052937f2002-02-11 02:20:53 +00001027
terryjreedy938e7382017-06-26 20:48:39 -04001028 def delete_custom_keys(self):
terryjreedye5bb1122017-07-05 00:54:55 -04001029 """Handle event to delete a custom key set.
1030
1031 Applying the delete deactivates the current configuration and
1032 reverts to the default. The custom key set is permanently
1033 deleted from the config file.
1034 """
terryjreedy938e7382017-06-26 20:48:39 -04001035 keyset_name=self.custom_keys.get()
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001036 delmsg = 'Are you sure you wish to delete the key set %r ?'
1037 if not tkMessageBox.askyesno(
terryjreedy938e7382017-06-26 20:48:39 -04001038 'Delete Key Set', delmsg % keyset_name, parent=self):
Steven M. Gava49745752002-02-18 01:43:11 +00001039 return
terryjreedy938e7382017-06-26 20:48:39 -04001040 self.deactivate_current_config()
terryjreedyedc03422017-07-07 16:37:39 -04001041 # Remove key set from changes, config, and file.
terryjreedyc0179482017-07-11 19:50:10 -04001042 changes.delete_section('keys', keyset_name)
terryjreedye5bb1122017-07-05 00:54:55 -04001043 # Reload user key set list.
terryjreedy938e7382017-06-26 20:48:39 -04001044 item_list = idleConf.GetSectionList('user', 'keys')
1045 item_list.sort()
1046 if not item_list:
1047 self.radio_keys_custom.config(state=DISABLED)
1048 self.opt_menu_keys_custom.SetMenu(item_list, '- no custom keys -')
Steven M. Gava49745752002-02-18 01:43:11 +00001049 else:
terryjreedy938e7382017-06-26 20:48:39 -04001050 self.opt_menu_keys_custom.SetMenu(item_list, item_list[0])
terryjreedye5bb1122017-07-05 00:54:55 -04001051 # Revert to default key set.
terryjreedy938e7382017-06-26 20:48:39 -04001052 self.are_keys_builtin.set(idleConf.defaultCfg['main']
Terry Jan Reedy9bdb1ed2016-07-10 13:46:34 -04001053 .Get('Keys', 'default'))
terryjreedy938e7382017-06-26 20:48:39 -04001054 self.builtin_keys.set(idleConf.defaultCfg['main'].Get('Keys', 'name')
Terry Jan Reedy9bdb1ed2016-07-10 13:46:34 -04001055 or idleConf.default_keys())
terryjreedye5bb1122017-07-05 00:54:55 -04001056 # User can't back out of these changes, they must be applied now.
terryjreedyedc03422017-07-07 16:37:39 -04001057 changes.save_all()
1058 self.save_all_changed_extensions()
terryjreedy938e7382017-06-26 20:48:39 -04001059 self.activate_config_changes()
1060 self.set_keys_type()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001061
terryjreedy938e7382017-06-26 20:48:39 -04001062 def delete_custom_theme(self):
terryjreedye5bb1122017-07-05 00:54:55 -04001063 """Handle event to delete custom theme.
1064
1065 The current theme is deactivated and the default theme is
1066 activated. The custom theme is permanently removed from
1067 the config file.
terryjreedy9a09c662017-07-13 23:53:30 -04001068
1069 Attributes accessed:
1070 custom_theme
1071
1072 Attributes updated:
1073 radio_theme_custom
1074 opt_menu_theme_custom
1075 is_builtin_theme
1076 builtin_theme
1077
1078 Methods:
1079 deactivate_current_config
1080 save_all_changed_extensions
1081 activate_config_changes
1082 set_theme_type
terryjreedye5bb1122017-07-05 00:54:55 -04001083 """
terryjreedy938e7382017-06-26 20:48:39 -04001084 theme_name = self.custom_theme.get()
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001085 delmsg = 'Are you sure you wish to delete the theme %r ?'
1086 if not tkMessageBox.askyesno(
terryjreedy938e7382017-06-26 20:48:39 -04001087 'Delete Theme', delmsg % theme_name, parent=self):
Steven M. Gava49745752002-02-18 01:43:11 +00001088 return
terryjreedy938e7382017-06-26 20:48:39 -04001089 self.deactivate_current_config()
terryjreedyedc03422017-07-07 16:37:39 -04001090 # Remove theme from changes, config, and file.
terryjreedyc0179482017-07-11 19:50:10 -04001091 changes.delete_section('highlight', theme_name)
terryjreedye5bb1122017-07-05 00:54:55 -04001092 # Reload user theme list.
terryjreedy938e7382017-06-26 20:48:39 -04001093 item_list = idleConf.GetSectionList('user', 'highlight')
1094 item_list.sort()
1095 if not item_list:
1096 self.radio_theme_custom.config(state=DISABLED)
1097 self.opt_menu_theme_custom.SetMenu(item_list, '- no custom themes -')
Steven M. Gava49745752002-02-18 01:43:11 +00001098 else:
terryjreedy938e7382017-06-26 20:48:39 -04001099 self.opt_menu_theme_custom.SetMenu(item_list, item_list[0])
terryjreedye5bb1122017-07-05 00:54:55 -04001100 # Revert to default theme.
terryjreedy938e7382017-06-26 20:48:39 -04001101 self.is_builtin_theme.set(idleConf.defaultCfg['main'].Get('Theme', 'default'))
1102 self.builtin_theme.set(idleConf.defaultCfg['main'].Get('Theme', 'name'))
terryjreedye5bb1122017-07-05 00:54:55 -04001103 # User can't back out of these changes, they must be applied now.
terryjreedyedc03422017-07-07 16:37:39 -04001104 changes.save_all()
1105 self.save_all_changed_extensions()
terryjreedy938e7382017-06-26 20:48:39 -04001106 self.activate_config_changes()
1107 self.set_theme_type()
Steven M. Gava49745752002-02-18 01:43:11 +00001108
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001109 def get_color(self):
terryjreedye5bb1122017-07-05 00:54:55 -04001110 """Handle button to select a new color for the target tag.
1111
1112 If a new color is selected while using a builtin theme, a
1113 name must be supplied to create a custom theme.
terryjreedy9a09c662017-07-13 23:53:30 -04001114
1115 Attributes accessed:
1116 highlight_target
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001117 frame_color_set
terryjreedy9a09c662017-07-13 23:53:30 -04001118 is_builtin_theme
1119
1120 Attributes updated:
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001121 color
terryjreedy9a09c662017-07-13 23:53:30 -04001122
1123 Methods:
1124 get_new_theme_name
1125 create_new_theme
terryjreedye5bb1122017-07-05 00:54:55 -04001126 """
terryjreedy938e7382017-06-26 20:48:39 -04001127 target = self.highlight_target.get()
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001128 prev_color = self.frame_color_set.cget('bg')
1129 rgbTuplet, color_string = tkColorChooser.askcolor(
1130 parent=self, title='Pick new color for : '+target,
1131 initialcolor=prev_color)
1132 if color_string and (color_string != prev_color):
1133 # User didn't cancel and they chose a new color.
terryjreedye5bb1122017-07-05 00:54:55 -04001134 if self.is_builtin_theme.get(): # Current theme is a built-in.
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001135 message = ('Your changes will be saved as a new Custom Theme. '
1136 'Enter a name for your new Custom Theme below.')
terryjreedy938e7382017-06-26 20:48:39 -04001137 new_theme = self.get_new_theme_name(message)
terryjreedye5bb1122017-07-05 00:54:55 -04001138 if not new_theme: # User cancelled custom theme creation.
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +00001139 return
terryjreedye5bb1122017-07-05 00:54:55 -04001140 else: # Create new custom theme based on previously active theme.
terryjreedy938e7382017-06-26 20:48:39 -04001141 self.create_new_theme(new_theme)
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001142 self.color.set(color_string)
terryjreedye5bb1122017-07-05 00:54:55 -04001143 else: # Current theme is user defined.
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001144 self.color.set(color_string)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001145
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001146 def on_new_color_set(self):
terryjreedye5bb1122017-07-05 00:54:55 -04001147 "Display sample of new color selection on the dialog."
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001148 new_color=self.color.get()
1149 self.frame_color_set.config(bg=new_color) # Set sample.
terryjreedy938e7382017-06-26 20:48:39 -04001150 plane ='foreground' if self.fg_bg_toggle.get() else 'background'
1151 sample_element = self.theme_elements[self.highlight_target.get()][0]
Terry Jan Reedy04864b42017-07-22 00:56:18 -04001152 self.highlight_sample.tag_config(sample_element, **{plane:new_color})
terryjreedy938e7382017-06-26 20:48:39 -04001153 theme = self.custom_theme.get()
1154 theme_element = sample_element + '-' + plane
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001155 changes.add_option('highlight', theme, theme_element, new_color)
Steven M. Gava052937f2002-02-11 02:20:53 +00001156
terryjreedy938e7382017-06-26 20:48:39 -04001157 def get_new_theme_name(self, message):
terryjreedye5bb1122017-07-05 00:54:55 -04001158 "Return name of new theme from query popup."
terryjreedy938e7382017-06-26 20:48:39 -04001159 used_names = (idleConf.GetSectionList('user', 'highlight') +
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001160 idleConf.GetSectionList('default', 'highlight'))
terryjreedy938e7382017-06-26 20:48:39 -04001161 new_theme = SectionName(
1162 self, 'New Custom Theme', message, used_names).result
1163 return new_theme
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001164
terryjreedy938e7382017-06-26 20:48:39 -04001165 def save_as_new_theme(self):
terryjreedy9a09c662017-07-13 23:53:30 -04001166 """Prompt for new theme name and create the theme.
1167
1168 Methods:
1169 get_new_theme_name
1170 create_new_theme
1171 """
terryjreedy938e7382017-06-26 20:48:39 -04001172 new_theme_name = self.get_new_theme_name('New Theme Name:')
1173 if new_theme_name:
1174 self.create_new_theme(new_theme_name)
Steven M. Gava085eb1b2002-02-05 04:52:32 +00001175
terryjreedy938e7382017-06-26 20:48:39 -04001176 def create_new_theme(self, new_theme_name):
terryjreedye5bb1122017-07-05 00:54:55 -04001177 """Create a new custom theme with the given name.
1178
1179 Create the new theme based on the previously active theme
1180 with the current changes applied. Once it is saved, then
1181 activate the new theme.
terryjreedy9a09c662017-07-13 23:53:30 -04001182
1183 Attributes accessed:
1184 builtin_theme
1185 custom_theme
1186
1187 Attributes updated:
1188 opt_menu_theme_custom
1189 is_builtin_theme
1190
1191 Method:
1192 save_new_theme
1193 set_theme_type
terryjreedye5bb1122017-07-05 00:54:55 -04001194 """
terryjreedy938e7382017-06-26 20:48:39 -04001195 if self.is_builtin_theme.get():
1196 theme_type = 'default'
1197 theme_name = self.builtin_theme.get()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001198 else:
terryjreedy938e7382017-06-26 20:48:39 -04001199 theme_type = 'user'
1200 theme_name = self.custom_theme.get()
1201 new_theme = idleConf.GetThemeDict(theme_type, theme_name)
terryjreedye5bb1122017-07-05 00:54:55 -04001202 # Apply any of the old theme's unsaved changes to the new theme.
terryjreedyedc03422017-07-07 16:37:39 -04001203 if theme_name in changes['highlight']:
1204 theme_changes = changes['highlight'][theme_name]
terryjreedy938e7382017-06-26 20:48:39 -04001205 for element in theme_changes:
1206 new_theme[element] = theme_changes[element]
terryjreedye5bb1122017-07-05 00:54:55 -04001207 # Save the new theme.
terryjreedy938e7382017-06-26 20:48:39 -04001208 self.save_new_theme(new_theme_name, new_theme)
terryjreedye5bb1122017-07-05 00:54:55 -04001209 # Change GUI over to the new theme.
terryjreedy938e7382017-06-26 20:48:39 -04001210 custom_theme_list = idleConf.GetSectionList('user', 'highlight')
1211 custom_theme_list.sort()
1212 self.opt_menu_theme_custom.SetMenu(custom_theme_list, new_theme_name)
1213 self.is_builtin_theme.set(0)
1214 self.set_theme_type()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001215
terryjreedy7ab33422017-07-09 19:26:32 -04001216 def on_fontlist_select(self, event):
1217 """Handle selecting a font from the list.
terryjreedye5bb1122017-07-05 00:54:55 -04001218
terryjreedy7ab33422017-07-09 19:26:32 -04001219 Event can result from either mouse click or Up or Down key.
Terry Jan Reedy04864b42017-07-22 00:56:18 -04001220 Set font_name and example displays to selection.
terryjreedye5bb1122017-07-05 00:54:55 -04001221 """
terryjreedy953e5272017-07-11 02:16:41 -04001222 font = self.fontlist.get(
1223 ACTIVE if event.type.name == 'KeyRelease' else ANCHOR)
terryjreedy938e7382017-06-26 20:48:39 -04001224 self.font_name.set(font.lower())
Terry Jan Reedy04864b42017-07-22 00:56:18 -04001225 self.set_samples()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001226
Terry Jan Reedy04864b42017-07-22 00:56:18 -04001227 def set_samples(self, event=None):
1228 """Update update both screen samples with the font settings.
terryjreedy9a09c662017-07-13 23:53:30 -04001229
Terry Jan Reedy04864b42017-07-22 00:56:18 -04001230 Called on font initialization and change events.
1231 Accesses font_name, font_size, and font_bold Variables.
1232 Updates font_sample and hightlight page highlight_sample.
terryjreedy9a09c662017-07-13 23:53:30 -04001233 """
terryjreedy938e7382017-06-26 20:48:39 -04001234 font_name = self.font_name.get()
1235 font_weight = tkFont.BOLD if self.font_bold.get() else tkFont.NORMAL
1236 new_font = (font_name, self.font_size.get(), font_weight)
Terry Jan Reedy04864b42017-07-22 00:56:18 -04001237 self.font_sample['font'] = new_font
1238 self.highlight_sample['font'] = new_font
Steven M. Gava5f28e8f2002-01-21 06:38:21 +00001239
terryjreedy938e7382017-06-26 20:48:39 -04001240 def set_highlight_target(self):
terryjreedy9a09c662017-07-13 23:53:30 -04001241 """Set fg/bg toggle and color based on highlight tag target.
1242
1243 Instance variables accessed:
1244 highlight_target
1245
1246 Attributes updated:
1247 radio_fg
1248 radio_bg
1249 fg_bg_toggle
1250
1251 Methods:
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001252 set_color_sample
terryjreedy9a09c662017-07-13 23:53:30 -04001253
1254 Called from:
1255 var_changed_highlight_target
1256 load_theme_cfg
1257 """
terryjreedye5bb1122017-07-05 00:54:55 -04001258 if self.highlight_target.get() == 'Cursor': # bg not possible
terryjreedy938e7382017-06-26 20:48:39 -04001259 self.radio_fg.config(state=DISABLED)
1260 self.radio_bg.config(state=DISABLED)
1261 self.fg_bg_toggle.set(1)
terryjreedye5bb1122017-07-05 00:54:55 -04001262 else: # Both fg and bg can be set.
terryjreedy938e7382017-06-26 20:48:39 -04001263 self.radio_fg.config(state=NORMAL)
1264 self.radio_bg.config(state=NORMAL)
1265 self.fg_bg_toggle.set(1)
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001266 self.set_color_sample()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001267
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001268 def set_color_sample_binding(self, *args):
terryjreedy9a09c662017-07-13 23:53:30 -04001269 """Change color sample based on foreground/background toggle.
1270
1271 Methods:
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001272 set_color_sample
terryjreedy9a09c662017-07-13 23:53:30 -04001273 """
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001274 self.set_color_sample()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001275
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001276 def set_color_sample(self):
terryjreedy9a09c662017-07-13 23:53:30 -04001277 """Set the color of the frame background to reflect the selected target.
1278
1279 Instance variables accessed:
1280 theme_elements
1281 highlight_target
1282 fg_bg_toggle
Terry Jan Reedy04864b42017-07-22 00:56:18 -04001283 highlight_sample
terryjreedy9a09c662017-07-13 23:53:30 -04001284
1285 Attributes updated:
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001286 frame_color_set
terryjreedy9a09c662017-07-13 23:53:30 -04001287 """
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001288 # Set the color sample area.
terryjreedy938e7382017-06-26 20:48:39 -04001289 tag = self.theme_elements[self.highlight_target.get()][0]
1290 plane = 'foreground' if self.fg_bg_toggle.get() else 'background'
Terry Jan Reedy04864b42017-07-22 00:56:18 -04001291 color = self.highlight_sample.tag_cget(tag, plane)
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001292 self.frame_color_set.config(bg=color)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001293
terryjreedy938e7382017-06-26 20:48:39 -04001294 def paint_theme_sample(self):
terryjreedy9a09c662017-07-13 23:53:30 -04001295 """Apply the theme colors to each element tag in the sample text.
1296
1297 Instance attributes accessed:
1298 theme_elements
1299 is_builtin_theme
1300 builtin_theme
1301 custom_theme
1302
1303 Attributes updated:
Terry Jan Reedy04864b42017-07-22 00:56:18 -04001304 highlight_sample: Set the tag elements to the theme.
terryjreedy9a09c662017-07-13 23:53:30 -04001305
1306 Methods:
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001307 set_color_sample
terryjreedy9a09c662017-07-13 23:53:30 -04001308
1309 Called from:
1310 var_changed_builtin_theme
1311 var_changed_custom_theme
1312 load_theme_cfg
1313 """
terryjreedye5bb1122017-07-05 00:54:55 -04001314 if self.is_builtin_theme.get(): # Default theme
terryjreedy938e7382017-06-26 20:48:39 -04001315 theme = self.builtin_theme.get()
terryjreedye5bb1122017-07-05 00:54:55 -04001316 else: # User theme
terryjreedy938e7382017-06-26 20:48:39 -04001317 theme = self.custom_theme.get()
1318 for element_title in self.theme_elements:
1319 element = self.theme_elements[element_title][0]
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001320 colors = idleConf.GetHighlight(theme, element)
terryjreedye5bb1122017-07-05 00:54:55 -04001321 if element == 'cursor': # Cursor sample needs special painting.
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001322 colors['background'] = idleConf.GetHighlight(
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001323 theme, 'normal', fgBg='bg')
terryjreedye5bb1122017-07-05 00:54:55 -04001324 # Handle any unsaved changes to this theme.
terryjreedyedc03422017-07-07 16:37:39 -04001325 if theme in changes['highlight']:
1326 theme_dict = changes['highlight'][theme]
terryjreedy938e7382017-06-26 20:48:39 -04001327 if element + '-foreground' in theme_dict:
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001328 colors['foreground'] = theme_dict[element + '-foreground']
terryjreedy938e7382017-06-26 20:48:39 -04001329 if element + '-background' in theme_dict:
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001330 colors['background'] = theme_dict[element + '-background']
Terry Jan Reedy04864b42017-07-22 00:56:18 -04001331 self.highlight_sample.tag_config(element, **colors)
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001332 self.set_color_sample()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001333
terryjreedy938e7382017-06-26 20:48:39 -04001334 def help_source_selected(self, event):
terryjreedye5bb1122017-07-05 00:54:55 -04001335 "Handle event for selecting additional help."
terryjreedy938e7382017-06-26 20:48:39 -04001336 self.set_helplist_button_states()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001337
terryjreedy938e7382017-06-26 20:48:39 -04001338 def set_helplist_button_states(self):
terryjreedye5bb1122017-07-05 00:54:55 -04001339 "Toggle the state for the help list buttons based on list entries."
1340 if self.list_help.size() < 1: # No entries in list.
terryjreedy938e7382017-06-26 20:48:39 -04001341 self.button_helplist_edit.config(state=DISABLED)
1342 self.button_helplist_remove.config(state=DISABLED)
terryjreedye5bb1122017-07-05 00:54:55 -04001343 else: # Some entries.
1344 if self.list_help.curselection(): # There currently is a selection.
terryjreedy938e7382017-06-26 20:48:39 -04001345 self.button_helplist_edit.config(state=NORMAL)
1346 self.button_helplist_remove.config(state=NORMAL)
terryjreedye5bb1122017-07-05 00:54:55 -04001347 else: # There currently is not a selection.
terryjreedy938e7382017-06-26 20:48:39 -04001348 self.button_helplist_edit.config(state=DISABLED)
1349 self.button_helplist_remove.config(state=DISABLED)
Steven M. Gava085eb1b2002-02-05 04:52:32 +00001350
terryjreedy938e7382017-06-26 20:48:39 -04001351 def helplist_item_add(self):
terryjreedye5bb1122017-07-05 00:54:55 -04001352 """Handle add button for the help list.
1353
1354 Query for name and location of new help sources and add
1355 them to the list.
1356 """
terryjreedy938e7382017-06-26 20:48:39 -04001357 help_source = HelpSource(self, 'New Help Source',
Terry Jan Reedy8b22c0a2016-07-08 00:22:50 -04001358 ).result
terryjreedy938e7382017-06-26 20:48:39 -04001359 if help_source:
1360 self.user_helplist.append((help_source[0], help_source[1]))
1361 self.list_help.insert(END, help_source[0])
1362 self.update_user_help_changed_items()
1363 self.set_helplist_button_states()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001364
terryjreedy938e7382017-06-26 20:48:39 -04001365 def helplist_item_edit(self):
terryjreedye5bb1122017-07-05 00:54:55 -04001366 """Handle edit button for the help list.
1367
1368 Query with existing help source information and update
1369 config if the values are changed.
1370 """
terryjreedy938e7382017-06-26 20:48:39 -04001371 item_index = self.list_help.index(ANCHOR)
1372 help_source = self.user_helplist[item_index]
1373 new_help_source = HelpSource(
Terry Jan Reedy8b22c0a2016-07-08 00:22:50 -04001374 self, 'Edit Help Source',
terryjreedy938e7382017-06-26 20:48:39 -04001375 menuitem=help_source[0],
1376 filepath=help_source[1],
Terry Jan Reedy8b22c0a2016-07-08 00:22:50 -04001377 ).result
terryjreedy938e7382017-06-26 20:48:39 -04001378 if new_help_source and new_help_source != help_source:
1379 self.user_helplist[item_index] = new_help_source
1380 self.list_help.delete(item_index)
1381 self.list_help.insert(item_index, new_help_source[0])
1382 self.update_user_help_changed_items()
1383 self.set_helplist_button_states()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001384
terryjreedy938e7382017-06-26 20:48:39 -04001385 def helplist_item_remove(self):
terryjreedye5bb1122017-07-05 00:54:55 -04001386 """Handle remove button for the help list.
1387
1388 Delete the help list item from config.
1389 """
terryjreedy938e7382017-06-26 20:48:39 -04001390 item_index = self.list_help.index(ANCHOR)
1391 del(self.user_helplist[item_index])
1392 self.list_help.delete(item_index)
1393 self.update_user_help_changed_items()
1394 self.set_helplist_button_states()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001395
terryjreedy938e7382017-06-26 20:48:39 -04001396 def update_user_help_changed_items(self):
terryjreedyedc03422017-07-07 16:37:39 -04001397 "Clear and rebuild the HelpFiles section in changes"
1398 changes['main']['HelpFiles'] = {}
terryjreedy938e7382017-06-26 20:48:39 -04001399 for num in range(1, len(self.user_helplist) + 1):
terryjreedyedc03422017-07-07 16:37:39 -04001400 changes.add_option(
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001401 'main', 'HelpFiles', str(num),
terryjreedy938e7382017-06-26 20:48:39 -04001402 ';'.join(self.user_helplist[num-1][:2]))
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001403
terryjreedy938e7382017-06-26 20:48:39 -04001404 def load_font_cfg(self):
terryjreedy9a09c662017-07-13 23:53:30 -04001405 """Load current configuration settings for the font options.
1406
1407 Retrieve current font values from idleConf.GetFont to set
1408 as initial values for font widgets.
1409
1410 Attributes updated:
1411 fontlist: Populate with fonts from tkinter.font.
1412 font_name: Set to current font.
1413 opt_menu_font_size: Populate valid options tuple and set
1414 to current size.
1415 font_bold: Set to current font weight.
1416
1417 Methods:
Terry Jan Reedy04864b42017-07-22 00:56:18 -04001418 set_samples
terryjreedy9a09c662017-07-13 23:53:30 -04001419 """
terryjreedye5bb1122017-07-05 00:54:55 -04001420 # Set base editor font selection list.
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001421 fonts = list(tkFont.families(self))
Steven M. Gavac11ccf32001-09-24 09:43:17 +00001422 fonts.sort()
1423 for font in fonts:
terryjreedy7ab33422017-07-09 19:26:32 -04001424 self.fontlist.insert(END, font)
terryjreedy938e7382017-06-26 20:48:39 -04001425 configured_font = idleConf.GetFont(self, 'main', 'EditorWindow')
1426 font_name = configured_font[0].lower()
1427 font_size = configured_font[1]
1428 font_bold = configured_font[2]=='bold'
1429 self.font_name.set(font_name)
Kurt B. Kaiser05391692003-05-26 20:35:53 +00001430 lc_fonts = [s.lower() for s in fonts]
Terry Jan Reedyd87d1682015-08-01 18:57:33 -04001431 try:
terryjreedy938e7382017-06-26 20:48:39 -04001432 current_font_index = lc_fonts.index(font_name)
terryjreedy7ab33422017-07-09 19:26:32 -04001433 self.fontlist.see(current_font_index)
1434 self.fontlist.select_set(current_font_index)
1435 self.fontlist.select_anchor(current_font_index)
1436 self.fontlist.activate(current_font_index)
Terry Jan Reedyd87d1682015-08-01 18:57:33 -04001437 except ValueError:
1438 pass
terryjreedye5bb1122017-07-05 00:54:55 -04001439 # Set font size dropdown.
terryjreedy938e7382017-06-26 20:48:39 -04001440 self.opt_menu_font_size.SetMenu(('7', '8', '9', '10', '11', '12', '13',
Terry Jan Reedyda028872016-08-30 20:19:13 -04001441 '14', '16', '18', '20', '22',
terryjreedy938e7382017-06-26 20:48:39 -04001442 '25', '29', '34', '40'), font_size )
terryjreedye5bb1122017-07-05 00:54:55 -04001443 # Set font weight.
terryjreedy938e7382017-06-26 20:48:39 -04001444 self.font_bold.set(font_bold)
terryjreedye5bb1122017-07-05 00:54:55 -04001445 # Set font sample.
Terry Jan Reedy04864b42017-07-22 00:56:18 -04001446 self.set_samples()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001447
terryjreedy938e7382017-06-26 20:48:39 -04001448 def load_tab_cfg(self):
terryjreedy9a09c662017-07-13 23:53:30 -04001449 """Load current configuration settings for the tab options.
1450
1451 Attributes updated:
1452 space_num: Set to value from idleConf.
1453 """
terryjreedye5bb1122017-07-05 00:54:55 -04001454 # Set indent sizes.
terryjreedy938e7382017-06-26 20:48:39 -04001455 space_num = idleConf.GetOption(
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001456 'main', 'Indent', 'num-spaces', default=4, type='int')
terryjreedy938e7382017-06-26 20:48:39 -04001457 self.space_num.set(space_num)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001458
terryjreedy938e7382017-06-26 20:48:39 -04001459 def load_theme_cfg(self):
terryjreedy9a09c662017-07-13 23:53:30 -04001460 """Load current configuration settings for the theme options.
1461
1462 Based on the is_builtin_theme toggle, the theme is set as
1463 either builtin or custom and the initial widget values
1464 reflect the current settings from idleConf.
1465
1466 Attributes updated:
1467 is_builtin_theme: Set from idleConf.
1468 opt_menu_theme_builtin: List of default themes from idleConf.
1469 opt_menu_theme_custom: List of custom themes from idleConf.
1470 radio_theme_custom: Disabled if there are no custom themes.
1471 custom_theme: Message with additional information.
1472 opt_menu_highlight_target: Create menu from self.theme_elements.
1473
1474 Methods:
1475 set_theme_type
1476 paint_theme_sample
1477 set_highlight_target
1478 """
terryjreedye5bb1122017-07-05 00:54:55 -04001479 # Set current theme type radiobutton.
terryjreedy938e7382017-06-26 20:48:39 -04001480 self.is_builtin_theme.set(idleConf.GetOption(
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001481 'main', 'Theme', 'default', type='bool', default=1))
terryjreedye5bb1122017-07-05 00:54:55 -04001482 # Set current theme.
terryjreedy938e7382017-06-26 20:48:39 -04001483 current_option = idleConf.CurrentTheme()
terryjreedye5bb1122017-07-05 00:54:55 -04001484 # Load available theme option menus.
1485 if self.is_builtin_theme.get(): # Default theme selected.
terryjreedy938e7382017-06-26 20:48:39 -04001486 item_list = idleConf.GetSectionList('default', 'highlight')
1487 item_list.sort()
1488 self.opt_menu_theme_builtin.SetMenu(item_list, current_option)
1489 item_list = idleConf.GetSectionList('user', 'highlight')
1490 item_list.sort()
1491 if not item_list:
1492 self.radio_theme_custom.config(state=DISABLED)
1493 self.custom_theme.set('- no custom themes -')
Steven M. Gava41a85322001-10-29 08:05:34 +00001494 else:
terryjreedy938e7382017-06-26 20:48:39 -04001495 self.opt_menu_theme_custom.SetMenu(item_list, item_list[0])
terryjreedye5bb1122017-07-05 00:54:55 -04001496 else: # User theme selected.
terryjreedy938e7382017-06-26 20:48:39 -04001497 item_list = idleConf.GetSectionList('user', 'highlight')
1498 item_list.sort()
1499 self.opt_menu_theme_custom.SetMenu(item_list, current_option)
1500 item_list = idleConf.GetSectionList('default', 'highlight')
1501 item_list.sort()
1502 self.opt_menu_theme_builtin.SetMenu(item_list, item_list[0])
1503 self.set_theme_type()
terryjreedye5bb1122017-07-05 00:54:55 -04001504 # Load theme element option menu.
terryjreedy938e7382017-06-26 20:48:39 -04001505 theme_names = list(self.theme_elements.keys())
1506 theme_names.sort(key=lambda x: self.theme_elements[x][1])
1507 self.opt_menu_highlight_target.SetMenu(theme_names, theme_names[0])
1508 self.paint_theme_sample()
1509 self.set_highlight_target()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001510
terryjreedy938e7382017-06-26 20:48:39 -04001511 def load_key_cfg(self):
terryjreedye5bb1122017-07-05 00:54:55 -04001512 "Load current configuration settings for the keybinding options."
1513 # Set current keys type radiobutton.
terryjreedy938e7382017-06-26 20:48:39 -04001514 self.are_keys_builtin.set(idleConf.GetOption(
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001515 'main', 'Keys', 'default', type='bool', default=1))
terryjreedye5bb1122017-07-05 00:54:55 -04001516 # Set current keys.
terryjreedy938e7382017-06-26 20:48:39 -04001517 current_option = idleConf.CurrentKeys()
terryjreedye5bb1122017-07-05 00:54:55 -04001518 # Load available keyset option menus.
1519 if self.are_keys_builtin.get(): # Default theme selected.
terryjreedy938e7382017-06-26 20:48:39 -04001520 item_list = idleConf.GetSectionList('default', 'keys')
1521 item_list.sort()
1522 self.opt_menu_keys_builtin.SetMenu(item_list, current_option)
1523 item_list = idleConf.GetSectionList('user', 'keys')
1524 item_list.sort()
1525 if not item_list:
1526 self.radio_keys_custom.config(state=DISABLED)
1527 self.custom_keys.set('- no custom keys -')
Steven M. Gava41a85322001-10-29 08:05:34 +00001528 else:
terryjreedy938e7382017-06-26 20:48:39 -04001529 self.opt_menu_keys_custom.SetMenu(item_list, item_list[0])
terryjreedye5bb1122017-07-05 00:54:55 -04001530 else: # User key set selected.
terryjreedy938e7382017-06-26 20:48:39 -04001531 item_list = idleConf.GetSectionList('user', 'keys')
1532 item_list.sort()
1533 self.opt_menu_keys_custom.SetMenu(item_list, current_option)
1534 item_list = idleConf.GetSectionList('default', 'keys')
1535 item_list.sort()
1536 self.opt_menu_keys_builtin.SetMenu(item_list, idleConf.default_keys())
1537 self.set_keys_type()
terryjreedye5bb1122017-07-05 00:54:55 -04001538 # Load keyset element list.
terryjreedy938e7382017-06-26 20:48:39 -04001539 keyset_name = idleConf.CurrentKeys()
1540 self.load_keys_list(keyset_name)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001541
terryjreedy938e7382017-06-26 20:48:39 -04001542 def load_general_cfg(self):
terryjreedye5bb1122017-07-05 00:54:55 -04001543 "Load current configuration settings for the general options."
1544 # Set startup state.
terryjreedy938e7382017-06-26 20:48:39 -04001545 self.startup_edit.set(idleConf.GetOption(
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001546 'main', 'General', 'editor-on-startup', default=1, type='bool'))
terryjreedye5bb1122017-07-05 00:54:55 -04001547 # Set autosave state.
terryjreedy938e7382017-06-26 20:48:39 -04001548 self.autosave.set(idleConf.GetOption(
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001549 'main', 'General', 'autosave', default=0, type='bool'))
terryjreedye5bb1122017-07-05 00:54:55 -04001550 # Set initial window size.
terryjreedy938e7382017-06-26 20:48:39 -04001551 self.win_width.set(idleConf.GetOption(
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001552 'main', 'EditorWindow', 'width', type='int'))
terryjreedy938e7382017-06-26 20:48:39 -04001553 self.win_height.set(idleConf.GetOption(
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001554 'main', 'EditorWindow', 'height', type='int'))
terryjreedye5bb1122017-07-05 00:54:55 -04001555 # Set additional help sources.
terryjreedy938e7382017-06-26 20:48:39 -04001556 self.user_helplist = idleConf.GetAllExtraHelpSourcesList()
1557 for help_item in self.user_helplist:
1558 self.list_help.insert(END, help_item[0])
1559 self.set_helplist_button_states()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001560
terryjreedy938e7382017-06-26 20:48:39 -04001561 def load_configs(self):
terryjreedye5bb1122017-07-05 00:54:55 -04001562 """Load configuration for each page.
1563
1564 Load configuration from default and user config files and populate
Steven M. Gava429a86af2001-10-23 10:42:12 +00001565 the widgets on the config dialog pages.
terryjreedy9a09c662017-07-13 23:53:30 -04001566
1567 Methods:
1568 load_font_cfg
1569 load_tab_cfg
1570 load_theme_cfg
1571 load_key_cfg
1572 load_general_cfg
Steven M. Gava429a86af2001-10-23 10:42:12 +00001573 """
terryjreedy938e7382017-06-26 20:48:39 -04001574 self.load_font_cfg()
1575 self.load_tab_cfg()
terryjreedy938e7382017-06-26 20:48:39 -04001576 self.load_theme_cfg()
terryjreedy938e7382017-06-26 20:48:39 -04001577 self.load_key_cfg()
terryjreedy938e7382017-06-26 20:48:39 -04001578 self.load_general_cfg()
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001579 # note: extension page handled separately
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001580
terryjreedy938e7382017-06-26 20:48:39 -04001581 def save_new_key_set(self, keyset_name, keyset):
terryjreedye5bb1122017-07-05 00:54:55 -04001582 """Save a newly created core key set.
1583
terryjreedy938e7382017-06-26 20:48:39 -04001584 keyset_name - string, the name of the new key set
1585 keyset - dictionary containing the new key set
Steven M. Gava052937f2002-02-11 02:20:53 +00001586 """
terryjreedy938e7382017-06-26 20:48:39 -04001587 if not idleConf.userCfg['keys'].has_section(keyset_name):
1588 idleConf.userCfg['keys'].add_section(keyset_name)
1589 for event in keyset:
1590 value = keyset[event]
1591 idleConf.userCfg['keys'].SetOption(keyset_name, event, value)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001592
terryjreedy938e7382017-06-26 20:48:39 -04001593 def save_new_theme(self, theme_name, theme):
terryjreedy9a09c662017-07-13 23:53:30 -04001594 """Save a newly created theme to idleConf.
terryjreedye5bb1122017-07-05 00:54:55 -04001595
terryjreedy938e7382017-06-26 20:48:39 -04001596 theme_name - string, the name of the new theme
Steven M. Gava052937f2002-02-11 02:20:53 +00001597 theme - dictionary containing the new theme
1598 """
terryjreedy938e7382017-06-26 20:48:39 -04001599 if not idleConf.userCfg['highlight'].has_section(theme_name):
1600 idleConf.userCfg['highlight'].add_section(theme_name)
Kurt B. Kaisere0712772007-08-23 05:25:55 +00001601 for element in theme:
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001602 value = theme[element]
terryjreedy938e7382017-06-26 20:48:39 -04001603 idleConf.userCfg['highlight'].SetOption(theme_name, element, value)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001604
terryjreedy938e7382017-06-26 20:48:39 -04001605 def deactivate_current_config(self):
terryjreedy9a09c662017-07-13 23:53:30 -04001606 """Remove current key bindings.
1607
1608 Iterate over window instances defined in parent and remove
1609 the keybindings.
1610 """
terryjreedye5bb1122017-07-05 00:54:55 -04001611 # Before a config is saved, some cleanup of current
1612 # config must be done - remove the previous keybindings.
terryjreedy938e7382017-06-26 20:48:39 -04001613 win_instances = self.parent.instance_dict.keys()
1614 for instance in win_instances:
Kurt B. Kaiserb1754452005-11-18 22:05:48 +00001615 instance.RemoveKeybindings()
1616
terryjreedy938e7382017-06-26 20:48:39 -04001617 def activate_config_changes(self):
terryjreedy9a09c662017-07-13 23:53:30 -04001618 """Apply configuration changes to current windows.
1619
1620 Dynamically update the current parent window instances
1621 with some of the configuration changes.
1622 """
terryjreedy938e7382017-06-26 20:48:39 -04001623 win_instances = self.parent.instance_dict.keys()
1624 for instance in win_instances:
Steven M. Gavab77d3432002-03-02 07:16:21 +00001625 instance.ResetColorizer()
Steven M. Gavab1585412002-03-12 00:21:56 +00001626 instance.ResetFont()
Kurt B. Kaiseracdef852005-01-31 03:34:26 +00001627 instance.set_notabs_indentwidth()
Kurt B. Kaiserb1754452005-11-18 22:05:48 +00001628 instance.ApplyKeybindings()
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +00001629 instance.reset_help_menu_entries()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001630
terryjreedy938e7382017-06-26 20:48:39 -04001631 def cancel(self):
terryjreedy9a09c662017-07-13 23:53:30 -04001632 """Dismiss config dialog.
1633
1634 Methods:
1635 destroy: inherited
1636 """
Steven M. Gava5f28e8f2002-01-21 06:38:21 +00001637 self.destroy()
1638
terryjreedy938e7382017-06-26 20:48:39 -04001639 def ok(self):
terryjreedy9a09c662017-07-13 23:53:30 -04001640 """Apply config changes, then dismiss dialog.
1641
1642 Methods:
1643 apply
1644 destroy: inherited
1645 """
terryjreedy938e7382017-06-26 20:48:39 -04001646 self.apply()
Steven M. Gava5f28e8f2002-01-21 06:38:21 +00001647 self.destroy()
1648
terryjreedy938e7382017-06-26 20:48:39 -04001649 def apply(self):
terryjreedy9a09c662017-07-13 23:53:30 -04001650 """Apply config changes and leave dialog open.
1651
1652 Methods:
1653 deactivate_current_config
1654 save_all_changed_extensions
1655 activate_config_changes
1656 """
terryjreedy938e7382017-06-26 20:48:39 -04001657 self.deactivate_current_config()
terryjreedyedc03422017-07-07 16:37:39 -04001658 changes.save_all()
1659 self.save_all_changed_extensions()
terryjreedy938e7382017-06-26 20:48:39 -04001660 self.activate_config_changes()
Steven M. Gava5f28e8f2002-01-21 06:38:21 +00001661
terryjreedy938e7382017-06-26 20:48:39 -04001662 def help(self):
terryjreedy9a09c662017-07-13 23:53:30 -04001663 """Create textview for config dialog help.
1664
1665 Attrbutes accessed:
1666 tab_pages
1667
1668 Methods:
1669 view_text: Method from textview module.
1670 """
terryjreedy938e7382017-06-26 20:48:39 -04001671 page = self.tab_pages._current_page
Terry Jan Reedyd0cadba2015-10-11 22:07:31 -04001672 view_text(self, title='Help for IDLE preferences',
1673 text=help_common+help_pages.get(page, ''))
1674
terryjreedy938e7382017-06-26 20:48:39 -04001675 def create_page_extensions(self):
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001676 """Part of the config dialog used for configuring IDLE extensions.
1677
1678 This code is generic - it works for any and all IDLE extensions.
1679
1680 IDLE extensions save their configuration options using idleConf.
Terry Jan Reedyb2f87602015-10-13 22:09:06 -04001681 This code reads the current configuration using idleConf, supplies a
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001682 GUI interface to change the configuration values, and saves the
1683 changes using idleConf.
1684
1685 Not all changes take effect immediately - some may require restarting IDLE.
1686 This depends on each extension's implementation.
1687
1688 All values are treated as text, and it is up to the user to supply
1689 reasonable values. The only exception to this are the 'enable*' options,
Serhiy Storchaka6a7b3a72016-04-17 08:32:47 +03001690 which are boolean, and can be toggled with a True/False button.
terryjreedy9a09c662017-07-13 23:53:30 -04001691
1692 Methods:
1693 load_extentions:
1694 extension_selected: Handle selection from list.
1695 create_extension_frame: Hold widgets for one extension.
1696 set_extension_value: Set in userCfg['extensions'].
1697 save_all_changed_extensions: Call extension page Save().
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001698 """
1699 parent = self.parent
terryjreedy938e7382017-06-26 20:48:39 -04001700 frame = self.tab_pages.pages['Extensions'].frame
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001701 self.ext_defaultCfg = idleConf.defaultCfg['extensions']
1702 self.ext_userCfg = idleConf.userCfg['extensions']
1703 self.is_int = self.register(is_int)
1704 self.load_extensions()
terryjreedye5bb1122017-07-05 00:54:55 -04001705 # Create widgets - a listbox shows all available extensions, with the
1706 # controls for the extension selected in the listbox to the right.
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001707 self.extension_names = StringVar(self)
1708 frame.rowconfigure(0, weight=1)
1709 frame.columnconfigure(2, weight=1)
1710 self.extension_list = Listbox(frame, listvariable=self.extension_names,
1711 selectmode='browse')
1712 self.extension_list.bind('<<ListboxSelect>>', self.extension_selected)
1713 scroll = Scrollbar(frame, command=self.extension_list.yview)
1714 self.extension_list.yscrollcommand=scroll.set
1715 self.details_frame = LabelFrame(frame, width=250, height=250)
1716 self.extension_list.grid(column=0, row=0, sticky='nws')
1717 scroll.grid(column=1, row=0, sticky='ns')
1718 self.details_frame.grid(column=2, row=0, sticky='nsew', padx=[10, 0])
1719 frame.configure(padx=10, pady=10)
1720 self.config_frame = {}
1721 self.current_extension = None
1722
1723 self.outerframe = self # TEMPORARY
1724 self.tabbed_page_set = self.extension_list # TEMPORARY
1725
terryjreedye5bb1122017-07-05 00:54:55 -04001726 # Create the frame holding controls for each extension.
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001727 ext_names = ''
1728 for ext_name in sorted(self.extensions):
1729 self.create_extension_frame(ext_name)
1730 ext_names = ext_names + '{' + ext_name + '} '
1731 self.extension_names.set(ext_names)
1732 self.extension_list.selection_set(0)
1733 self.extension_selected(None)
1734
1735 def load_extensions(self):
1736 "Fill self.extensions with data from the default and user configs."
1737 self.extensions = {}
1738 for ext_name in idleConf.GetExtensions(active_only=False):
1739 self.extensions[ext_name] = []
1740
1741 for ext_name in self.extensions:
1742 opt_list = sorted(self.ext_defaultCfg.GetOptionList(ext_name))
1743
terryjreedye5bb1122017-07-05 00:54:55 -04001744 # Bring 'enable' options to the beginning of the list.
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001745 enables = [opt_name for opt_name in opt_list
1746 if opt_name.startswith('enable')]
1747 for opt_name in enables:
1748 opt_list.remove(opt_name)
1749 opt_list = enables + opt_list
1750
1751 for opt_name in opt_list:
1752 def_str = self.ext_defaultCfg.Get(
1753 ext_name, opt_name, raw=True)
1754 try:
1755 def_obj = {'True':True, 'False':False}[def_str]
1756 opt_type = 'bool'
1757 except KeyError:
1758 try:
1759 def_obj = int(def_str)
1760 opt_type = 'int'
1761 except ValueError:
1762 def_obj = def_str
1763 opt_type = None
1764 try:
1765 value = self.ext_userCfg.Get(
1766 ext_name, opt_name, type=opt_type, raw=True,
1767 default=def_obj)
terryjreedye5bb1122017-07-05 00:54:55 -04001768 except ValueError: # Need this until .Get fixed.
1769 value = def_obj # Bad values overwritten by entry.
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001770 var = StringVar(self)
1771 var.set(str(value))
1772
1773 self.extensions[ext_name].append({'name': opt_name,
1774 'type': opt_type,
1775 'default': def_str,
1776 'value': value,
1777 'var': var,
1778 })
1779
1780 def extension_selected(self, event):
terryjreedye5bb1122017-07-05 00:54:55 -04001781 "Handle selection of an extension from the list."
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001782 newsel = self.extension_list.curselection()
1783 if newsel:
1784 newsel = self.extension_list.get(newsel)
1785 if newsel is None or newsel != self.current_extension:
1786 if self.current_extension:
1787 self.details_frame.config(text='')
1788 self.config_frame[self.current_extension].grid_forget()
1789 self.current_extension = None
1790 if newsel:
1791 self.details_frame.config(text=newsel)
1792 self.config_frame[newsel].grid(column=0, row=0, sticky='nsew')
1793 self.current_extension = newsel
1794
1795 def create_extension_frame(self, ext_name):
1796 """Create a frame holding the widgets to configure one extension"""
1797 f = VerticalScrolledFrame(self.details_frame, height=250, width=250)
1798 self.config_frame[ext_name] = f
1799 entry_area = f.interior
terryjreedye5bb1122017-07-05 00:54:55 -04001800 # Create an entry for each configuration option.
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001801 for row, opt in enumerate(self.extensions[ext_name]):
terryjreedye5bb1122017-07-05 00:54:55 -04001802 # Create a row with a label and entry/checkbutton.
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001803 label = Label(entry_area, text=opt['name'])
1804 label.grid(row=row, column=0, sticky=NW)
1805 var = opt['var']
1806 if opt['type'] == 'bool':
1807 Checkbutton(entry_area, textvariable=var, variable=var,
1808 onvalue='True', offvalue='False',
1809 indicatoron=FALSE, selectcolor='', width=8
1810 ).grid(row=row, column=1, sticky=W, padx=7)
1811 elif opt['type'] == 'int':
1812 Entry(entry_area, textvariable=var, validate='key',
1813 validatecommand=(self.is_int, '%P')
1814 ).grid(row=row, column=1, sticky=NSEW, padx=7)
1815
1816 else:
1817 Entry(entry_area, textvariable=var
1818 ).grid(row=row, column=1, sticky=NSEW, padx=7)
1819 return
1820
1821 def set_extension_value(self, section, opt):
terryjreedye5bb1122017-07-05 00:54:55 -04001822 """Return True if the configuration was added or changed.
1823
1824 If the value is the same as the default, then remove it
1825 from user config file.
1826 """
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001827 name = opt['name']
1828 default = opt['default']
1829 value = opt['var'].get().strip() or default
1830 opt['var'].set(value)
1831 # if self.defaultCfg.has_section(section):
terryjreedye5bb1122017-07-05 00:54:55 -04001832 # Currently, always true; if not, indent to return.
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001833 if (value == default):
1834 return self.ext_userCfg.RemoveOption(section, name)
terryjreedye5bb1122017-07-05 00:54:55 -04001835 # Set the option.
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001836 return self.ext_userCfg.SetOption(section, name, value)
1837
1838 def save_all_changed_extensions(self):
terryjreedy9a09c662017-07-13 23:53:30 -04001839 """Save configuration changes to the user config file.
1840
1841 Attributes accessed:
1842 extensions
1843
1844 Methods:
1845 set_extension_value
1846 """
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001847 has_changes = False
1848 for ext_name in self.extensions:
1849 options = self.extensions[ext_name]
1850 for opt in options:
1851 if self.set_extension_value(ext_name, opt):
1852 has_changes = True
1853 if has_changes:
1854 self.ext_userCfg.Save()
1855
1856
Terry Jan Reedyd0cadba2015-10-11 22:07:31 -04001857help_common = '''\
1858When you click either the Apply or Ok buttons, settings in this
1859dialog that are different from IDLE's default are saved in
1860a .idlerc directory in your home directory. Except as noted,
Terry Jan Reedyd0c0f002015-11-12 15:02:57 -05001861these changes apply to all versions of IDLE installed on this
Terry Jan Reedyd0cadba2015-10-11 22:07:31 -04001862machine. Some do not take affect until IDLE is restarted.
1863[Cancel] only cancels changes made since the last save.
1864'''
1865help_pages = {
Terry Jan Reedy9bdb1ed2016-07-10 13:46:34 -04001866 'Highlighting': '''
Terry Jan Reedyd0cadba2015-10-11 22:07:31 -04001867Highlighting:
Terry Jan Reedyd0c0f002015-11-12 15:02:57 -05001868The IDLE Dark color theme is new in October 2015. It can only
Terry Jan Reedyd0cadba2015-10-11 22:07:31 -04001869be used with older IDLE releases if it is saved as a custom
1870theme, with a different name.
Terry Jan Reedy9bdb1ed2016-07-10 13:46:34 -04001871''',
1872 'Keys': '''
1873Keys:
1874The IDLE Modern Unix key set is new in June 2016. It can only
1875be used with older IDLE releases if it is saved as a custom
1876key set, with a different name.
1877''',
terryjreedyaf683822017-06-27 23:02:19 -04001878 'Extensions': '''
1879Extensions:
1880
1881Autocomplete: Popupwait is milleseconds to wait after key char, without
1882cursor movement, before popping up completion box. Key char is '.' after
1883identifier or a '/' (or '\\' on Windows) within a string.
1884
1885FormatParagraph: Max-width is max chars in lines after re-formatting.
1886Use with paragraphs in both strings and comment blocks.
1887
1888ParenMatch: Style indicates what is highlighted when closer is entered:
1889'opener' - opener '({[' corresponding to closer; 'parens' - both chars;
1890'expression' (default) - also everything in between. Flash-delay is how
1891long to highlight if cursor is not moved (0 means forever).
1892'''
Terry Jan Reedyd0cadba2015-10-11 22:07:31 -04001893}
1894
Steven M. Gavac11ccf32001-09-24 09:43:17 +00001895
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001896def is_int(s):
1897 "Return 's is blank or represents an int'"
1898 if not s:
1899 return True
1900 try:
1901 int(s)
1902 return True
1903 except ValueError:
1904 return False
1905
1906
Terry Jan Reedya9421fb2014-10-22 20:15:18 -04001907class VerticalScrolledFrame(Frame):
1908 """A pure Tkinter vertically scrollable frame.
1909
1910 * Use the 'interior' attribute to place widgets inside the scrollable frame
1911 * Construct and pack/place/grid normally
1912 * This frame only allows vertical scrolling
1913 """
1914 def __init__(self, parent, *args, **kw):
1915 Frame.__init__(self, parent, *args, **kw)
1916
terryjreedye5bb1122017-07-05 00:54:55 -04001917 # Create a canvas object and a vertical scrollbar for scrolling it.
Terry Jan Reedya9421fb2014-10-22 20:15:18 -04001918 vscrollbar = Scrollbar(self, orient=VERTICAL)
1919 vscrollbar.pack(fill=Y, side=RIGHT, expand=FALSE)
1920 canvas = Canvas(self, bd=0, highlightthickness=0,
Terry Jan Reedyd0812292015-10-22 03:27:31 -04001921 yscrollcommand=vscrollbar.set, width=240)
Terry Jan Reedya9421fb2014-10-22 20:15:18 -04001922 canvas.pack(side=LEFT, fill=BOTH, expand=TRUE)
1923 vscrollbar.config(command=canvas.yview)
1924
terryjreedye5bb1122017-07-05 00:54:55 -04001925 # Reset the view.
Terry Jan Reedya9421fb2014-10-22 20:15:18 -04001926 canvas.xview_moveto(0)
1927 canvas.yview_moveto(0)
1928
terryjreedye5bb1122017-07-05 00:54:55 -04001929 # Create a frame inside the canvas which will be scrolled with it.
Terry Jan Reedya9421fb2014-10-22 20:15:18 -04001930 self.interior = interior = Frame(canvas)
1931 interior_id = canvas.create_window(0, 0, window=interior, anchor=NW)
1932
terryjreedye5bb1122017-07-05 00:54:55 -04001933 # Track changes to the canvas and frame width and sync them,
1934 # also updating the scrollbar.
Terry Jan Reedya9421fb2014-10-22 20:15:18 -04001935 def _configure_interior(event):
terryjreedye5bb1122017-07-05 00:54:55 -04001936 # Update the scrollbars to match the size of the inner frame.
Terry Jan Reedya9421fb2014-10-22 20:15:18 -04001937 size = (interior.winfo_reqwidth(), interior.winfo_reqheight())
1938 canvas.config(scrollregion="0 0 %s %s" % size)
Terry Jan Reedya9421fb2014-10-22 20:15:18 -04001939 interior.bind('<Configure>', _configure_interior)
1940
1941 def _configure_canvas(event):
1942 if interior.winfo_reqwidth() != canvas.winfo_width():
terryjreedye5bb1122017-07-05 00:54:55 -04001943 # Update the inner frame's width to fill the canvas.
Terry Jan Reedya9421fb2014-10-22 20:15:18 -04001944 canvas.itemconfigure(interior_id, width=canvas.winfo_width())
1945 canvas.bind('<Configure>', _configure_canvas)
1946
1947 return
1948
Terry Jan Reedya9421fb2014-10-22 20:15:18 -04001949
Steven M. Gava44d3d1a2001-07-31 06:59:02 +00001950if __name__ == '__main__':
Terry Jan Reedycfa89502014-07-14 23:07:32 -04001951 import unittest
1952 unittest.main('idlelib.idle_test.test_configdialog',
1953 verbosity=2, exit=False)
Terry Jan Reedy2e8234a2014-05-29 01:46:26 -04001954 from idlelib.idle_test.htest import run
Terry Jan Reedy47304c02015-10-20 02:15:28 -04001955 run(ConfigDialog)