blob: d51eca6f5332760608f004acca25b20de5c36b0d [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 Reedy5aa3bf02017-07-23 14:18:27 -0400156 Fonts: Enable users to provisionally change font face, size, or
Terry Jan Reedy04864b42017-07-22 00:56:18 -0400157 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
Terry Jan Reedy5aa3bf02017-07-23 14:18:27 -0400162 Fontlist: mouse button 1 click or up or down key invoke
163 on_fontlist_select(), which sets Var font_name and calls
164 set_samples.
Terry Jan Reedy04864b42017-07-22 00:56:18 -0400165
Terry Jan Reedy5aa3bf02017-07-23 14:18:27 -0400166 Sizelist: clicking the menubutton opens the dropdown menu. A
167 mouse button 1 click or return key invokes an internal command
168 which sets Var font_size and calls set_samples.
terryjreedy9a09c662017-07-13 23:53:30 -0400169
Terry Jan Reedy5aa3bf02017-07-23 14:18:27 -0400170 Bold_toggle, clicking the box toggles font_bold and calls
171 set_samples.
terryjreedy9a09c662017-07-13 23:53:30 -0400172
Terry Jan Reedy5aa3bf02017-07-23 14:18:27 -0400173 Setting any of the font vars invokes var_changed_font, which
174 adds all 3 font options to changes. Set_samples applies a new
175 font constructed from the font vars to font_sample and
176 highlight_sample on the hightlight page.
177
178 Tabs: Enable users to change spaces entered for indent tabs.
179 Changing indent_scale value with the mouse sets Var space_num,
180 which invokes var_changed_space_num, which adds an entry to
181 changes.
182
183 Load_font_cfg and load_tab_cfg initialize vars and widgets from
184 idleConf entries.
terryjreedy9a09c662017-07-13 23:53:30 -0400185
186 Widget Structure: (*) widgets bound to self
Terry Jan Reedy5aa3bf02017-07-23 14:18:27 -0400187 frame (of tab_pages)
terryjreedy9a09c662017-07-13 23:53:30 -0400188 frame_font: LabelFrame
189 frame_font_name: Frame
190 font_name_title: Label
Terry Jan Reedy5aa3bf02017-07-23 14:18:27 -0400191 (*)fontlist: ListBox - font_name
terryjreedy9a09c662017-07-13 23:53:30 -0400192 scroll_font: Scrollbar
193 frame_font_param: Frame
194 font_size_title: Label
Terry Jan Reedy5aa3bf02017-07-23 14:18:27 -0400195 (*)sizelist: DynOptionMenu - font_size
Terry Jan Reedy04864b42017-07-22 00:56:18 -0400196 (*)bold_toggle: Checkbutton - font_bold
terryjreedy9a09c662017-07-13 23:53:30 -0400197 frame_font_sample: Frame
198 (*)font_sample: Label
199 frame_indent: LabelFrame
Terry Jan Reedy5aa3bf02017-07-23 14:18:27 -0400200 indent_title: Label
201 (*)indent_scale: Scale - space_num
terryjreedye5bb1122017-07-05 00:54:55 -0400202 """
Terry Jan Reedy22405332014-07-30 19:24:32 -0400203 parent = self.parent
Terry Jan Reedy04864b42017-07-22 00:56:18 -0400204 self.font_name = StringVar(parent)
terryjreedy938e7382017-06-26 20:48:39 -0400205 self.font_size = StringVar(parent)
206 self.font_bold = BooleanVar(parent)
terryjreedy938e7382017-06-26 20:48:39 -0400207 self.space_num = IntVar(parent)
Terry Jan Reedy22405332014-07-30 19:24:32 -0400208
Terry Jan Reedy5aa3bf02017-07-23 14:18:27 -0400209 # Create widgets:
terryjreedy7ab33422017-07-09 19:26:32 -0400210 # body and body section frames.
terryjreedy938e7382017-06-26 20:48:39 -0400211 frame = self.tab_pages.pages['Fonts/Tabs'].frame
terryjreedy938e7382017-06-26 20:48:39 -0400212 frame_font = LabelFrame(
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400213 frame, borderwidth=2, relief=GROOVE, text=' Base Editor Font ')
terryjreedy938e7382017-06-26 20:48:39 -0400214 frame_indent = LabelFrame(
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400215 frame, borderwidth=2, relief=GROOVE, text=' Indentation Width ')
Terry Jan Reedy5aa3bf02017-07-23 14:18:27 -0400216 # frame_font.
terryjreedy938e7382017-06-26 20:48:39 -0400217 frame_font_name = Frame(frame_font)
218 frame_font_param = Frame(frame_font)
219 font_name_title = Label(
220 frame_font_name, justify=LEFT, text='Font Face :')
Terry Jan Reedy5aa3bf02017-07-23 14:18:27 -0400221 self.fontlist = Listbox(frame_font_name, height=5,
222 takefocus=FALSE, exportselection=FALSE)
terryjreedy953e5272017-07-11 02:16:41 -0400223 self.fontlist.bind('<ButtonRelease-1>', self.on_fontlist_select)
224 self.fontlist.bind('<KeyRelease-Up>', self.on_fontlist_select)
225 self.fontlist.bind('<KeyRelease-Down>', self.on_fontlist_select)
terryjreedy938e7382017-06-26 20:48:39 -0400226 scroll_font = Scrollbar(frame_font_name)
terryjreedy7ab33422017-07-09 19:26:32 -0400227 scroll_font.config(command=self.fontlist.yview)
228 self.fontlist.config(yscrollcommand=scroll_font.set)
terryjreedy938e7382017-06-26 20:48:39 -0400229 font_size_title = Label(frame_font_param, text='Size :')
Terry Jan Reedy5aa3bf02017-07-23 14:18:27 -0400230 self.sizelist = DynOptionMenu(frame_font_param, self.font_size,
231 None, command=self.set_samples)
Terry Jan Reedy04864b42017-07-22 00:56:18 -0400232 self.bold_toggle = Checkbutton(
terryjreedy938e7382017-06-26 20:48:39 -0400233 frame_font_param, variable=self.font_bold, onvalue=1,
Terry Jan Reedy04864b42017-07-22 00:56:18 -0400234 offvalue=0, text='Bold', command=self.set_samples)
terryjreedy938e7382017-06-26 20:48:39 -0400235 frame_font_sample = Frame(frame_font, relief=SOLID, borderwidth=1)
Terry Jan Reedy5aa3bf02017-07-23 14:18:27 -0400236 temp_font = tkFont.Font(parent, ('courier', 10, 'normal'))
terryjreedy938e7382017-06-26 20:48:39 -0400237 self.font_sample = Label(
Terry Jan Reedy5aa3bf02017-07-23 14:18:27 -0400238 frame_font_sample, justify=LEFT, font=temp_font,
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400239 text='AaBbCcDdEe\nFfGgHhIiJjK\n1234567890\n#:+=(){}[]')
Terry Jan Reedy5aa3bf02017-07-23 14:18:27 -0400240 # frame_indent.
241 indent_title = Label(
242 frame_indent, justify=LEFT,
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400243 text='Python Standard: 4 Spaces!')
Terry Jan Reedy5aa3bf02017-07-23 14:18:27 -0400244 self.indent_scale = Scale(
245 frame_indent, variable=self.space_num,
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400246 orient='horizontal', tickinterval=2, from_=2, to=16)
Terry Jan Reedy22405332014-07-30 19:24:32 -0400247
Terry Jan Reedy5aa3bf02017-07-23 14:18:27 -0400248 # Pack widgets:
249 # body.
terryjreedy938e7382017-06-26 20:48:39 -0400250 frame_font.pack(side=LEFT, padx=5, pady=5, expand=TRUE, fill=BOTH)
251 frame_indent.pack(side=LEFT, padx=5, pady=5, fill=Y)
Terry Jan Reedy5aa3bf02017-07-23 14:18:27 -0400252 # frame_font.
terryjreedy938e7382017-06-26 20:48:39 -0400253 frame_font_name.pack(side=TOP, padx=5, pady=5, fill=X)
254 frame_font_param.pack(side=TOP, padx=5, pady=5, fill=X)
255 font_name_title.pack(side=TOP, anchor=W)
terryjreedy7ab33422017-07-09 19:26:32 -0400256 self.fontlist.pack(side=LEFT, expand=TRUE, fill=X)
terryjreedy938e7382017-06-26 20:48:39 -0400257 scroll_font.pack(side=LEFT, fill=Y)
258 font_size_title.pack(side=LEFT, anchor=W)
Terry Jan Reedy5aa3bf02017-07-23 14:18:27 -0400259 self.sizelist.pack(side=LEFT, anchor=W)
Terry Jan Reedy04864b42017-07-22 00:56:18 -0400260 self.bold_toggle.pack(side=LEFT, anchor=W, padx=20)
terryjreedy938e7382017-06-26 20:48:39 -0400261 frame_font_sample.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH)
262 self.font_sample.pack(expand=TRUE, fill=BOTH)
Terry Jan Reedy5aa3bf02017-07-23 14:18:27 -0400263 # frame_indent.
264 frame_indent.pack(side=TOP, fill=X)
265 indent_title.pack(side=TOP, anchor=W, padx=5)
266 self.indent_scale.pack(side=TOP, padx=5, fill=X)
terryjreedy7ab33422017-07-09 19:26:32 -0400267
Steven M. Gava952d0a52001-08-03 04:43:44 +0000268 return frame
269
terryjreedy938e7382017-06-26 20:48:39 -0400270 def create_page_highlight(self):
terryjreedye5bb1122017-07-05 00:54:55 -0400271 """Return frame of widgets for Highlighting tab.
272
terryjreedy9a09c662017-07-13 23:53:30 -0400273 Tk Variables:
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -0400274 color: Color of selected target.
terryjreedye5bb1122017-07-05 00:54:55 -0400275 builtin_theme: Menu variable for built-in theme.
276 custom_theme: Menu variable for custom theme.
277 fg_bg_toggle: Toggle for foreground/background color.
terryjreedy9a09c662017-07-13 23:53:30 -0400278 Note: this has no callback.
terryjreedye5bb1122017-07-05 00:54:55 -0400279 is_builtin_theme: Selector for built-in or custom theme.
280 highlight_target: Menu variable for the highlight tag target.
terryjreedy9a09c662017-07-13 23:53:30 -0400281
282 Instance Data Attributes:
283 theme_elements: Dictionary of tags for text highlighting.
284 The key is the display name and the value is a tuple of
285 (tag name, display sort order).
286
287 Methods [attachment]:
288 load_theme_cfg: Load current highlight colors.
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -0400289 get_color: Invoke colorchooser [button_set_color].
290 set_color_sample_binding: Call set_color_sample [fg_bg_toggle].
terryjreedy9a09c662017-07-13 23:53:30 -0400291 set_highlight_target: set fg_bg_toggle, set_color_sample().
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -0400292 set_color_sample: Set frame background to target.
293 on_new_color_set: Set new color and add option.
terryjreedy9a09c662017-07-13 23:53:30 -0400294 paint_theme_sample: Recolor sample.
295 get_new_theme_name: Get from popup.
296 create_new_theme: Combine theme with changes and save.
297 save_as_new_theme: Save [button_save_custom_theme].
298 set_theme_type: Command for [is_builtin_theme].
299 delete_custom_theme: Ativate default [button_delete_custom_theme].
300 save_new_theme: Save to userCfg['theme'] (is function).
301
302 Widget Structure: (*) widgets bound to self
303 frame
304 frame_custom: LabelFrame
Terry Jan Reedy04864b42017-07-22 00:56:18 -0400305 (*)highlight_sample: Text
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -0400306 (*)frame_color_set: Frame
307 button_set_color: Button
terryjreedy9a09c662017-07-13 23:53:30 -0400308 (*)opt_menu_highlight_target: DynOptionMenu - highlight_target
309 frame_fg_bg_toggle: Frame
310 (*)radio_fg: Radiobutton - fg_bg_toggle
311 (*)radio_bg: Radiobutton - fg_bg_toggle
312 button_save_custom_theme: Button
313 frame_theme: LabelFrame
314 theme_type_title: Label
315 (*)radio_theme_builtin: Radiobutton - is_builtin_theme
316 (*)radio_theme_custom: Radiobutton - is_builtin_theme
317 (*)opt_menu_theme_builtin: DynOptionMenu - builtin_theme
318 (*)opt_menu_theme_custom: DynOptionMenu - custom_theme
319 (*)button_delete_custom_theme: Button
320 (*)new_custom_theme: Label
terryjreedye5bb1122017-07-05 00:54:55 -0400321 """
terryjreedy9a09c662017-07-13 23:53:30 -0400322 self.theme_elements={
323 'Normal Text': ('normal', '00'),
324 'Python Keywords': ('keyword', '01'),
325 'Python Definitions': ('definition', '02'),
326 'Python Builtins': ('builtin', '03'),
327 'Python Comments': ('comment', '04'),
328 'Python Strings': ('string', '05'),
329 'Selected Text': ('hilite', '06'),
330 'Found Text': ('hit', '07'),
331 'Cursor': ('cursor', '08'),
332 'Editor Breakpoint': ('break', '09'),
333 'Shell Normal Text': ('console', '10'),
334 'Shell Error Text': ('error', '11'),
335 'Shell Stdout Text': ('stdout', '12'),
336 'Shell Stderr Text': ('stderr', '13'),
337 }
Terry Jan Reedy22405332014-07-30 19:24:32 -0400338 parent = self.parent
terryjreedy938e7382017-06-26 20:48:39 -0400339 self.builtin_theme = StringVar(parent)
340 self.custom_theme = StringVar(parent)
341 self.fg_bg_toggle = BooleanVar(parent)
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -0400342 self.color = StringVar(parent)
terryjreedy938e7382017-06-26 20:48:39 -0400343 self.is_builtin_theme = BooleanVar(parent)
344 self.highlight_target = StringVar(parent)
Terry Jan Reedy22405332014-07-30 19:24:32 -0400345
Steven M. Gava952d0a52001-08-03 04:43:44 +0000346 ##widget creation
347 #body frame
terryjreedy938e7382017-06-26 20:48:39 -0400348 frame = self.tab_pages.pages['Highlighting'].frame
Steven M. Gava952d0a52001-08-03 04:43:44 +0000349 #body section frames
terryjreedy938e7382017-06-26 20:48:39 -0400350 frame_custom = LabelFrame(frame, borderwidth=2, relief=GROOVE,
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400351 text=' Custom Highlighting ')
terryjreedy938e7382017-06-26 20:48:39 -0400352 frame_theme = LabelFrame(frame, borderwidth=2, relief=GROOVE,
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400353 text=' Highlighting Theme ')
terryjreedy938e7382017-06-26 20:48:39 -0400354 #frame_custom
Terry Jan Reedy04864b42017-07-22 00:56:18 -0400355 self.highlight_sample=Text(
terryjreedy938e7382017-06-26 20:48:39 -0400356 frame_custom, relief=SOLID, borderwidth=1,
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400357 font=('courier', 12, ''), cursor='hand2', width=21, height=11,
358 takefocus=FALSE, highlightthickness=0, wrap=NONE)
Terry Jan Reedy04864b42017-07-22 00:56:18 -0400359 text=self.highlight_sample
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400360 text.bind('<Double-Button-1>', lambda e: 'break')
361 text.bind('<B1-Motion>', lambda e: 'break')
terryjreedy938e7382017-06-26 20:48:39 -0400362 text_and_tags=(
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400363 ('#you can click here', 'comment'), ('\n', 'normal'),
364 ('#to choose items', 'comment'), ('\n', 'normal'),
365 ('def', 'keyword'), (' ', 'normal'),
366 ('func', 'definition'), ('(param):\n ', 'normal'),
367 ('"""string"""', 'string'), ('\n var0 = ', 'normal'),
368 ("'string'", 'string'), ('\n var1 = ', 'normal'),
369 ("'selected'", 'hilite'), ('\n var2 = ', 'normal'),
370 ("'found'", 'hit'), ('\n var3 = ', 'normal'),
371 ('list', 'builtin'), ('(', 'normal'),
Terry Jan Reedya8aa4d52015-10-02 22:12:17 -0400372 ('None', 'keyword'), (')\n', 'normal'),
373 (' breakpoint("line")', 'break'), ('\n\n', 'normal'),
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400374 (' error ', 'error'), (' ', 'normal'),
375 ('cursor |', 'cursor'), ('\n ', 'normal'),
376 ('shell', 'console'), (' ', 'normal'),
377 ('stdout', 'stdout'), (' ', 'normal'),
378 ('stderr', 'stderr'), ('\n', 'normal'))
terryjreedy938e7382017-06-26 20:48:39 -0400379 for texttag in text_and_tags:
380 text.insert(END, texttag[0], texttag[1])
381 for element in self.theme_elements:
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400382 def tem(event, elem=element):
terryjreedy938e7382017-06-26 20:48:39 -0400383 event.widget.winfo_toplevel().highlight_target.set(elem)
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400384 text.tag_bind(
terryjreedy938e7382017-06-26 20:48:39 -0400385 self.theme_elements[element][0], '<ButtonPress-1>', tem)
Steven M. Gavae16d94b2001-11-03 05:07:28 +0000386 text.config(state=DISABLED)
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -0400387 self.frame_color_set = Frame(frame_custom, relief=SOLID, borderwidth=1)
terryjreedy938e7382017-06-26 20:48:39 -0400388 frame_fg_bg_toggle = Frame(frame_custom)
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -0400389 button_set_color = Button(
390 self.frame_color_set, text='Choose Color for :',
391 command=self.get_color, highlightthickness=0)
terryjreedy938e7382017-06-26 20:48:39 -0400392 self.opt_menu_highlight_target = DynOptionMenu(
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -0400393 self.frame_color_set, self.highlight_target, None,
terryjreedy938e7382017-06-26 20:48:39 -0400394 highlightthickness=0) #, command=self.set_highlight_targetBinding
395 self.radio_fg = Radiobutton(
396 frame_fg_bg_toggle, variable=self.fg_bg_toggle, value=1,
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -0400397 text='Foreground', command=self.set_color_sample_binding)
terryjreedy938e7382017-06-26 20:48:39 -0400398 self.radio_bg=Radiobutton(
399 frame_fg_bg_toggle, variable=self.fg_bg_toggle, value=0,
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -0400400 text='Background', command=self.set_color_sample_binding)
terryjreedy938e7382017-06-26 20:48:39 -0400401 self.fg_bg_toggle.set(1)
402 button_save_custom_theme = Button(
403 frame_custom, text='Save as New Custom Theme',
404 command=self.save_as_new_theme)
405 #frame_theme
406 theme_type_title = Label(frame_theme, text='Select : ')
407 self.radio_theme_builtin = Radiobutton(
408 frame_theme, variable=self.is_builtin_theme, value=1,
409 command=self.set_theme_type, text='a Built-in Theme')
410 self.radio_theme_custom = Radiobutton(
411 frame_theme, variable=self.is_builtin_theme, value=0,
412 command=self.set_theme_type, text='a Custom Theme')
413 self.opt_menu_theme_builtin = DynOptionMenu(
414 frame_theme, self.builtin_theme, None, command=None)
415 self.opt_menu_theme_custom=DynOptionMenu(
416 frame_theme, self.custom_theme, None, command=None)
417 self.button_delete_custom_theme=Button(
418 frame_theme, text='Delete Custom Theme',
419 command=self.delete_custom_theme)
420 self.new_custom_theme = Label(frame_theme, bd=2)
Terry Jan Reedy22405332014-07-30 19:24:32 -0400421
Steven M. Gava952d0a52001-08-03 04:43:44 +0000422 ##widget packing
423 #body
terryjreedy938e7382017-06-26 20:48:39 -0400424 frame_custom.pack(side=LEFT, padx=5, pady=5, expand=TRUE, fill=BOTH)
425 frame_theme.pack(side=LEFT, padx=5, pady=5, fill=Y)
426 #frame_custom
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -0400427 self.frame_color_set.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=X)
terryjreedy938e7382017-06-26 20:48:39 -0400428 frame_fg_bg_toggle.pack(side=TOP, padx=5, pady=0)
Terry Jan Reedy04864b42017-07-22 00:56:18 -0400429 self.highlight_sample.pack(
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400430 side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH)
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -0400431 button_set_color.pack(side=TOP, expand=TRUE, fill=X, padx=8, pady=4)
terryjreedy938e7382017-06-26 20:48:39 -0400432 self.opt_menu_highlight_target.pack(
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400433 side=TOP, expand=TRUE, fill=X, padx=8, pady=3)
terryjreedy938e7382017-06-26 20:48:39 -0400434 self.radio_fg.pack(side=LEFT, anchor=E)
435 self.radio_bg.pack(side=RIGHT, anchor=W)
436 button_save_custom_theme.pack(side=BOTTOM, fill=X, padx=5, pady=5)
437 #frame_theme
438 theme_type_title.pack(side=TOP, anchor=W, padx=5, pady=5)
439 self.radio_theme_builtin.pack(side=TOP, anchor=W, padx=5)
440 self.radio_theme_custom.pack(side=TOP, anchor=W, padx=5, pady=2)
441 self.opt_menu_theme_builtin.pack(side=TOP, fill=X, padx=5, pady=5)
442 self.opt_menu_theme_custom.pack(side=TOP, fill=X, anchor=W, padx=5, pady=5)
443 self.button_delete_custom_theme.pack(side=TOP, fill=X, padx=5, pady=5)
Terry Jan Reedyd0c0f002015-11-12 15:02:57 -0500444 self.new_custom_theme.pack(side=TOP, fill=X, pady=5)
Steven M. Gava952d0a52001-08-03 04:43:44 +0000445 return frame
446
terryjreedy938e7382017-06-26 20:48:39 -0400447 def create_page_keys(self):
terryjreedye5bb1122017-07-05 00:54:55 -0400448 """Return frame of widgets for Keys tab.
449
terryjreedy9a09c662017-07-13 23:53:30 -0400450 Tk Variables:
terryjreedye5bb1122017-07-05 00:54:55 -0400451 builtin_keys: Menu variable for built-in keybindings.
452 custom_keys: Menu variable for custom keybindings.
453 are_keys_builtin: Selector for built-in or custom keybindings.
454 keybinding: Action/key bindings.
terryjreedy9a09c662017-07-13 23:53:30 -0400455
456 Methods:
457 load_key_config: Set table.
458 load_keys_list: Reload active set.
459 keybinding_selected: Bound to list_bindings button release.
460 get_new_keys: Command for button_new_keys.
461 get_new_keys_name: Call popup.
462 create_new_key_set: Combine active keyset and changes.
463 set_keys_type: Command for are_keys_builtin.
464 delete_custom_keys: Command for button_delete_custom_keys.
465 save_as_new_key_set: Command for button_save_custom_keys.
466 save_new_key_set: Save to idleConf.userCfg['keys'] (is function).
467 deactivate_current_config: Remove keys bindings in editors.
468
469 Widget Structure: (*) widgets bound to self
470 frame
471 frame_custom: LabelFrame
472 frame_target: Frame
473 target_title: Label
474 scroll_target_y: Scrollbar
475 scroll_target_x: Scrollbar
476 (*)list_bindings: ListBox
477 (*)button_new_keys: Button
478 frame_key_sets: LabelFrame
479 frames[0]: Frame
480 (*)radio_keys_builtin: Radiobutton - are_keys_builtin
481 (*)radio_keys_custom: Radiobutton - are_keys_builtin
482 (*)opt_menu_keys_builtin: DynOptionMenu - builtin_keys
483 (*)opt_menu_keys_custom: DynOptionMenu - custom_keys
484 (*)new_custom_keys: Label
485 frames[1]: Frame
486 (*)button_delete_custom_keys: Button
487 button_save_custom_keys: Button
terryjreedye5bb1122017-07-05 00:54:55 -0400488 """
Terry Jan Reedy22405332014-07-30 19:24:32 -0400489 parent = self.parent
terryjreedy938e7382017-06-26 20:48:39 -0400490 self.builtin_keys = StringVar(parent)
491 self.custom_keys = StringVar(parent)
492 self.are_keys_builtin = BooleanVar(parent)
493 self.keybinding = StringVar(parent)
Terry Jan Reedy22405332014-07-30 19:24:32 -0400494
Steven M. Gava60fc7072001-08-04 13:58:22 +0000495 ##widget creation
496 #body frame
terryjreedy938e7382017-06-26 20:48:39 -0400497 frame = self.tab_pages.pages['Keys'].frame
Steven M. Gava60fc7072001-08-04 13:58:22 +0000498 #body section frames
terryjreedy938e7382017-06-26 20:48:39 -0400499 frame_custom = LabelFrame(
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400500 frame, borderwidth=2, relief=GROOVE,
501 text=' Custom Key Bindings ')
terryjreedy938e7382017-06-26 20:48:39 -0400502 frame_key_sets = LabelFrame(
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400503 frame, borderwidth=2, relief=GROOVE, text=' Key Set ')
terryjreedy938e7382017-06-26 20:48:39 -0400504 #frame_custom
505 frame_target = Frame(frame_custom)
506 target_title = Label(frame_target, text='Action - Key(s)')
507 scroll_target_y = Scrollbar(frame_target)
508 scroll_target_x = Scrollbar(frame_target, orient=HORIZONTAL)
509 self.list_bindings = Listbox(
510 frame_target, takefocus=FALSE, exportselection=FALSE)
511 self.list_bindings.bind('<ButtonRelease-1>', self.keybinding_selected)
512 scroll_target_y.config(command=self.list_bindings.yview)
513 scroll_target_x.config(command=self.list_bindings.xview)
514 self.list_bindings.config(yscrollcommand=scroll_target_y.set)
515 self.list_bindings.config(xscrollcommand=scroll_target_x.set)
516 self.button_new_keys = Button(
517 frame_custom, text='Get New Keys for Selection',
518 command=self.get_new_keys, state=DISABLED)
519 #frame_key_sets
520 frames = [Frame(frame_key_sets, padx=2, pady=2, borderwidth=0)
Christian Heimes9a371592007-12-28 14:08:13 +0000521 for i in range(2)]
terryjreedy938e7382017-06-26 20:48:39 -0400522 self.radio_keys_builtin = Radiobutton(
523 frames[0], variable=self.are_keys_builtin, value=1,
524 command=self.set_keys_type, text='Use a Built-in Key Set')
525 self.radio_keys_custom = Radiobutton(
526 frames[0], variable=self.are_keys_builtin, value=0,
527 command=self.set_keys_type, text='Use a Custom Key Set')
528 self.opt_menu_keys_builtin = DynOptionMenu(
529 frames[0], self.builtin_keys, None, command=None)
530 self.opt_menu_keys_custom = DynOptionMenu(
531 frames[0], self.custom_keys, None, command=None)
532 self.button_delete_custom_keys = Button(
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400533 frames[1], text='Delete Custom Key Set',
terryjreedy938e7382017-06-26 20:48:39 -0400534 command=self.delete_custom_keys)
535 button_save_custom_keys = Button(
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400536 frames[1], text='Save as New Custom Key Set',
terryjreedy938e7382017-06-26 20:48:39 -0400537 command=self.save_as_new_key_set)
Terry Jan Reedy9bdb1ed2016-07-10 13:46:34 -0400538 self.new_custom_keys = Label(frames[0], bd=2)
Terry Jan Reedy22405332014-07-30 19:24:32 -0400539
Steven M. Gava60fc7072001-08-04 13:58:22 +0000540 ##widget packing
541 #body
terryjreedy938e7382017-06-26 20:48:39 -0400542 frame_custom.pack(side=BOTTOM, padx=5, pady=5, expand=TRUE, fill=BOTH)
543 frame_key_sets.pack(side=BOTTOM, padx=5, pady=5, fill=BOTH)
544 #frame_custom
545 self.button_new_keys.pack(side=BOTTOM, fill=X, padx=5, pady=5)
546 frame_target.pack(side=LEFT, padx=5, pady=5, expand=TRUE, fill=BOTH)
Steven M. Gavafacfc092002-01-19 00:29:54 +0000547 #frame target
terryjreedy938e7382017-06-26 20:48:39 -0400548 frame_target.columnconfigure(0, weight=1)
549 frame_target.rowconfigure(1, weight=1)
550 target_title.grid(row=0, column=0, columnspan=2, sticky=W)
551 self.list_bindings.grid(row=1, column=0, sticky=NSEW)
552 scroll_target_y.grid(row=1, column=1, sticky=NS)
553 scroll_target_x.grid(row=2, column=0, sticky=EW)
554 #frame_key_sets
555 self.radio_keys_builtin.grid(row=0, column=0, sticky=W+NS)
556 self.radio_keys_custom.grid(row=1, column=0, sticky=W+NS)
557 self.opt_menu_keys_builtin.grid(row=0, column=1, sticky=NSEW)
558 self.opt_menu_keys_custom.grid(row=1, column=1, sticky=NSEW)
Terry Jan Reedy9bdb1ed2016-07-10 13:46:34 -0400559 self.new_custom_keys.grid(row=0, column=2, sticky=NSEW, padx=5, pady=5)
terryjreedy938e7382017-06-26 20:48:39 -0400560 self.button_delete_custom_keys.pack(side=LEFT, fill=X, expand=True, padx=2)
561 button_save_custom_keys.pack(side=LEFT, fill=X, expand=True, padx=2)
Christian Heimes9a371592007-12-28 14:08:13 +0000562 frames[0].pack(side=TOP, fill=BOTH, expand=True)
563 frames[1].pack(side=TOP, fill=X, expand=True, pady=2)
Steven M. Gava952d0a52001-08-03 04:43:44 +0000564 return frame
565
terryjreedy938e7382017-06-26 20:48:39 -0400566 def create_page_general(self):
terryjreedye5bb1122017-07-05 00:54:55 -0400567 """Return frame of widgets for General tab.
568
terryjreedy9a09c662017-07-13 23:53:30 -0400569 Tk Variables:
terryjreedye5bb1122017-07-05 00:54:55 -0400570 win_width: Initial window width in characters.
571 win_height: Initial window height in characters.
572 startup_edit: Selector for opening in editor or shell mode.
573 autosave: Selector for save prompt popup when using Run.
terryjreedy9a09c662017-07-13 23:53:30 -0400574
575 Methods:
576 load_general_config:
577 help_source_selected: Bound to list_help button release.
578 set_helplist_button_states: Toggle based on list.
579 helplist_item_edit: Command for button_helplist_edit.
580 helplist_item_add: Command for button_helplist_add.
581 helplist_item_remove: Command for button_helplist_remove.
582 update_user_help_changed_items: Fill in changes.
583
584 Widget Structure: (*) widgets bound to self
585 frame
586 frame_run: LabelFrame
587 startup_title: Label
588 (*)radio_startup_edit: Radiobutton - startup_edit
589 (*)radio_startup_shell: Radiobutton - startup_edit
590 frame_save: LabelFrame
591 run_save_title: Label
592 (*)radio_save_ask: Radiobutton - autosave
593 (*)radio_save_auto: Radiobutton - autosave
594 frame_win_size: LabelFrame
595 win_size_title: Label
596 win_width_title: Label
597 (*)entry_win_width: Entry - win_width
598 win_height_title: Label
599 (*)entry_win_height: Entry - win_height
600 frame_help: LabelFrame
601 frame_helplist: Frame
602 frame_helplist_buttons: Frame
603 (*)button_helplist_edit
604 (*)button_helplist_add
605 (*)button_helplist_remove
606 scroll_helplist: Scrollbar
607 (*)list_help: ListBox
terryjreedye5bb1122017-07-05 00:54:55 -0400608 """
Terry Jan Reedy22405332014-07-30 19:24:32 -0400609 parent = self.parent
terryjreedy938e7382017-06-26 20:48:39 -0400610 self.win_width = StringVar(parent)
611 self.win_height = StringVar(parent)
612 self.startup_edit = IntVar(parent)
613 self.autosave = IntVar(parent)
Terry Jan Reedy22405332014-07-30 19:24:32 -0400614
Steven M. Gava230e5782001-08-07 03:28:25 +0000615 #widget creation
616 #body
terryjreedy938e7382017-06-26 20:48:39 -0400617 frame = self.tab_pages.pages['General'].frame
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000618 #body section frames
terryjreedy938e7382017-06-26 20:48:39 -0400619 frame_run = LabelFrame(frame, borderwidth=2, relief=GROOVE,
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400620 text=' Startup Preferences ')
terryjreedy938e7382017-06-26 20:48:39 -0400621 frame_save = LabelFrame(frame, borderwidth=2, relief=GROOVE,
622 text=' autosave Preferences ')
623 frame_win_size = Frame(frame, borderwidth=2, relief=GROOVE)
624 frame_help = LabelFrame(frame, borderwidth=2, relief=GROOVE,
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400625 text=' Additional Help Sources ')
terryjreedy938e7382017-06-26 20:48:39 -0400626 #frame_run
627 startup_title = Label(frame_run, text='At Startup')
628 self.radio_startup_edit = Radiobutton(
629 frame_run, variable=self.startup_edit, value=1,
Terry Jan Reedyf46b7822016-11-07 17:15:01 -0500630 text="Open Edit Window")
terryjreedy938e7382017-06-26 20:48:39 -0400631 self.radio_startup_shell = Radiobutton(
632 frame_run, variable=self.startup_edit, value=0,
Terry Jan Reedyf46b7822016-11-07 17:15:01 -0500633 text='Open Shell Window')
terryjreedy938e7382017-06-26 20:48:39 -0400634 #frame_save
635 run_save_title = Label(frame_save, text='At Start of Run (F5) ')
636 self.radio_save_ask = Radiobutton(
637 frame_save, variable=self.autosave, value=0,
Terry Jan Reedyf46b7822016-11-07 17:15:01 -0500638 text="Prompt to Save")
terryjreedy938e7382017-06-26 20:48:39 -0400639 self.radio_save_auto = Radiobutton(
640 frame_save, variable=self.autosave, value=1,
Terry Jan Reedyf46b7822016-11-07 17:15:01 -0500641 text='No Prompt')
terryjreedy938e7382017-06-26 20:48:39 -0400642 #frame_win_size
643 win_size_title = Label(
644 frame_win_size, text='Initial Window Size (in characters)')
645 win_width_title = Label(frame_win_size, text='Width')
646 self.entry_win_width = Entry(
647 frame_win_size, textvariable=self.win_width, width=3)
648 win_height_title = Label(frame_win_size, text='Height')
649 self.entry_win_height = Entry(
650 frame_win_size, textvariable=self.win_height, width=3)
651 #frame_help
652 frame_helplist = Frame(frame_help)
653 frame_helplist_buttons = Frame(frame_helplist)
654 scroll_helplist = Scrollbar(frame_helplist)
655 self.list_help = Listbox(
656 frame_helplist, height=5, takefocus=FALSE,
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000657 exportselection=FALSE)
terryjreedy938e7382017-06-26 20:48:39 -0400658 scroll_helplist.config(command=self.list_help.yview)
659 self.list_help.config(yscrollcommand=scroll_helplist.set)
660 self.list_help.bind('<ButtonRelease-1>', self.help_source_selected)
661 self.button_helplist_edit = Button(
662 frame_helplist_buttons, text='Edit', state=DISABLED,
663 width=8, command=self.helplist_item_edit)
664 self.button_helplist_add = Button(
665 frame_helplist_buttons, text='Add',
666 width=8, command=self.helplist_item_add)
667 self.button_helplist_remove = Button(
668 frame_helplist_buttons, text='Remove', state=DISABLED,
669 width=8, command=self.helplist_item_remove)
Terry Jan Reedy22405332014-07-30 19:24:32 -0400670
Steven M. Gava230e5782001-08-07 03:28:25 +0000671 #widget packing
672 #body
terryjreedy938e7382017-06-26 20:48:39 -0400673 frame_run.pack(side=TOP, padx=5, pady=5, fill=X)
674 frame_save.pack(side=TOP, padx=5, pady=5, fill=X)
675 frame_win_size.pack(side=TOP, padx=5, pady=5, fill=X)
676 frame_help.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH)
677 #frame_run
678 startup_title.pack(side=LEFT, anchor=W, padx=5, pady=5)
679 self.radio_startup_shell.pack(side=RIGHT, anchor=W, padx=5, pady=5)
680 self.radio_startup_edit.pack(side=RIGHT, anchor=W, padx=5, pady=5)
681 #frame_save
682 run_save_title.pack(side=LEFT, anchor=W, padx=5, pady=5)
683 self.radio_save_auto.pack(side=RIGHT, anchor=W, padx=5, pady=5)
684 self.radio_save_ask.pack(side=RIGHT, anchor=W, padx=5, pady=5)
685 #frame_win_size
686 win_size_title.pack(side=LEFT, anchor=W, padx=5, pady=5)
687 self.entry_win_height.pack(side=RIGHT, anchor=E, padx=10, pady=5)
688 win_height_title.pack(side=RIGHT, anchor=E, pady=5)
689 self.entry_win_width.pack(side=RIGHT, anchor=E, padx=10, pady=5)
690 win_width_title.pack(side=RIGHT, anchor=E, pady=5)
691 #frame_help
692 frame_helplist_buttons.pack(side=RIGHT, padx=5, pady=5, fill=Y)
693 frame_helplist.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH)
694 scroll_helplist.pack(side=RIGHT, anchor=W, fill=Y)
695 self.list_help.pack(side=LEFT, anchor=E, expand=TRUE, fill=BOTH)
696 self.button_helplist_edit.pack(side=TOP, anchor=W, pady=5)
697 self.button_helplist_add.pack(side=TOP, anchor=W)
698 self.button_helplist_remove.pack(side=TOP, anchor=W, pady=5)
Steven M. Gava952d0a52001-08-03 04:43:44 +0000699 return frame
700
terryjreedy938e7382017-06-26 20:48:39 -0400701 def attach_var_callbacks(self):
terryjreedye5bb1122017-07-05 00:54:55 -0400702 "Attach callbacks to variables that can be changed."
terryjreedy938e7382017-06-26 20:48:39 -0400703 self.font_size.trace_add('write', self.var_changed_font)
704 self.font_name.trace_add('write', self.var_changed_font)
705 self.font_bold.trace_add('write', self.var_changed_font)
706 self.space_num.trace_add('write', self.var_changed_space_num)
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -0400707 self.color.trace_add('write', self.var_changed_color)
terryjreedy938e7382017-06-26 20:48:39 -0400708 self.builtin_theme.trace_add('write', self.var_changed_builtin_theme)
709 self.custom_theme.trace_add('write', self.var_changed_custom_theme)
710 self.is_builtin_theme.trace_add('write', self.var_changed_is_builtin_theme)
711 self.highlight_target.trace_add('write', self.var_changed_highlight_target)
712 self.keybinding.trace_add('write', self.var_changed_keybinding)
713 self.builtin_keys.trace_add('write', self.var_changed_builtin_keys)
714 self.custom_keys.trace_add('write', self.var_changed_custom_keys)
715 self.are_keys_builtin.trace_add('write', self.var_changed_are_keys_builtin)
716 self.win_width.trace_add('write', self.var_changed_win_width)
717 self.win_height.trace_add('write', self.var_changed_win_height)
718 self.startup_edit.trace_add('write', self.var_changed_startup_edit)
719 self.autosave.trace_add('write', self.var_changed_autosave)
Steven M. Gava052937f2002-02-11 02:20:53 +0000720
Terry Jan Reedy6b98ce22016-05-16 22:27:28 -0400721 def remove_var_callbacks(self):
722 "Remove callbacks to prevent memory leaks."
723 for var in (
terryjreedy938e7382017-06-26 20:48:39 -0400724 self.font_size, self.font_name, self.font_bold,
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -0400725 self.space_num, self.color, self.builtin_theme,
terryjreedy938e7382017-06-26 20:48:39 -0400726 self.custom_theme, self.is_builtin_theme, self.highlight_target,
727 self.keybinding, self.builtin_keys, self.custom_keys,
728 self.are_keys_builtin, self.win_width, self.win_height,
terryjreedy8e3f73e2017-07-10 15:11:45 -0400729 self.startup_edit, self.autosave,):
Serhiy Storchaka81221742016-06-26 09:46:57 +0300730 var.trace_remove('write', var.trace_info()[0][1])
Terry Jan Reedy6b98ce22016-05-16 22:27:28 -0400731
terryjreedy938e7382017-06-26 20:48:39 -0400732 def var_changed_font(self, *params):
terryjreedye5bb1122017-07-05 00:54:55 -0400733 """Store changes to font attributes.
734
735 When one font attribute changes, save them all, as they are
Terry Jan Reedyd87d1682015-08-01 18:57:33 -0400736 not independent from each other. In particular, when we are
737 overriding the default font, we need to write out everything.
terryjreedye5bb1122017-07-05 00:54:55 -0400738 """
terryjreedy938e7382017-06-26 20:48:39 -0400739 value = self.font_name.get()
terryjreedyedc03422017-07-07 16:37:39 -0400740 changes.add_option('main', 'EditorWindow', 'font', value)
terryjreedy938e7382017-06-26 20:48:39 -0400741 value = self.font_size.get()
terryjreedyedc03422017-07-07 16:37:39 -0400742 changes.add_option('main', 'EditorWindow', 'font-size', value)
terryjreedy938e7382017-06-26 20:48:39 -0400743 value = self.font_bold.get()
terryjreedyedc03422017-07-07 16:37:39 -0400744 changes.add_option('main', 'EditorWindow', 'font-bold', value)
Steven M. Gavac112cd82002-01-22 05:56:40 +0000745
terryjreedy938e7382017-06-26 20:48:39 -0400746 def var_changed_space_num(self, *params):
terryjreedye5bb1122017-07-05 00:54:55 -0400747 "Store change to indentation size."
terryjreedy938e7382017-06-26 20:48:39 -0400748 value = self.space_num.get()
terryjreedyedc03422017-07-07 16:37:39 -0400749 changes.add_option('main', 'Indent', 'num-spaces', value)
Steven M. Gavac112cd82002-01-22 05:56:40 +0000750
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -0400751 def var_changed_color(self, *params):
terryjreedye5bb1122017-07-05 00:54:55 -0400752 "Process change to color choice."
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -0400753 self.on_new_color_set()
Steven M. Gava052937f2002-02-11 02:20:53 +0000754
terryjreedy938e7382017-06-26 20:48:39 -0400755 def var_changed_builtin_theme(self, *params):
terryjreedye5bb1122017-07-05 00:54:55 -0400756 """Process new builtin theme selection.
757
758 Add the changed theme's name to the changed_items and recreate
759 the sample with the values from the selected theme.
760 """
terryjreedy938e7382017-06-26 20:48:39 -0400761 old_themes = ('IDLE Classic', 'IDLE New')
762 value = self.builtin_theme.get()
763 if value not in old_themes:
764 if idleConf.GetOption('main', 'Theme', 'name') not in old_themes:
terryjreedyedc03422017-07-07 16:37:39 -0400765 changes.add_option('main', 'Theme', 'name', old_themes[0])
766 changes.add_option('main', 'Theme', 'name2', value)
Terry Jan Reedyd0c0f002015-11-12 15:02:57 -0500767 self.new_custom_theme.config(text='New theme, see Help',
768 fg='#500000')
769 else:
terryjreedyedc03422017-07-07 16:37:39 -0400770 changes.add_option('main', 'Theme', 'name', value)
771 changes.add_option('main', 'Theme', 'name2', '')
Terry Jan Reedyd0c0f002015-11-12 15:02:57 -0500772 self.new_custom_theme.config(text='', fg='black')
terryjreedy938e7382017-06-26 20:48:39 -0400773 self.paint_theme_sample()
Steven M. Gava052937f2002-02-11 02:20:53 +0000774
terryjreedy938e7382017-06-26 20:48:39 -0400775 def var_changed_custom_theme(self, *params):
terryjreedye5bb1122017-07-05 00:54:55 -0400776 """Process new custom theme selection.
777
778 If a new custom theme is selected, add the name to the
779 changed_items and apply the theme to the sample.
780 """
terryjreedy938e7382017-06-26 20:48:39 -0400781 value = self.custom_theme.get()
Steven M. Gava49745752002-02-18 01:43:11 +0000782 if value != '- no custom themes -':
terryjreedyedc03422017-07-07 16:37:39 -0400783 changes.add_option('main', 'Theme', 'name', value)
terryjreedy938e7382017-06-26 20:48:39 -0400784 self.paint_theme_sample()
Steven M. Gava052937f2002-02-11 02:20:53 +0000785
terryjreedy938e7382017-06-26 20:48:39 -0400786 def var_changed_is_builtin_theme(self, *params):
terryjreedye5bb1122017-07-05 00:54:55 -0400787 """Process toggle between builtin and custom theme.
788
789 Update the default toggle value and apply the newly
790 selected theme type.
791 """
terryjreedy938e7382017-06-26 20:48:39 -0400792 value = self.is_builtin_theme.get()
terryjreedyedc03422017-07-07 16:37:39 -0400793 changes.add_option('main', 'Theme', 'default', value)
Steven M. Gavaf31eec02002-03-05 00:25:58 +0000794 if value:
terryjreedy938e7382017-06-26 20:48:39 -0400795 self.var_changed_builtin_theme()
Steven M. Gavaf31eec02002-03-05 00:25:58 +0000796 else:
terryjreedy938e7382017-06-26 20:48:39 -0400797 self.var_changed_custom_theme()
Steven M. Gava052937f2002-02-11 02:20:53 +0000798
terryjreedy938e7382017-06-26 20:48:39 -0400799 def var_changed_highlight_target(self, *params):
terryjreedye5bb1122017-07-05 00:54:55 -0400800 "Process selection of new target tag for highlighting."
terryjreedy938e7382017-06-26 20:48:39 -0400801 self.set_highlight_target()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000802
terryjreedy938e7382017-06-26 20:48:39 -0400803 def var_changed_keybinding(self, *params):
terryjreedye5bb1122017-07-05 00:54:55 -0400804 "Store change to a keybinding."
terryjreedy938e7382017-06-26 20:48:39 -0400805 value = self.keybinding.get()
806 key_set = self.custom_keys.get()
807 event = self.list_bindings.get(ANCHOR).split()[0]
Steven M. Gavaa498af22002-02-01 01:33:36 +0000808 if idleConf.IsCoreBinding(event):
terryjreedyedc03422017-07-07 16:37:39 -0400809 changes.add_option('keys', key_set, event, value)
terryjreedye5bb1122017-07-05 00:54:55 -0400810 else: # Event is an extension binding.
terryjreedy938e7382017-06-26 20:48:39 -0400811 ext_name = idleConf.GetExtnNameForEvent(event)
812 ext_keybind_section = ext_name + '_cfgBindings'
terryjreedyedc03422017-07-07 16:37:39 -0400813 changes.add_option('extensions', ext_keybind_section, event, value)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000814
terryjreedy938e7382017-06-26 20:48:39 -0400815 def var_changed_builtin_keys(self, *params):
terryjreedye5bb1122017-07-05 00:54:55 -0400816 "Process selection of builtin key set."
terryjreedy938e7382017-06-26 20:48:39 -0400817 old_keys = (
Terry Jan Reedy9bdb1ed2016-07-10 13:46:34 -0400818 'IDLE Classic Windows',
819 'IDLE Classic Unix',
820 'IDLE Classic Mac',
821 'IDLE Classic OSX',
822 )
terryjreedy938e7382017-06-26 20:48:39 -0400823 value = self.builtin_keys.get()
824 if value not in old_keys:
825 if idleConf.GetOption('main', 'Keys', 'name') not in old_keys:
terryjreedyedc03422017-07-07 16:37:39 -0400826 changes.add_option('main', 'Keys', 'name', old_keys[0])
827 changes.add_option('main', 'Keys', 'name2', value)
Terry Jan Reedy9bdb1ed2016-07-10 13:46:34 -0400828 self.new_custom_keys.config(text='New key set, see Help',
829 fg='#500000')
830 else:
terryjreedyedc03422017-07-07 16:37:39 -0400831 changes.add_option('main', 'Keys', 'name', value)
832 changes.add_option('main', 'Keys', 'name2', '')
Terry Jan Reedy9bdb1ed2016-07-10 13:46:34 -0400833 self.new_custom_keys.config(text='', fg='black')
terryjreedy938e7382017-06-26 20:48:39 -0400834 self.load_keys_list(value)
Steven M. Gava052937f2002-02-11 02:20:53 +0000835
terryjreedy938e7382017-06-26 20:48:39 -0400836 def var_changed_custom_keys(self, *params):
terryjreedye5bb1122017-07-05 00:54:55 -0400837 "Process selection of custom key set."
terryjreedy938e7382017-06-26 20:48:39 -0400838 value = self.custom_keys.get()
Steven M. Gava49745752002-02-18 01:43:11 +0000839 if value != '- no custom keys -':
terryjreedyedc03422017-07-07 16:37:39 -0400840 changes.add_option('main', 'Keys', 'name', value)
terryjreedy938e7382017-06-26 20:48:39 -0400841 self.load_keys_list(value)
Steven M. Gava052937f2002-02-11 02:20:53 +0000842
terryjreedy938e7382017-06-26 20:48:39 -0400843 def var_changed_are_keys_builtin(self, *params):
terryjreedye5bb1122017-07-05 00:54:55 -0400844 "Process toggle between builtin key set and custom key set."
terryjreedy938e7382017-06-26 20:48:39 -0400845 value = self.are_keys_builtin.get()
terryjreedyedc03422017-07-07 16:37:39 -0400846 changes.add_option('main', 'Keys', 'default', value)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000847 if value:
terryjreedy938e7382017-06-26 20:48:39 -0400848 self.var_changed_builtin_keys()
Steven M. Gava052937f2002-02-11 02:20:53 +0000849 else:
terryjreedy938e7382017-06-26 20:48:39 -0400850 self.var_changed_custom_keys()
Steven M. Gava052937f2002-02-11 02:20:53 +0000851
terryjreedy938e7382017-06-26 20:48:39 -0400852 def var_changed_win_width(self, *params):
terryjreedye5bb1122017-07-05 00:54:55 -0400853 "Store change to window width."
terryjreedy938e7382017-06-26 20:48:39 -0400854 value = self.win_width.get()
terryjreedyedc03422017-07-07 16:37:39 -0400855 changes.add_option('main', 'EditorWindow', 'width', value)
Steven M. Gavac112cd82002-01-22 05:56:40 +0000856
terryjreedy938e7382017-06-26 20:48:39 -0400857 def var_changed_win_height(self, *params):
terryjreedye5bb1122017-07-05 00:54:55 -0400858 "Store change to window height."
terryjreedy938e7382017-06-26 20:48:39 -0400859 value = self.win_height.get()
terryjreedyedc03422017-07-07 16:37:39 -0400860 changes.add_option('main', 'EditorWindow', 'height', value)
Steven M. Gavac112cd82002-01-22 05:56:40 +0000861
terryjreedy938e7382017-06-26 20:48:39 -0400862 def var_changed_startup_edit(self, *params):
terryjreedye5bb1122017-07-05 00:54:55 -0400863 "Store change to toggle for starting IDLE in the editor or shell."
terryjreedy938e7382017-06-26 20:48:39 -0400864 value = self.startup_edit.get()
terryjreedyedc03422017-07-07 16:37:39 -0400865 changes.add_option('main', 'General', 'editor-on-startup', value)
Steven M. Gavac112cd82002-01-22 05:56:40 +0000866
terryjreedy938e7382017-06-26 20:48:39 -0400867 def var_changed_autosave(self, *params):
terryjreedye5bb1122017-07-05 00:54:55 -0400868 "Store change to autosave."
terryjreedy938e7382017-06-26 20:48:39 -0400869 value = self.autosave.get()
terryjreedyedc03422017-07-07 16:37:39 -0400870 changes.add_option('main', 'General', 'autosave', value)
Kurt B. Kaiser6c638b62003-05-26 06:23:10 +0000871
terryjreedy938e7382017-06-26 20:48:39 -0400872 def set_theme_type(self):
terryjreedy9a09c662017-07-13 23:53:30 -0400873 """Set available screen options based on builtin or custom theme.
874
875 Attributes accessed:
876 is_builtin_theme
877
878 Attributes updated:
879 opt_menu_theme_builtin
880 opt_menu_theme_custom
881 button_delete_custom_theme
882 radio_theme_custom
883
884 Called from:
885 handler for radio_theme_builtin and radio_theme_custom
886 delete_custom_theme
887 create_new_theme
888 load_theme_cfg
889 """
terryjreedy938e7382017-06-26 20:48:39 -0400890 if self.is_builtin_theme.get():
891 self.opt_menu_theme_builtin.config(state=NORMAL)
892 self.opt_menu_theme_custom.config(state=DISABLED)
893 self.button_delete_custom_theme.config(state=DISABLED)
Steven M. Gava5f28e8f2002-01-21 06:38:21 +0000894 else:
terryjreedy938e7382017-06-26 20:48:39 -0400895 self.opt_menu_theme_builtin.config(state=DISABLED)
896 self.radio_theme_custom.config(state=NORMAL)
897 self.opt_menu_theme_custom.config(state=NORMAL)
898 self.button_delete_custom_theme.config(state=NORMAL)
Steven M. Gava5f28e8f2002-01-21 06:38:21 +0000899
terryjreedy938e7382017-06-26 20:48:39 -0400900 def set_keys_type(self):
terryjreedye5bb1122017-07-05 00:54:55 -0400901 "Set available screen options based on builtin or custom key set."
terryjreedy938e7382017-06-26 20:48:39 -0400902 if self.are_keys_builtin.get():
903 self.opt_menu_keys_builtin.config(state=NORMAL)
904 self.opt_menu_keys_custom.config(state=DISABLED)
905 self.button_delete_custom_keys.config(state=DISABLED)
Steven M. Gava5f28e8f2002-01-21 06:38:21 +0000906 else:
terryjreedy938e7382017-06-26 20:48:39 -0400907 self.opt_menu_keys_builtin.config(state=DISABLED)
908 self.radio_keys_custom.config(state=NORMAL)
909 self.opt_menu_keys_custom.config(state=NORMAL)
910 self.button_delete_custom_keys.config(state=NORMAL)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000911
terryjreedy938e7382017-06-26 20:48:39 -0400912 def get_new_keys(self):
terryjreedye5bb1122017-07-05 00:54:55 -0400913 """Handle event to change key binding for selected line.
914
915 A selection of a key/binding in the list of current
916 bindings pops up a dialog to enter a new binding. If
917 the current key set is builtin and a binding has
918 changed, then a name for a custom key set needs to be
919 entered for the change to be applied.
920 """
terryjreedy938e7382017-06-26 20:48:39 -0400921 list_index = self.list_bindings.index(ANCHOR)
922 binding = self.list_bindings.get(list_index)
terryjreedye5bb1122017-07-05 00:54:55 -0400923 bind_name = binding.split()[0]
terryjreedy938e7382017-06-26 20:48:39 -0400924 if self.are_keys_builtin.get():
925 current_key_set_name = self.builtin_keys.get()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000926 else:
terryjreedy938e7382017-06-26 20:48:39 -0400927 current_key_set_name = self.custom_keys.get()
928 current_bindings = idleConf.GetCurrentKeySet()
terryjreedyedc03422017-07-07 16:37:39 -0400929 if current_key_set_name in changes['keys']: # unsaved changes
930 key_set_changes = changes['keys'][current_key_set_name]
terryjreedy938e7382017-06-26 20:48:39 -0400931 for event in key_set_changes:
932 current_bindings[event] = key_set_changes[event].split()
933 current_key_sequences = list(current_bindings.values())
934 new_keys = GetKeysDialog(self, 'Get New Keys', bind_name,
935 current_key_sequences).result
terryjreedye5bb1122017-07-05 00:54:55 -0400936 if new_keys:
937 if self.are_keys_builtin.get(): # Current key set is a built-in.
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400938 message = ('Your changes will be saved as a new Custom Key Set.'
939 ' Enter a name for your new Custom Key Set below.')
terryjreedy938e7382017-06-26 20:48:39 -0400940 new_keyset = self.get_new_keys_name(message)
terryjreedye5bb1122017-07-05 00:54:55 -0400941 if not new_keyset: # User cancelled custom key set creation.
terryjreedy938e7382017-06-26 20:48:39 -0400942 self.list_bindings.select_set(list_index)
943 self.list_bindings.select_anchor(list_index)
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000944 return
terryjreedye5bb1122017-07-05 00:54:55 -0400945 else: # Create new custom key set based on previously active key set.
terryjreedy938e7382017-06-26 20:48:39 -0400946 self.create_new_key_set(new_keyset)
947 self.list_bindings.delete(list_index)
948 self.list_bindings.insert(list_index, bind_name+' - '+new_keys)
949 self.list_bindings.select_set(list_index)
950 self.list_bindings.select_anchor(list_index)
951 self.keybinding.set(new_keys)
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000952 else:
terryjreedy938e7382017-06-26 20:48:39 -0400953 self.list_bindings.select_set(list_index)
954 self.list_bindings.select_anchor(list_index)
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000955
terryjreedy938e7382017-06-26 20:48:39 -0400956 def get_new_keys_name(self, message):
terryjreedye5bb1122017-07-05 00:54:55 -0400957 "Return new key set name from query popup."
terryjreedy938e7382017-06-26 20:48:39 -0400958 used_names = (idleConf.GetSectionList('user', 'keys') +
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400959 idleConf.GetSectionList('default', 'keys'))
terryjreedy938e7382017-06-26 20:48:39 -0400960 new_keyset = SectionName(
961 self, 'New Custom Key Set', message, used_names).result
962 return new_keyset
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000963
terryjreedy938e7382017-06-26 20:48:39 -0400964 def save_as_new_key_set(self):
terryjreedye5bb1122017-07-05 00:54:55 -0400965 "Prompt for name of new key set and save changes using that name."
terryjreedy938e7382017-06-26 20:48:39 -0400966 new_keys_name = self.get_new_keys_name('New Key Set Name:')
967 if new_keys_name:
968 self.create_new_key_set(new_keys_name)
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000969
terryjreedy938e7382017-06-26 20:48:39 -0400970 def keybinding_selected(self, event):
terryjreedye5bb1122017-07-05 00:54:55 -0400971 "Activate button to assign new keys to selected action."
terryjreedy938e7382017-06-26 20:48:39 -0400972 self.button_new_keys.config(state=NORMAL)
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000973
terryjreedy938e7382017-06-26 20:48:39 -0400974 def create_new_key_set(self, new_key_set_name):
terryjreedye5bb1122017-07-05 00:54:55 -0400975 """Create a new custom key set with the given name.
976
977 Create the new key set based on the previously active set
978 with the current changes applied. Once it is saved, then
979 activate the new key set.
980 """
terryjreedy938e7382017-06-26 20:48:39 -0400981 if self.are_keys_builtin.get():
982 prev_key_set_name = self.builtin_keys.get()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000983 else:
terryjreedy938e7382017-06-26 20:48:39 -0400984 prev_key_set_name = self.custom_keys.get()
985 prev_keys = idleConf.GetCoreKeys(prev_key_set_name)
986 new_keys = {}
terryjreedye5bb1122017-07-05 00:54:55 -0400987 for event in prev_keys: # Add key set to changed items.
988 event_name = event[2:-2] # Trim off the angle brackets.
terryjreedy938e7382017-06-26 20:48:39 -0400989 binding = ' '.join(prev_keys[event])
990 new_keys[event_name] = binding
terryjreedye5bb1122017-07-05 00:54:55 -0400991 # Handle any unsaved changes to prev key set.
terryjreedyedc03422017-07-07 16:37:39 -0400992 if prev_key_set_name in changes['keys']:
993 key_set_changes = changes['keys'][prev_key_set_name]
terryjreedy938e7382017-06-26 20:48:39 -0400994 for event in key_set_changes:
995 new_keys[event] = key_set_changes[event]
terryjreedye5bb1122017-07-05 00:54:55 -0400996 # Save the new key set.
terryjreedy938e7382017-06-26 20:48:39 -0400997 self.save_new_key_set(new_key_set_name, new_keys)
terryjreedye5bb1122017-07-05 00:54:55 -0400998 # Change GUI over to the new key set.
terryjreedy938e7382017-06-26 20:48:39 -0400999 custom_key_list = idleConf.GetSectionList('user', 'keys')
1000 custom_key_list.sort()
1001 self.opt_menu_keys_custom.SetMenu(custom_key_list, new_key_set_name)
1002 self.are_keys_builtin.set(0)
1003 self.set_keys_type()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001004
terryjreedy938e7382017-06-26 20:48:39 -04001005 def load_keys_list(self, keyset_name):
terryjreedye5bb1122017-07-05 00:54:55 -04001006 """Reload the list of action/key binding pairs for the active key set.
1007
1008 An action/key binding can be selected to change the key binding.
1009 """
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001010 reselect = 0
terryjreedy938e7382017-06-26 20:48:39 -04001011 if self.list_bindings.curselection():
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001012 reselect = 1
terryjreedy938e7382017-06-26 20:48:39 -04001013 list_index = self.list_bindings.index(ANCHOR)
1014 keyset = idleConf.GetKeySet(keyset_name)
1015 bind_names = list(keyset.keys())
1016 bind_names.sort()
1017 self.list_bindings.delete(0, END)
1018 for bind_name in bind_names:
terryjreedye5bb1122017-07-05 00:54:55 -04001019 key = ' '.join(keyset[bind_name])
1020 bind_name = bind_name[2:-2] # Trim off the angle brackets.
terryjreedyedc03422017-07-07 16:37:39 -04001021 if keyset_name in changes['keys']:
terryjreedye5bb1122017-07-05 00:54:55 -04001022 # Handle any unsaved changes to this key set.
terryjreedyedc03422017-07-07 16:37:39 -04001023 if bind_name in changes['keys'][keyset_name]:
1024 key = changes['keys'][keyset_name][bind_name]
terryjreedy938e7382017-06-26 20:48:39 -04001025 self.list_bindings.insert(END, bind_name+' - '+key)
Steven M. Gava052937f2002-02-11 02:20:53 +00001026 if reselect:
terryjreedy938e7382017-06-26 20:48:39 -04001027 self.list_bindings.see(list_index)
1028 self.list_bindings.select_set(list_index)
1029 self.list_bindings.select_anchor(list_index)
Steven M. Gava052937f2002-02-11 02:20:53 +00001030
terryjreedy938e7382017-06-26 20:48:39 -04001031 def delete_custom_keys(self):
terryjreedye5bb1122017-07-05 00:54:55 -04001032 """Handle event to delete a custom key set.
1033
1034 Applying the delete deactivates the current configuration and
1035 reverts to the default. The custom key set is permanently
1036 deleted from the config file.
1037 """
terryjreedy938e7382017-06-26 20:48:39 -04001038 keyset_name=self.custom_keys.get()
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001039 delmsg = 'Are you sure you wish to delete the key set %r ?'
1040 if not tkMessageBox.askyesno(
terryjreedy938e7382017-06-26 20:48:39 -04001041 'Delete Key Set', delmsg % keyset_name, parent=self):
Steven M. Gava49745752002-02-18 01:43:11 +00001042 return
terryjreedy938e7382017-06-26 20:48:39 -04001043 self.deactivate_current_config()
terryjreedyedc03422017-07-07 16:37:39 -04001044 # Remove key set from changes, config, and file.
terryjreedyc0179482017-07-11 19:50:10 -04001045 changes.delete_section('keys', keyset_name)
terryjreedye5bb1122017-07-05 00:54:55 -04001046 # Reload user key set list.
terryjreedy938e7382017-06-26 20:48:39 -04001047 item_list = idleConf.GetSectionList('user', 'keys')
1048 item_list.sort()
1049 if not item_list:
1050 self.radio_keys_custom.config(state=DISABLED)
1051 self.opt_menu_keys_custom.SetMenu(item_list, '- no custom keys -')
Steven M. Gava49745752002-02-18 01:43:11 +00001052 else:
terryjreedy938e7382017-06-26 20:48:39 -04001053 self.opt_menu_keys_custom.SetMenu(item_list, item_list[0])
terryjreedye5bb1122017-07-05 00:54:55 -04001054 # Revert to default key set.
terryjreedy938e7382017-06-26 20:48:39 -04001055 self.are_keys_builtin.set(idleConf.defaultCfg['main']
Terry Jan Reedy9bdb1ed2016-07-10 13:46:34 -04001056 .Get('Keys', 'default'))
terryjreedy938e7382017-06-26 20:48:39 -04001057 self.builtin_keys.set(idleConf.defaultCfg['main'].Get('Keys', 'name')
Terry Jan Reedy9bdb1ed2016-07-10 13:46:34 -04001058 or idleConf.default_keys())
terryjreedye5bb1122017-07-05 00:54:55 -04001059 # User can't back out of these changes, they must be applied now.
terryjreedyedc03422017-07-07 16:37:39 -04001060 changes.save_all()
1061 self.save_all_changed_extensions()
terryjreedy938e7382017-06-26 20:48:39 -04001062 self.activate_config_changes()
1063 self.set_keys_type()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001064
terryjreedy938e7382017-06-26 20:48:39 -04001065 def delete_custom_theme(self):
terryjreedye5bb1122017-07-05 00:54:55 -04001066 """Handle event to delete custom theme.
1067
1068 The current theme is deactivated and the default theme is
1069 activated. The custom theme is permanently removed from
1070 the config file.
terryjreedy9a09c662017-07-13 23:53:30 -04001071
1072 Attributes accessed:
1073 custom_theme
1074
1075 Attributes updated:
1076 radio_theme_custom
1077 opt_menu_theme_custom
1078 is_builtin_theme
1079 builtin_theme
1080
1081 Methods:
1082 deactivate_current_config
1083 save_all_changed_extensions
1084 activate_config_changes
1085 set_theme_type
terryjreedye5bb1122017-07-05 00:54:55 -04001086 """
terryjreedy938e7382017-06-26 20:48:39 -04001087 theme_name = self.custom_theme.get()
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001088 delmsg = 'Are you sure you wish to delete the theme %r ?'
1089 if not tkMessageBox.askyesno(
terryjreedy938e7382017-06-26 20:48:39 -04001090 'Delete Theme', delmsg % theme_name, parent=self):
Steven M. Gava49745752002-02-18 01:43:11 +00001091 return
terryjreedy938e7382017-06-26 20:48:39 -04001092 self.deactivate_current_config()
terryjreedyedc03422017-07-07 16:37:39 -04001093 # Remove theme from changes, config, and file.
terryjreedyc0179482017-07-11 19:50:10 -04001094 changes.delete_section('highlight', theme_name)
terryjreedye5bb1122017-07-05 00:54:55 -04001095 # Reload user theme list.
terryjreedy938e7382017-06-26 20:48:39 -04001096 item_list = idleConf.GetSectionList('user', 'highlight')
1097 item_list.sort()
1098 if not item_list:
1099 self.radio_theme_custom.config(state=DISABLED)
1100 self.opt_menu_theme_custom.SetMenu(item_list, '- no custom themes -')
Steven M. Gava49745752002-02-18 01:43:11 +00001101 else:
terryjreedy938e7382017-06-26 20:48:39 -04001102 self.opt_menu_theme_custom.SetMenu(item_list, item_list[0])
terryjreedye5bb1122017-07-05 00:54:55 -04001103 # Revert to default theme.
terryjreedy938e7382017-06-26 20:48:39 -04001104 self.is_builtin_theme.set(idleConf.defaultCfg['main'].Get('Theme', 'default'))
1105 self.builtin_theme.set(idleConf.defaultCfg['main'].Get('Theme', 'name'))
terryjreedye5bb1122017-07-05 00:54:55 -04001106 # User can't back out of these changes, they must be applied now.
terryjreedyedc03422017-07-07 16:37:39 -04001107 changes.save_all()
1108 self.save_all_changed_extensions()
terryjreedy938e7382017-06-26 20:48:39 -04001109 self.activate_config_changes()
1110 self.set_theme_type()
Steven M. Gava49745752002-02-18 01:43:11 +00001111
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001112 def get_color(self):
terryjreedye5bb1122017-07-05 00:54:55 -04001113 """Handle button to select a new color for the target tag.
1114
1115 If a new color is selected while using a builtin theme, a
1116 name must be supplied to create a custom theme.
terryjreedy9a09c662017-07-13 23:53:30 -04001117
1118 Attributes accessed:
1119 highlight_target
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001120 frame_color_set
terryjreedy9a09c662017-07-13 23:53:30 -04001121 is_builtin_theme
1122
1123 Attributes updated:
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001124 color
terryjreedy9a09c662017-07-13 23:53:30 -04001125
1126 Methods:
1127 get_new_theme_name
1128 create_new_theme
terryjreedye5bb1122017-07-05 00:54:55 -04001129 """
terryjreedy938e7382017-06-26 20:48:39 -04001130 target = self.highlight_target.get()
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001131 prev_color = self.frame_color_set.cget('bg')
1132 rgbTuplet, color_string = tkColorChooser.askcolor(
1133 parent=self, title='Pick new color for : '+target,
1134 initialcolor=prev_color)
1135 if color_string and (color_string != prev_color):
1136 # User didn't cancel and they chose a new color.
terryjreedye5bb1122017-07-05 00:54:55 -04001137 if self.is_builtin_theme.get(): # Current theme is a built-in.
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001138 message = ('Your changes will be saved as a new Custom Theme. '
1139 'Enter a name for your new Custom Theme below.')
terryjreedy938e7382017-06-26 20:48:39 -04001140 new_theme = self.get_new_theme_name(message)
terryjreedye5bb1122017-07-05 00:54:55 -04001141 if not new_theme: # User cancelled custom theme creation.
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +00001142 return
terryjreedye5bb1122017-07-05 00:54:55 -04001143 else: # Create new custom theme based on previously active theme.
terryjreedy938e7382017-06-26 20:48:39 -04001144 self.create_new_theme(new_theme)
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001145 self.color.set(color_string)
terryjreedye5bb1122017-07-05 00:54:55 -04001146 else: # Current theme is user defined.
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001147 self.color.set(color_string)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001148
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001149 def on_new_color_set(self):
terryjreedye5bb1122017-07-05 00:54:55 -04001150 "Display sample of new color selection on the dialog."
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001151 new_color=self.color.get()
1152 self.frame_color_set.config(bg=new_color) # Set sample.
terryjreedy938e7382017-06-26 20:48:39 -04001153 plane ='foreground' if self.fg_bg_toggle.get() else 'background'
1154 sample_element = self.theme_elements[self.highlight_target.get()][0]
Terry Jan Reedy04864b42017-07-22 00:56:18 -04001155 self.highlight_sample.tag_config(sample_element, **{plane:new_color})
terryjreedy938e7382017-06-26 20:48:39 -04001156 theme = self.custom_theme.get()
1157 theme_element = sample_element + '-' + plane
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001158 changes.add_option('highlight', theme, theme_element, new_color)
Steven M. Gava052937f2002-02-11 02:20:53 +00001159
terryjreedy938e7382017-06-26 20:48:39 -04001160 def get_new_theme_name(self, message):
terryjreedye5bb1122017-07-05 00:54:55 -04001161 "Return name of new theme from query popup."
terryjreedy938e7382017-06-26 20:48:39 -04001162 used_names = (idleConf.GetSectionList('user', 'highlight') +
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001163 idleConf.GetSectionList('default', 'highlight'))
terryjreedy938e7382017-06-26 20:48:39 -04001164 new_theme = SectionName(
1165 self, 'New Custom Theme', message, used_names).result
1166 return new_theme
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001167
terryjreedy938e7382017-06-26 20:48:39 -04001168 def save_as_new_theme(self):
terryjreedy9a09c662017-07-13 23:53:30 -04001169 """Prompt for new theme name and create the theme.
1170
1171 Methods:
1172 get_new_theme_name
1173 create_new_theme
1174 """
terryjreedy938e7382017-06-26 20:48:39 -04001175 new_theme_name = self.get_new_theme_name('New Theme Name:')
1176 if new_theme_name:
1177 self.create_new_theme(new_theme_name)
Steven M. Gava085eb1b2002-02-05 04:52:32 +00001178
terryjreedy938e7382017-06-26 20:48:39 -04001179 def create_new_theme(self, new_theme_name):
terryjreedye5bb1122017-07-05 00:54:55 -04001180 """Create a new custom theme with the given name.
1181
1182 Create the new theme based on the previously active theme
1183 with the current changes applied. Once it is saved, then
1184 activate the new theme.
terryjreedy9a09c662017-07-13 23:53:30 -04001185
1186 Attributes accessed:
1187 builtin_theme
1188 custom_theme
1189
1190 Attributes updated:
1191 opt_menu_theme_custom
1192 is_builtin_theme
1193
1194 Method:
1195 save_new_theme
1196 set_theme_type
terryjreedye5bb1122017-07-05 00:54:55 -04001197 """
terryjreedy938e7382017-06-26 20:48:39 -04001198 if self.is_builtin_theme.get():
1199 theme_type = 'default'
1200 theme_name = self.builtin_theme.get()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001201 else:
terryjreedy938e7382017-06-26 20:48:39 -04001202 theme_type = 'user'
1203 theme_name = self.custom_theme.get()
1204 new_theme = idleConf.GetThemeDict(theme_type, theme_name)
terryjreedye5bb1122017-07-05 00:54:55 -04001205 # Apply any of the old theme's unsaved changes to the new theme.
terryjreedyedc03422017-07-07 16:37:39 -04001206 if theme_name in changes['highlight']:
1207 theme_changes = changes['highlight'][theme_name]
terryjreedy938e7382017-06-26 20:48:39 -04001208 for element in theme_changes:
1209 new_theme[element] = theme_changes[element]
terryjreedye5bb1122017-07-05 00:54:55 -04001210 # Save the new theme.
terryjreedy938e7382017-06-26 20:48:39 -04001211 self.save_new_theme(new_theme_name, new_theme)
terryjreedye5bb1122017-07-05 00:54:55 -04001212 # Change GUI over to the new theme.
terryjreedy938e7382017-06-26 20:48:39 -04001213 custom_theme_list = idleConf.GetSectionList('user', 'highlight')
1214 custom_theme_list.sort()
1215 self.opt_menu_theme_custom.SetMenu(custom_theme_list, new_theme_name)
1216 self.is_builtin_theme.set(0)
1217 self.set_theme_type()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001218
terryjreedy7ab33422017-07-09 19:26:32 -04001219 def on_fontlist_select(self, event):
1220 """Handle selecting a font from the list.
terryjreedye5bb1122017-07-05 00:54:55 -04001221
terryjreedy7ab33422017-07-09 19:26:32 -04001222 Event can result from either mouse click or Up or Down key.
Terry Jan Reedy04864b42017-07-22 00:56:18 -04001223 Set font_name and example displays to selection.
terryjreedye5bb1122017-07-05 00:54:55 -04001224 """
terryjreedy953e5272017-07-11 02:16:41 -04001225 font = self.fontlist.get(
1226 ACTIVE if event.type.name == 'KeyRelease' else ANCHOR)
terryjreedy938e7382017-06-26 20:48:39 -04001227 self.font_name.set(font.lower())
Terry Jan Reedy04864b42017-07-22 00:56:18 -04001228 self.set_samples()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001229
Terry Jan Reedy04864b42017-07-22 00:56:18 -04001230 def set_samples(self, event=None):
1231 """Update update both screen samples with the font settings.
terryjreedy9a09c662017-07-13 23:53:30 -04001232
Terry Jan Reedy04864b42017-07-22 00:56:18 -04001233 Called on font initialization and change events.
1234 Accesses font_name, font_size, and font_bold Variables.
1235 Updates font_sample and hightlight page highlight_sample.
terryjreedy9a09c662017-07-13 23:53:30 -04001236 """
terryjreedy938e7382017-06-26 20:48:39 -04001237 font_name = self.font_name.get()
1238 font_weight = tkFont.BOLD if self.font_bold.get() else tkFont.NORMAL
1239 new_font = (font_name, self.font_size.get(), font_weight)
Terry Jan Reedy04864b42017-07-22 00:56:18 -04001240 self.font_sample['font'] = new_font
1241 self.highlight_sample['font'] = new_font
Steven M. Gava5f28e8f2002-01-21 06:38:21 +00001242
terryjreedy938e7382017-06-26 20:48:39 -04001243 def set_highlight_target(self):
terryjreedy9a09c662017-07-13 23:53:30 -04001244 """Set fg/bg toggle and color based on highlight tag target.
1245
1246 Instance variables accessed:
1247 highlight_target
1248
1249 Attributes updated:
1250 radio_fg
1251 radio_bg
1252 fg_bg_toggle
1253
1254 Methods:
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001255 set_color_sample
terryjreedy9a09c662017-07-13 23:53:30 -04001256
1257 Called from:
1258 var_changed_highlight_target
1259 load_theme_cfg
1260 """
terryjreedye5bb1122017-07-05 00:54:55 -04001261 if self.highlight_target.get() == 'Cursor': # bg not possible
terryjreedy938e7382017-06-26 20:48:39 -04001262 self.radio_fg.config(state=DISABLED)
1263 self.radio_bg.config(state=DISABLED)
1264 self.fg_bg_toggle.set(1)
terryjreedye5bb1122017-07-05 00:54:55 -04001265 else: # Both fg and bg can be set.
terryjreedy938e7382017-06-26 20:48:39 -04001266 self.radio_fg.config(state=NORMAL)
1267 self.radio_bg.config(state=NORMAL)
1268 self.fg_bg_toggle.set(1)
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001269 self.set_color_sample()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001270
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001271 def set_color_sample_binding(self, *args):
terryjreedy9a09c662017-07-13 23:53:30 -04001272 """Change color sample based on foreground/background toggle.
1273
1274 Methods:
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001275 set_color_sample
terryjreedy9a09c662017-07-13 23:53:30 -04001276 """
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001277 self.set_color_sample()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001278
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001279 def set_color_sample(self):
terryjreedy9a09c662017-07-13 23:53:30 -04001280 """Set the color of the frame background to reflect the selected target.
1281
1282 Instance variables accessed:
1283 theme_elements
1284 highlight_target
1285 fg_bg_toggle
Terry Jan Reedy04864b42017-07-22 00:56:18 -04001286 highlight_sample
terryjreedy9a09c662017-07-13 23:53:30 -04001287
1288 Attributes updated:
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001289 frame_color_set
terryjreedy9a09c662017-07-13 23:53:30 -04001290 """
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001291 # Set the color sample area.
terryjreedy938e7382017-06-26 20:48:39 -04001292 tag = self.theme_elements[self.highlight_target.get()][0]
1293 plane = 'foreground' if self.fg_bg_toggle.get() else 'background'
Terry Jan Reedy04864b42017-07-22 00:56:18 -04001294 color = self.highlight_sample.tag_cget(tag, plane)
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001295 self.frame_color_set.config(bg=color)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001296
terryjreedy938e7382017-06-26 20:48:39 -04001297 def paint_theme_sample(self):
terryjreedy9a09c662017-07-13 23:53:30 -04001298 """Apply the theme colors to each element tag in the sample text.
1299
1300 Instance attributes accessed:
1301 theme_elements
1302 is_builtin_theme
1303 builtin_theme
1304 custom_theme
1305
1306 Attributes updated:
Terry Jan Reedy04864b42017-07-22 00:56:18 -04001307 highlight_sample: Set the tag elements to the theme.
terryjreedy9a09c662017-07-13 23:53:30 -04001308
1309 Methods:
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001310 set_color_sample
terryjreedy9a09c662017-07-13 23:53:30 -04001311
1312 Called from:
1313 var_changed_builtin_theme
1314 var_changed_custom_theme
1315 load_theme_cfg
1316 """
terryjreedye5bb1122017-07-05 00:54:55 -04001317 if self.is_builtin_theme.get(): # Default theme
terryjreedy938e7382017-06-26 20:48:39 -04001318 theme = self.builtin_theme.get()
terryjreedye5bb1122017-07-05 00:54:55 -04001319 else: # User theme
terryjreedy938e7382017-06-26 20:48:39 -04001320 theme = self.custom_theme.get()
1321 for element_title in self.theme_elements:
1322 element = self.theme_elements[element_title][0]
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001323 colors = idleConf.GetHighlight(theme, element)
terryjreedye5bb1122017-07-05 00:54:55 -04001324 if element == 'cursor': # Cursor sample needs special painting.
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001325 colors['background'] = idleConf.GetHighlight(
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001326 theme, 'normal', fgBg='bg')
terryjreedye5bb1122017-07-05 00:54:55 -04001327 # Handle any unsaved changes to this theme.
terryjreedyedc03422017-07-07 16:37:39 -04001328 if theme in changes['highlight']:
1329 theme_dict = changes['highlight'][theme]
terryjreedy938e7382017-06-26 20:48:39 -04001330 if element + '-foreground' in theme_dict:
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001331 colors['foreground'] = theme_dict[element + '-foreground']
terryjreedy938e7382017-06-26 20:48:39 -04001332 if element + '-background' in theme_dict:
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001333 colors['background'] = theme_dict[element + '-background']
Terry Jan Reedy04864b42017-07-22 00:56:18 -04001334 self.highlight_sample.tag_config(element, **colors)
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001335 self.set_color_sample()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001336
terryjreedy938e7382017-06-26 20:48:39 -04001337 def help_source_selected(self, event):
terryjreedye5bb1122017-07-05 00:54:55 -04001338 "Handle event for selecting additional help."
terryjreedy938e7382017-06-26 20:48:39 -04001339 self.set_helplist_button_states()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001340
terryjreedy938e7382017-06-26 20:48:39 -04001341 def set_helplist_button_states(self):
terryjreedye5bb1122017-07-05 00:54:55 -04001342 "Toggle the state for the help list buttons based on list entries."
1343 if self.list_help.size() < 1: # No entries in list.
terryjreedy938e7382017-06-26 20:48:39 -04001344 self.button_helplist_edit.config(state=DISABLED)
1345 self.button_helplist_remove.config(state=DISABLED)
terryjreedye5bb1122017-07-05 00:54:55 -04001346 else: # Some entries.
1347 if self.list_help.curselection(): # There currently is a selection.
terryjreedy938e7382017-06-26 20:48:39 -04001348 self.button_helplist_edit.config(state=NORMAL)
1349 self.button_helplist_remove.config(state=NORMAL)
terryjreedye5bb1122017-07-05 00:54:55 -04001350 else: # There currently is not a selection.
terryjreedy938e7382017-06-26 20:48:39 -04001351 self.button_helplist_edit.config(state=DISABLED)
1352 self.button_helplist_remove.config(state=DISABLED)
Steven M. Gava085eb1b2002-02-05 04:52:32 +00001353
terryjreedy938e7382017-06-26 20:48:39 -04001354 def helplist_item_add(self):
terryjreedye5bb1122017-07-05 00:54:55 -04001355 """Handle add button for the help list.
1356
1357 Query for name and location of new help sources and add
1358 them to the list.
1359 """
terryjreedy938e7382017-06-26 20:48:39 -04001360 help_source = HelpSource(self, 'New Help Source',
Terry Jan Reedy8b22c0a2016-07-08 00:22:50 -04001361 ).result
terryjreedy938e7382017-06-26 20:48:39 -04001362 if help_source:
1363 self.user_helplist.append((help_source[0], help_source[1]))
1364 self.list_help.insert(END, help_source[0])
1365 self.update_user_help_changed_items()
1366 self.set_helplist_button_states()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001367
terryjreedy938e7382017-06-26 20:48:39 -04001368 def helplist_item_edit(self):
terryjreedye5bb1122017-07-05 00:54:55 -04001369 """Handle edit button for the help list.
1370
1371 Query with existing help source information and update
1372 config if the values are changed.
1373 """
terryjreedy938e7382017-06-26 20:48:39 -04001374 item_index = self.list_help.index(ANCHOR)
1375 help_source = self.user_helplist[item_index]
1376 new_help_source = HelpSource(
Terry Jan Reedy8b22c0a2016-07-08 00:22:50 -04001377 self, 'Edit Help Source',
terryjreedy938e7382017-06-26 20:48:39 -04001378 menuitem=help_source[0],
1379 filepath=help_source[1],
Terry Jan Reedy8b22c0a2016-07-08 00:22:50 -04001380 ).result
terryjreedy938e7382017-06-26 20:48:39 -04001381 if new_help_source and new_help_source != help_source:
1382 self.user_helplist[item_index] = new_help_source
1383 self.list_help.delete(item_index)
1384 self.list_help.insert(item_index, new_help_source[0])
1385 self.update_user_help_changed_items()
1386 self.set_helplist_button_states()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001387
terryjreedy938e7382017-06-26 20:48:39 -04001388 def helplist_item_remove(self):
terryjreedye5bb1122017-07-05 00:54:55 -04001389 """Handle remove button for the help list.
1390
1391 Delete the help list item from config.
1392 """
terryjreedy938e7382017-06-26 20:48:39 -04001393 item_index = self.list_help.index(ANCHOR)
1394 del(self.user_helplist[item_index])
1395 self.list_help.delete(item_index)
1396 self.update_user_help_changed_items()
1397 self.set_helplist_button_states()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001398
terryjreedy938e7382017-06-26 20:48:39 -04001399 def update_user_help_changed_items(self):
terryjreedyedc03422017-07-07 16:37:39 -04001400 "Clear and rebuild the HelpFiles section in changes"
1401 changes['main']['HelpFiles'] = {}
terryjreedy938e7382017-06-26 20:48:39 -04001402 for num in range(1, len(self.user_helplist) + 1):
terryjreedyedc03422017-07-07 16:37:39 -04001403 changes.add_option(
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001404 'main', 'HelpFiles', str(num),
terryjreedy938e7382017-06-26 20:48:39 -04001405 ';'.join(self.user_helplist[num-1][:2]))
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001406
terryjreedy938e7382017-06-26 20:48:39 -04001407 def load_font_cfg(self):
terryjreedy9a09c662017-07-13 23:53:30 -04001408 """Load current configuration settings for the font options.
1409
1410 Retrieve current font values from idleConf.GetFont to set
1411 as initial values for font widgets.
1412
1413 Attributes updated:
1414 fontlist: Populate with fonts from tkinter.font.
1415 font_name: Set to current font.
Terry Jan Reedy5aa3bf02017-07-23 14:18:27 -04001416 sizelist: Populate valid options tuple and set
terryjreedy9a09c662017-07-13 23:53:30 -04001417 to current size.
1418 font_bold: Set to current font weight.
1419
1420 Methods:
Terry Jan Reedy04864b42017-07-22 00:56:18 -04001421 set_samples
terryjreedy9a09c662017-07-13 23:53:30 -04001422 """
terryjreedye5bb1122017-07-05 00:54:55 -04001423 # Set base editor font selection list.
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001424 fonts = list(tkFont.families(self))
Steven M. Gavac11ccf32001-09-24 09:43:17 +00001425 fonts.sort()
1426 for font in fonts:
terryjreedy7ab33422017-07-09 19:26:32 -04001427 self.fontlist.insert(END, font)
terryjreedy938e7382017-06-26 20:48:39 -04001428 configured_font = idleConf.GetFont(self, 'main', 'EditorWindow')
1429 font_name = configured_font[0].lower()
1430 font_size = configured_font[1]
1431 font_bold = configured_font[2]=='bold'
1432 self.font_name.set(font_name)
Kurt B. Kaiser05391692003-05-26 20:35:53 +00001433 lc_fonts = [s.lower() for s in fonts]
Terry Jan Reedyd87d1682015-08-01 18:57:33 -04001434 try:
terryjreedy938e7382017-06-26 20:48:39 -04001435 current_font_index = lc_fonts.index(font_name)
terryjreedy7ab33422017-07-09 19:26:32 -04001436 self.fontlist.see(current_font_index)
1437 self.fontlist.select_set(current_font_index)
1438 self.fontlist.select_anchor(current_font_index)
1439 self.fontlist.activate(current_font_index)
Terry Jan Reedyd87d1682015-08-01 18:57:33 -04001440 except ValueError:
1441 pass
terryjreedye5bb1122017-07-05 00:54:55 -04001442 # Set font size dropdown.
Terry Jan Reedy5aa3bf02017-07-23 14:18:27 -04001443 self.sizelist.SetMenu(('7', '8', '9', '10', '11', '12', '13', '14',
1444 '16', '18', '20', '22', '25', '29', '34', '40'),
1445 font_size)
terryjreedye5bb1122017-07-05 00:54:55 -04001446 # Set font weight.
terryjreedy938e7382017-06-26 20:48:39 -04001447 self.font_bold.set(font_bold)
terryjreedye5bb1122017-07-05 00:54:55 -04001448 # Set font sample.
Terry Jan Reedy04864b42017-07-22 00:56:18 -04001449 self.set_samples()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001450
terryjreedy938e7382017-06-26 20:48:39 -04001451 def load_tab_cfg(self):
terryjreedy9a09c662017-07-13 23:53:30 -04001452 """Load current configuration settings for the tab options.
1453
1454 Attributes updated:
1455 space_num: Set to value from idleConf.
1456 """
terryjreedye5bb1122017-07-05 00:54:55 -04001457 # Set indent sizes.
terryjreedy938e7382017-06-26 20:48:39 -04001458 space_num = idleConf.GetOption(
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001459 'main', 'Indent', 'num-spaces', default=4, type='int')
terryjreedy938e7382017-06-26 20:48:39 -04001460 self.space_num.set(space_num)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001461
terryjreedy938e7382017-06-26 20:48:39 -04001462 def load_theme_cfg(self):
terryjreedy9a09c662017-07-13 23:53:30 -04001463 """Load current configuration settings for the theme options.
1464
1465 Based on the is_builtin_theme toggle, the theme is set as
1466 either builtin or custom and the initial widget values
1467 reflect the current settings from idleConf.
1468
1469 Attributes updated:
1470 is_builtin_theme: Set from idleConf.
1471 opt_menu_theme_builtin: List of default themes from idleConf.
1472 opt_menu_theme_custom: List of custom themes from idleConf.
1473 radio_theme_custom: Disabled if there are no custom themes.
1474 custom_theme: Message with additional information.
1475 opt_menu_highlight_target: Create menu from self.theme_elements.
1476
1477 Methods:
1478 set_theme_type
1479 paint_theme_sample
1480 set_highlight_target
1481 """
terryjreedye5bb1122017-07-05 00:54:55 -04001482 # Set current theme type radiobutton.
terryjreedy938e7382017-06-26 20:48:39 -04001483 self.is_builtin_theme.set(idleConf.GetOption(
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001484 'main', 'Theme', 'default', type='bool', default=1))
terryjreedye5bb1122017-07-05 00:54:55 -04001485 # Set current theme.
terryjreedy938e7382017-06-26 20:48:39 -04001486 current_option = idleConf.CurrentTheme()
terryjreedye5bb1122017-07-05 00:54:55 -04001487 # Load available theme option menus.
1488 if self.is_builtin_theme.get(): # Default theme selected.
terryjreedy938e7382017-06-26 20:48:39 -04001489 item_list = idleConf.GetSectionList('default', 'highlight')
1490 item_list.sort()
1491 self.opt_menu_theme_builtin.SetMenu(item_list, current_option)
1492 item_list = idleConf.GetSectionList('user', 'highlight')
1493 item_list.sort()
1494 if not item_list:
1495 self.radio_theme_custom.config(state=DISABLED)
1496 self.custom_theme.set('- no custom themes -')
Steven M. Gava41a85322001-10-29 08:05:34 +00001497 else:
terryjreedy938e7382017-06-26 20:48:39 -04001498 self.opt_menu_theme_custom.SetMenu(item_list, item_list[0])
terryjreedye5bb1122017-07-05 00:54:55 -04001499 else: # User theme selected.
terryjreedy938e7382017-06-26 20:48:39 -04001500 item_list = idleConf.GetSectionList('user', 'highlight')
1501 item_list.sort()
1502 self.opt_menu_theme_custom.SetMenu(item_list, current_option)
1503 item_list = idleConf.GetSectionList('default', 'highlight')
1504 item_list.sort()
1505 self.opt_menu_theme_builtin.SetMenu(item_list, item_list[0])
1506 self.set_theme_type()
terryjreedye5bb1122017-07-05 00:54:55 -04001507 # Load theme element option menu.
terryjreedy938e7382017-06-26 20:48:39 -04001508 theme_names = list(self.theme_elements.keys())
1509 theme_names.sort(key=lambda x: self.theme_elements[x][1])
1510 self.opt_menu_highlight_target.SetMenu(theme_names, theme_names[0])
1511 self.paint_theme_sample()
1512 self.set_highlight_target()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001513
terryjreedy938e7382017-06-26 20:48:39 -04001514 def load_key_cfg(self):
terryjreedye5bb1122017-07-05 00:54:55 -04001515 "Load current configuration settings for the keybinding options."
1516 # Set current keys type radiobutton.
terryjreedy938e7382017-06-26 20:48:39 -04001517 self.are_keys_builtin.set(idleConf.GetOption(
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001518 'main', 'Keys', 'default', type='bool', default=1))
terryjreedye5bb1122017-07-05 00:54:55 -04001519 # Set current keys.
terryjreedy938e7382017-06-26 20:48:39 -04001520 current_option = idleConf.CurrentKeys()
terryjreedye5bb1122017-07-05 00:54:55 -04001521 # Load available keyset option menus.
1522 if self.are_keys_builtin.get(): # Default theme selected.
terryjreedy938e7382017-06-26 20:48:39 -04001523 item_list = idleConf.GetSectionList('default', 'keys')
1524 item_list.sort()
1525 self.opt_menu_keys_builtin.SetMenu(item_list, current_option)
1526 item_list = idleConf.GetSectionList('user', 'keys')
1527 item_list.sort()
1528 if not item_list:
1529 self.radio_keys_custom.config(state=DISABLED)
1530 self.custom_keys.set('- no custom keys -')
Steven M. Gava41a85322001-10-29 08:05:34 +00001531 else:
terryjreedy938e7382017-06-26 20:48:39 -04001532 self.opt_menu_keys_custom.SetMenu(item_list, item_list[0])
terryjreedye5bb1122017-07-05 00:54:55 -04001533 else: # User key set selected.
terryjreedy938e7382017-06-26 20:48:39 -04001534 item_list = idleConf.GetSectionList('user', 'keys')
1535 item_list.sort()
1536 self.opt_menu_keys_custom.SetMenu(item_list, current_option)
1537 item_list = idleConf.GetSectionList('default', 'keys')
1538 item_list.sort()
1539 self.opt_menu_keys_builtin.SetMenu(item_list, idleConf.default_keys())
1540 self.set_keys_type()
terryjreedye5bb1122017-07-05 00:54:55 -04001541 # Load keyset element list.
terryjreedy938e7382017-06-26 20:48:39 -04001542 keyset_name = idleConf.CurrentKeys()
1543 self.load_keys_list(keyset_name)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001544
terryjreedy938e7382017-06-26 20:48:39 -04001545 def load_general_cfg(self):
terryjreedye5bb1122017-07-05 00:54:55 -04001546 "Load current configuration settings for the general options."
1547 # Set startup state.
terryjreedy938e7382017-06-26 20:48:39 -04001548 self.startup_edit.set(idleConf.GetOption(
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001549 'main', 'General', 'editor-on-startup', default=1, type='bool'))
terryjreedye5bb1122017-07-05 00:54:55 -04001550 # Set autosave state.
terryjreedy938e7382017-06-26 20:48:39 -04001551 self.autosave.set(idleConf.GetOption(
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001552 'main', 'General', 'autosave', default=0, type='bool'))
terryjreedye5bb1122017-07-05 00:54:55 -04001553 # Set initial window size.
terryjreedy938e7382017-06-26 20:48:39 -04001554 self.win_width.set(idleConf.GetOption(
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001555 'main', 'EditorWindow', 'width', type='int'))
terryjreedy938e7382017-06-26 20:48:39 -04001556 self.win_height.set(idleConf.GetOption(
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001557 'main', 'EditorWindow', 'height', type='int'))
terryjreedye5bb1122017-07-05 00:54:55 -04001558 # Set additional help sources.
terryjreedy938e7382017-06-26 20:48:39 -04001559 self.user_helplist = idleConf.GetAllExtraHelpSourcesList()
1560 for help_item in self.user_helplist:
1561 self.list_help.insert(END, help_item[0])
1562 self.set_helplist_button_states()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001563
terryjreedy938e7382017-06-26 20:48:39 -04001564 def load_configs(self):
terryjreedye5bb1122017-07-05 00:54:55 -04001565 """Load configuration for each page.
1566
1567 Load configuration from default and user config files and populate
Steven M. Gava429a86af2001-10-23 10:42:12 +00001568 the widgets on the config dialog pages.
terryjreedy9a09c662017-07-13 23:53:30 -04001569
1570 Methods:
1571 load_font_cfg
1572 load_tab_cfg
1573 load_theme_cfg
1574 load_key_cfg
1575 load_general_cfg
Steven M. Gava429a86af2001-10-23 10:42:12 +00001576 """
terryjreedy938e7382017-06-26 20:48:39 -04001577 self.load_font_cfg()
1578 self.load_tab_cfg()
terryjreedy938e7382017-06-26 20:48:39 -04001579 self.load_theme_cfg()
terryjreedy938e7382017-06-26 20:48:39 -04001580 self.load_key_cfg()
terryjreedy938e7382017-06-26 20:48:39 -04001581 self.load_general_cfg()
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001582 # note: extension page handled separately
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001583
terryjreedy938e7382017-06-26 20:48:39 -04001584 def save_new_key_set(self, keyset_name, keyset):
terryjreedye5bb1122017-07-05 00:54:55 -04001585 """Save a newly created core key set.
1586
terryjreedy938e7382017-06-26 20:48:39 -04001587 keyset_name - string, the name of the new key set
1588 keyset - dictionary containing the new key set
Steven M. Gava052937f2002-02-11 02:20:53 +00001589 """
terryjreedy938e7382017-06-26 20:48:39 -04001590 if not idleConf.userCfg['keys'].has_section(keyset_name):
1591 idleConf.userCfg['keys'].add_section(keyset_name)
1592 for event in keyset:
1593 value = keyset[event]
1594 idleConf.userCfg['keys'].SetOption(keyset_name, event, value)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001595
terryjreedy938e7382017-06-26 20:48:39 -04001596 def save_new_theme(self, theme_name, theme):
terryjreedy9a09c662017-07-13 23:53:30 -04001597 """Save a newly created theme to idleConf.
terryjreedye5bb1122017-07-05 00:54:55 -04001598
terryjreedy938e7382017-06-26 20:48:39 -04001599 theme_name - string, the name of the new theme
Steven M. Gava052937f2002-02-11 02:20:53 +00001600 theme - dictionary containing the new theme
1601 """
terryjreedy938e7382017-06-26 20:48:39 -04001602 if not idleConf.userCfg['highlight'].has_section(theme_name):
1603 idleConf.userCfg['highlight'].add_section(theme_name)
Kurt B. Kaisere0712772007-08-23 05:25:55 +00001604 for element in theme:
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001605 value = theme[element]
terryjreedy938e7382017-06-26 20:48:39 -04001606 idleConf.userCfg['highlight'].SetOption(theme_name, element, value)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001607
terryjreedy938e7382017-06-26 20:48:39 -04001608 def deactivate_current_config(self):
terryjreedy9a09c662017-07-13 23:53:30 -04001609 """Remove current key bindings.
1610
1611 Iterate over window instances defined in parent and remove
1612 the keybindings.
1613 """
terryjreedye5bb1122017-07-05 00:54:55 -04001614 # Before a config is saved, some cleanup of current
1615 # config must be done - remove the previous keybindings.
terryjreedy938e7382017-06-26 20:48:39 -04001616 win_instances = self.parent.instance_dict.keys()
1617 for instance in win_instances:
Kurt B. Kaiserb1754452005-11-18 22:05:48 +00001618 instance.RemoveKeybindings()
1619
terryjreedy938e7382017-06-26 20:48:39 -04001620 def activate_config_changes(self):
terryjreedy9a09c662017-07-13 23:53:30 -04001621 """Apply configuration changes to current windows.
1622
1623 Dynamically update the current parent window instances
1624 with some of the configuration changes.
1625 """
terryjreedy938e7382017-06-26 20:48:39 -04001626 win_instances = self.parent.instance_dict.keys()
1627 for instance in win_instances:
Steven M. Gavab77d3432002-03-02 07:16:21 +00001628 instance.ResetColorizer()
Steven M. Gavab1585412002-03-12 00:21:56 +00001629 instance.ResetFont()
Kurt B. Kaiseracdef852005-01-31 03:34:26 +00001630 instance.set_notabs_indentwidth()
Kurt B. Kaiserb1754452005-11-18 22:05:48 +00001631 instance.ApplyKeybindings()
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +00001632 instance.reset_help_menu_entries()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001633
terryjreedy938e7382017-06-26 20:48:39 -04001634 def cancel(self):
terryjreedy9a09c662017-07-13 23:53:30 -04001635 """Dismiss config dialog.
1636
1637 Methods:
1638 destroy: inherited
1639 """
Steven M. Gava5f28e8f2002-01-21 06:38:21 +00001640 self.destroy()
1641
terryjreedy938e7382017-06-26 20:48:39 -04001642 def ok(self):
terryjreedy9a09c662017-07-13 23:53:30 -04001643 """Apply config changes, then dismiss dialog.
1644
1645 Methods:
1646 apply
1647 destroy: inherited
1648 """
terryjreedy938e7382017-06-26 20:48:39 -04001649 self.apply()
Steven M. Gava5f28e8f2002-01-21 06:38:21 +00001650 self.destroy()
1651
terryjreedy938e7382017-06-26 20:48:39 -04001652 def apply(self):
terryjreedy9a09c662017-07-13 23:53:30 -04001653 """Apply config changes and leave dialog open.
1654
1655 Methods:
1656 deactivate_current_config
1657 save_all_changed_extensions
1658 activate_config_changes
1659 """
terryjreedy938e7382017-06-26 20:48:39 -04001660 self.deactivate_current_config()
terryjreedyedc03422017-07-07 16:37:39 -04001661 changes.save_all()
1662 self.save_all_changed_extensions()
terryjreedy938e7382017-06-26 20:48:39 -04001663 self.activate_config_changes()
Steven M. Gava5f28e8f2002-01-21 06:38:21 +00001664
terryjreedy938e7382017-06-26 20:48:39 -04001665 def help(self):
terryjreedy9a09c662017-07-13 23:53:30 -04001666 """Create textview for config dialog help.
1667
1668 Attrbutes accessed:
1669 tab_pages
1670
1671 Methods:
1672 view_text: Method from textview module.
1673 """
terryjreedy938e7382017-06-26 20:48:39 -04001674 page = self.tab_pages._current_page
Terry Jan Reedyd0cadba2015-10-11 22:07:31 -04001675 view_text(self, title='Help for IDLE preferences',
1676 text=help_common+help_pages.get(page, ''))
1677
terryjreedy938e7382017-06-26 20:48:39 -04001678 def create_page_extensions(self):
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001679 """Part of the config dialog used for configuring IDLE extensions.
1680
1681 This code is generic - it works for any and all IDLE extensions.
1682
1683 IDLE extensions save their configuration options using idleConf.
Terry Jan Reedyb2f87602015-10-13 22:09:06 -04001684 This code reads the current configuration using idleConf, supplies a
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001685 GUI interface to change the configuration values, and saves the
1686 changes using idleConf.
1687
1688 Not all changes take effect immediately - some may require restarting IDLE.
1689 This depends on each extension's implementation.
1690
1691 All values are treated as text, and it is up to the user to supply
1692 reasonable values. The only exception to this are the 'enable*' options,
Serhiy Storchaka6a7b3a72016-04-17 08:32:47 +03001693 which are boolean, and can be toggled with a True/False button.
terryjreedy9a09c662017-07-13 23:53:30 -04001694
1695 Methods:
1696 load_extentions:
1697 extension_selected: Handle selection from list.
1698 create_extension_frame: Hold widgets for one extension.
1699 set_extension_value: Set in userCfg['extensions'].
1700 save_all_changed_extensions: Call extension page Save().
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001701 """
1702 parent = self.parent
terryjreedy938e7382017-06-26 20:48:39 -04001703 frame = self.tab_pages.pages['Extensions'].frame
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001704 self.ext_defaultCfg = idleConf.defaultCfg['extensions']
1705 self.ext_userCfg = idleConf.userCfg['extensions']
1706 self.is_int = self.register(is_int)
1707 self.load_extensions()
terryjreedye5bb1122017-07-05 00:54:55 -04001708 # Create widgets - a listbox shows all available extensions, with the
1709 # controls for the extension selected in the listbox to the right.
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001710 self.extension_names = StringVar(self)
1711 frame.rowconfigure(0, weight=1)
1712 frame.columnconfigure(2, weight=1)
1713 self.extension_list = Listbox(frame, listvariable=self.extension_names,
1714 selectmode='browse')
1715 self.extension_list.bind('<<ListboxSelect>>', self.extension_selected)
1716 scroll = Scrollbar(frame, command=self.extension_list.yview)
1717 self.extension_list.yscrollcommand=scroll.set
1718 self.details_frame = LabelFrame(frame, width=250, height=250)
1719 self.extension_list.grid(column=0, row=0, sticky='nws')
1720 scroll.grid(column=1, row=0, sticky='ns')
1721 self.details_frame.grid(column=2, row=0, sticky='nsew', padx=[10, 0])
1722 frame.configure(padx=10, pady=10)
1723 self.config_frame = {}
1724 self.current_extension = None
1725
1726 self.outerframe = self # TEMPORARY
1727 self.tabbed_page_set = self.extension_list # TEMPORARY
1728
terryjreedye5bb1122017-07-05 00:54:55 -04001729 # Create the frame holding controls for each extension.
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001730 ext_names = ''
1731 for ext_name in sorted(self.extensions):
1732 self.create_extension_frame(ext_name)
1733 ext_names = ext_names + '{' + ext_name + '} '
1734 self.extension_names.set(ext_names)
1735 self.extension_list.selection_set(0)
1736 self.extension_selected(None)
1737
1738 def load_extensions(self):
1739 "Fill self.extensions with data from the default and user configs."
1740 self.extensions = {}
1741 for ext_name in idleConf.GetExtensions(active_only=False):
1742 self.extensions[ext_name] = []
1743
1744 for ext_name in self.extensions:
1745 opt_list = sorted(self.ext_defaultCfg.GetOptionList(ext_name))
1746
terryjreedye5bb1122017-07-05 00:54:55 -04001747 # Bring 'enable' options to the beginning of the list.
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001748 enables = [opt_name for opt_name in opt_list
1749 if opt_name.startswith('enable')]
1750 for opt_name in enables:
1751 opt_list.remove(opt_name)
1752 opt_list = enables + opt_list
1753
1754 for opt_name in opt_list:
1755 def_str = self.ext_defaultCfg.Get(
1756 ext_name, opt_name, raw=True)
1757 try:
1758 def_obj = {'True':True, 'False':False}[def_str]
1759 opt_type = 'bool'
1760 except KeyError:
1761 try:
1762 def_obj = int(def_str)
1763 opt_type = 'int'
1764 except ValueError:
1765 def_obj = def_str
1766 opt_type = None
1767 try:
1768 value = self.ext_userCfg.Get(
1769 ext_name, opt_name, type=opt_type, raw=True,
1770 default=def_obj)
terryjreedye5bb1122017-07-05 00:54:55 -04001771 except ValueError: # Need this until .Get fixed.
1772 value = def_obj # Bad values overwritten by entry.
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001773 var = StringVar(self)
1774 var.set(str(value))
1775
1776 self.extensions[ext_name].append({'name': opt_name,
1777 'type': opt_type,
1778 'default': def_str,
1779 'value': value,
1780 'var': var,
1781 })
1782
1783 def extension_selected(self, event):
terryjreedye5bb1122017-07-05 00:54:55 -04001784 "Handle selection of an extension from the list."
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001785 newsel = self.extension_list.curselection()
1786 if newsel:
1787 newsel = self.extension_list.get(newsel)
1788 if newsel is None or newsel != self.current_extension:
1789 if self.current_extension:
1790 self.details_frame.config(text='')
1791 self.config_frame[self.current_extension].grid_forget()
1792 self.current_extension = None
1793 if newsel:
1794 self.details_frame.config(text=newsel)
1795 self.config_frame[newsel].grid(column=0, row=0, sticky='nsew')
1796 self.current_extension = newsel
1797
1798 def create_extension_frame(self, ext_name):
1799 """Create a frame holding the widgets to configure one extension"""
1800 f = VerticalScrolledFrame(self.details_frame, height=250, width=250)
1801 self.config_frame[ext_name] = f
1802 entry_area = f.interior
terryjreedye5bb1122017-07-05 00:54:55 -04001803 # Create an entry for each configuration option.
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001804 for row, opt in enumerate(self.extensions[ext_name]):
terryjreedye5bb1122017-07-05 00:54:55 -04001805 # Create a row with a label and entry/checkbutton.
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001806 label = Label(entry_area, text=opt['name'])
1807 label.grid(row=row, column=0, sticky=NW)
1808 var = opt['var']
1809 if opt['type'] == 'bool':
1810 Checkbutton(entry_area, textvariable=var, variable=var,
1811 onvalue='True', offvalue='False',
1812 indicatoron=FALSE, selectcolor='', width=8
1813 ).grid(row=row, column=1, sticky=W, padx=7)
1814 elif opt['type'] == 'int':
1815 Entry(entry_area, textvariable=var, validate='key',
1816 validatecommand=(self.is_int, '%P')
1817 ).grid(row=row, column=1, sticky=NSEW, padx=7)
1818
1819 else:
1820 Entry(entry_area, textvariable=var
1821 ).grid(row=row, column=1, sticky=NSEW, padx=7)
1822 return
1823
1824 def set_extension_value(self, section, opt):
terryjreedye5bb1122017-07-05 00:54:55 -04001825 """Return True if the configuration was added or changed.
1826
1827 If the value is the same as the default, then remove it
1828 from user config file.
1829 """
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001830 name = opt['name']
1831 default = opt['default']
1832 value = opt['var'].get().strip() or default
1833 opt['var'].set(value)
1834 # if self.defaultCfg.has_section(section):
terryjreedye5bb1122017-07-05 00:54:55 -04001835 # Currently, always true; if not, indent to return.
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001836 if (value == default):
1837 return self.ext_userCfg.RemoveOption(section, name)
terryjreedye5bb1122017-07-05 00:54:55 -04001838 # Set the option.
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001839 return self.ext_userCfg.SetOption(section, name, value)
1840
1841 def save_all_changed_extensions(self):
terryjreedy9a09c662017-07-13 23:53:30 -04001842 """Save configuration changes to the user config file.
1843
1844 Attributes accessed:
1845 extensions
1846
1847 Methods:
1848 set_extension_value
1849 """
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001850 has_changes = False
1851 for ext_name in self.extensions:
1852 options = self.extensions[ext_name]
1853 for opt in options:
1854 if self.set_extension_value(ext_name, opt):
1855 has_changes = True
1856 if has_changes:
1857 self.ext_userCfg.Save()
1858
1859
Terry Jan Reedyd0cadba2015-10-11 22:07:31 -04001860help_common = '''\
1861When you click either the Apply or Ok buttons, settings in this
1862dialog that are different from IDLE's default are saved in
1863a .idlerc directory in your home directory. Except as noted,
Terry Jan Reedyd0c0f002015-11-12 15:02:57 -05001864these changes apply to all versions of IDLE installed on this
Terry Jan Reedyd0cadba2015-10-11 22:07:31 -04001865machine. Some do not take affect until IDLE is restarted.
1866[Cancel] only cancels changes made since the last save.
1867'''
1868help_pages = {
Terry Jan Reedy9bdb1ed2016-07-10 13:46:34 -04001869 'Highlighting': '''
Terry Jan Reedyd0cadba2015-10-11 22:07:31 -04001870Highlighting:
Terry Jan Reedyd0c0f002015-11-12 15:02:57 -05001871The IDLE Dark color theme is new in October 2015. It can only
Terry Jan Reedyd0cadba2015-10-11 22:07:31 -04001872be used with older IDLE releases if it is saved as a custom
1873theme, with a different name.
Terry Jan Reedy9bdb1ed2016-07-10 13:46:34 -04001874''',
1875 'Keys': '''
1876Keys:
1877The IDLE Modern Unix key set is new in June 2016. It can only
1878be used with older IDLE releases if it is saved as a custom
1879key set, with a different name.
1880''',
terryjreedyaf683822017-06-27 23:02:19 -04001881 'Extensions': '''
1882Extensions:
1883
1884Autocomplete: Popupwait is milleseconds to wait after key char, without
1885cursor movement, before popping up completion box. Key char is '.' after
1886identifier or a '/' (or '\\' on Windows) within a string.
1887
1888FormatParagraph: Max-width is max chars in lines after re-formatting.
1889Use with paragraphs in both strings and comment blocks.
1890
1891ParenMatch: Style indicates what is highlighted when closer is entered:
1892'opener' - opener '({[' corresponding to closer; 'parens' - both chars;
1893'expression' (default) - also everything in between. Flash-delay is how
1894long to highlight if cursor is not moved (0 means forever).
1895'''
Terry Jan Reedyd0cadba2015-10-11 22:07:31 -04001896}
1897
Steven M. Gavac11ccf32001-09-24 09:43:17 +00001898
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001899def is_int(s):
1900 "Return 's is blank or represents an int'"
1901 if not s:
1902 return True
1903 try:
1904 int(s)
1905 return True
1906 except ValueError:
1907 return False
1908
1909
Terry Jan Reedya9421fb2014-10-22 20:15:18 -04001910class VerticalScrolledFrame(Frame):
1911 """A pure Tkinter vertically scrollable frame.
1912
1913 * Use the 'interior' attribute to place widgets inside the scrollable frame
1914 * Construct and pack/place/grid normally
1915 * This frame only allows vertical scrolling
1916 """
1917 def __init__(self, parent, *args, **kw):
1918 Frame.__init__(self, parent, *args, **kw)
1919
terryjreedye5bb1122017-07-05 00:54:55 -04001920 # Create a canvas object and a vertical scrollbar for scrolling it.
Terry Jan Reedya9421fb2014-10-22 20:15:18 -04001921 vscrollbar = Scrollbar(self, orient=VERTICAL)
1922 vscrollbar.pack(fill=Y, side=RIGHT, expand=FALSE)
1923 canvas = Canvas(self, bd=0, highlightthickness=0,
Terry Jan Reedyd0812292015-10-22 03:27:31 -04001924 yscrollcommand=vscrollbar.set, width=240)
Terry Jan Reedya9421fb2014-10-22 20:15:18 -04001925 canvas.pack(side=LEFT, fill=BOTH, expand=TRUE)
1926 vscrollbar.config(command=canvas.yview)
1927
terryjreedye5bb1122017-07-05 00:54:55 -04001928 # Reset the view.
Terry Jan Reedya9421fb2014-10-22 20:15:18 -04001929 canvas.xview_moveto(0)
1930 canvas.yview_moveto(0)
1931
terryjreedye5bb1122017-07-05 00:54:55 -04001932 # Create a frame inside the canvas which will be scrolled with it.
Terry Jan Reedya9421fb2014-10-22 20:15:18 -04001933 self.interior = interior = Frame(canvas)
1934 interior_id = canvas.create_window(0, 0, window=interior, anchor=NW)
1935
terryjreedye5bb1122017-07-05 00:54:55 -04001936 # Track changes to the canvas and frame width and sync them,
1937 # also updating the scrollbar.
Terry Jan Reedya9421fb2014-10-22 20:15:18 -04001938 def _configure_interior(event):
terryjreedye5bb1122017-07-05 00:54:55 -04001939 # Update the scrollbars to match the size of the inner frame.
Terry Jan Reedya9421fb2014-10-22 20:15:18 -04001940 size = (interior.winfo_reqwidth(), interior.winfo_reqheight())
1941 canvas.config(scrollregion="0 0 %s %s" % size)
Terry Jan Reedya9421fb2014-10-22 20:15:18 -04001942 interior.bind('<Configure>', _configure_interior)
1943
1944 def _configure_canvas(event):
1945 if interior.winfo_reqwidth() != canvas.winfo_width():
terryjreedye5bb1122017-07-05 00:54:55 -04001946 # Update the inner frame's width to fill the canvas.
Terry Jan Reedya9421fb2014-10-22 20:15:18 -04001947 canvas.itemconfigure(interior_id, width=canvas.winfo_width())
1948 canvas.bind('<Configure>', _configure_canvas)
1949
1950 return
1951
Terry Jan Reedya9421fb2014-10-22 20:15:18 -04001952
Steven M. Gava44d3d1a2001-07-31 06:59:02 +00001953if __name__ == '__main__':
Terry Jan Reedycfa89502014-07-14 23:07:32 -04001954 import unittest
1955 unittest.main('idlelib.idle_test.test_configdialog',
1956 verbosity=2, exit=False)
Terry Jan Reedy2e8234a2014-05-29 01:46:26 -04001957 from idlelib.idle_test.htest import run
Terry Jan Reedy47304c02015-10-20 02:15:28 -04001958 run(ConfigDialog)