blob: daaa34459e2fa648a625e878d73426dbb2b8713b [file] [log] [blame]
Kurt B. Kaisere7a161e2003-01-10 20:13:57 +00001"""IDLE Configuration Dialog: support user customization of IDLE by GUI
2
3Customize font faces, sizes, and colorization attributes. Set indentation
4defaults. Customize keybindings. Colorization and keybindings can be
5saved as user defined sets. Select startup options including shell/editor
6and default window size. Define additional help sources.
7
8Note that tab width in IDLE is currently fixed at eight due to Tk issues.
Kurt B. Kaiseracdef852005-01-31 03:34:26 +00009Refer to comments in EditorWindow autoindent code for details.
Kurt B. Kaisere7a161e2003-01-10 20:13:57 +000010
Steven M. Gava44d3d1a2001-07-31 06:59:02 +000011"""
terryjreedy938e7382017-06-26 20:48:39 -040012from tkinter import (Toplevel, Frame, LabelFrame, Listbox, Label, Button,
13 Entry, Text, Scale, Radiobutton, Checkbutton, Canvas,
14 StringVar, BooleanVar, IntVar, TRUE, FALSE,
15 TOP, BOTTOM, RIGHT, LEFT, SOLID, GROOVE, NORMAL, DISABLED,
16 NONE, BOTH, X, Y, W, E, EW, NS, NSEW, NW,
terryjreedy7ab33422017-07-09 19:26:32 -040017 HORIZONTAL, VERTICAL, ANCHOR, ACTIVE, END)
Terry Jan Reedy01e35752016-06-10 18:19:21 -040018from tkinter.ttk import Scrollbar
Georg Brandl14fc4272008-05-17 18:39:55 +000019import tkinter.colorchooser as tkColorChooser
20import tkinter.font as tkFont
Terry Jan Reedybfbaa6b2016-08-31 00:50:55 -040021import tkinter.messagebox as tkMessageBox
Steven M. Gava44d3d1a2001-07-31 06:59:02 +000022
terryjreedyedc03422017-07-07 16:37:39 -040023from idlelib.config import idleConf, ConfigChanges
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -040024from idlelib.config_key import GetKeysDialog
Terry Jan Reedybfbaa6b2016-08-31 00:50:55 -040025from idlelib.dynoption import DynOptionMenu
26from idlelib import macosx
Terry Jan Reedy8b22c0a2016-07-08 00:22:50 -040027from idlelib.query import SectionName, HelpSource
Terry Jan Reedya9421fb2014-10-22 20:15:18 -040028from idlelib.tabbedpages import TabbedPageSet
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -040029from idlelib.textview import view_text
Terry Jan Reedyd0cadba2015-10-11 22:07:31 -040030
terryjreedyedc03422017-07-07 16:37:39 -040031changes = ConfigChanges()
32
terryjreedye5bb1122017-07-05 00:54:55 -040033
Steven M. Gava44d3d1a2001-07-31 06:59:02 +000034class ConfigDialog(Toplevel):
terryjreedye5bb1122017-07-05 00:54:55 -040035 """Config dialog for IDLE.
36 """
Kurt B. Kaiseracdef852005-01-31 03:34:26 +000037
Terry Jan Reedycd567362014-10-17 01:31:35 -040038 def __init__(self, parent, title='', _htest=False, _utest=False):
terryjreedye5bb1122017-07-05 00:54:55 -040039 """Show the tabbed dialog for user configuration.
40
terryjreedy9a09c662017-07-13 23:53:30 -040041 Args:
42 parent - parent of this dialog
43 title - string which is the title of this popup dialog
44 _htest - bool, change box location when running htest
45 _utest - bool, don't wait_window when running unittest
46
47 Note: Focus set on font page fontlist.
48
49 Methods:
50 create_widgets
51 cancel: Bound to DELETE_WINDOW protocol.
Terry Jan Reedy2e8234a2014-05-29 01:46:26 -040052 """
Steven M. Gavad721c482001-07-31 10:46:53 +000053 Toplevel.__init__(self, parent)
Terry Jan Reedy22405332014-07-30 19:24:32 -040054 self.parent = parent
Terry Jan Reedy4036d872014-08-03 23:02:58 -040055 if _htest:
56 parent.instance_dict = {}
terryjreedy42abf7f2017-07-13 22:24:55 -040057 if not _utest:
58 self.withdraw()
Guido van Rossum8ce8a782007-11-01 19:42:39 +000059
Steven M. Gavad721c482001-07-31 10:46:53 +000060 self.configure(borderwidth=5)
Terry Jan Reedycd567362014-10-17 01:31:35 -040061 self.title(title or 'IDLE Preferences')
terryjreedy938e7382017-06-26 20:48:39 -040062 x = parent.winfo_rootx() + 20
63 y = parent.winfo_rooty() + (30 if not _htest else 150)
64 self.geometry(f'+{x}+{y}')
terryjreedye5bb1122017-07-05 00:54:55 -040065 # Each theme element key is its display name.
66 # The first value of the tuple is the sample area tag name.
67 # The second value is the display name list sort index.
terryjreedy938e7382017-06-26 20:48:39 -040068 self.create_widgets()
Terry Jan Reedy4036d872014-08-03 23:02:58 -040069 self.resizable(height=FALSE, width=FALSE)
Steven M. Gavad721c482001-07-31 10:46:53 +000070 self.transient(parent)
terryjreedy938e7382017-06-26 20:48:39 -040071 self.protocol("WM_DELETE_WINDOW", self.cancel)
terryjreedy7ab33422017-07-09 19:26:32 -040072 self.fontlist.focus_set()
terryjreedye5bb1122017-07-05 00:54:55 -040073 # XXX Decide whether to keep or delete these key bindings.
74 # Key bindings for this dialog.
75 # self.bind('<Escape>', self.Cancel) #dismiss dialog, no save
76 # self.bind('<Alt-a>', self.Apply) #apply changes, save
77 # self.bind('<F1>', self.Help) #context help
terryjreedy938e7382017-06-26 20:48:39 -040078 self.load_configs()
terryjreedye5bb1122017-07-05 00:54:55 -040079 self.attach_var_callbacks() # Avoid callbacks during load_configs.
Guido van Rossum8ce8a782007-11-01 19:42:39 +000080
Terry Jan Reedycfa89502014-07-14 23:07:32 -040081 if not _utest:
terryjreedy42abf7f2017-07-13 22:24:55 -040082 self.grab_set()
Terry Jan Reedycfa89502014-07-14 23:07:32 -040083 self.wm_deiconify()
84 self.wait_window()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000085
terryjreedy938e7382017-06-26 20:48:39 -040086 def create_widgets(self):
terryjreedy9a09c662017-07-13 23:53:30 -040087 """Create and place widgets for tabbed dialog.
88
89 Widgets Bound to self:
90 tab_pages: TabbedPageSet
91
92 Methods:
93 create_page_font_tab
94 create_page_highlight
95 create_page_keys
96 create_page_general
97 create_page_extensions
98 create_action_buttons
99 load_configs: Load pages except for extensions.
100 attach_var_callbacks
101 remove_var_callbacks
102 activate_config_changes: Tell editors to reload.
103 """
terryjreedy938e7382017-06-26 20:48:39 -0400104 self.tab_pages = TabbedPageSet(self,
Terry Jan Reedy93f35422015-10-13 22:03:51 -0400105 page_names=['Fonts/Tabs', 'Highlighting', 'Keys', 'General',
106 'Extensions'])
terryjreedy938e7382017-06-26 20:48:39 -0400107 self.tab_pages.pack(side=TOP, expand=TRUE, fill=BOTH)
108 self.create_page_font_tab()
109 self.create_page_highlight()
110 self.create_page_keys()
111 self.create_page_general()
112 self.create_page_extensions()
Terry Jan Reedy92cb0a32014-10-08 20:29:13 -0400113 self.create_action_buttons().pack(side=BOTTOM)
Terry Jan Reedyd0cadba2015-10-11 22:07:31 -0400114
Terry Jan Reedy92cb0a32014-10-08 20:29:13 -0400115 def create_action_buttons(self):
terryjreedy9a09c662017-07-13 23:53:30 -0400116 """Return frame of action buttons for dialog.
117
118 Methods:
119 ok
120 apply
121 cancel
122 help
123
124 Widget Structure:
125 outer: Frame
126 buttons: Frame
127 (no assignment): Button (ok)
128 (no assignment): Button (apply)
129 (no assignment): Button (cancel)
130 (no assignment): Button (help)
131 (no assignment): Frame
132 """
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -0400133 if macosx.isAquaTk():
Terry Jan Reedye3416e62014-07-26 19:40:16 -0400134 # Changing the default padding on OSX results in unreadable
terryjreedye5bb1122017-07-05 00:54:55 -0400135 # text in the buttons.
terryjreedy938e7382017-06-26 20:48:39 -0400136 padding_args = {}
Ronald Oussoren9e350042009-02-12 16:02:11 +0000137 else:
terryjreedy938e7382017-06-26 20:48:39 -0400138 padding_args = {'padx':6, 'pady':3}
Terry Jan Reedya9421fb2014-10-22 20:15:18 -0400139 outer = Frame(self, pady=2)
140 buttons = Frame(outer, pady=2)
Terry Jan Reedyd0cadba2015-10-11 22:07:31 -0400141 for txt, cmd in (
terryjreedy938e7382017-06-26 20:48:39 -0400142 ('Ok', self.ok),
143 ('Apply', self.apply),
144 ('Cancel', self.cancel),
145 ('Help', self.help)):
Terry Jan Reedyd0cadba2015-10-11 22:07:31 -0400146 Button(buttons, text=txt, command=cmd, takefocus=FALSE,
terryjreedy938e7382017-06-26 20:48:39 -0400147 **padding_args).pack(side=LEFT, padx=5)
terryjreedye5bb1122017-07-05 00:54:55 -0400148 # Add space above buttons.
Terry Jan Reedya9421fb2014-10-22 20:15:18 -0400149 Frame(outer, height=2, borderwidth=0).pack(side=TOP)
150 buttons.pack(side=BOTTOM)
151 return outer
Terry Jan Reedyd0cadba2015-10-11 22:07:31 -0400152
Terry Jan Reedy1daeb252017-07-24 02:50:28 -0400153
terryjreedy938e7382017-06-26 20:48:39 -0400154 def create_page_font_tab(self):
terryjreedye5bb1122017-07-05 00:54:55 -0400155 """Return frame of widgets for Font/Tabs tab.
156
Terry Jan Reedy5aa3bf02017-07-23 14:18:27 -0400157 Fonts: Enable users to provisionally change font face, size, or
Terry Jan Reedy04864b42017-07-22 00:56:18 -0400158 boldness and to see the consequence of proposed choices. Each
159 action set 3 options in changes structuree and changes the
160 corresponding aspect of the font sample on this page and
161 highlight sample on highlight page.
162
Terry Jan Reedy0c4c6512017-07-26 21:41:26 -0400163 Funtion load_font_cfg initializes font vars and widgets from
Terry Jan Reedy1daeb252017-07-24 02:50:28 -0400164 idleConf entries and tk.
165
Terry Jan Reedy5aa3bf02017-07-23 14:18:27 -0400166 Fontlist: mouse button 1 click or up or down key invoke
Terry Jan Reedy1daeb252017-07-24 02:50:28 -0400167 on_fontlist_select(), which sets var font_name.
Terry Jan Reedy04864b42017-07-22 00:56:18 -0400168
Terry Jan Reedy5aa3bf02017-07-23 14:18:27 -0400169 Sizelist: clicking the menubutton opens the dropdown menu. A
Terry Jan Reedy1daeb252017-07-24 02:50:28 -0400170 mouse button 1 click or return key sets var font_size.
terryjreedy9a09c662017-07-13 23:53:30 -0400171
Terry Jan Reedy1daeb252017-07-24 02:50:28 -0400172 Bold_toggle: clicking the box toggles var font_bold.
terryjreedy9a09c662017-07-13 23:53:30 -0400173
Terry Jan Reedy1daeb252017-07-24 02:50:28 -0400174 Changing any of the font vars invokes var_changed_font, which
175 adds all 3 font options to changes and calls set_samples.
176 Set_samples applies a new font constructed from the font vars to
177 font_sample and to highlight_sample on the hightlight page.
Terry Jan Reedy5aa3bf02017-07-23 14:18:27 -0400178
179 Tabs: Enable users to change spaces entered for indent tabs.
180 Changing indent_scale value with the mouse sets Var space_num,
181 which invokes var_changed_space_num, which adds an entry to
Terry Jan Reedy1daeb252017-07-24 02:50:28 -0400182 changes. Load_tab_cfg initializes space_num to default.
terryjreedy9a09c662017-07-13 23:53:30 -0400183
184 Widget Structure: (*) widgets bound to self
Terry Jan Reedy5aa3bf02017-07-23 14:18:27 -0400185 frame (of tab_pages)
terryjreedy9a09c662017-07-13 23:53:30 -0400186 frame_font: LabelFrame
187 frame_font_name: Frame
188 font_name_title: Label
Terry Jan Reedy5aa3bf02017-07-23 14:18:27 -0400189 (*)fontlist: ListBox - font_name
terryjreedy9a09c662017-07-13 23:53:30 -0400190 scroll_font: Scrollbar
191 frame_font_param: Frame
192 font_size_title: Label
Terry Jan Reedy5aa3bf02017-07-23 14:18:27 -0400193 (*)sizelist: DynOptionMenu - font_size
Terry Jan Reedy04864b42017-07-22 00:56:18 -0400194 (*)bold_toggle: Checkbutton - font_bold
terryjreedy9a09c662017-07-13 23:53:30 -0400195 frame_font_sample: Frame
196 (*)font_sample: Label
197 frame_indent: LabelFrame
Terry Jan Reedy5aa3bf02017-07-23 14:18:27 -0400198 indent_title: Label
199 (*)indent_scale: Scale - space_num
terryjreedye5bb1122017-07-05 00:54:55 -0400200 """
Terry Jan Reedy22405332014-07-30 19:24:32 -0400201 parent = self.parent
Terry Jan Reedy04864b42017-07-22 00:56:18 -0400202 self.font_name = StringVar(parent)
terryjreedy938e7382017-06-26 20:48:39 -0400203 self.font_size = StringVar(parent)
204 self.font_bold = BooleanVar(parent)
terryjreedy938e7382017-06-26 20:48:39 -0400205 self.space_num = IntVar(parent)
Terry Jan Reedy22405332014-07-30 19:24:32 -0400206
Terry Jan Reedy5aa3bf02017-07-23 14:18:27 -0400207 # Create widgets:
terryjreedy7ab33422017-07-09 19:26:32 -0400208 # body and body section frames.
terryjreedy938e7382017-06-26 20:48:39 -0400209 frame = self.tab_pages.pages['Fonts/Tabs'].frame
terryjreedy938e7382017-06-26 20:48:39 -0400210 frame_font = LabelFrame(
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400211 frame, borderwidth=2, relief=GROOVE, text=' Base Editor Font ')
terryjreedy938e7382017-06-26 20:48:39 -0400212 frame_indent = LabelFrame(
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400213 frame, borderwidth=2, relief=GROOVE, text=' Indentation Width ')
Terry Jan Reedy5aa3bf02017-07-23 14:18:27 -0400214 # frame_font.
terryjreedy938e7382017-06-26 20:48:39 -0400215 frame_font_name = Frame(frame_font)
216 frame_font_param = Frame(frame_font)
217 font_name_title = Label(
218 frame_font_name, justify=LEFT, text='Font Face :')
Terry Jan Reedy5aa3bf02017-07-23 14:18:27 -0400219 self.fontlist = Listbox(frame_font_name, height=5,
220 takefocus=FALSE, exportselection=FALSE)
terryjreedy953e5272017-07-11 02:16:41 -0400221 self.fontlist.bind('<ButtonRelease-1>', self.on_fontlist_select)
222 self.fontlist.bind('<KeyRelease-Up>', self.on_fontlist_select)
223 self.fontlist.bind('<KeyRelease-Down>', self.on_fontlist_select)
terryjreedy938e7382017-06-26 20:48:39 -0400224 scroll_font = Scrollbar(frame_font_name)
terryjreedy7ab33422017-07-09 19:26:32 -0400225 scroll_font.config(command=self.fontlist.yview)
226 self.fontlist.config(yscrollcommand=scroll_font.set)
terryjreedy938e7382017-06-26 20:48:39 -0400227 font_size_title = Label(frame_font_param, text='Size :')
Terry Jan Reedy1daeb252017-07-24 02:50:28 -0400228 self.sizelist = DynOptionMenu(frame_font_param, self.font_size, None)
Terry Jan Reedy04864b42017-07-22 00:56:18 -0400229 self.bold_toggle = Checkbutton(
Terry Jan Reedy1daeb252017-07-24 02:50:28 -0400230 frame_font_param, variable=self.font_bold,
231 onvalue=1, offvalue=0, text='Bold')
terryjreedy938e7382017-06-26 20:48:39 -0400232 frame_font_sample = Frame(frame_font, relief=SOLID, borderwidth=1)
Terry Jan Reedy5aa3bf02017-07-23 14:18:27 -0400233 temp_font = tkFont.Font(parent, ('courier', 10, 'normal'))
terryjreedy938e7382017-06-26 20:48:39 -0400234 self.font_sample = Label(
Terry Jan Reedy5aa3bf02017-07-23 14:18:27 -0400235 frame_font_sample, justify=LEFT, font=temp_font,
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400236 text='AaBbCcDdEe\nFfGgHhIiJjK\n1234567890\n#:+=(){}[]')
Terry Jan Reedy5aa3bf02017-07-23 14:18:27 -0400237 # frame_indent.
238 indent_title = Label(
239 frame_indent, justify=LEFT,
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400240 text='Python Standard: 4 Spaces!')
Terry Jan Reedy5aa3bf02017-07-23 14:18:27 -0400241 self.indent_scale = Scale(
242 frame_indent, variable=self.space_num,
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400243 orient='horizontal', tickinterval=2, from_=2, to=16)
Terry Jan Reedy22405332014-07-30 19:24:32 -0400244
Terry Jan Reedy5aa3bf02017-07-23 14:18:27 -0400245 # Pack widgets:
246 # body.
terryjreedy938e7382017-06-26 20:48:39 -0400247 frame_font.pack(side=LEFT, padx=5, pady=5, expand=TRUE, fill=BOTH)
248 frame_indent.pack(side=LEFT, padx=5, pady=5, fill=Y)
Terry Jan Reedy5aa3bf02017-07-23 14:18:27 -0400249 # frame_font.
terryjreedy938e7382017-06-26 20:48:39 -0400250 frame_font_name.pack(side=TOP, padx=5, pady=5, fill=X)
251 frame_font_param.pack(side=TOP, padx=5, pady=5, fill=X)
252 font_name_title.pack(side=TOP, anchor=W)
terryjreedy7ab33422017-07-09 19:26:32 -0400253 self.fontlist.pack(side=LEFT, expand=TRUE, fill=X)
terryjreedy938e7382017-06-26 20:48:39 -0400254 scroll_font.pack(side=LEFT, fill=Y)
255 font_size_title.pack(side=LEFT, anchor=W)
Terry Jan Reedy5aa3bf02017-07-23 14:18:27 -0400256 self.sizelist.pack(side=LEFT, anchor=W)
Terry Jan Reedy04864b42017-07-22 00:56:18 -0400257 self.bold_toggle.pack(side=LEFT, anchor=W, padx=20)
terryjreedy938e7382017-06-26 20:48:39 -0400258 frame_font_sample.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH)
259 self.font_sample.pack(expand=TRUE, fill=BOTH)
Terry Jan Reedy5aa3bf02017-07-23 14:18:27 -0400260 # frame_indent.
261 frame_indent.pack(side=TOP, fill=X)
262 indent_title.pack(side=TOP, anchor=W, padx=5)
263 self.indent_scale.pack(side=TOP, padx=5, fill=X)
terryjreedy7ab33422017-07-09 19:26:32 -0400264
Steven M. Gava952d0a52001-08-03 04:43:44 +0000265 return frame
266
Terry Jan Reedy1daeb252017-07-24 02:50:28 -0400267 def load_font_cfg(self):
268 """Load current configuration settings for the font options.
269
270 Retrieve current font with idleConf.GetFont and font families
271 from tk. Setup fontlist and set font_name. Setup sizelist,
272 which sets font_size. Set font_bold. Setting font variables
273 calls set_samples (thrice).
274 """
275 configured_font = idleConf.GetFont(self, 'main', 'EditorWindow')
276 font_name = configured_font[0].lower()
277 font_size = configured_font[1]
278 font_bold = configured_font[2]=='bold'
279
280 # Set editor font selection list and font_name.
281 fonts = list(tkFont.families(self))
282 fonts.sort()
283 for font in fonts:
284 self.fontlist.insert(END, font)
285 self.font_name.set(font_name)
286 lc_fonts = [s.lower() for s in fonts]
287 try:
288 current_font_index = lc_fonts.index(font_name)
289 self.fontlist.see(current_font_index)
290 self.fontlist.select_set(current_font_index)
291 self.fontlist.select_anchor(current_font_index)
292 self.fontlist.activate(current_font_index)
293 except ValueError:
294 pass
295 # Set font size dropdown.
296 self.sizelist.SetMenu(('7', '8', '9', '10', '11', '12', '13', '14',
297 '16', '18', '20', '22', '25', '29', '34', '40'),
298 font_size)
299 # Set font weight.
300 self.font_bold.set(font_bold)
301
302 def on_fontlist_select(self, event):
303 """Handle selecting a font from the list.
304
305 Event can result from either mouse click or Up or Down key.
306 Set font_name and example displays to selection.
307 """
308 font = self.fontlist.get(
309 ACTIVE if event.type.name == 'KeyRelease' else ANCHOR)
310 self.font_name.set(font.lower())
311
312 def var_changed_font(self, *params):
313 """Store changes to font attributes.
314
315 When one font attribute changes, save them all, as they are
316 not independent from each other. In particular, when we are
317 overriding the default font, we need to write out everything.
318 """
319 value = self.font_name.get()
320 changes.add_option('main', 'EditorWindow', 'font', value)
321 value = self.font_size.get()
322 changes.add_option('main', 'EditorWindow', 'font-size', value)
323 value = self.font_bold.get()
324 changes.add_option('main', 'EditorWindow', 'font-bold', value)
325 self.set_samples()
326
327 def set_samples(self, event=None):
328 """Update update both screen samples with the font settings.
329
330 Called on font initialization and change events.
331 Accesses font_name, font_size, and font_bold Variables.
332 Updates font_sample and hightlight page highlight_sample.
333 """
334 font_name = self.font_name.get()
335 font_weight = tkFont.BOLD if self.font_bold.get() else tkFont.NORMAL
336 new_font = (font_name, self.font_size.get(), font_weight)
337 self.font_sample['font'] = new_font
338 self.highlight_sample['font'] = new_font
339
340 def load_tab_cfg(self):
341 """Load current configuration settings for the tab options.
342
343 Attributes updated:
344 space_num: Set to value from idleConf.
345 """
346 # Set indent sizes.
347 space_num = idleConf.GetOption(
348 'main', 'Indent', 'num-spaces', default=4, type='int')
349 self.space_num.set(space_num)
350
351 def var_changed_space_num(self, *params):
352 "Store change to indentation size."
353 value = self.space_num.get()
354 changes.add_option('main', 'Indent', 'num-spaces', value)
355
356
terryjreedy938e7382017-06-26 20:48:39 -0400357 def create_page_highlight(self):
terryjreedye5bb1122017-07-05 00:54:55 -0400358 """Return frame of widgets for Highlighting tab.
359
terryjreedy9a09c662017-07-13 23:53:30 -0400360 Tk Variables:
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -0400361 color: Color of selected target.
terryjreedye5bb1122017-07-05 00:54:55 -0400362 builtin_theme: Menu variable for built-in theme.
363 custom_theme: Menu variable for custom theme.
364 fg_bg_toggle: Toggle for foreground/background color.
terryjreedy9a09c662017-07-13 23:53:30 -0400365 Note: this has no callback.
terryjreedye5bb1122017-07-05 00:54:55 -0400366 is_builtin_theme: Selector for built-in or custom theme.
367 highlight_target: Menu variable for the highlight tag target.
terryjreedy9a09c662017-07-13 23:53:30 -0400368
369 Instance Data Attributes:
370 theme_elements: Dictionary of tags for text highlighting.
371 The key is the display name and the value is a tuple of
372 (tag name, display sort order).
373
374 Methods [attachment]:
375 load_theme_cfg: Load current highlight colors.
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -0400376 get_color: Invoke colorchooser [button_set_color].
377 set_color_sample_binding: Call set_color_sample [fg_bg_toggle].
terryjreedy9a09c662017-07-13 23:53:30 -0400378 set_highlight_target: set fg_bg_toggle, set_color_sample().
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -0400379 set_color_sample: Set frame background to target.
380 on_new_color_set: Set new color and add option.
terryjreedy9a09c662017-07-13 23:53:30 -0400381 paint_theme_sample: Recolor sample.
382 get_new_theme_name: Get from popup.
383 create_new_theme: Combine theme with changes and save.
384 save_as_new_theme: Save [button_save_custom_theme].
385 set_theme_type: Command for [is_builtin_theme].
386 delete_custom_theme: Ativate default [button_delete_custom_theme].
387 save_new_theme: Save to userCfg['theme'] (is function).
388
389 Widget Structure: (*) widgets bound to self
390 frame
391 frame_custom: LabelFrame
Terry Jan Reedy04864b42017-07-22 00:56:18 -0400392 (*)highlight_sample: Text
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -0400393 (*)frame_color_set: Frame
394 button_set_color: Button
terryjreedy9a09c662017-07-13 23:53:30 -0400395 (*)opt_menu_highlight_target: DynOptionMenu - highlight_target
396 frame_fg_bg_toggle: Frame
397 (*)radio_fg: Radiobutton - fg_bg_toggle
398 (*)radio_bg: Radiobutton - fg_bg_toggle
399 button_save_custom_theme: Button
400 frame_theme: LabelFrame
401 theme_type_title: Label
402 (*)radio_theme_builtin: Radiobutton - is_builtin_theme
403 (*)radio_theme_custom: Radiobutton - is_builtin_theme
404 (*)opt_menu_theme_builtin: DynOptionMenu - builtin_theme
405 (*)opt_menu_theme_custom: DynOptionMenu - custom_theme
406 (*)button_delete_custom_theme: Button
407 (*)new_custom_theme: Label
terryjreedye5bb1122017-07-05 00:54:55 -0400408 """
terryjreedy9a09c662017-07-13 23:53:30 -0400409 self.theme_elements={
410 'Normal Text': ('normal', '00'),
411 'Python Keywords': ('keyword', '01'),
412 'Python Definitions': ('definition', '02'),
413 'Python Builtins': ('builtin', '03'),
414 'Python Comments': ('comment', '04'),
415 'Python Strings': ('string', '05'),
416 'Selected Text': ('hilite', '06'),
417 'Found Text': ('hit', '07'),
418 'Cursor': ('cursor', '08'),
419 'Editor Breakpoint': ('break', '09'),
420 'Shell Normal Text': ('console', '10'),
421 'Shell Error Text': ('error', '11'),
422 'Shell Stdout Text': ('stdout', '12'),
423 'Shell Stderr Text': ('stderr', '13'),
424 }
Terry Jan Reedy22405332014-07-30 19:24:32 -0400425 parent = self.parent
terryjreedy938e7382017-06-26 20:48:39 -0400426 self.builtin_theme = StringVar(parent)
427 self.custom_theme = StringVar(parent)
428 self.fg_bg_toggle = BooleanVar(parent)
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -0400429 self.color = StringVar(parent)
terryjreedy938e7382017-06-26 20:48:39 -0400430 self.is_builtin_theme = BooleanVar(parent)
431 self.highlight_target = StringVar(parent)
Terry Jan Reedy22405332014-07-30 19:24:32 -0400432
Steven M. Gava952d0a52001-08-03 04:43:44 +0000433 ##widget creation
434 #body frame
terryjreedy938e7382017-06-26 20:48:39 -0400435 frame = self.tab_pages.pages['Highlighting'].frame
Steven M. Gava952d0a52001-08-03 04:43:44 +0000436 #body section frames
terryjreedy938e7382017-06-26 20:48:39 -0400437 frame_custom = LabelFrame(frame, borderwidth=2, relief=GROOVE,
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400438 text=' Custom Highlighting ')
terryjreedy938e7382017-06-26 20:48:39 -0400439 frame_theme = LabelFrame(frame, borderwidth=2, relief=GROOVE,
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400440 text=' Highlighting Theme ')
terryjreedy938e7382017-06-26 20:48:39 -0400441 #frame_custom
Terry Jan Reedy04864b42017-07-22 00:56:18 -0400442 self.highlight_sample=Text(
terryjreedy938e7382017-06-26 20:48:39 -0400443 frame_custom, relief=SOLID, borderwidth=1,
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400444 font=('courier', 12, ''), cursor='hand2', width=21, height=11,
445 takefocus=FALSE, highlightthickness=0, wrap=NONE)
Terry Jan Reedy04864b42017-07-22 00:56:18 -0400446 text=self.highlight_sample
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400447 text.bind('<Double-Button-1>', lambda e: 'break')
448 text.bind('<B1-Motion>', lambda e: 'break')
terryjreedy938e7382017-06-26 20:48:39 -0400449 text_and_tags=(
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400450 ('#you can click here', 'comment'), ('\n', 'normal'),
451 ('#to choose items', 'comment'), ('\n', 'normal'),
452 ('def', 'keyword'), (' ', 'normal'),
453 ('func', 'definition'), ('(param):\n ', 'normal'),
454 ('"""string"""', 'string'), ('\n var0 = ', 'normal'),
455 ("'string'", 'string'), ('\n var1 = ', 'normal'),
456 ("'selected'", 'hilite'), ('\n var2 = ', 'normal'),
457 ("'found'", 'hit'), ('\n var3 = ', 'normal'),
458 ('list', 'builtin'), ('(', 'normal'),
Terry Jan Reedya8aa4d52015-10-02 22:12:17 -0400459 ('None', 'keyword'), (')\n', 'normal'),
460 (' breakpoint("line")', 'break'), ('\n\n', 'normal'),
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400461 (' error ', 'error'), (' ', 'normal'),
462 ('cursor |', 'cursor'), ('\n ', 'normal'),
463 ('shell', 'console'), (' ', 'normal'),
464 ('stdout', 'stdout'), (' ', 'normal'),
465 ('stderr', 'stderr'), ('\n', 'normal'))
terryjreedy938e7382017-06-26 20:48:39 -0400466 for texttag in text_and_tags:
467 text.insert(END, texttag[0], texttag[1])
468 for element in self.theme_elements:
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400469 def tem(event, elem=element):
terryjreedy938e7382017-06-26 20:48:39 -0400470 event.widget.winfo_toplevel().highlight_target.set(elem)
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400471 text.tag_bind(
terryjreedy938e7382017-06-26 20:48:39 -0400472 self.theme_elements[element][0], '<ButtonPress-1>', tem)
Terry Jan Reedy0c4c6512017-07-26 21:41:26 -0400473 text['state'] = DISABLED
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -0400474 self.frame_color_set = Frame(frame_custom, relief=SOLID, borderwidth=1)
terryjreedy938e7382017-06-26 20:48:39 -0400475 frame_fg_bg_toggle = Frame(frame_custom)
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -0400476 button_set_color = Button(
477 self.frame_color_set, text='Choose Color for :',
478 command=self.get_color, highlightthickness=0)
terryjreedy938e7382017-06-26 20:48:39 -0400479 self.opt_menu_highlight_target = DynOptionMenu(
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -0400480 self.frame_color_set, self.highlight_target, None,
terryjreedy938e7382017-06-26 20:48:39 -0400481 highlightthickness=0) #, command=self.set_highlight_targetBinding
482 self.radio_fg = Radiobutton(
483 frame_fg_bg_toggle, variable=self.fg_bg_toggle, value=1,
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -0400484 text='Foreground', command=self.set_color_sample_binding)
terryjreedy938e7382017-06-26 20:48:39 -0400485 self.radio_bg=Radiobutton(
486 frame_fg_bg_toggle, variable=self.fg_bg_toggle, value=0,
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -0400487 text='Background', command=self.set_color_sample_binding)
terryjreedy938e7382017-06-26 20:48:39 -0400488 self.fg_bg_toggle.set(1)
489 button_save_custom_theme = Button(
490 frame_custom, text='Save as New Custom Theme',
491 command=self.save_as_new_theme)
492 #frame_theme
493 theme_type_title = Label(frame_theme, text='Select : ')
494 self.radio_theme_builtin = Radiobutton(
495 frame_theme, variable=self.is_builtin_theme, value=1,
496 command=self.set_theme_type, text='a Built-in Theme')
497 self.radio_theme_custom = Radiobutton(
498 frame_theme, variable=self.is_builtin_theme, value=0,
499 command=self.set_theme_type, text='a Custom Theme')
500 self.opt_menu_theme_builtin = DynOptionMenu(
501 frame_theme, self.builtin_theme, None, command=None)
502 self.opt_menu_theme_custom=DynOptionMenu(
503 frame_theme, self.custom_theme, None, command=None)
504 self.button_delete_custom_theme=Button(
505 frame_theme, text='Delete Custom Theme',
506 command=self.delete_custom_theme)
507 self.new_custom_theme = Label(frame_theme, bd=2)
Terry Jan Reedy22405332014-07-30 19:24:32 -0400508
Steven M. Gava952d0a52001-08-03 04:43:44 +0000509 ##widget packing
510 #body
terryjreedy938e7382017-06-26 20:48:39 -0400511 frame_custom.pack(side=LEFT, padx=5, pady=5, expand=TRUE, fill=BOTH)
512 frame_theme.pack(side=LEFT, padx=5, pady=5, fill=Y)
513 #frame_custom
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -0400514 self.frame_color_set.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=X)
terryjreedy938e7382017-06-26 20:48:39 -0400515 frame_fg_bg_toggle.pack(side=TOP, padx=5, pady=0)
Terry Jan Reedy04864b42017-07-22 00:56:18 -0400516 self.highlight_sample.pack(
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400517 side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH)
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -0400518 button_set_color.pack(side=TOP, expand=TRUE, fill=X, padx=8, pady=4)
terryjreedy938e7382017-06-26 20:48:39 -0400519 self.opt_menu_highlight_target.pack(
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400520 side=TOP, expand=TRUE, fill=X, padx=8, pady=3)
terryjreedy938e7382017-06-26 20:48:39 -0400521 self.radio_fg.pack(side=LEFT, anchor=E)
522 self.radio_bg.pack(side=RIGHT, anchor=W)
523 button_save_custom_theme.pack(side=BOTTOM, fill=X, padx=5, pady=5)
524 #frame_theme
525 theme_type_title.pack(side=TOP, anchor=W, padx=5, pady=5)
526 self.radio_theme_builtin.pack(side=TOP, anchor=W, padx=5)
527 self.radio_theme_custom.pack(side=TOP, anchor=W, padx=5, pady=2)
528 self.opt_menu_theme_builtin.pack(side=TOP, fill=X, padx=5, pady=5)
529 self.opt_menu_theme_custom.pack(side=TOP, fill=X, anchor=W, padx=5, pady=5)
530 self.button_delete_custom_theme.pack(side=TOP, fill=X, padx=5, pady=5)
Terry Jan Reedyd0c0f002015-11-12 15:02:57 -0500531 self.new_custom_theme.pack(side=TOP, fill=X, pady=5)
Steven M. Gava952d0a52001-08-03 04:43:44 +0000532 return frame
533
terryjreedy938e7382017-06-26 20:48:39 -0400534 def create_page_keys(self):
terryjreedye5bb1122017-07-05 00:54:55 -0400535 """Return frame of widgets for Keys tab.
536
terryjreedy9a09c662017-07-13 23:53:30 -0400537 Tk Variables:
terryjreedye5bb1122017-07-05 00:54:55 -0400538 builtin_keys: Menu variable for built-in keybindings.
539 custom_keys: Menu variable for custom keybindings.
540 are_keys_builtin: Selector for built-in or custom keybindings.
541 keybinding: Action/key bindings.
terryjreedy9a09c662017-07-13 23:53:30 -0400542
543 Methods:
544 load_key_config: Set table.
545 load_keys_list: Reload active set.
546 keybinding_selected: Bound to list_bindings button release.
547 get_new_keys: Command for button_new_keys.
548 get_new_keys_name: Call popup.
549 create_new_key_set: Combine active keyset and changes.
550 set_keys_type: Command for are_keys_builtin.
551 delete_custom_keys: Command for button_delete_custom_keys.
552 save_as_new_key_set: Command for button_save_custom_keys.
553 save_new_key_set: Save to idleConf.userCfg['keys'] (is function).
554 deactivate_current_config: Remove keys bindings in editors.
555
556 Widget Structure: (*) widgets bound to self
557 frame
558 frame_custom: LabelFrame
559 frame_target: Frame
560 target_title: Label
561 scroll_target_y: Scrollbar
562 scroll_target_x: Scrollbar
563 (*)list_bindings: ListBox
564 (*)button_new_keys: Button
565 frame_key_sets: LabelFrame
566 frames[0]: Frame
567 (*)radio_keys_builtin: Radiobutton - are_keys_builtin
568 (*)radio_keys_custom: Radiobutton - are_keys_builtin
569 (*)opt_menu_keys_builtin: DynOptionMenu - builtin_keys
570 (*)opt_menu_keys_custom: DynOptionMenu - custom_keys
571 (*)new_custom_keys: Label
572 frames[1]: Frame
573 (*)button_delete_custom_keys: Button
574 button_save_custom_keys: Button
terryjreedye5bb1122017-07-05 00:54:55 -0400575 """
Terry Jan Reedy22405332014-07-30 19:24:32 -0400576 parent = self.parent
terryjreedy938e7382017-06-26 20:48:39 -0400577 self.builtin_keys = StringVar(parent)
578 self.custom_keys = StringVar(parent)
579 self.are_keys_builtin = BooleanVar(parent)
580 self.keybinding = StringVar(parent)
Terry Jan Reedy22405332014-07-30 19:24:32 -0400581
Steven M. Gava60fc7072001-08-04 13:58:22 +0000582 ##widget creation
583 #body frame
terryjreedy938e7382017-06-26 20:48:39 -0400584 frame = self.tab_pages.pages['Keys'].frame
Steven M. Gava60fc7072001-08-04 13:58:22 +0000585 #body section frames
terryjreedy938e7382017-06-26 20:48:39 -0400586 frame_custom = LabelFrame(
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400587 frame, borderwidth=2, relief=GROOVE,
588 text=' Custom Key Bindings ')
terryjreedy938e7382017-06-26 20:48:39 -0400589 frame_key_sets = LabelFrame(
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400590 frame, borderwidth=2, relief=GROOVE, text=' Key Set ')
terryjreedy938e7382017-06-26 20:48:39 -0400591 #frame_custom
592 frame_target = Frame(frame_custom)
593 target_title = Label(frame_target, text='Action - Key(s)')
594 scroll_target_y = Scrollbar(frame_target)
595 scroll_target_x = Scrollbar(frame_target, orient=HORIZONTAL)
596 self.list_bindings = Listbox(
597 frame_target, takefocus=FALSE, exportselection=FALSE)
598 self.list_bindings.bind('<ButtonRelease-1>', self.keybinding_selected)
599 scroll_target_y.config(command=self.list_bindings.yview)
600 scroll_target_x.config(command=self.list_bindings.xview)
601 self.list_bindings.config(yscrollcommand=scroll_target_y.set)
602 self.list_bindings.config(xscrollcommand=scroll_target_x.set)
603 self.button_new_keys = Button(
604 frame_custom, text='Get New Keys for Selection',
605 command=self.get_new_keys, state=DISABLED)
606 #frame_key_sets
607 frames = [Frame(frame_key_sets, padx=2, pady=2, borderwidth=0)
Christian Heimes9a371592007-12-28 14:08:13 +0000608 for i in range(2)]
terryjreedy938e7382017-06-26 20:48:39 -0400609 self.radio_keys_builtin = Radiobutton(
610 frames[0], variable=self.are_keys_builtin, value=1,
611 command=self.set_keys_type, text='Use a Built-in Key Set')
612 self.radio_keys_custom = Radiobutton(
613 frames[0], variable=self.are_keys_builtin, value=0,
614 command=self.set_keys_type, text='Use a Custom Key Set')
615 self.opt_menu_keys_builtin = DynOptionMenu(
616 frames[0], self.builtin_keys, None, command=None)
617 self.opt_menu_keys_custom = DynOptionMenu(
618 frames[0], self.custom_keys, None, command=None)
619 self.button_delete_custom_keys = Button(
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400620 frames[1], text='Delete Custom Key Set',
terryjreedy938e7382017-06-26 20:48:39 -0400621 command=self.delete_custom_keys)
622 button_save_custom_keys = Button(
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400623 frames[1], text='Save as New Custom Key Set',
terryjreedy938e7382017-06-26 20:48:39 -0400624 command=self.save_as_new_key_set)
Terry Jan Reedy9bdb1ed2016-07-10 13:46:34 -0400625 self.new_custom_keys = Label(frames[0], bd=2)
Terry Jan Reedy22405332014-07-30 19:24:32 -0400626
Steven M. Gava60fc7072001-08-04 13:58:22 +0000627 ##widget packing
628 #body
terryjreedy938e7382017-06-26 20:48:39 -0400629 frame_custom.pack(side=BOTTOM, padx=5, pady=5, expand=TRUE, fill=BOTH)
630 frame_key_sets.pack(side=BOTTOM, padx=5, pady=5, fill=BOTH)
631 #frame_custom
632 self.button_new_keys.pack(side=BOTTOM, fill=X, padx=5, pady=5)
633 frame_target.pack(side=LEFT, padx=5, pady=5, expand=TRUE, fill=BOTH)
Steven M. Gavafacfc092002-01-19 00:29:54 +0000634 #frame target
terryjreedy938e7382017-06-26 20:48:39 -0400635 frame_target.columnconfigure(0, weight=1)
636 frame_target.rowconfigure(1, weight=1)
637 target_title.grid(row=0, column=0, columnspan=2, sticky=W)
638 self.list_bindings.grid(row=1, column=0, sticky=NSEW)
639 scroll_target_y.grid(row=1, column=1, sticky=NS)
640 scroll_target_x.grid(row=2, column=0, sticky=EW)
641 #frame_key_sets
642 self.radio_keys_builtin.grid(row=0, column=0, sticky=W+NS)
643 self.radio_keys_custom.grid(row=1, column=0, sticky=W+NS)
644 self.opt_menu_keys_builtin.grid(row=0, column=1, sticky=NSEW)
645 self.opt_menu_keys_custom.grid(row=1, column=1, sticky=NSEW)
Terry Jan Reedy9bdb1ed2016-07-10 13:46:34 -0400646 self.new_custom_keys.grid(row=0, column=2, sticky=NSEW, padx=5, pady=5)
terryjreedy938e7382017-06-26 20:48:39 -0400647 self.button_delete_custom_keys.pack(side=LEFT, fill=X, expand=True, padx=2)
648 button_save_custom_keys.pack(side=LEFT, fill=X, expand=True, padx=2)
Christian Heimes9a371592007-12-28 14:08:13 +0000649 frames[0].pack(side=TOP, fill=BOTH, expand=True)
650 frames[1].pack(side=TOP, fill=X, expand=True, pady=2)
Steven M. Gava952d0a52001-08-03 04:43:44 +0000651 return frame
652
Terry Jan Reedy0c4c6512017-07-26 21:41:26 -0400653
terryjreedy938e7382017-06-26 20:48:39 -0400654 def create_page_general(self):
terryjreedye5bb1122017-07-05 00:54:55 -0400655 """Return frame of widgets for General tab.
656
Terry Jan Reedy0c4c6512017-07-26 21:41:26 -0400657 Enable users to provisionally change general options. Function
658 load_general_cfg intializes tk variables and helplist using
659 idleConf. Radiobuttons startup_shell_on and startup_editor_on
660 set var startup_edit. Radiobuttons save_ask_on and save_auto_on
661 set var autosave. Entry boxes win_width_int and win_height_int
662 set var win_width and win_height. Setting var_name invokes the
663 var_changed_var_name callback that adds option to changes.
terryjreedy9a09c662017-07-13 23:53:30 -0400664
Terry Jan Reedy0c4c6512017-07-26 21:41:26 -0400665 Helplist: load_general_cfg loads list user_helplist with
666 name, position pairs and copies names to listbox helplist.
667 Clicking a name invokes help_source selected. Clicking
668 button_helplist_name invokes helplist_item_name, which also
669 changes user_helplist. These functions all call
670 set_add_delete_state. All but load call update_help_changes to
671 rewrite changes['main']['HelpFiles'].
terryjreedy9a09c662017-07-13 23:53:30 -0400672
673 Widget Structure: (*) widgets bound to self
674 frame
675 frame_run: LabelFrame
676 startup_title: Label
Terry Jan Reedy0c4c6512017-07-26 21:41:26 -0400677 (*)startup_editor_on: Radiobutton - startup_edit
678 (*)startup_shell_on: Radiobutton - startup_edit
terryjreedy9a09c662017-07-13 23:53:30 -0400679 frame_save: LabelFrame
680 run_save_title: Label
Terry Jan Reedy0c4c6512017-07-26 21:41:26 -0400681 (*)save_ask_on: Radiobutton - autosave
682 (*)save_auto_on: Radiobutton - autosave
terryjreedy9a09c662017-07-13 23:53:30 -0400683 frame_win_size: LabelFrame
684 win_size_title: Label
685 win_width_title: Label
Terry Jan Reedy0c4c6512017-07-26 21:41:26 -0400686 (*)win_width_int: Entry - win_width
terryjreedy9a09c662017-07-13 23:53:30 -0400687 win_height_title: Label
Terry Jan Reedy0c4c6512017-07-26 21:41:26 -0400688 (*)win_height_int: Entry - win_height
terryjreedy9a09c662017-07-13 23:53:30 -0400689 frame_help: LabelFrame
690 frame_helplist: Frame
691 frame_helplist_buttons: Frame
692 (*)button_helplist_edit
693 (*)button_helplist_add
694 (*)button_helplist_remove
Terry Jan Reedy0c4c6512017-07-26 21:41:26 -0400695 (*)helplist: ListBox
terryjreedy9a09c662017-07-13 23:53:30 -0400696 scroll_helplist: Scrollbar
terryjreedye5bb1122017-07-05 00:54:55 -0400697 """
Terry Jan Reedy22405332014-07-30 19:24:32 -0400698 parent = self.parent
terryjreedy938e7382017-06-26 20:48:39 -0400699 self.startup_edit = IntVar(parent)
700 self.autosave = IntVar(parent)
Terry Jan Reedy0c4c6512017-07-26 21:41:26 -0400701 self.win_width = StringVar(parent)
702 self.win_height = StringVar(parent)
Terry Jan Reedy22405332014-07-30 19:24:32 -0400703
Terry Jan Reedy0c4c6512017-07-26 21:41:26 -0400704 # Create widgets:
705 # body.
terryjreedy938e7382017-06-26 20:48:39 -0400706 frame = self.tab_pages.pages['General'].frame
Terry Jan Reedy0c4c6512017-07-26 21:41:26 -0400707 # body section frames.
terryjreedy938e7382017-06-26 20:48:39 -0400708 frame_run = LabelFrame(frame, borderwidth=2, relief=GROOVE,
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400709 text=' Startup Preferences ')
terryjreedy938e7382017-06-26 20:48:39 -0400710 frame_save = LabelFrame(frame, borderwidth=2, relief=GROOVE,
711 text=' autosave Preferences ')
712 frame_win_size = Frame(frame, borderwidth=2, relief=GROOVE)
713 frame_help = LabelFrame(frame, borderwidth=2, relief=GROOVE,
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400714 text=' Additional Help Sources ')
Terry Jan Reedy0c4c6512017-07-26 21:41:26 -0400715 # frame_run.
terryjreedy938e7382017-06-26 20:48:39 -0400716 startup_title = Label(frame_run, text='At Startup')
Terry Jan Reedy0c4c6512017-07-26 21:41:26 -0400717 self.startup_editor_on = Radiobutton(
terryjreedy938e7382017-06-26 20:48:39 -0400718 frame_run, variable=self.startup_edit, value=1,
Terry Jan Reedyf46b7822016-11-07 17:15:01 -0500719 text="Open Edit Window")
Terry Jan Reedy0c4c6512017-07-26 21:41:26 -0400720 self.startup_shell_on = Radiobutton(
terryjreedy938e7382017-06-26 20:48:39 -0400721 frame_run, variable=self.startup_edit, value=0,
Terry Jan Reedyf46b7822016-11-07 17:15:01 -0500722 text='Open Shell Window')
Terry Jan Reedy0c4c6512017-07-26 21:41:26 -0400723 # frame_save.
terryjreedy938e7382017-06-26 20:48:39 -0400724 run_save_title = Label(frame_save, text='At Start of Run (F5) ')
Terry Jan Reedy0c4c6512017-07-26 21:41:26 -0400725 self.save_ask_on = Radiobutton(
terryjreedy938e7382017-06-26 20:48:39 -0400726 frame_save, variable=self.autosave, value=0,
Terry Jan Reedyf46b7822016-11-07 17:15:01 -0500727 text="Prompt to Save")
Terry Jan Reedy0c4c6512017-07-26 21:41:26 -0400728 self.save_auto_on = Radiobutton(
terryjreedy938e7382017-06-26 20:48:39 -0400729 frame_save, variable=self.autosave, value=1,
Terry Jan Reedyf46b7822016-11-07 17:15:01 -0500730 text='No Prompt')
Terry Jan Reedy0c4c6512017-07-26 21:41:26 -0400731 # frame_win_size.
terryjreedy938e7382017-06-26 20:48:39 -0400732 win_size_title = Label(
733 frame_win_size, text='Initial Window Size (in characters)')
734 win_width_title = Label(frame_win_size, text='Width')
Terry Jan Reedy0c4c6512017-07-26 21:41:26 -0400735 self.win_width_int = Entry(
terryjreedy938e7382017-06-26 20:48:39 -0400736 frame_win_size, textvariable=self.win_width, width=3)
737 win_height_title = Label(frame_win_size, text='Height')
Terry Jan Reedy0c4c6512017-07-26 21:41:26 -0400738 self.win_height_int = Entry(
terryjreedy938e7382017-06-26 20:48:39 -0400739 frame_win_size, textvariable=self.win_height, width=3)
Terry Jan Reedy0c4c6512017-07-26 21:41:26 -0400740 # frame_help.
terryjreedy938e7382017-06-26 20:48:39 -0400741 frame_helplist = Frame(frame_help)
742 frame_helplist_buttons = Frame(frame_helplist)
Terry Jan Reedy0c4c6512017-07-26 21:41:26 -0400743 self.helplist = Listbox(
terryjreedy938e7382017-06-26 20:48:39 -0400744 frame_helplist, height=5, takefocus=FALSE,
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000745 exportselection=FALSE)
Terry Jan Reedy0c4c6512017-07-26 21:41:26 -0400746 scroll_helplist = Scrollbar(frame_helplist)
747 scroll_helplist['command'] = self.helplist.yview
748 self.helplist['yscrollcommand'] = scroll_helplist.set
749 self.helplist.bind('<ButtonRelease-1>', self.help_source_selected)
terryjreedy938e7382017-06-26 20:48:39 -0400750 self.button_helplist_edit = Button(
751 frame_helplist_buttons, text='Edit', state=DISABLED,
752 width=8, command=self.helplist_item_edit)
753 self.button_helplist_add = Button(
754 frame_helplist_buttons, text='Add',
755 width=8, command=self.helplist_item_add)
756 self.button_helplist_remove = Button(
757 frame_helplist_buttons, text='Remove', state=DISABLED,
758 width=8, command=self.helplist_item_remove)
Terry Jan Reedy22405332014-07-30 19:24:32 -0400759
Terry Jan Reedy0c4c6512017-07-26 21:41:26 -0400760 # Pack widgets:
761 # body.
terryjreedy938e7382017-06-26 20:48:39 -0400762 frame_run.pack(side=TOP, padx=5, pady=5, fill=X)
763 frame_save.pack(side=TOP, padx=5, pady=5, fill=X)
764 frame_win_size.pack(side=TOP, padx=5, pady=5, fill=X)
765 frame_help.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH)
Terry Jan Reedy0c4c6512017-07-26 21:41:26 -0400766 # frame_run.
terryjreedy938e7382017-06-26 20:48:39 -0400767 startup_title.pack(side=LEFT, anchor=W, padx=5, pady=5)
Terry Jan Reedy0c4c6512017-07-26 21:41:26 -0400768 self.startup_shell_on.pack(side=RIGHT, anchor=W, padx=5, pady=5)
769 self.startup_editor_on.pack(side=RIGHT, anchor=W, padx=5, pady=5)
770 # frame_save.
terryjreedy938e7382017-06-26 20:48:39 -0400771 run_save_title.pack(side=LEFT, anchor=W, padx=5, pady=5)
Terry Jan Reedy0c4c6512017-07-26 21:41:26 -0400772 self.save_auto_on.pack(side=RIGHT, anchor=W, padx=5, pady=5)
773 self.save_ask_on.pack(side=RIGHT, anchor=W, padx=5, pady=5)
774 # frame_win_size.
terryjreedy938e7382017-06-26 20:48:39 -0400775 win_size_title.pack(side=LEFT, anchor=W, padx=5, pady=5)
Terry Jan Reedy0c4c6512017-07-26 21:41:26 -0400776 self.win_height_int.pack(side=RIGHT, anchor=E, padx=10, pady=5)
terryjreedy938e7382017-06-26 20:48:39 -0400777 win_height_title.pack(side=RIGHT, anchor=E, pady=5)
Terry Jan Reedy0c4c6512017-07-26 21:41:26 -0400778 self.win_width_int.pack(side=RIGHT, anchor=E, padx=10, pady=5)
terryjreedy938e7382017-06-26 20:48:39 -0400779 win_width_title.pack(side=RIGHT, anchor=E, pady=5)
Terry Jan Reedy0c4c6512017-07-26 21:41:26 -0400780 # frame_help.
terryjreedy938e7382017-06-26 20:48:39 -0400781 frame_helplist_buttons.pack(side=RIGHT, padx=5, pady=5, fill=Y)
782 frame_helplist.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH)
783 scroll_helplist.pack(side=RIGHT, anchor=W, fill=Y)
Terry Jan Reedy0c4c6512017-07-26 21:41:26 -0400784 self.helplist.pack(side=LEFT, anchor=E, expand=TRUE, fill=BOTH)
terryjreedy938e7382017-06-26 20:48:39 -0400785 self.button_helplist_edit.pack(side=TOP, anchor=W, pady=5)
786 self.button_helplist_add.pack(side=TOP, anchor=W)
787 self.button_helplist_remove.pack(side=TOP, anchor=W, pady=5)
Terry Jan Reedy0c4c6512017-07-26 21:41:26 -0400788
Steven M. Gava952d0a52001-08-03 04:43:44 +0000789 return frame
790
Terry Jan Reedy0c4c6512017-07-26 21:41:26 -0400791 def load_general_cfg(self):
792 "Load current configuration settings for the general options."
793 # Set startup state.
794 self.startup_edit.set(idleConf.GetOption(
795 'main', 'General', 'editor-on-startup', default=0, type='bool'))
796 # Set autosave state.
797 self.autosave.set(idleConf.GetOption(
798 'main', 'General', 'autosave', default=0, type='bool'))
799 # Set initial window size.
800 self.win_width.set(idleConf.GetOption(
801 'main', 'EditorWindow', 'width', type='int'))
802 self.win_height.set(idleConf.GetOption(
803 'main', 'EditorWindow', 'height', type='int'))
804 # Set additional help sources.
805 self.user_helplist = idleConf.GetAllExtraHelpSourcesList()
806 self.helplist.delete(0, 'end')
807 for help_item in self.user_helplist:
808 self.helplist.insert(END, help_item[0])
809 self.set_add_delete_state()
810
811 def var_changed_startup_edit(self, *params):
812 "Store change to toggle for starting IDLE in the editor or shell."
813 value = self.startup_edit.get()
814 changes.add_option('main', 'General', 'editor-on-startup', value)
815
816 def var_changed_autosave(self, *params):
817 "Store change to autosave."
818 value = self.autosave.get()
819 changes.add_option('main', 'General', 'autosave', value)
820
821 def var_changed_win_width(self, *params):
822 "Store change to window width."
823 value = self.win_width.get()
824 changes.add_option('main', 'EditorWindow', 'width', value)
825
826 def var_changed_win_height(self, *params):
827 "Store change to window height."
828 value = self.win_height.get()
829 changes.add_option('main', 'EditorWindow', 'height', value)
830
831 def help_source_selected(self, event):
832 "Handle event for selecting additional help."
833 self.set_add_delete_state()
834
835 def set_add_delete_state(self):
836 "Toggle the state for the help list buttons based on list entries."
837 if self.helplist.size() < 1: # No entries in list.
838 self.button_helplist_edit['state'] = DISABLED
839 self.button_helplist_remove['state'] = DISABLED
840 else: # Some entries.
841 if self.helplist.curselection(): # There currently is a selection.
842 self.button_helplist_edit['state'] = NORMAL
843 self.button_helplist_remove['state'] = NORMAL
844 else: # There currently is not a selection.
845 self.button_helplist_edit['state'] = DISABLED
846 self.button_helplist_remove['state'] = DISABLED
847
848 def helplist_item_add(self):
849 """Handle add button for the help list.
850
851 Query for name and location of new help sources and add
852 them to the list.
853 """
854 help_source = HelpSource(self, 'New Help Source').result
855 if help_source:
856 self.user_helplist.append(help_source)
857 self.helplist.insert(END, help_source[0])
858 self.update_help_changes()
859
860 def helplist_item_edit(self):
861 """Handle edit button for the help list.
862
863 Query with existing help source information and update
864 config if the values are changed.
865 """
866 item_index = self.helplist.index(ANCHOR)
867 help_source = self.user_helplist[item_index]
868 new_help_source = HelpSource(
869 self, 'Edit Help Source',
870 menuitem=help_source[0],
871 filepath=help_source[1],
872 ).result
873 if new_help_source and new_help_source != help_source:
874 self.user_helplist[item_index] = new_help_source
875 self.helplist.delete(item_index)
876 self.helplist.insert(item_index, new_help_source[0])
877 self.update_help_changes()
878 self.set_add_delete_state() # Selected will be un-selected
879
880 def helplist_item_remove(self):
881 """Handle remove button for the help list.
882
883 Delete the help list item from config.
884 """
885 item_index = self.helplist.index(ANCHOR)
886 del(self.user_helplist[item_index])
887 self.helplist.delete(item_index)
888 self.update_help_changes()
889 self.set_add_delete_state()
890
891 def update_help_changes(self):
892 "Clear and rebuild the HelpFiles section in changes"
893 changes['main']['HelpFiles'] = {}
894 for num in range(1, len(self.user_helplist) + 1):
895 changes.add_option(
896 'main', 'HelpFiles', str(num),
897 ';'.join(self.user_helplist[num-1][:2]))
898
899
terryjreedy938e7382017-06-26 20:48:39 -0400900 def attach_var_callbacks(self):
terryjreedye5bb1122017-07-05 00:54:55 -0400901 "Attach callbacks to variables that can be changed."
terryjreedy938e7382017-06-26 20:48:39 -0400902 self.font_size.trace_add('write', self.var_changed_font)
903 self.font_name.trace_add('write', self.var_changed_font)
904 self.font_bold.trace_add('write', self.var_changed_font)
905 self.space_num.trace_add('write', self.var_changed_space_num)
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -0400906 self.color.trace_add('write', self.var_changed_color)
terryjreedy938e7382017-06-26 20:48:39 -0400907 self.builtin_theme.trace_add('write', self.var_changed_builtin_theme)
908 self.custom_theme.trace_add('write', self.var_changed_custom_theme)
909 self.is_builtin_theme.trace_add('write', self.var_changed_is_builtin_theme)
910 self.highlight_target.trace_add('write', self.var_changed_highlight_target)
911 self.keybinding.trace_add('write', self.var_changed_keybinding)
912 self.builtin_keys.trace_add('write', self.var_changed_builtin_keys)
913 self.custom_keys.trace_add('write', self.var_changed_custom_keys)
914 self.are_keys_builtin.trace_add('write', self.var_changed_are_keys_builtin)
915 self.win_width.trace_add('write', self.var_changed_win_width)
916 self.win_height.trace_add('write', self.var_changed_win_height)
917 self.startup_edit.trace_add('write', self.var_changed_startup_edit)
918 self.autosave.trace_add('write', self.var_changed_autosave)
Steven M. Gava052937f2002-02-11 02:20:53 +0000919
Terry Jan Reedy6b98ce22016-05-16 22:27:28 -0400920 def remove_var_callbacks(self):
921 "Remove callbacks to prevent memory leaks."
922 for var in (
terryjreedy938e7382017-06-26 20:48:39 -0400923 self.font_size, self.font_name, self.font_bold,
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -0400924 self.space_num, self.color, self.builtin_theme,
terryjreedy938e7382017-06-26 20:48:39 -0400925 self.custom_theme, self.is_builtin_theme, self.highlight_target,
926 self.keybinding, self.builtin_keys, self.custom_keys,
927 self.are_keys_builtin, self.win_width, self.win_height,
terryjreedy8e3f73e2017-07-10 15:11:45 -0400928 self.startup_edit, self.autosave,):
Serhiy Storchaka81221742016-06-26 09:46:57 +0300929 var.trace_remove('write', var.trace_info()[0][1])
Terry Jan Reedy6b98ce22016-05-16 22:27:28 -0400930
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -0400931 def var_changed_color(self, *params):
terryjreedye5bb1122017-07-05 00:54:55 -0400932 "Process change to color choice."
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -0400933 self.on_new_color_set()
Steven M. Gava052937f2002-02-11 02:20:53 +0000934
terryjreedy938e7382017-06-26 20:48:39 -0400935 def var_changed_builtin_theme(self, *params):
terryjreedye5bb1122017-07-05 00:54:55 -0400936 """Process new builtin theme selection.
937
938 Add the changed theme's name to the changed_items and recreate
939 the sample with the values from the selected theme.
940 """
terryjreedy938e7382017-06-26 20:48:39 -0400941 old_themes = ('IDLE Classic', 'IDLE New')
942 value = self.builtin_theme.get()
943 if value not in old_themes:
944 if idleConf.GetOption('main', 'Theme', 'name') not in old_themes:
terryjreedyedc03422017-07-07 16:37:39 -0400945 changes.add_option('main', 'Theme', 'name', old_themes[0])
946 changes.add_option('main', 'Theme', 'name2', value)
Terry Jan Reedyd0c0f002015-11-12 15:02:57 -0500947 self.new_custom_theme.config(text='New theme, see Help',
948 fg='#500000')
949 else:
terryjreedyedc03422017-07-07 16:37:39 -0400950 changes.add_option('main', 'Theme', 'name', value)
951 changes.add_option('main', 'Theme', 'name2', '')
Terry Jan Reedyd0c0f002015-11-12 15:02:57 -0500952 self.new_custom_theme.config(text='', fg='black')
terryjreedy938e7382017-06-26 20:48:39 -0400953 self.paint_theme_sample()
Steven M. Gava052937f2002-02-11 02:20:53 +0000954
terryjreedy938e7382017-06-26 20:48:39 -0400955 def var_changed_custom_theme(self, *params):
terryjreedye5bb1122017-07-05 00:54:55 -0400956 """Process new custom theme selection.
957
958 If a new custom theme is selected, add the name to the
959 changed_items and apply the theme to the sample.
960 """
terryjreedy938e7382017-06-26 20:48:39 -0400961 value = self.custom_theme.get()
Steven M. Gava49745752002-02-18 01:43:11 +0000962 if value != '- no custom themes -':
terryjreedyedc03422017-07-07 16:37:39 -0400963 changes.add_option('main', 'Theme', 'name', value)
terryjreedy938e7382017-06-26 20:48:39 -0400964 self.paint_theme_sample()
Steven M. Gava052937f2002-02-11 02:20:53 +0000965
terryjreedy938e7382017-06-26 20:48:39 -0400966 def var_changed_is_builtin_theme(self, *params):
terryjreedye5bb1122017-07-05 00:54:55 -0400967 """Process toggle between builtin and custom theme.
968
969 Update the default toggle value and apply the newly
970 selected theme type.
971 """
terryjreedy938e7382017-06-26 20:48:39 -0400972 value = self.is_builtin_theme.get()
terryjreedyedc03422017-07-07 16:37:39 -0400973 changes.add_option('main', 'Theme', 'default', value)
Steven M. Gavaf31eec02002-03-05 00:25:58 +0000974 if value:
terryjreedy938e7382017-06-26 20:48:39 -0400975 self.var_changed_builtin_theme()
Steven M. Gavaf31eec02002-03-05 00:25:58 +0000976 else:
terryjreedy938e7382017-06-26 20:48:39 -0400977 self.var_changed_custom_theme()
Steven M. Gava052937f2002-02-11 02:20:53 +0000978
terryjreedy938e7382017-06-26 20:48:39 -0400979 def var_changed_highlight_target(self, *params):
terryjreedye5bb1122017-07-05 00:54:55 -0400980 "Process selection of new target tag for highlighting."
terryjreedy938e7382017-06-26 20:48:39 -0400981 self.set_highlight_target()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000982
terryjreedy938e7382017-06-26 20:48:39 -0400983 def var_changed_keybinding(self, *params):
terryjreedye5bb1122017-07-05 00:54:55 -0400984 "Store change to a keybinding."
terryjreedy938e7382017-06-26 20:48:39 -0400985 value = self.keybinding.get()
986 key_set = self.custom_keys.get()
987 event = self.list_bindings.get(ANCHOR).split()[0]
Steven M. Gavaa498af22002-02-01 01:33:36 +0000988 if idleConf.IsCoreBinding(event):
terryjreedyedc03422017-07-07 16:37:39 -0400989 changes.add_option('keys', key_set, event, value)
terryjreedye5bb1122017-07-05 00:54:55 -0400990 else: # Event is an extension binding.
terryjreedy938e7382017-06-26 20:48:39 -0400991 ext_name = idleConf.GetExtnNameForEvent(event)
992 ext_keybind_section = ext_name + '_cfgBindings'
terryjreedyedc03422017-07-07 16:37:39 -0400993 changes.add_option('extensions', ext_keybind_section, event, value)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000994
terryjreedy938e7382017-06-26 20:48:39 -0400995 def var_changed_builtin_keys(self, *params):
terryjreedye5bb1122017-07-05 00:54:55 -0400996 "Process selection of builtin key set."
terryjreedy938e7382017-06-26 20:48:39 -0400997 old_keys = (
Terry Jan Reedy9bdb1ed2016-07-10 13:46:34 -0400998 'IDLE Classic Windows',
999 'IDLE Classic Unix',
1000 'IDLE Classic Mac',
1001 'IDLE Classic OSX',
1002 )
terryjreedy938e7382017-06-26 20:48:39 -04001003 value = self.builtin_keys.get()
1004 if value not in old_keys:
1005 if idleConf.GetOption('main', 'Keys', 'name') not in old_keys:
terryjreedyedc03422017-07-07 16:37:39 -04001006 changes.add_option('main', 'Keys', 'name', old_keys[0])
1007 changes.add_option('main', 'Keys', 'name2', value)
Terry Jan Reedy9bdb1ed2016-07-10 13:46:34 -04001008 self.new_custom_keys.config(text='New key set, see Help',
1009 fg='#500000')
1010 else:
terryjreedyedc03422017-07-07 16:37:39 -04001011 changes.add_option('main', 'Keys', 'name', value)
1012 changes.add_option('main', 'Keys', 'name2', '')
Terry Jan Reedy9bdb1ed2016-07-10 13:46:34 -04001013 self.new_custom_keys.config(text='', fg='black')
terryjreedy938e7382017-06-26 20:48:39 -04001014 self.load_keys_list(value)
Steven M. Gava052937f2002-02-11 02:20:53 +00001015
terryjreedy938e7382017-06-26 20:48:39 -04001016 def var_changed_custom_keys(self, *params):
terryjreedye5bb1122017-07-05 00:54:55 -04001017 "Process selection of custom key set."
terryjreedy938e7382017-06-26 20:48:39 -04001018 value = self.custom_keys.get()
Steven M. Gava49745752002-02-18 01:43:11 +00001019 if value != '- no custom keys -':
terryjreedyedc03422017-07-07 16:37:39 -04001020 changes.add_option('main', 'Keys', 'name', value)
terryjreedy938e7382017-06-26 20:48:39 -04001021 self.load_keys_list(value)
Steven M. Gava052937f2002-02-11 02:20:53 +00001022
terryjreedy938e7382017-06-26 20:48:39 -04001023 def var_changed_are_keys_builtin(self, *params):
terryjreedye5bb1122017-07-05 00:54:55 -04001024 "Process toggle between builtin key set and custom key set."
terryjreedy938e7382017-06-26 20:48:39 -04001025 value = self.are_keys_builtin.get()
terryjreedyedc03422017-07-07 16:37:39 -04001026 changes.add_option('main', 'Keys', 'default', value)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001027 if value:
terryjreedy938e7382017-06-26 20:48:39 -04001028 self.var_changed_builtin_keys()
Steven M. Gava052937f2002-02-11 02:20:53 +00001029 else:
terryjreedy938e7382017-06-26 20:48:39 -04001030 self.var_changed_custom_keys()
Steven M. Gava052937f2002-02-11 02:20:53 +00001031
terryjreedy938e7382017-06-26 20:48:39 -04001032 def set_theme_type(self):
terryjreedy9a09c662017-07-13 23:53:30 -04001033 """Set available screen options based on builtin or custom theme.
1034
1035 Attributes accessed:
1036 is_builtin_theme
1037
1038 Attributes updated:
1039 opt_menu_theme_builtin
1040 opt_menu_theme_custom
1041 button_delete_custom_theme
1042 radio_theme_custom
1043
1044 Called from:
1045 handler for radio_theme_builtin and radio_theme_custom
1046 delete_custom_theme
1047 create_new_theme
1048 load_theme_cfg
1049 """
terryjreedy938e7382017-06-26 20:48:39 -04001050 if self.is_builtin_theme.get():
Terry Jan Reedy0c4c6512017-07-26 21:41:26 -04001051 self.opt_menu_theme_builtin['state'] = NORMAL
1052 self.opt_menu_theme_custom['state'] = DISABLED
1053 self.button_delete_custom_theme['state'] = DISABLED
Steven M. Gava5f28e8f2002-01-21 06:38:21 +00001054 else:
Terry Jan Reedy0c4c6512017-07-26 21:41:26 -04001055 self.opt_menu_theme_builtin['state'] = DISABLED
1056 self.radio_theme_custom['state'] = NORMAL
1057 self.opt_menu_theme_custom['state'] = NORMAL
1058 self.button_delete_custom_theme['state'] = NORMAL
Steven M. Gava5f28e8f2002-01-21 06:38:21 +00001059
terryjreedy938e7382017-06-26 20:48:39 -04001060 def set_keys_type(self):
terryjreedye5bb1122017-07-05 00:54:55 -04001061 "Set available screen options based on builtin or custom key set."
terryjreedy938e7382017-06-26 20:48:39 -04001062 if self.are_keys_builtin.get():
Terry Jan Reedy0c4c6512017-07-26 21:41:26 -04001063 self.opt_menu_keys_builtin['state'] = NORMAL
1064 self.opt_menu_keys_custom['state'] = DISABLED
1065 self.button_delete_custom_keys['state'] = DISABLED
Steven M. Gava5f28e8f2002-01-21 06:38:21 +00001066 else:
Terry Jan Reedy0c4c6512017-07-26 21:41:26 -04001067 self.opt_menu_keys_builtin['state'] = DISABLED
1068 self.radio_keys_custom['state'] = NORMAL
1069 self.opt_menu_keys_custom['state'] = NORMAL
1070 self.button_delete_custom_keys['state'] = NORMAL
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001071
terryjreedy938e7382017-06-26 20:48:39 -04001072 def get_new_keys(self):
terryjreedye5bb1122017-07-05 00:54:55 -04001073 """Handle event to change key binding for selected line.
1074
1075 A selection of a key/binding in the list of current
1076 bindings pops up a dialog to enter a new binding. If
1077 the current key set is builtin and a binding has
1078 changed, then a name for a custom key set needs to be
1079 entered for the change to be applied.
1080 """
terryjreedy938e7382017-06-26 20:48:39 -04001081 list_index = self.list_bindings.index(ANCHOR)
1082 binding = self.list_bindings.get(list_index)
terryjreedye5bb1122017-07-05 00:54:55 -04001083 bind_name = binding.split()[0]
terryjreedy938e7382017-06-26 20:48:39 -04001084 if self.are_keys_builtin.get():
1085 current_key_set_name = self.builtin_keys.get()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001086 else:
terryjreedy938e7382017-06-26 20:48:39 -04001087 current_key_set_name = self.custom_keys.get()
1088 current_bindings = idleConf.GetCurrentKeySet()
terryjreedyedc03422017-07-07 16:37:39 -04001089 if current_key_set_name in changes['keys']: # unsaved changes
1090 key_set_changes = changes['keys'][current_key_set_name]
terryjreedy938e7382017-06-26 20:48:39 -04001091 for event in key_set_changes:
1092 current_bindings[event] = key_set_changes[event].split()
1093 current_key_sequences = list(current_bindings.values())
1094 new_keys = GetKeysDialog(self, 'Get New Keys', bind_name,
1095 current_key_sequences).result
terryjreedye5bb1122017-07-05 00:54:55 -04001096 if new_keys:
1097 if self.are_keys_builtin.get(): # Current key set is a built-in.
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001098 message = ('Your changes will be saved as a new Custom Key Set.'
1099 ' Enter a name for your new Custom Key Set below.')
terryjreedy938e7382017-06-26 20:48:39 -04001100 new_keyset = self.get_new_keys_name(message)
terryjreedye5bb1122017-07-05 00:54:55 -04001101 if not new_keyset: # User cancelled custom key set creation.
terryjreedy938e7382017-06-26 20:48:39 -04001102 self.list_bindings.select_set(list_index)
1103 self.list_bindings.select_anchor(list_index)
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +00001104 return
terryjreedye5bb1122017-07-05 00:54:55 -04001105 else: # Create new custom key set based on previously active key set.
terryjreedy938e7382017-06-26 20:48:39 -04001106 self.create_new_key_set(new_keyset)
1107 self.list_bindings.delete(list_index)
1108 self.list_bindings.insert(list_index, bind_name+' - '+new_keys)
1109 self.list_bindings.select_set(list_index)
1110 self.list_bindings.select_anchor(list_index)
1111 self.keybinding.set(new_keys)
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +00001112 else:
terryjreedy938e7382017-06-26 20:48:39 -04001113 self.list_bindings.select_set(list_index)
1114 self.list_bindings.select_anchor(list_index)
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +00001115
terryjreedy938e7382017-06-26 20:48:39 -04001116 def get_new_keys_name(self, message):
terryjreedye5bb1122017-07-05 00:54:55 -04001117 "Return new key set name from query popup."
terryjreedy938e7382017-06-26 20:48:39 -04001118 used_names = (idleConf.GetSectionList('user', 'keys') +
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001119 idleConf.GetSectionList('default', 'keys'))
terryjreedy938e7382017-06-26 20:48:39 -04001120 new_keyset = SectionName(
1121 self, 'New Custom Key Set', message, used_names).result
1122 return new_keyset
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001123
terryjreedy938e7382017-06-26 20:48:39 -04001124 def save_as_new_key_set(self):
terryjreedye5bb1122017-07-05 00:54:55 -04001125 "Prompt for name of new key set and save changes using that name."
terryjreedy938e7382017-06-26 20:48:39 -04001126 new_keys_name = self.get_new_keys_name('New Key Set Name:')
1127 if new_keys_name:
1128 self.create_new_key_set(new_keys_name)
Steven M. Gava085eb1b2002-02-05 04:52:32 +00001129
terryjreedy938e7382017-06-26 20:48:39 -04001130 def keybinding_selected(self, event):
terryjreedye5bb1122017-07-05 00:54:55 -04001131 "Activate button to assign new keys to selected action."
Terry Jan Reedy0c4c6512017-07-26 21:41:26 -04001132 self.button_new_keys['state'] = NORMAL
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +00001133
terryjreedy938e7382017-06-26 20:48:39 -04001134 def create_new_key_set(self, new_key_set_name):
terryjreedye5bb1122017-07-05 00:54:55 -04001135 """Create a new custom key set with the given name.
1136
1137 Create the new key set based on the previously active set
1138 with the current changes applied. Once it is saved, then
1139 activate the new key set.
1140 """
terryjreedy938e7382017-06-26 20:48:39 -04001141 if self.are_keys_builtin.get():
1142 prev_key_set_name = self.builtin_keys.get()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001143 else:
terryjreedy938e7382017-06-26 20:48:39 -04001144 prev_key_set_name = self.custom_keys.get()
1145 prev_keys = idleConf.GetCoreKeys(prev_key_set_name)
1146 new_keys = {}
terryjreedye5bb1122017-07-05 00:54:55 -04001147 for event in prev_keys: # Add key set to changed items.
1148 event_name = event[2:-2] # Trim off the angle brackets.
terryjreedy938e7382017-06-26 20:48:39 -04001149 binding = ' '.join(prev_keys[event])
1150 new_keys[event_name] = binding
terryjreedye5bb1122017-07-05 00:54:55 -04001151 # Handle any unsaved changes to prev key set.
terryjreedyedc03422017-07-07 16:37:39 -04001152 if prev_key_set_name in changes['keys']:
1153 key_set_changes = changes['keys'][prev_key_set_name]
terryjreedy938e7382017-06-26 20:48:39 -04001154 for event in key_set_changes:
1155 new_keys[event] = key_set_changes[event]
terryjreedye5bb1122017-07-05 00:54:55 -04001156 # Save the new key set.
terryjreedy938e7382017-06-26 20:48:39 -04001157 self.save_new_key_set(new_key_set_name, new_keys)
terryjreedye5bb1122017-07-05 00:54:55 -04001158 # Change GUI over to the new key set.
terryjreedy938e7382017-06-26 20:48:39 -04001159 custom_key_list = idleConf.GetSectionList('user', 'keys')
1160 custom_key_list.sort()
1161 self.opt_menu_keys_custom.SetMenu(custom_key_list, new_key_set_name)
1162 self.are_keys_builtin.set(0)
1163 self.set_keys_type()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001164
terryjreedy938e7382017-06-26 20:48:39 -04001165 def load_keys_list(self, keyset_name):
terryjreedye5bb1122017-07-05 00:54:55 -04001166 """Reload the list of action/key binding pairs for the active key set.
1167
1168 An action/key binding can be selected to change the key binding.
1169 """
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001170 reselect = 0
terryjreedy938e7382017-06-26 20:48:39 -04001171 if self.list_bindings.curselection():
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001172 reselect = 1
terryjreedy938e7382017-06-26 20:48:39 -04001173 list_index = self.list_bindings.index(ANCHOR)
1174 keyset = idleConf.GetKeySet(keyset_name)
1175 bind_names = list(keyset.keys())
1176 bind_names.sort()
1177 self.list_bindings.delete(0, END)
1178 for bind_name in bind_names:
terryjreedye5bb1122017-07-05 00:54:55 -04001179 key = ' '.join(keyset[bind_name])
1180 bind_name = bind_name[2:-2] # Trim off the angle brackets.
terryjreedyedc03422017-07-07 16:37:39 -04001181 if keyset_name in changes['keys']:
terryjreedye5bb1122017-07-05 00:54:55 -04001182 # Handle any unsaved changes to this key set.
terryjreedyedc03422017-07-07 16:37:39 -04001183 if bind_name in changes['keys'][keyset_name]:
1184 key = changes['keys'][keyset_name][bind_name]
terryjreedy938e7382017-06-26 20:48:39 -04001185 self.list_bindings.insert(END, bind_name+' - '+key)
Steven M. Gava052937f2002-02-11 02:20:53 +00001186 if reselect:
terryjreedy938e7382017-06-26 20:48:39 -04001187 self.list_bindings.see(list_index)
1188 self.list_bindings.select_set(list_index)
1189 self.list_bindings.select_anchor(list_index)
Steven M. Gava052937f2002-02-11 02:20:53 +00001190
terryjreedy938e7382017-06-26 20:48:39 -04001191 def delete_custom_keys(self):
terryjreedye5bb1122017-07-05 00:54:55 -04001192 """Handle event to delete a custom key set.
1193
1194 Applying the delete deactivates the current configuration and
1195 reverts to the default. The custom key set is permanently
1196 deleted from the config file.
1197 """
terryjreedy938e7382017-06-26 20:48:39 -04001198 keyset_name=self.custom_keys.get()
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001199 delmsg = 'Are you sure you wish to delete the key set %r ?'
1200 if not tkMessageBox.askyesno(
terryjreedy938e7382017-06-26 20:48:39 -04001201 'Delete Key Set', delmsg % keyset_name, parent=self):
Steven M. Gava49745752002-02-18 01:43:11 +00001202 return
terryjreedy938e7382017-06-26 20:48:39 -04001203 self.deactivate_current_config()
terryjreedyedc03422017-07-07 16:37:39 -04001204 # Remove key set from changes, config, and file.
terryjreedyc0179482017-07-11 19:50:10 -04001205 changes.delete_section('keys', keyset_name)
terryjreedye5bb1122017-07-05 00:54:55 -04001206 # Reload user key set list.
terryjreedy938e7382017-06-26 20:48:39 -04001207 item_list = idleConf.GetSectionList('user', 'keys')
1208 item_list.sort()
1209 if not item_list:
Terry Jan Reedy0c4c6512017-07-26 21:41:26 -04001210 self.radio_keys_custom['state'] = DISABLED
terryjreedy938e7382017-06-26 20:48:39 -04001211 self.opt_menu_keys_custom.SetMenu(item_list, '- no custom keys -')
Steven M. Gava49745752002-02-18 01:43:11 +00001212 else:
terryjreedy938e7382017-06-26 20:48:39 -04001213 self.opt_menu_keys_custom.SetMenu(item_list, item_list[0])
terryjreedye5bb1122017-07-05 00:54:55 -04001214 # Revert to default key set.
terryjreedy938e7382017-06-26 20:48:39 -04001215 self.are_keys_builtin.set(idleConf.defaultCfg['main']
Terry Jan Reedy9bdb1ed2016-07-10 13:46:34 -04001216 .Get('Keys', 'default'))
terryjreedy938e7382017-06-26 20:48:39 -04001217 self.builtin_keys.set(idleConf.defaultCfg['main'].Get('Keys', 'name')
Terry Jan Reedy9bdb1ed2016-07-10 13:46:34 -04001218 or idleConf.default_keys())
terryjreedye5bb1122017-07-05 00:54:55 -04001219 # User can't back out of these changes, they must be applied now.
terryjreedyedc03422017-07-07 16:37:39 -04001220 changes.save_all()
1221 self.save_all_changed_extensions()
terryjreedy938e7382017-06-26 20:48:39 -04001222 self.activate_config_changes()
1223 self.set_keys_type()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001224
terryjreedy938e7382017-06-26 20:48:39 -04001225 def delete_custom_theme(self):
terryjreedye5bb1122017-07-05 00:54:55 -04001226 """Handle event to delete custom theme.
1227
1228 The current theme is deactivated and the default theme is
1229 activated. The custom theme is permanently removed from
1230 the config file.
terryjreedy9a09c662017-07-13 23:53:30 -04001231
1232 Attributes accessed:
1233 custom_theme
1234
1235 Attributes updated:
1236 radio_theme_custom
1237 opt_menu_theme_custom
1238 is_builtin_theme
1239 builtin_theme
1240
1241 Methods:
1242 deactivate_current_config
1243 save_all_changed_extensions
1244 activate_config_changes
1245 set_theme_type
terryjreedye5bb1122017-07-05 00:54:55 -04001246 """
terryjreedy938e7382017-06-26 20:48:39 -04001247 theme_name = self.custom_theme.get()
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001248 delmsg = 'Are you sure you wish to delete the theme %r ?'
1249 if not tkMessageBox.askyesno(
terryjreedy938e7382017-06-26 20:48:39 -04001250 'Delete Theme', delmsg % theme_name, parent=self):
Steven M. Gava49745752002-02-18 01:43:11 +00001251 return
terryjreedy938e7382017-06-26 20:48:39 -04001252 self.deactivate_current_config()
terryjreedyedc03422017-07-07 16:37:39 -04001253 # Remove theme from changes, config, and file.
terryjreedyc0179482017-07-11 19:50:10 -04001254 changes.delete_section('highlight', theme_name)
terryjreedye5bb1122017-07-05 00:54:55 -04001255 # Reload user theme list.
terryjreedy938e7382017-06-26 20:48:39 -04001256 item_list = idleConf.GetSectionList('user', 'highlight')
1257 item_list.sort()
1258 if not item_list:
Terry Jan Reedy0c4c6512017-07-26 21:41:26 -04001259 self.radio_theme_custom['state'] = DISABLED
terryjreedy938e7382017-06-26 20:48:39 -04001260 self.opt_menu_theme_custom.SetMenu(item_list, '- no custom themes -')
Steven M. Gava49745752002-02-18 01:43:11 +00001261 else:
terryjreedy938e7382017-06-26 20:48:39 -04001262 self.opt_menu_theme_custom.SetMenu(item_list, item_list[0])
terryjreedye5bb1122017-07-05 00:54:55 -04001263 # Revert to default theme.
terryjreedy938e7382017-06-26 20:48:39 -04001264 self.is_builtin_theme.set(idleConf.defaultCfg['main'].Get('Theme', 'default'))
1265 self.builtin_theme.set(idleConf.defaultCfg['main'].Get('Theme', 'name'))
terryjreedye5bb1122017-07-05 00:54:55 -04001266 # User can't back out of these changes, they must be applied now.
terryjreedyedc03422017-07-07 16:37:39 -04001267 changes.save_all()
1268 self.save_all_changed_extensions()
terryjreedy938e7382017-06-26 20:48:39 -04001269 self.activate_config_changes()
1270 self.set_theme_type()
Steven M. Gava49745752002-02-18 01:43:11 +00001271
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001272 def get_color(self):
terryjreedye5bb1122017-07-05 00:54:55 -04001273 """Handle button to select a new color for the target tag.
1274
1275 If a new color is selected while using a builtin theme, a
1276 name must be supplied to create a custom theme.
terryjreedy9a09c662017-07-13 23:53:30 -04001277
1278 Attributes accessed:
1279 highlight_target
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001280 frame_color_set
terryjreedy9a09c662017-07-13 23:53:30 -04001281 is_builtin_theme
1282
1283 Attributes updated:
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001284 color
terryjreedy9a09c662017-07-13 23:53:30 -04001285
1286 Methods:
1287 get_new_theme_name
1288 create_new_theme
terryjreedye5bb1122017-07-05 00:54:55 -04001289 """
terryjreedy938e7382017-06-26 20:48:39 -04001290 target = self.highlight_target.get()
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001291 prev_color = self.frame_color_set.cget('bg')
1292 rgbTuplet, color_string = tkColorChooser.askcolor(
1293 parent=self, title='Pick new color for : '+target,
1294 initialcolor=prev_color)
1295 if color_string and (color_string != prev_color):
1296 # User didn't cancel and they chose a new color.
terryjreedye5bb1122017-07-05 00:54:55 -04001297 if self.is_builtin_theme.get(): # Current theme is a built-in.
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001298 message = ('Your changes will be saved as a new Custom Theme. '
1299 'Enter a name for your new Custom Theme below.')
terryjreedy938e7382017-06-26 20:48:39 -04001300 new_theme = self.get_new_theme_name(message)
terryjreedye5bb1122017-07-05 00:54:55 -04001301 if not new_theme: # User cancelled custom theme creation.
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +00001302 return
terryjreedye5bb1122017-07-05 00:54:55 -04001303 else: # Create new custom theme based on previously active theme.
terryjreedy938e7382017-06-26 20:48:39 -04001304 self.create_new_theme(new_theme)
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001305 self.color.set(color_string)
terryjreedye5bb1122017-07-05 00:54:55 -04001306 else: # Current theme is user defined.
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001307 self.color.set(color_string)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001308
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001309 def on_new_color_set(self):
terryjreedye5bb1122017-07-05 00:54:55 -04001310 "Display sample of new color selection on the dialog."
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001311 new_color=self.color.get()
1312 self.frame_color_set.config(bg=new_color) # Set sample.
terryjreedy938e7382017-06-26 20:48:39 -04001313 plane ='foreground' if self.fg_bg_toggle.get() else 'background'
1314 sample_element = self.theme_elements[self.highlight_target.get()][0]
Terry Jan Reedy04864b42017-07-22 00:56:18 -04001315 self.highlight_sample.tag_config(sample_element, **{plane:new_color})
terryjreedy938e7382017-06-26 20:48:39 -04001316 theme = self.custom_theme.get()
1317 theme_element = sample_element + '-' + plane
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001318 changes.add_option('highlight', theme, theme_element, new_color)
Steven M. Gava052937f2002-02-11 02:20:53 +00001319
terryjreedy938e7382017-06-26 20:48:39 -04001320 def get_new_theme_name(self, message):
terryjreedye5bb1122017-07-05 00:54:55 -04001321 "Return name of new theme from query popup."
terryjreedy938e7382017-06-26 20:48:39 -04001322 used_names = (idleConf.GetSectionList('user', 'highlight') +
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001323 idleConf.GetSectionList('default', 'highlight'))
terryjreedy938e7382017-06-26 20:48:39 -04001324 new_theme = SectionName(
1325 self, 'New Custom Theme', message, used_names).result
1326 return new_theme
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001327
terryjreedy938e7382017-06-26 20:48:39 -04001328 def save_as_new_theme(self):
terryjreedy9a09c662017-07-13 23:53:30 -04001329 """Prompt for new theme name and create the theme.
1330
1331 Methods:
1332 get_new_theme_name
1333 create_new_theme
1334 """
terryjreedy938e7382017-06-26 20:48:39 -04001335 new_theme_name = self.get_new_theme_name('New Theme Name:')
1336 if new_theme_name:
1337 self.create_new_theme(new_theme_name)
Steven M. Gava085eb1b2002-02-05 04:52:32 +00001338
terryjreedy938e7382017-06-26 20:48:39 -04001339 def create_new_theme(self, new_theme_name):
terryjreedye5bb1122017-07-05 00:54:55 -04001340 """Create a new custom theme with the given name.
1341
1342 Create the new theme based on the previously active theme
1343 with the current changes applied. Once it is saved, then
1344 activate the new theme.
terryjreedy9a09c662017-07-13 23:53:30 -04001345
1346 Attributes accessed:
1347 builtin_theme
1348 custom_theme
1349
1350 Attributes updated:
1351 opt_menu_theme_custom
1352 is_builtin_theme
1353
1354 Method:
1355 save_new_theme
1356 set_theme_type
terryjreedye5bb1122017-07-05 00:54:55 -04001357 """
terryjreedy938e7382017-06-26 20:48:39 -04001358 if self.is_builtin_theme.get():
1359 theme_type = 'default'
1360 theme_name = self.builtin_theme.get()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001361 else:
terryjreedy938e7382017-06-26 20:48:39 -04001362 theme_type = 'user'
1363 theme_name = self.custom_theme.get()
1364 new_theme = idleConf.GetThemeDict(theme_type, theme_name)
terryjreedye5bb1122017-07-05 00:54:55 -04001365 # Apply any of the old theme's unsaved changes to the new theme.
terryjreedyedc03422017-07-07 16:37:39 -04001366 if theme_name in changes['highlight']:
1367 theme_changes = changes['highlight'][theme_name]
terryjreedy938e7382017-06-26 20:48:39 -04001368 for element in theme_changes:
1369 new_theme[element] = theme_changes[element]
terryjreedye5bb1122017-07-05 00:54:55 -04001370 # Save the new theme.
terryjreedy938e7382017-06-26 20:48:39 -04001371 self.save_new_theme(new_theme_name, new_theme)
terryjreedye5bb1122017-07-05 00:54:55 -04001372 # Change GUI over to the new theme.
terryjreedy938e7382017-06-26 20:48:39 -04001373 custom_theme_list = idleConf.GetSectionList('user', 'highlight')
1374 custom_theme_list.sort()
1375 self.opt_menu_theme_custom.SetMenu(custom_theme_list, new_theme_name)
1376 self.is_builtin_theme.set(0)
1377 self.set_theme_type()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001378
terryjreedy938e7382017-06-26 20:48:39 -04001379 def set_highlight_target(self):
terryjreedy9a09c662017-07-13 23:53:30 -04001380 """Set fg/bg toggle and color based on highlight tag target.
1381
1382 Instance variables accessed:
1383 highlight_target
1384
1385 Attributes updated:
1386 radio_fg
1387 radio_bg
1388 fg_bg_toggle
1389
1390 Methods:
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001391 set_color_sample
terryjreedy9a09c662017-07-13 23:53:30 -04001392
1393 Called from:
1394 var_changed_highlight_target
1395 load_theme_cfg
1396 """
terryjreedye5bb1122017-07-05 00:54:55 -04001397 if self.highlight_target.get() == 'Cursor': # bg not possible
Terry Jan Reedy0c4c6512017-07-26 21:41:26 -04001398 self.radio_fg['state'] = DISABLED
1399 self.radio_bg['state'] = DISABLED
terryjreedy938e7382017-06-26 20:48:39 -04001400 self.fg_bg_toggle.set(1)
terryjreedye5bb1122017-07-05 00:54:55 -04001401 else: # Both fg and bg can be set.
Terry Jan Reedy0c4c6512017-07-26 21:41:26 -04001402 self.radio_fg['state'] = NORMAL
1403 self.radio_bg['state'] = NORMAL
terryjreedy938e7382017-06-26 20:48:39 -04001404 self.fg_bg_toggle.set(1)
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001405 self.set_color_sample()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001406
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001407 def set_color_sample_binding(self, *args):
terryjreedy9a09c662017-07-13 23:53:30 -04001408 """Change color sample based on foreground/background toggle.
1409
1410 Methods:
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001411 set_color_sample
terryjreedy9a09c662017-07-13 23:53:30 -04001412 """
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001413 self.set_color_sample()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001414
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001415 def set_color_sample(self):
terryjreedy9a09c662017-07-13 23:53:30 -04001416 """Set the color of the frame background to reflect the selected target.
1417
1418 Instance variables accessed:
1419 theme_elements
1420 highlight_target
1421 fg_bg_toggle
Terry Jan Reedy04864b42017-07-22 00:56:18 -04001422 highlight_sample
terryjreedy9a09c662017-07-13 23:53:30 -04001423
1424 Attributes updated:
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001425 frame_color_set
terryjreedy9a09c662017-07-13 23:53:30 -04001426 """
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001427 # Set the color sample area.
terryjreedy938e7382017-06-26 20:48:39 -04001428 tag = self.theme_elements[self.highlight_target.get()][0]
1429 plane = 'foreground' if self.fg_bg_toggle.get() else 'background'
Terry Jan Reedy04864b42017-07-22 00:56:18 -04001430 color = self.highlight_sample.tag_cget(tag, plane)
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001431 self.frame_color_set.config(bg=color)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001432
terryjreedy938e7382017-06-26 20:48:39 -04001433 def paint_theme_sample(self):
terryjreedy9a09c662017-07-13 23:53:30 -04001434 """Apply the theme colors to each element tag in the sample text.
1435
1436 Instance attributes accessed:
1437 theme_elements
1438 is_builtin_theme
1439 builtin_theme
1440 custom_theme
1441
1442 Attributes updated:
Terry Jan Reedy04864b42017-07-22 00:56:18 -04001443 highlight_sample: Set the tag elements to the theme.
terryjreedy9a09c662017-07-13 23:53:30 -04001444
1445 Methods:
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001446 set_color_sample
terryjreedy9a09c662017-07-13 23:53:30 -04001447
1448 Called from:
1449 var_changed_builtin_theme
1450 var_changed_custom_theme
1451 load_theme_cfg
1452 """
terryjreedye5bb1122017-07-05 00:54:55 -04001453 if self.is_builtin_theme.get(): # Default theme
terryjreedy938e7382017-06-26 20:48:39 -04001454 theme = self.builtin_theme.get()
terryjreedye5bb1122017-07-05 00:54:55 -04001455 else: # User theme
terryjreedy938e7382017-06-26 20:48:39 -04001456 theme = self.custom_theme.get()
1457 for element_title in self.theme_elements:
1458 element = self.theme_elements[element_title][0]
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001459 colors = idleConf.GetHighlight(theme, element)
terryjreedye5bb1122017-07-05 00:54:55 -04001460 if element == 'cursor': # Cursor sample needs special painting.
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001461 colors['background'] = idleConf.GetHighlight(
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001462 theme, 'normal', fgBg='bg')
terryjreedye5bb1122017-07-05 00:54:55 -04001463 # Handle any unsaved changes to this theme.
terryjreedyedc03422017-07-07 16:37:39 -04001464 if theme in changes['highlight']:
1465 theme_dict = changes['highlight'][theme]
terryjreedy938e7382017-06-26 20:48:39 -04001466 if element + '-foreground' in theme_dict:
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001467 colors['foreground'] = theme_dict[element + '-foreground']
terryjreedy938e7382017-06-26 20:48:39 -04001468 if element + '-background' in theme_dict:
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001469 colors['background'] = theme_dict[element + '-background']
Terry Jan Reedy04864b42017-07-22 00:56:18 -04001470 self.highlight_sample.tag_config(element, **colors)
Terry Jan Reedyac5c1e22017-07-21 01:29:09 -04001471 self.set_color_sample()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001472
terryjreedy938e7382017-06-26 20:48:39 -04001473 def load_theme_cfg(self):
terryjreedy9a09c662017-07-13 23:53:30 -04001474 """Load current configuration settings for the theme options.
1475
1476 Based on the is_builtin_theme toggle, the theme is set as
1477 either builtin or custom and the initial widget values
1478 reflect the current settings from idleConf.
1479
1480 Attributes updated:
1481 is_builtin_theme: Set from idleConf.
1482 opt_menu_theme_builtin: List of default themes from idleConf.
1483 opt_menu_theme_custom: List of custom themes from idleConf.
1484 radio_theme_custom: Disabled if there are no custom themes.
1485 custom_theme: Message with additional information.
1486 opt_menu_highlight_target: Create menu from self.theme_elements.
1487
1488 Methods:
1489 set_theme_type
1490 paint_theme_sample
1491 set_highlight_target
1492 """
terryjreedye5bb1122017-07-05 00:54:55 -04001493 # Set current theme type radiobutton.
terryjreedy938e7382017-06-26 20:48:39 -04001494 self.is_builtin_theme.set(idleConf.GetOption(
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001495 'main', 'Theme', 'default', type='bool', default=1))
terryjreedye5bb1122017-07-05 00:54:55 -04001496 # Set current theme.
terryjreedy938e7382017-06-26 20:48:39 -04001497 current_option = idleConf.CurrentTheme()
terryjreedye5bb1122017-07-05 00:54:55 -04001498 # Load available theme option menus.
1499 if self.is_builtin_theme.get(): # Default theme selected.
terryjreedy938e7382017-06-26 20:48:39 -04001500 item_list = idleConf.GetSectionList('default', 'highlight')
1501 item_list.sort()
1502 self.opt_menu_theme_builtin.SetMenu(item_list, current_option)
1503 item_list = idleConf.GetSectionList('user', 'highlight')
1504 item_list.sort()
1505 if not item_list:
Terry Jan Reedy0c4c6512017-07-26 21:41:26 -04001506 self.radio_theme_custom['state'] = DISABLED
terryjreedy938e7382017-06-26 20:48:39 -04001507 self.custom_theme.set('- no custom themes -')
Steven M. Gava41a85322001-10-29 08:05:34 +00001508 else:
terryjreedy938e7382017-06-26 20:48:39 -04001509 self.opt_menu_theme_custom.SetMenu(item_list, item_list[0])
terryjreedye5bb1122017-07-05 00:54:55 -04001510 else: # User theme selected.
terryjreedy938e7382017-06-26 20:48:39 -04001511 item_list = idleConf.GetSectionList('user', 'highlight')
1512 item_list.sort()
1513 self.opt_menu_theme_custom.SetMenu(item_list, current_option)
1514 item_list = idleConf.GetSectionList('default', 'highlight')
1515 item_list.sort()
1516 self.opt_menu_theme_builtin.SetMenu(item_list, item_list[0])
1517 self.set_theme_type()
terryjreedye5bb1122017-07-05 00:54:55 -04001518 # Load theme element option menu.
terryjreedy938e7382017-06-26 20:48:39 -04001519 theme_names = list(self.theme_elements.keys())
1520 theme_names.sort(key=lambda x: self.theme_elements[x][1])
1521 self.opt_menu_highlight_target.SetMenu(theme_names, theme_names[0])
1522 self.paint_theme_sample()
1523 self.set_highlight_target()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001524
terryjreedy938e7382017-06-26 20:48:39 -04001525 def load_key_cfg(self):
terryjreedye5bb1122017-07-05 00:54:55 -04001526 "Load current configuration settings for the keybinding options."
1527 # Set current keys type radiobutton.
terryjreedy938e7382017-06-26 20:48:39 -04001528 self.are_keys_builtin.set(idleConf.GetOption(
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001529 'main', 'Keys', 'default', type='bool', default=1))
terryjreedye5bb1122017-07-05 00:54:55 -04001530 # Set current keys.
terryjreedy938e7382017-06-26 20:48:39 -04001531 current_option = idleConf.CurrentKeys()
terryjreedye5bb1122017-07-05 00:54:55 -04001532 # Load available keyset option menus.
1533 if self.are_keys_builtin.get(): # Default theme selected.
terryjreedy938e7382017-06-26 20:48:39 -04001534 item_list = idleConf.GetSectionList('default', 'keys')
1535 item_list.sort()
1536 self.opt_menu_keys_builtin.SetMenu(item_list, current_option)
1537 item_list = idleConf.GetSectionList('user', 'keys')
1538 item_list.sort()
1539 if not item_list:
Terry Jan Reedy0c4c6512017-07-26 21:41:26 -04001540 self.radio_keys_custom['state'] = DISABLED
terryjreedy938e7382017-06-26 20:48:39 -04001541 self.custom_keys.set('- no custom keys -')
Steven M. Gava41a85322001-10-29 08:05:34 +00001542 else:
terryjreedy938e7382017-06-26 20:48:39 -04001543 self.opt_menu_keys_custom.SetMenu(item_list, item_list[0])
terryjreedye5bb1122017-07-05 00:54:55 -04001544 else: # User key set selected.
terryjreedy938e7382017-06-26 20:48:39 -04001545 item_list = idleConf.GetSectionList('user', 'keys')
1546 item_list.sort()
1547 self.opt_menu_keys_custom.SetMenu(item_list, current_option)
1548 item_list = idleConf.GetSectionList('default', 'keys')
1549 item_list.sort()
1550 self.opt_menu_keys_builtin.SetMenu(item_list, idleConf.default_keys())
1551 self.set_keys_type()
terryjreedye5bb1122017-07-05 00:54:55 -04001552 # Load keyset element list.
terryjreedy938e7382017-06-26 20:48:39 -04001553 keyset_name = idleConf.CurrentKeys()
1554 self.load_keys_list(keyset_name)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001555
terryjreedy938e7382017-06-26 20:48:39 -04001556 def load_configs(self):
terryjreedye5bb1122017-07-05 00:54:55 -04001557 """Load configuration for each page.
1558
1559 Load configuration from default and user config files and populate
Steven M. Gava429a86af2001-10-23 10:42:12 +00001560 the widgets on the config dialog pages.
terryjreedy9a09c662017-07-13 23:53:30 -04001561
1562 Methods:
1563 load_font_cfg
1564 load_tab_cfg
1565 load_theme_cfg
1566 load_key_cfg
1567 load_general_cfg
Steven M. Gava429a86af2001-10-23 10:42:12 +00001568 """
terryjreedy938e7382017-06-26 20:48:39 -04001569 self.load_font_cfg()
1570 self.load_tab_cfg()
terryjreedy938e7382017-06-26 20:48:39 -04001571 self.load_theme_cfg()
terryjreedy938e7382017-06-26 20:48:39 -04001572 self.load_key_cfg()
terryjreedy938e7382017-06-26 20:48:39 -04001573 self.load_general_cfg()
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001574 # note: extension page handled separately
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001575
terryjreedy938e7382017-06-26 20:48:39 -04001576 def save_new_key_set(self, keyset_name, keyset):
terryjreedye5bb1122017-07-05 00:54:55 -04001577 """Save a newly created core key set.
1578
terryjreedy938e7382017-06-26 20:48:39 -04001579 keyset_name - string, the name of the new key set
1580 keyset - dictionary containing the new key set
Steven M. Gava052937f2002-02-11 02:20:53 +00001581 """
terryjreedy938e7382017-06-26 20:48:39 -04001582 if not idleConf.userCfg['keys'].has_section(keyset_name):
1583 idleConf.userCfg['keys'].add_section(keyset_name)
1584 for event in keyset:
1585 value = keyset[event]
1586 idleConf.userCfg['keys'].SetOption(keyset_name, event, value)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001587
terryjreedy938e7382017-06-26 20:48:39 -04001588 def save_new_theme(self, theme_name, theme):
terryjreedy9a09c662017-07-13 23:53:30 -04001589 """Save a newly created theme to idleConf.
terryjreedye5bb1122017-07-05 00:54:55 -04001590
terryjreedy938e7382017-06-26 20:48:39 -04001591 theme_name - string, the name of the new theme
Steven M. Gava052937f2002-02-11 02:20:53 +00001592 theme - dictionary containing the new theme
1593 """
terryjreedy938e7382017-06-26 20:48:39 -04001594 if not idleConf.userCfg['highlight'].has_section(theme_name):
1595 idleConf.userCfg['highlight'].add_section(theme_name)
Kurt B. Kaisere0712772007-08-23 05:25:55 +00001596 for element in theme:
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001597 value = theme[element]
terryjreedy938e7382017-06-26 20:48:39 -04001598 idleConf.userCfg['highlight'].SetOption(theme_name, element, value)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001599
terryjreedy938e7382017-06-26 20:48:39 -04001600 def deactivate_current_config(self):
terryjreedy9a09c662017-07-13 23:53:30 -04001601 """Remove current key bindings.
1602
1603 Iterate over window instances defined in parent and remove
1604 the keybindings.
1605 """
terryjreedye5bb1122017-07-05 00:54:55 -04001606 # Before a config is saved, some cleanup of current
1607 # config must be done - remove the previous keybindings.
terryjreedy938e7382017-06-26 20:48:39 -04001608 win_instances = self.parent.instance_dict.keys()
1609 for instance in win_instances:
Kurt B. Kaiserb1754452005-11-18 22:05:48 +00001610 instance.RemoveKeybindings()
1611
terryjreedy938e7382017-06-26 20:48:39 -04001612 def activate_config_changes(self):
terryjreedy9a09c662017-07-13 23:53:30 -04001613 """Apply configuration changes to current windows.
1614
1615 Dynamically update the current parent window instances
1616 with some of the configuration changes.
1617 """
terryjreedy938e7382017-06-26 20:48:39 -04001618 win_instances = self.parent.instance_dict.keys()
1619 for instance in win_instances:
Steven M. Gavab77d3432002-03-02 07:16:21 +00001620 instance.ResetColorizer()
Steven M. Gavab1585412002-03-12 00:21:56 +00001621 instance.ResetFont()
Kurt B. Kaiseracdef852005-01-31 03:34:26 +00001622 instance.set_notabs_indentwidth()
Kurt B. Kaiserb1754452005-11-18 22:05:48 +00001623 instance.ApplyKeybindings()
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +00001624 instance.reset_help_menu_entries()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001625
terryjreedy938e7382017-06-26 20:48:39 -04001626 def cancel(self):
terryjreedy9a09c662017-07-13 23:53:30 -04001627 """Dismiss config dialog.
1628
1629 Methods:
1630 destroy: inherited
1631 """
Steven M. Gava5f28e8f2002-01-21 06:38:21 +00001632 self.destroy()
1633
terryjreedy938e7382017-06-26 20:48:39 -04001634 def ok(self):
terryjreedy9a09c662017-07-13 23:53:30 -04001635 """Apply config changes, then dismiss dialog.
1636
1637 Methods:
1638 apply
1639 destroy: inherited
1640 """
terryjreedy938e7382017-06-26 20:48:39 -04001641 self.apply()
Steven M. Gava5f28e8f2002-01-21 06:38:21 +00001642 self.destroy()
1643
terryjreedy938e7382017-06-26 20:48:39 -04001644 def apply(self):
terryjreedy9a09c662017-07-13 23:53:30 -04001645 """Apply config changes and leave dialog open.
1646
1647 Methods:
1648 deactivate_current_config
1649 save_all_changed_extensions
1650 activate_config_changes
1651 """
terryjreedy938e7382017-06-26 20:48:39 -04001652 self.deactivate_current_config()
terryjreedyedc03422017-07-07 16:37:39 -04001653 changes.save_all()
1654 self.save_all_changed_extensions()
terryjreedy938e7382017-06-26 20:48:39 -04001655 self.activate_config_changes()
Steven M. Gava5f28e8f2002-01-21 06:38:21 +00001656
terryjreedy938e7382017-06-26 20:48:39 -04001657 def help(self):
terryjreedy9a09c662017-07-13 23:53:30 -04001658 """Create textview for config dialog help.
1659
1660 Attrbutes accessed:
1661 tab_pages
1662
1663 Methods:
1664 view_text: Method from textview module.
1665 """
terryjreedy938e7382017-06-26 20:48:39 -04001666 page = self.tab_pages._current_page
Terry Jan Reedyd0cadba2015-10-11 22:07:31 -04001667 view_text(self, title='Help for IDLE preferences',
1668 text=help_common+help_pages.get(page, ''))
1669
terryjreedy938e7382017-06-26 20:48:39 -04001670 def create_page_extensions(self):
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001671 """Part of the config dialog used for configuring IDLE extensions.
1672
1673 This code is generic - it works for any and all IDLE extensions.
1674
1675 IDLE extensions save their configuration options using idleConf.
Terry Jan Reedyb2f87602015-10-13 22:09:06 -04001676 This code reads the current configuration using idleConf, supplies a
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001677 GUI interface to change the configuration values, and saves the
1678 changes using idleConf.
1679
1680 Not all changes take effect immediately - some may require restarting IDLE.
1681 This depends on each extension's implementation.
1682
1683 All values are treated as text, and it is up to the user to supply
1684 reasonable values. The only exception to this are the 'enable*' options,
Serhiy Storchaka6a7b3a72016-04-17 08:32:47 +03001685 which are boolean, and can be toggled with a True/False button.
terryjreedy9a09c662017-07-13 23:53:30 -04001686
1687 Methods:
1688 load_extentions:
1689 extension_selected: Handle selection from list.
1690 create_extension_frame: Hold widgets for one extension.
1691 set_extension_value: Set in userCfg['extensions'].
1692 save_all_changed_extensions: Call extension page Save().
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001693 """
1694 parent = self.parent
terryjreedy938e7382017-06-26 20:48:39 -04001695 frame = self.tab_pages.pages['Extensions'].frame
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001696 self.ext_defaultCfg = idleConf.defaultCfg['extensions']
1697 self.ext_userCfg = idleConf.userCfg['extensions']
1698 self.is_int = self.register(is_int)
1699 self.load_extensions()
terryjreedye5bb1122017-07-05 00:54:55 -04001700 # Create widgets - a listbox shows all available extensions, with the
1701 # controls for the extension selected in the listbox to the right.
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001702 self.extension_names = StringVar(self)
1703 frame.rowconfigure(0, weight=1)
1704 frame.columnconfigure(2, weight=1)
1705 self.extension_list = Listbox(frame, listvariable=self.extension_names,
1706 selectmode='browse')
1707 self.extension_list.bind('<<ListboxSelect>>', self.extension_selected)
1708 scroll = Scrollbar(frame, command=self.extension_list.yview)
1709 self.extension_list.yscrollcommand=scroll.set
1710 self.details_frame = LabelFrame(frame, width=250, height=250)
1711 self.extension_list.grid(column=0, row=0, sticky='nws')
1712 scroll.grid(column=1, row=0, sticky='ns')
1713 self.details_frame.grid(column=2, row=0, sticky='nsew', padx=[10, 0])
1714 frame.configure(padx=10, pady=10)
1715 self.config_frame = {}
1716 self.current_extension = None
1717
1718 self.outerframe = self # TEMPORARY
1719 self.tabbed_page_set = self.extension_list # TEMPORARY
1720
terryjreedye5bb1122017-07-05 00:54:55 -04001721 # Create the frame holding controls for each extension.
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001722 ext_names = ''
1723 for ext_name in sorted(self.extensions):
1724 self.create_extension_frame(ext_name)
1725 ext_names = ext_names + '{' + ext_name + '} '
1726 self.extension_names.set(ext_names)
1727 self.extension_list.selection_set(0)
1728 self.extension_selected(None)
1729
1730 def load_extensions(self):
1731 "Fill self.extensions with data from the default and user configs."
1732 self.extensions = {}
1733 for ext_name in idleConf.GetExtensions(active_only=False):
1734 self.extensions[ext_name] = []
1735
1736 for ext_name in self.extensions:
1737 opt_list = sorted(self.ext_defaultCfg.GetOptionList(ext_name))
1738
terryjreedye5bb1122017-07-05 00:54:55 -04001739 # Bring 'enable' options to the beginning of the list.
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001740 enables = [opt_name for opt_name in opt_list
1741 if opt_name.startswith('enable')]
1742 for opt_name in enables:
1743 opt_list.remove(opt_name)
1744 opt_list = enables + opt_list
1745
1746 for opt_name in opt_list:
1747 def_str = self.ext_defaultCfg.Get(
1748 ext_name, opt_name, raw=True)
1749 try:
1750 def_obj = {'True':True, 'False':False}[def_str]
1751 opt_type = 'bool'
1752 except KeyError:
1753 try:
1754 def_obj = int(def_str)
1755 opt_type = 'int'
1756 except ValueError:
1757 def_obj = def_str
1758 opt_type = None
1759 try:
1760 value = self.ext_userCfg.Get(
1761 ext_name, opt_name, type=opt_type, raw=True,
1762 default=def_obj)
terryjreedye5bb1122017-07-05 00:54:55 -04001763 except ValueError: # Need this until .Get fixed.
1764 value = def_obj # Bad values overwritten by entry.
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001765 var = StringVar(self)
1766 var.set(str(value))
1767
1768 self.extensions[ext_name].append({'name': opt_name,
1769 'type': opt_type,
1770 'default': def_str,
1771 'value': value,
1772 'var': var,
1773 })
1774
1775 def extension_selected(self, event):
terryjreedye5bb1122017-07-05 00:54:55 -04001776 "Handle selection of an extension from the list."
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001777 newsel = self.extension_list.curselection()
1778 if newsel:
1779 newsel = self.extension_list.get(newsel)
1780 if newsel is None or newsel != self.current_extension:
1781 if self.current_extension:
1782 self.details_frame.config(text='')
1783 self.config_frame[self.current_extension].grid_forget()
1784 self.current_extension = None
1785 if newsel:
1786 self.details_frame.config(text=newsel)
1787 self.config_frame[newsel].grid(column=0, row=0, sticky='nsew')
1788 self.current_extension = newsel
1789
1790 def create_extension_frame(self, ext_name):
1791 """Create a frame holding the widgets to configure one extension"""
1792 f = VerticalScrolledFrame(self.details_frame, height=250, width=250)
1793 self.config_frame[ext_name] = f
1794 entry_area = f.interior
terryjreedye5bb1122017-07-05 00:54:55 -04001795 # Create an entry for each configuration option.
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001796 for row, opt in enumerate(self.extensions[ext_name]):
terryjreedye5bb1122017-07-05 00:54:55 -04001797 # Create a row with a label and entry/checkbutton.
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001798 label = Label(entry_area, text=opt['name'])
1799 label.grid(row=row, column=0, sticky=NW)
1800 var = opt['var']
1801 if opt['type'] == 'bool':
1802 Checkbutton(entry_area, textvariable=var, variable=var,
1803 onvalue='True', offvalue='False',
1804 indicatoron=FALSE, selectcolor='', width=8
1805 ).grid(row=row, column=1, sticky=W, padx=7)
1806 elif opt['type'] == 'int':
1807 Entry(entry_area, textvariable=var, validate='key',
1808 validatecommand=(self.is_int, '%P')
1809 ).grid(row=row, column=1, sticky=NSEW, padx=7)
1810
1811 else:
1812 Entry(entry_area, textvariable=var
1813 ).grid(row=row, column=1, sticky=NSEW, padx=7)
1814 return
1815
1816 def set_extension_value(self, section, opt):
terryjreedye5bb1122017-07-05 00:54:55 -04001817 """Return True if the configuration was added or changed.
1818
1819 If the value is the same as the default, then remove it
1820 from user config file.
1821 """
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001822 name = opt['name']
1823 default = opt['default']
1824 value = opt['var'].get().strip() or default
1825 opt['var'].set(value)
1826 # if self.defaultCfg.has_section(section):
terryjreedye5bb1122017-07-05 00:54:55 -04001827 # Currently, always true; if not, indent to return.
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001828 if (value == default):
1829 return self.ext_userCfg.RemoveOption(section, name)
terryjreedye5bb1122017-07-05 00:54:55 -04001830 # Set the option.
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001831 return self.ext_userCfg.SetOption(section, name, value)
1832
1833 def save_all_changed_extensions(self):
terryjreedy9a09c662017-07-13 23:53:30 -04001834 """Save configuration changes to the user config file.
1835
1836 Attributes accessed:
1837 extensions
1838
1839 Methods:
1840 set_extension_value
1841 """
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001842 has_changes = False
1843 for ext_name in self.extensions:
1844 options = self.extensions[ext_name]
1845 for opt in options:
1846 if self.set_extension_value(ext_name, opt):
1847 has_changes = True
1848 if has_changes:
1849 self.ext_userCfg.Save()
1850
1851
Terry Jan Reedy0243bea2017-07-26 20:53:13 -04001852class VarTrace:
1853 """Maintain Tk variables trace state."""
1854
1855 def __init__(self):
1856 """Store Tk variables and callbacks.
1857
1858 untraced: List of tuples (var, callback)
1859 that do not have the callback attached
1860 to the Tk var.
1861 traced: List of tuples (var, callback) where
1862 that callback has been attached to the var.
1863 """
1864 self.untraced = []
1865 self.traced = []
1866
1867 def add(self, var, callback):
1868 """Add (var, callback) tuple to untraced list.
1869
1870 Args:
1871 var: Tk variable instance.
1872 callback: Function to be used as a callback or
1873 a tuple with IdleConf values for default
1874 callback.
1875
1876 Return:
1877 Tk variable instance.
1878 """
1879 if isinstance(callback, tuple):
1880 callback = self.make_callback(var, callback)
1881 self.untraced.append((var, callback))
1882 return var
1883
1884 @staticmethod
1885 def make_callback(var, config):
1886 "Return default callback function to add values to changes instance."
1887 def default_callback(*params):
1888 "Add config values to changes instance."
1889 changes.add_option(*config, var.get())
1890 return default_callback
1891
1892 def attach(self):
1893 "Attach callback to all vars that are not traced."
1894 while self.untraced:
1895 var, callback = self.untraced.pop()
1896 var.trace_add('write', callback)
1897 self.traced.append((var, callback))
1898
1899 def detach(self):
1900 "Remove callback from traced vars."
1901 while self.traced:
1902 var, callback = self.traced.pop()
1903 var.trace_remove('write', var.trace_info()[0][1])
1904 self.untraced.append((var, callback))
1905
1906
Terry Jan Reedyd0cadba2015-10-11 22:07:31 -04001907help_common = '''\
1908When you click either the Apply or Ok buttons, settings in this
1909dialog that are different from IDLE's default are saved in
1910a .idlerc directory in your home directory. Except as noted,
Terry Jan Reedyd0c0f002015-11-12 15:02:57 -05001911these changes apply to all versions of IDLE installed on this
Terry Jan Reedyd0cadba2015-10-11 22:07:31 -04001912machine. Some do not take affect until IDLE is restarted.
1913[Cancel] only cancels changes made since the last save.
1914'''
1915help_pages = {
Terry Jan Reedy9bdb1ed2016-07-10 13:46:34 -04001916 'Highlighting': '''
Terry Jan Reedyd0cadba2015-10-11 22:07:31 -04001917Highlighting:
Terry Jan Reedyd0c0f002015-11-12 15:02:57 -05001918The IDLE Dark color theme is new in October 2015. It can only
Terry Jan Reedyd0cadba2015-10-11 22:07:31 -04001919be used with older IDLE releases if it is saved as a custom
1920theme, with a different name.
Terry Jan Reedy9bdb1ed2016-07-10 13:46:34 -04001921''',
1922 'Keys': '''
1923Keys:
1924The IDLE Modern Unix key set is new in June 2016. It can only
1925be used with older IDLE releases if it is saved as a custom
1926key set, with a different name.
1927''',
terryjreedyaf683822017-06-27 23:02:19 -04001928 'Extensions': '''
1929Extensions:
1930
1931Autocomplete: Popupwait is milleseconds to wait after key char, without
1932cursor movement, before popping up completion box. Key char is '.' after
1933identifier or a '/' (or '\\' on Windows) within a string.
1934
1935FormatParagraph: Max-width is max chars in lines after re-formatting.
1936Use with paragraphs in both strings and comment blocks.
1937
1938ParenMatch: Style indicates what is highlighted when closer is entered:
1939'opener' - opener '({[' corresponding to closer; 'parens' - both chars;
1940'expression' (default) - also everything in between. Flash-delay is how
1941long to highlight if cursor is not moved (0 means forever).
1942'''
Terry Jan Reedyd0cadba2015-10-11 22:07:31 -04001943}
1944
Steven M. Gavac11ccf32001-09-24 09:43:17 +00001945
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001946def is_int(s):
1947 "Return 's is blank or represents an int'"
1948 if not s:
1949 return True
1950 try:
1951 int(s)
1952 return True
1953 except ValueError:
1954 return False
1955
1956
Terry Jan Reedya9421fb2014-10-22 20:15:18 -04001957class VerticalScrolledFrame(Frame):
1958 """A pure Tkinter vertically scrollable frame.
1959
1960 * Use the 'interior' attribute to place widgets inside the scrollable frame
1961 * Construct and pack/place/grid normally
1962 * This frame only allows vertical scrolling
1963 """
1964 def __init__(self, parent, *args, **kw):
1965 Frame.__init__(self, parent, *args, **kw)
1966
terryjreedye5bb1122017-07-05 00:54:55 -04001967 # Create a canvas object and a vertical scrollbar for scrolling it.
Terry Jan Reedya9421fb2014-10-22 20:15:18 -04001968 vscrollbar = Scrollbar(self, orient=VERTICAL)
1969 vscrollbar.pack(fill=Y, side=RIGHT, expand=FALSE)
1970 canvas = Canvas(self, bd=0, highlightthickness=0,
Terry Jan Reedyd0812292015-10-22 03:27:31 -04001971 yscrollcommand=vscrollbar.set, width=240)
Terry Jan Reedya9421fb2014-10-22 20:15:18 -04001972 canvas.pack(side=LEFT, fill=BOTH, expand=TRUE)
1973 vscrollbar.config(command=canvas.yview)
1974
terryjreedye5bb1122017-07-05 00:54:55 -04001975 # Reset the view.
Terry Jan Reedya9421fb2014-10-22 20:15:18 -04001976 canvas.xview_moveto(0)
1977 canvas.yview_moveto(0)
1978
terryjreedye5bb1122017-07-05 00:54:55 -04001979 # Create a frame inside the canvas which will be scrolled with it.
Terry Jan Reedya9421fb2014-10-22 20:15:18 -04001980 self.interior = interior = Frame(canvas)
1981 interior_id = canvas.create_window(0, 0, window=interior, anchor=NW)
1982
terryjreedye5bb1122017-07-05 00:54:55 -04001983 # Track changes to the canvas and frame width and sync them,
1984 # also updating the scrollbar.
Terry Jan Reedya9421fb2014-10-22 20:15:18 -04001985 def _configure_interior(event):
terryjreedye5bb1122017-07-05 00:54:55 -04001986 # Update the scrollbars to match the size of the inner frame.
Terry Jan Reedya9421fb2014-10-22 20:15:18 -04001987 size = (interior.winfo_reqwidth(), interior.winfo_reqheight())
1988 canvas.config(scrollregion="0 0 %s %s" % size)
Terry Jan Reedya9421fb2014-10-22 20:15:18 -04001989 interior.bind('<Configure>', _configure_interior)
1990
1991 def _configure_canvas(event):
1992 if interior.winfo_reqwidth() != canvas.winfo_width():
terryjreedye5bb1122017-07-05 00:54:55 -04001993 # Update the inner frame's width to fill the canvas.
Terry Jan Reedya9421fb2014-10-22 20:15:18 -04001994 canvas.itemconfigure(interior_id, width=canvas.winfo_width())
1995 canvas.bind('<Configure>', _configure_canvas)
1996
1997 return
1998
Terry Jan Reedya9421fb2014-10-22 20:15:18 -04001999
Steven M. Gava44d3d1a2001-07-31 06:59:02 +00002000if __name__ == '__main__':
Terry Jan Reedycfa89502014-07-14 23:07:32 -04002001 import unittest
2002 unittest.main('idlelib.idle_test.test_configdialog',
2003 verbosity=2, exit=False)
Terry Jan Reedy2e8234a2014-05-29 01:46:26 -04002004 from idlelib.idle_test.htest import run
Terry Jan Reedy47304c02015-10-20 02:15:28 -04002005 run(ConfigDialog)