blob: cf5bea7ec7ece67b09a579cc4409bcf85d548278 [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"""
csabellabac7d332017-06-26 17:46:26 -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,
Louie Lubb2bae82017-07-10 06:57:18 +080017 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
terryjreedy349abd92017-07-07 16:00:57 -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
terryjreedy349abd92017-07-07 16:00:57 -040031changes = ConfigChanges()
32
csabella7eb58832017-07-04 21:30:58 -040033
Steven M. Gava44d3d1a2001-07-31 06:59:02 +000034class ConfigDialog(Toplevel):
csabella7eb58832017-07-04 21:30:58 -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):
csabella7eb58832017-07-04 21:30:58 -040039 """Show the tabbed dialog for user configuration.
40
41 parent - parent of this dialog
42 title - string which is the title of this popup dialog
Terry Jan Reedy2e8234a2014-05-29 01:46:26 -040043 _htest - bool, change box location when running htest
Terry Jan Reedycfa89502014-07-14 23:07:32 -040044 _utest - bool, don't wait_window when running unittest
Terry Jan Reedy2e8234a2014-05-29 01:46:26 -040045 """
Steven M. Gavad721c482001-07-31 10:46:53 +000046 Toplevel.__init__(self, parent)
Terry Jan Reedy22405332014-07-30 19:24:32 -040047 self.parent = parent
Terry Jan Reedy4036d872014-08-03 23:02:58 -040048 if _htest:
49 parent.instance_dict = {}
csabellabac7d332017-06-26 17:46:26 -040050 self.withdraw()
Guido van Rossum8ce8a782007-11-01 19:42:39 +000051
Steven M. Gavad721c482001-07-31 10:46:53 +000052 self.configure(borderwidth=5)
Terry Jan Reedycd567362014-10-17 01:31:35 -040053 self.title(title or 'IDLE Preferences')
csabellabac7d332017-06-26 17:46:26 -040054 x = parent.winfo_rootx() + 20
55 y = parent.winfo_rooty() + (30 if not _htest else 150)
56 self.geometry(f'+{x}+{y}')
csabella7eb58832017-07-04 21:30:58 -040057 # Each theme element key is its display name.
58 # The first value of the tuple is the sample area tag name.
59 # The second value is the display name list sort index.
csabellabac7d332017-06-26 17:46:26 -040060 self.theme_elements={
Terry Jan Reedya8aa4d52015-10-02 22:12:17 -040061 'Normal Text': ('normal', '00'),
62 'Python Keywords': ('keyword', '01'),
63 'Python Definitions': ('definition', '02'),
64 'Python Builtins': ('builtin', '03'),
65 'Python Comments': ('comment', '04'),
66 'Python Strings': ('string', '05'),
67 'Selected Text': ('hilite', '06'),
68 'Found Text': ('hit', '07'),
69 'Cursor': ('cursor', '08'),
70 'Editor Breakpoint': ('break', '09'),
71 'Shell Normal Text': ('console', '10'),
72 'Shell Error Text': ('error', '11'),
73 'Shell Stdout Text': ('stdout', '12'),
74 'Shell Stderr Text': ('stderr', '13'),
Kurt B. Kaiser73360a32004-03-08 18:15:31 +000075 }
csabellabac7d332017-06-26 17:46:26 -040076 self.create_widgets()
Terry Jan Reedy4036d872014-08-03 23:02:58 -040077 self.resizable(height=FALSE, width=FALSE)
Steven M. Gavad721c482001-07-31 10:46:53 +000078 self.transient(parent)
79 self.grab_set()
csabellabac7d332017-06-26 17:46:26 -040080 self.protocol("WM_DELETE_WINDOW", self.cancel)
Louie Lubb2bae82017-07-10 06:57:18 +080081 self.fontlist.focus_set()
csabella7eb58832017-07-04 21:30:58 -040082 # XXX Decide whether to keep or delete these key bindings.
83 # Key bindings for this dialog.
84 # self.bind('<Escape>', self.Cancel) #dismiss dialog, no save
85 # self.bind('<Alt-a>', self.Apply) #apply changes, save
86 # self.bind('<F1>', self.Help) #context help
csabellabac7d332017-06-26 17:46:26 -040087 self.load_configs()
csabella7eb58832017-07-04 21:30:58 -040088 self.attach_var_callbacks() # Avoid callbacks during load_configs.
Guido van Rossum8ce8a782007-11-01 19:42:39 +000089
Terry Jan Reedycfa89502014-07-14 23:07:32 -040090 if not _utest:
91 self.wm_deiconify()
92 self.wait_window()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000093
csabellabac7d332017-06-26 17:46:26 -040094 def create_widgets(self):
csabella7eb58832017-07-04 21:30:58 -040095 "Create and place widgets for tabbed dialog."
csabellabac7d332017-06-26 17:46:26 -040096 self.tab_pages = TabbedPageSet(self,
Terry Jan Reedy93f35422015-10-13 22:03:51 -040097 page_names=['Fonts/Tabs', 'Highlighting', 'Keys', 'General',
98 'Extensions'])
csabellabac7d332017-06-26 17:46:26 -040099 self.tab_pages.pack(side=TOP, expand=TRUE, fill=BOTH)
100 self.create_page_font_tab()
101 self.create_page_highlight()
102 self.create_page_keys()
103 self.create_page_general()
104 self.create_page_extensions()
Terry Jan Reedy92cb0a32014-10-08 20:29:13 -0400105 self.create_action_buttons().pack(side=BOTTOM)
Terry Jan Reedyd0cadba2015-10-11 22:07:31 -0400106
Terry Jan Reedy92cb0a32014-10-08 20:29:13 -0400107 def create_action_buttons(self):
csabella7eb58832017-07-04 21:30:58 -0400108 "Return frame of action buttons for dialog."
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -0400109 if macosx.isAquaTk():
Terry Jan Reedye3416e62014-07-26 19:40:16 -0400110 # Changing the default padding on OSX results in unreadable
csabella7eb58832017-07-04 21:30:58 -0400111 # text in the buttons.
csabellabac7d332017-06-26 17:46:26 -0400112 padding_args = {}
Ronald Oussoren9e350042009-02-12 16:02:11 +0000113 else:
csabellabac7d332017-06-26 17:46:26 -0400114 padding_args = {'padx':6, 'pady':3}
Terry Jan Reedya9421fb2014-10-22 20:15:18 -0400115 outer = Frame(self, pady=2)
116 buttons = Frame(outer, pady=2)
Terry Jan Reedyd0cadba2015-10-11 22:07:31 -0400117 for txt, cmd in (
csabellabac7d332017-06-26 17:46:26 -0400118 ('Ok', self.ok),
119 ('Apply', self.apply),
120 ('Cancel', self.cancel),
121 ('Help', self.help)):
Terry Jan Reedyd0cadba2015-10-11 22:07:31 -0400122 Button(buttons, text=txt, command=cmd, takefocus=FALSE,
csabellabac7d332017-06-26 17:46:26 -0400123 **padding_args).pack(side=LEFT, padx=5)
csabella7eb58832017-07-04 21:30:58 -0400124 # Add space above buttons.
Terry Jan Reedya9421fb2014-10-22 20:15:18 -0400125 Frame(outer, height=2, borderwidth=0).pack(side=TOP)
126 buttons.pack(side=BOTTOM)
127 return outer
Terry Jan Reedyd0cadba2015-10-11 22:07:31 -0400128
csabellabac7d332017-06-26 17:46:26 -0400129 def create_page_font_tab(self):
csabella7eb58832017-07-04 21:30:58 -0400130 """Return frame of widgets for Font/Tabs tab.
131
132 Configuration attributes:
133 font_size: Font size.
134 font_bold: Select font bold or not.
135 font_name: Font face.
136 space_num: Indentation width.
137 edit_font: Font widget with default font name, size, and weight.
138 """
Terry Jan Reedy22405332014-07-30 19:24:32 -0400139 parent = self.parent
csabellabac7d332017-06-26 17:46:26 -0400140 self.font_size = StringVar(parent)
141 self.font_bold = BooleanVar(parent)
142 self.font_name = StringVar(parent)
143 self.space_num = IntVar(parent)
144 self.edit_font = tkFont.Font(parent, ('courier', 10, 'normal'))
Terry Jan Reedy22405332014-07-30 19:24:32 -0400145
Louie Lubb2bae82017-07-10 06:57:18 +0800146 # Create widgets.
147 # body and body section frames.
csabellabac7d332017-06-26 17:46:26 -0400148 frame = self.tab_pages.pages['Fonts/Tabs'].frame
csabellabac7d332017-06-26 17:46:26 -0400149 frame_font = LabelFrame(
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400150 frame, borderwidth=2, relief=GROOVE, text=' Base Editor Font ')
csabellabac7d332017-06-26 17:46:26 -0400151 frame_indent = LabelFrame(
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400152 frame, borderwidth=2, relief=GROOVE, text=' Indentation Width ')
Louie Lubb2bae82017-07-10 06:57:18 +0800153 # frame_font
csabellabac7d332017-06-26 17:46:26 -0400154 frame_font_name = Frame(frame_font)
155 frame_font_param = Frame(frame_font)
156 font_name_title = Label(
157 frame_font_name, justify=LEFT, text='Font Face :')
Louie Lubb2bae82017-07-10 06:57:18 +0800158 self.fontlist = Listbox(
csabellabac7d332017-06-26 17:46:26 -0400159 frame_font_name, height=5, takefocus=FALSE, exportselection=FALSE)
Louie Lubb2bae82017-07-10 06:57:18 +0800160 self.fontlist.bind('<<ListboxSelect>>', self.on_fontlist_select)
csabellabac7d332017-06-26 17:46:26 -0400161 scroll_font = Scrollbar(frame_font_name)
Louie Lubb2bae82017-07-10 06:57:18 +0800162 scroll_font.config(command=self.fontlist.yview)
163 self.fontlist.config(yscrollcommand=scroll_font.set)
csabellabac7d332017-06-26 17:46:26 -0400164 font_size_title = Label(frame_font_param, text='Size :')
165 self.opt_menu_font_size = DynOptionMenu(
166 frame_font_param, self.font_size, None, command=self.set_font_sample)
167 check_font_bold = Checkbutton(
168 frame_font_param, variable=self.font_bold, onvalue=1,
169 offvalue=0, text='Bold', command=self.set_font_sample)
170 frame_font_sample = Frame(frame_font, relief=SOLID, borderwidth=1)
171 self.font_sample = Label(
172 frame_font_sample, justify=LEFT, font=self.edit_font,
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400173 text='AaBbCcDdEe\nFfGgHhIiJjK\n1234567890\n#:+=(){}[]')
Louie Lubb2bae82017-07-10 06:57:18 +0800174 # frame_indent
csabellabac7d332017-06-26 17:46:26 -0400175 frame_indent_size = Frame(frame_indent)
176 indent_size_title = Label(
177 frame_indent_size, justify=LEFT,
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400178 text='Python Standard: 4 Spaces!')
csabellabac7d332017-06-26 17:46:26 -0400179 self.scale_indent_size = Scale(
180 frame_indent_size, variable=self.space_num,
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400181 orient='horizontal', tickinterval=2, from_=2, to=16)
Terry Jan Reedy22405332014-07-30 19:24:32 -0400182
Louie Lubb2bae82017-07-10 06:57:18 +0800183 # Pack widgets.
184 # body
csabellabac7d332017-06-26 17:46:26 -0400185 frame_font.pack(side=LEFT, padx=5, pady=5, expand=TRUE, fill=BOTH)
186 frame_indent.pack(side=LEFT, padx=5, pady=5, fill=Y)
Louie Lubb2bae82017-07-10 06:57:18 +0800187 # frame_font
csabellabac7d332017-06-26 17:46:26 -0400188 frame_font_name.pack(side=TOP, padx=5, pady=5, fill=X)
189 frame_font_param.pack(side=TOP, padx=5, pady=5, fill=X)
190 font_name_title.pack(side=TOP, anchor=W)
Louie Lubb2bae82017-07-10 06:57:18 +0800191 self.fontlist.pack(side=LEFT, expand=TRUE, fill=X)
csabellabac7d332017-06-26 17:46:26 -0400192 scroll_font.pack(side=LEFT, fill=Y)
193 font_size_title.pack(side=LEFT, anchor=W)
194 self.opt_menu_font_size.pack(side=LEFT, anchor=W)
195 check_font_bold.pack(side=LEFT, anchor=W, padx=20)
196 frame_font_sample.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH)
197 self.font_sample.pack(expand=TRUE, fill=BOTH)
Louie Lubb2bae82017-07-10 06:57:18 +0800198 # frame_indent
csabellabac7d332017-06-26 17:46:26 -0400199 frame_indent_size.pack(side=TOP, fill=X)
200 indent_size_title.pack(side=TOP, anchor=W, padx=5)
201 self.scale_indent_size.pack(side=TOP, padx=5, fill=X)
Louie Lubb2bae82017-07-10 06:57:18 +0800202
Steven M. Gava952d0a52001-08-03 04:43:44 +0000203 return frame
204
csabellabac7d332017-06-26 17:46:26 -0400205 def create_page_highlight(self):
csabella7eb58832017-07-04 21:30:58 -0400206 """Return frame of widgets for Highlighting tab.
207
208 Configuration attributes:
209 builtin_theme: Menu variable for built-in theme.
210 custom_theme: Menu variable for custom theme.
211 fg_bg_toggle: Toggle for foreground/background color.
212 colour: Color of selected target.
213 is_builtin_theme: Selector for built-in or custom theme.
214 highlight_target: Menu variable for the highlight tag target.
215 """
Terry Jan Reedy22405332014-07-30 19:24:32 -0400216 parent = self.parent
csabellabac7d332017-06-26 17:46:26 -0400217 self.builtin_theme = StringVar(parent)
218 self.custom_theme = StringVar(parent)
219 self.fg_bg_toggle = BooleanVar(parent)
Terry Jan Reedy22405332014-07-30 19:24:32 -0400220 self.colour = StringVar(parent)
csabellabac7d332017-06-26 17:46:26 -0400221 self.is_builtin_theme = BooleanVar(parent)
222 self.highlight_target = StringVar(parent)
Terry Jan Reedy22405332014-07-30 19:24:32 -0400223
Steven M. Gava952d0a52001-08-03 04:43:44 +0000224 ##widget creation
225 #body frame
csabellabac7d332017-06-26 17:46:26 -0400226 frame = self.tab_pages.pages['Highlighting'].frame
Steven M. Gava952d0a52001-08-03 04:43:44 +0000227 #body section frames
csabellabac7d332017-06-26 17:46:26 -0400228 frame_custom = LabelFrame(frame, borderwidth=2, relief=GROOVE,
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400229 text=' Custom Highlighting ')
csabellabac7d332017-06-26 17:46:26 -0400230 frame_theme = LabelFrame(frame, borderwidth=2, relief=GROOVE,
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400231 text=' Highlighting Theme ')
csabellabac7d332017-06-26 17:46:26 -0400232 #frame_custom
233 self.text_highlight_sample=Text(
234 frame_custom, relief=SOLID, borderwidth=1,
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400235 font=('courier', 12, ''), cursor='hand2', width=21, height=11,
236 takefocus=FALSE, highlightthickness=0, wrap=NONE)
csabellabac7d332017-06-26 17:46:26 -0400237 text=self.text_highlight_sample
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400238 text.bind('<Double-Button-1>', lambda e: 'break')
239 text.bind('<B1-Motion>', lambda e: 'break')
csabellabac7d332017-06-26 17:46:26 -0400240 text_and_tags=(
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400241 ('#you can click here', 'comment'), ('\n', 'normal'),
242 ('#to choose items', 'comment'), ('\n', 'normal'),
243 ('def', 'keyword'), (' ', 'normal'),
244 ('func', 'definition'), ('(param):\n ', 'normal'),
245 ('"""string"""', 'string'), ('\n var0 = ', 'normal'),
246 ("'string'", 'string'), ('\n var1 = ', 'normal'),
247 ("'selected'", 'hilite'), ('\n var2 = ', 'normal'),
248 ("'found'", 'hit'), ('\n var3 = ', 'normal'),
249 ('list', 'builtin'), ('(', 'normal'),
Terry Jan Reedya8aa4d52015-10-02 22:12:17 -0400250 ('None', 'keyword'), (')\n', 'normal'),
251 (' breakpoint("line")', 'break'), ('\n\n', 'normal'),
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400252 (' error ', 'error'), (' ', 'normal'),
253 ('cursor |', 'cursor'), ('\n ', 'normal'),
254 ('shell', 'console'), (' ', 'normal'),
255 ('stdout', 'stdout'), (' ', 'normal'),
256 ('stderr', 'stderr'), ('\n', 'normal'))
csabellabac7d332017-06-26 17:46:26 -0400257 for texttag in text_and_tags:
258 text.insert(END, texttag[0], texttag[1])
259 for element in self.theme_elements:
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400260 def tem(event, elem=element):
csabellabac7d332017-06-26 17:46:26 -0400261 event.widget.winfo_toplevel().highlight_target.set(elem)
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400262 text.tag_bind(
csabellabac7d332017-06-26 17:46:26 -0400263 self.theme_elements[element][0], '<ButtonPress-1>', tem)
Steven M. Gavae16d94b2001-11-03 05:07:28 +0000264 text.config(state=DISABLED)
csabellabac7d332017-06-26 17:46:26 -0400265 self.frame_colour_set = Frame(frame_custom, relief=SOLID, borderwidth=1)
266 frame_fg_bg_toggle = Frame(frame_custom)
267 button_set_colour = Button(
268 self.frame_colour_set, text='Choose Colour for :',
269 command=self.get_colour, highlightthickness=0)
270 self.opt_menu_highlight_target = DynOptionMenu(
271 self.frame_colour_set, self.highlight_target, None,
272 highlightthickness=0) #, command=self.set_highlight_targetBinding
273 self.radio_fg = Radiobutton(
274 frame_fg_bg_toggle, variable=self.fg_bg_toggle, value=1,
275 text='Foreground', command=self.set_colour_sample_binding)
276 self.radio_bg=Radiobutton(
277 frame_fg_bg_toggle, variable=self.fg_bg_toggle, value=0,
278 text='Background', command=self.set_colour_sample_binding)
279 self.fg_bg_toggle.set(1)
280 button_save_custom_theme = Button(
281 frame_custom, text='Save as New Custom Theme',
282 command=self.save_as_new_theme)
283 #frame_theme
284 theme_type_title = Label(frame_theme, text='Select : ')
285 self.radio_theme_builtin = Radiobutton(
286 frame_theme, variable=self.is_builtin_theme, value=1,
287 command=self.set_theme_type, text='a Built-in Theme')
288 self.radio_theme_custom = Radiobutton(
289 frame_theme, variable=self.is_builtin_theme, value=0,
290 command=self.set_theme_type, text='a Custom Theme')
291 self.opt_menu_theme_builtin = DynOptionMenu(
292 frame_theme, self.builtin_theme, None, command=None)
293 self.opt_menu_theme_custom=DynOptionMenu(
294 frame_theme, self.custom_theme, None, command=None)
295 self.button_delete_custom_theme=Button(
296 frame_theme, text='Delete Custom Theme',
297 command=self.delete_custom_theme)
298 self.new_custom_theme = Label(frame_theme, bd=2)
Terry Jan Reedy22405332014-07-30 19:24:32 -0400299
Steven M. Gava952d0a52001-08-03 04:43:44 +0000300 ##widget packing
301 #body
csabellabac7d332017-06-26 17:46:26 -0400302 frame_custom.pack(side=LEFT, padx=5, pady=5, expand=TRUE, fill=BOTH)
303 frame_theme.pack(side=LEFT, padx=5, pady=5, fill=Y)
304 #frame_custom
305 self.frame_colour_set.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=X)
306 frame_fg_bg_toggle.pack(side=TOP, padx=5, pady=0)
307 self.text_highlight_sample.pack(
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400308 side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH)
csabellabac7d332017-06-26 17:46:26 -0400309 button_set_colour.pack(side=TOP, expand=TRUE, fill=X, padx=8, pady=4)
310 self.opt_menu_highlight_target.pack(
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400311 side=TOP, expand=TRUE, fill=X, padx=8, pady=3)
csabellabac7d332017-06-26 17:46:26 -0400312 self.radio_fg.pack(side=LEFT, anchor=E)
313 self.radio_bg.pack(side=RIGHT, anchor=W)
314 button_save_custom_theme.pack(side=BOTTOM, fill=X, padx=5, pady=5)
315 #frame_theme
316 theme_type_title.pack(side=TOP, anchor=W, padx=5, pady=5)
317 self.radio_theme_builtin.pack(side=TOP, anchor=W, padx=5)
318 self.radio_theme_custom.pack(side=TOP, anchor=W, padx=5, pady=2)
319 self.opt_menu_theme_builtin.pack(side=TOP, fill=X, padx=5, pady=5)
320 self.opt_menu_theme_custom.pack(side=TOP, fill=X, anchor=W, padx=5, pady=5)
321 self.button_delete_custom_theme.pack(side=TOP, fill=X, padx=5, pady=5)
Terry Jan Reedyd0c0f002015-11-12 15:02:57 -0500322 self.new_custom_theme.pack(side=TOP, fill=X, pady=5)
Steven M. Gava952d0a52001-08-03 04:43:44 +0000323 return frame
324
csabellabac7d332017-06-26 17:46:26 -0400325 def create_page_keys(self):
csabella7eb58832017-07-04 21:30:58 -0400326 """Return frame of widgets for Keys tab.
327
328 Configuration attributes:
329 builtin_keys: Menu variable for built-in keybindings.
330 custom_keys: Menu variable for custom keybindings.
331 are_keys_builtin: Selector for built-in or custom keybindings.
332 keybinding: Action/key bindings.
333 """
Terry Jan Reedy22405332014-07-30 19:24:32 -0400334 parent = self.parent
csabellabac7d332017-06-26 17:46:26 -0400335 self.builtin_keys = StringVar(parent)
336 self.custom_keys = StringVar(parent)
337 self.are_keys_builtin = BooleanVar(parent)
338 self.keybinding = StringVar(parent)
Terry Jan Reedy22405332014-07-30 19:24:32 -0400339
Steven M. Gava60fc7072001-08-04 13:58:22 +0000340 ##widget creation
341 #body frame
csabellabac7d332017-06-26 17:46:26 -0400342 frame = self.tab_pages.pages['Keys'].frame
Steven M. Gava60fc7072001-08-04 13:58:22 +0000343 #body section frames
csabellabac7d332017-06-26 17:46:26 -0400344 frame_custom = LabelFrame(
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400345 frame, borderwidth=2, relief=GROOVE,
346 text=' Custom Key Bindings ')
csabellabac7d332017-06-26 17:46:26 -0400347 frame_key_sets = LabelFrame(
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400348 frame, borderwidth=2, relief=GROOVE, text=' Key Set ')
csabellabac7d332017-06-26 17:46:26 -0400349 #frame_custom
350 frame_target = Frame(frame_custom)
351 target_title = Label(frame_target, text='Action - Key(s)')
352 scroll_target_y = Scrollbar(frame_target)
353 scroll_target_x = Scrollbar(frame_target, orient=HORIZONTAL)
354 self.list_bindings = Listbox(
355 frame_target, takefocus=FALSE, exportselection=FALSE)
356 self.list_bindings.bind('<ButtonRelease-1>', self.keybinding_selected)
357 scroll_target_y.config(command=self.list_bindings.yview)
358 scroll_target_x.config(command=self.list_bindings.xview)
359 self.list_bindings.config(yscrollcommand=scroll_target_y.set)
360 self.list_bindings.config(xscrollcommand=scroll_target_x.set)
361 self.button_new_keys = Button(
362 frame_custom, text='Get New Keys for Selection',
363 command=self.get_new_keys, state=DISABLED)
364 #frame_key_sets
365 frames = [Frame(frame_key_sets, padx=2, pady=2, borderwidth=0)
Christian Heimes9a371592007-12-28 14:08:13 +0000366 for i in range(2)]
csabellabac7d332017-06-26 17:46:26 -0400367 self.radio_keys_builtin = Radiobutton(
368 frames[0], variable=self.are_keys_builtin, value=1,
369 command=self.set_keys_type, text='Use a Built-in Key Set')
370 self.radio_keys_custom = Radiobutton(
371 frames[0], variable=self.are_keys_builtin, value=0,
372 command=self.set_keys_type, text='Use a Custom Key Set')
373 self.opt_menu_keys_builtin = DynOptionMenu(
374 frames[0], self.builtin_keys, None, command=None)
375 self.opt_menu_keys_custom = DynOptionMenu(
376 frames[0], self.custom_keys, None, command=None)
377 self.button_delete_custom_keys = Button(
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400378 frames[1], text='Delete Custom Key Set',
csabellabac7d332017-06-26 17:46:26 -0400379 command=self.delete_custom_keys)
380 button_save_custom_keys = Button(
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400381 frames[1], text='Save as New Custom Key Set',
csabellabac7d332017-06-26 17:46:26 -0400382 command=self.save_as_new_key_set)
Terry Jan Reedy9bdb1ed2016-07-10 13:46:34 -0400383 self.new_custom_keys = Label(frames[0], bd=2)
Terry Jan Reedy22405332014-07-30 19:24:32 -0400384
Steven M. Gava60fc7072001-08-04 13:58:22 +0000385 ##widget packing
386 #body
csabellabac7d332017-06-26 17:46:26 -0400387 frame_custom.pack(side=BOTTOM, padx=5, pady=5, expand=TRUE, fill=BOTH)
388 frame_key_sets.pack(side=BOTTOM, padx=5, pady=5, fill=BOTH)
389 #frame_custom
390 self.button_new_keys.pack(side=BOTTOM, fill=X, padx=5, pady=5)
391 frame_target.pack(side=LEFT, padx=5, pady=5, expand=TRUE, fill=BOTH)
Steven M. Gavafacfc092002-01-19 00:29:54 +0000392 #frame target
csabellabac7d332017-06-26 17:46:26 -0400393 frame_target.columnconfigure(0, weight=1)
394 frame_target.rowconfigure(1, weight=1)
395 target_title.grid(row=0, column=0, columnspan=2, sticky=W)
396 self.list_bindings.grid(row=1, column=0, sticky=NSEW)
397 scroll_target_y.grid(row=1, column=1, sticky=NS)
398 scroll_target_x.grid(row=2, column=0, sticky=EW)
399 #frame_key_sets
400 self.radio_keys_builtin.grid(row=0, column=0, sticky=W+NS)
401 self.radio_keys_custom.grid(row=1, column=0, sticky=W+NS)
402 self.opt_menu_keys_builtin.grid(row=0, column=1, sticky=NSEW)
403 self.opt_menu_keys_custom.grid(row=1, column=1, sticky=NSEW)
Terry Jan Reedy9bdb1ed2016-07-10 13:46:34 -0400404 self.new_custom_keys.grid(row=0, column=2, sticky=NSEW, padx=5, pady=5)
csabellabac7d332017-06-26 17:46:26 -0400405 self.button_delete_custom_keys.pack(side=LEFT, fill=X, expand=True, padx=2)
406 button_save_custom_keys.pack(side=LEFT, fill=X, expand=True, padx=2)
Christian Heimes9a371592007-12-28 14:08:13 +0000407 frames[0].pack(side=TOP, fill=BOTH, expand=True)
408 frames[1].pack(side=TOP, fill=X, expand=True, pady=2)
Steven M. Gava952d0a52001-08-03 04:43:44 +0000409 return frame
410
csabellabac7d332017-06-26 17:46:26 -0400411 def create_page_general(self):
csabella7eb58832017-07-04 21:30:58 -0400412 """Return frame of widgets for General tab.
413
414 Configuration attributes:
415 win_width: Initial window width in characters.
416 win_height: Initial window height in characters.
417 startup_edit: Selector for opening in editor or shell mode.
418 autosave: Selector for save prompt popup when using Run.
csabella7eb58832017-07-04 21:30:58 -0400419 """
Terry Jan Reedy22405332014-07-30 19:24:32 -0400420 parent = self.parent
csabellabac7d332017-06-26 17:46:26 -0400421 self.win_width = StringVar(parent)
422 self.win_height = StringVar(parent)
423 self.startup_edit = IntVar(parent)
424 self.autosave = IntVar(parent)
Terry Jan Reedy22405332014-07-30 19:24:32 -0400425
Steven M. Gava230e5782001-08-07 03:28:25 +0000426 #widget creation
427 #body
csabellabac7d332017-06-26 17:46:26 -0400428 frame = self.tab_pages.pages['General'].frame
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000429 #body section frames
csabellabac7d332017-06-26 17:46:26 -0400430 frame_run = LabelFrame(frame, borderwidth=2, relief=GROOVE,
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400431 text=' Startup Preferences ')
csabellabac7d332017-06-26 17:46:26 -0400432 frame_save = LabelFrame(frame, borderwidth=2, relief=GROOVE,
433 text=' autosave Preferences ')
434 frame_win_size = Frame(frame, borderwidth=2, relief=GROOVE)
435 frame_help = LabelFrame(frame, borderwidth=2, relief=GROOVE,
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400436 text=' Additional Help Sources ')
csabellabac7d332017-06-26 17:46:26 -0400437 #frame_run
438 startup_title = Label(frame_run, text='At Startup')
439 self.radio_startup_edit = Radiobutton(
440 frame_run, variable=self.startup_edit, value=1,
Terry Jan Reedyf46b7822016-11-07 17:15:01 -0500441 text="Open Edit Window")
csabellabac7d332017-06-26 17:46:26 -0400442 self.radio_startup_shell = Radiobutton(
443 frame_run, variable=self.startup_edit, value=0,
Terry Jan Reedyf46b7822016-11-07 17:15:01 -0500444 text='Open Shell Window')
csabellabac7d332017-06-26 17:46:26 -0400445 #frame_save
446 run_save_title = Label(frame_save, text='At Start of Run (F5) ')
447 self.radio_save_ask = Radiobutton(
448 frame_save, variable=self.autosave, value=0,
Terry Jan Reedyf46b7822016-11-07 17:15:01 -0500449 text="Prompt to Save")
csabellabac7d332017-06-26 17:46:26 -0400450 self.radio_save_auto = Radiobutton(
451 frame_save, variable=self.autosave, value=1,
Terry Jan Reedyf46b7822016-11-07 17:15:01 -0500452 text='No Prompt')
csabellabac7d332017-06-26 17:46:26 -0400453 #frame_win_size
454 win_size_title = Label(
455 frame_win_size, text='Initial Window Size (in characters)')
456 win_width_title = Label(frame_win_size, text='Width')
457 self.entry_win_width = Entry(
458 frame_win_size, textvariable=self.win_width, width=3)
459 win_height_title = Label(frame_win_size, text='Height')
460 self.entry_win_height = Entry(
461 frame_win_size, textvariable=self.win_height, width=3)
462 #frame_help
463 frame_helplist = Frame(frame_help)
464 frame_helplist_buttons = Frame(frame_helplist)
465 scroll_helplist = Scrollbar(frame_helplist)
466 self.list_help = Listbox(
467 frame_helplist, height=5, takefocus=FALSE,
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000468 exportselection=FALSE)
csabellabac7d332017-06-26 17:46:26 -0400469 scroll_helplist.config(command=self.list_help.yview)
470 self.list_help.config(yscrollcommand=scroll_helplist.set)
471 self.list_help.bind('<ButtonRelease-1>', self.help_source_selected)
472 self.button_helplist_edit = Button(
473 frame_helplist_buttons, text='Edit', state=DISABLED,
474 width=8, command=self.helplist_item_edit)
475 self.button_helplist_add = Button(
476 frame_helplist_buttons, text='Add',
477 width=8, command=self.helplist_item_add)
478 self.button_helplist_remove = Button(
479 frame_helplist_buttons, text='Remove', state=DISABLED,
480 width=8, command=self.helplist_item_remove)
Terry Jan Reedy22405332014-07-30 19:24:32 -0400481
Steven M. Gava230e5782001-08-07 03:28:25 +0000482 #widget packing
483 #body
csabellabac7d332017-06-26 17:46:26 -0400484 frame_run.pack(side=TOP, padx=5, pady=5, fill=X)
485 frame_save.pack(side=TOP, padx=5, pady=5, fill=X)
486 frame_win_size.pack(side=TOP, padx=5, pady=5, fill=X)
487 frame_help.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH)
488 #frame_run
489 startup_title.pack(side=LEFT, anchor=W, padx=5, pady=5)
490 self.radio_startup_shell.pack(side=RIGHT, anchor=W, padx=5, pady=5)
491 self.radio_startup_edit.pack(side=RIGHT, anchor=W, padx=5, pady=5)
492 #frame_save
493 run_save_title.pack(side=LEFT, anchor=W, padx=5, pady=5)
494 self.radio_save_auto.pack(side=RIGHT, anchor=W, padx=5, pady=5)
495 self.radio_save_ask.pack(side=RIGHT, anchor=W, padx=5, pady=5)
496 #frame_win_size
497 win_size_title.pack(side=LEFT, anchor=W, padx=5, pady=5)
498 self.entry_win_height.pack(side=RIGHT, anchor=E, padx=10, pady=5)
499 win_height_title.pack(side=RIGHT, anchor=E, pady=5)
500 self.entry_win_width.pack(side=RIGHT, anchor=E, padx=10, pady=5)
501 win_width_title.pack(side=RIGHT, anchor=E, pady=5)
502 #frame_help
503 frame_helplist_buttons.pack(side=RIGHT, padx=5, pady=5, fill=Y)
504 frame_helplist.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH)
505 scroll_helplist.pack(side=RIGHT, anchor=W, fill=Y)
506 self.list_help.pack(side=LEFT, anchor=E, expand=TRUE, fill=BOTH)
507 self.button_helplist_edit.pack(side=TOP, anchor=W, pady=5)
508 self.button_helplist_add.pack(side=TOP, anchor=W)
509 self.button_helplist_remove.pack(side=TOP, anchor=W, pady=5)
Steven M. Gava952d0a52001-08-03 04:43:44 +0000510 return frame
511
csabellabac7d332017-06-26 17:46:26 -0400512 def attach_var_callbacks(self):
csabella7eb58832017-07-04 21:30:58 -0400513 "Attach callbacks to variables that can be changed."
csabellabac7d332017-06-26 17:46:26 -0400514 self.font_size.trace_add('write', self.var_changed_font)
515 self.font_name.trace_add('write', self.var_changed_font)
516 self.font_bold.trace_add('write', self.var_changed_font)
517 self.space_num.trace_add('write', self.var_changed_space_num)
518 self.colour.trace_add('write', self.var_changed_colour)
519 self.builtin_theme.trace_add('write', self.var_changed_builtin_theme)
520 self.custom_theme.trace_add('write', self.var_changed_custom_theme)
521 self.is_builtin_theme.trace_add('write', self.var_changed_is_builtin_theme)
522 self.highlight_target.trace_add('write', self.var_changed_highlight_target)
523 self.keybinding.trace_add('write', self.var_changed_keybinding)
524 self.builtin_keys.trace_add('write', self.var_changed_builtin_keys)
525 self.custom_keys.trace_add('write', self.var_changed_custom_keys)
526 self.are_keys_builtin.trace_add('write', self.var_changed_are_keys_builtin)
527 self.win_width.trace_add('write', self.var_changed_win_width)
528 self.win_height.trace_add('write', self.var_changed_win_height)
529 self.startup_edit.trace_add('write', self.var_changed_startup_edit)
530 self.autosave.trace_add('write', self.var_changed_autosave)
Steven M. Gava052937f2002-02-11 02:20:53 +0000531
Terry Jan Reedy6b98ce22016-05-16 22:27:28 -0400532 def remove_var_callbacks(self):
533 "Remove callbacks to prevent memory leaks."
534 for var in (
csabellabac7d332017-06-26 17:46:26 -0400535 self.font_size, self.font_name, self.font_bold,
536 self.space_num, self.colour, self.builtin_theme,
537 self.custom_theme, self.is_builtin_theme, self.highlight_target,
538 self.keybinding, self.builtin_keys, self.custom_keys,
539 self.are_keys_builtin, self.win_width, self.win_height,
csabellaaa8d0a22017-07-10 13:50:44 -0400540 self.startup_edit, self.autosave,):
Serhiy Storchaka81221742016-06-26 09:46:57 +0300541 var.trace_remove('write', var.trace_info()[0][1])
Terry Jan Reedy6b98ce22016-05-16 22:27:28 -0400542
csabellabac7d332017-06-26 17:46:26 -0400543 def var_changed_font(self, *params):
csabella7eb58832017-07-04 21:30:58 -0400544 """Store changes to font attributes.
545
546 When one font attribute changes, save them all, as they are
Terry Jan Reedyd87d1682015-08-01 18:57:33 -0400547 not independent from each other. In particular, when we are
548 overriding the default font, we need to write out everything.
csabella7eb58832017-07-04 21:30:58 -0400549 """
csabellabac7d332017-06-26 17:46:26 -0400550 value = self.font_name.get()
terryjreedy349abd92017-07-07 16:00:57 -0400551 changes.add_option('main', 'EditorWindow', 'font', value)
csabellabac7d332017-06-26 17:46:26 -0400552 value = self.font_size.get()
terryjreedy349abd92017-07-07 16:00:57 -0400553 changes.add_option('main', 'EditorWindow', 'font-size', value)
csabellabac7d332017-06-26 17:46:26 -0400554 value = self.font_bold.get()
terryjreedy349abd92017-07-07 16:00:57 -0400555 changes.add_option('main', 'EditorWindow', 'font-bold', value)
Steven M. Gavac112cd82002-01-22 05:56:40 +0000556
csabellabac7d332017-06-26 17:46:26 -0400557 def var_changed_space_num(self, *params):
csabella7eb58832017-07-04 21:30:58 -0400558 "Store change to indentation size."
csabellabac7d332017-06-26 17:46:26 -0400559 value = self.space_num.get()
terryjreedy349abd92017-07-07 16:00:57 -0400560 changes.add_option('main', 'Indent', 'num-spaces', value)
Steven M. Gavac112cd82002-01-22 05:56:40 +0000561
csabellabac7d332017-06-26 17:46:26 -0400562 def var_changed_colour(self, *params):
csabella7eb58832017-07-04 21:30:58 -0400563 "Process change to color choice."
csabellabac7d332017-06-26 17:46:26 -0400564 self.on_new_colour_set()
Steven M. Gava052937f2002-02-11 02:20:53 +0000565
csabellabac7d332017-06-26 17:46:26 -0400566 def var_changed_builtin_theme(self, *params):
csabella7eb58832017-07-04 21:30:58 -0400567 """Process new builtin theme selection.
568
569 Add the changed theme's name to the changed_items and recreate
570 the sample with the values from the selected theme.
571 """
csabellabac7d332017-06-26 17:46:26 -0400572 old_themes = ('IDLE Classic', 'IDLE New')
573 value = self.builtin_theme.get()
574 if value not in old_themes:
575 if idleConf.GetOption('main', 'Theme', 'name') not in old_themes:
terryjreedy349abd92017-07-07 16:00:57 -0400576 changes.add_option('main', 'Theme', 'name', old_themes[0])
577 changes.add_option('main', 'Theme', 'name2', value)
Terry Jan Reedyd0c0f002015-11-12 15:02:57 -0500578 self.new_custom_theme.config(text='New theme, see Help',
579 fg='#500000')
580 else:
terryjreedy349abd92017-07-07 16:00:57 -0400581 changes.add_option('main', 'Theme', 'name', value)
582 changes.add_option('main', 'Theme', 'name2', '')
Terry Jan Reedyd0c0f002015-11-12 15:02:57 -0500583 self.new_custom_theme.config(text='', fg='black')
csabellabac7d332017-06-26 17:46:26 -0400584 self.paint_theme_sample()
Steven M. Gava052937f2002-02-11 02:20:53 +0000585
csabellabac7d332017-06-26 17:46:26 -0400586 def var_changed_custom_theme(self, *params):
csabella7eb58832017-07-04 21:30:58 -0400587 """Process new custom theme selection.
588
589 If a new custom theme is selected, add the name to the
590 changed_items and apply the theme to the sample.
591 """
csabellabac7d332017-06-26 17:46:26 -0400592 value = self.custom_theme.get()
Steven M. Gava49745752002-02-18 01:43:11 +0000593 if value != '- no custom themes -':
terryjreedy349abd92017-07-07 16:00:57 -0400594 changes.add_option('main', 'Theme', 'name', value)
csabellabac7d332017-06-26 17:46:26 -0400595 self.paint_theme_sample()
Steven M. Gava052937f2002-02-11 02:20:53 +0000596
csabellabac7d332017-06-26 17:46:26 -0400597 def var_changed_is_builtin_theme(self, *params):
csabella7eb58832017-07-04 21:30:58 -0400598 """Process toggle between builtin and custom theme.
599
600 Update the default toggle value and apply the newly
601 selected theme type.
602 """
csabellabac7d332017-06-26 17:46:26 -0400603 value = self.is_builtin_theme.get()
terryjreedy349abd92017-07-07 16:00:57 -0400604 changes.add_option('main', 'Theme', 'default', value)
Steven M. Gavaf31eec02002-03-05 00:25:58 +0000605 if value:
csabellabac7d332017-06-26 17:46:26 -0400606 self.var_changed_builtin_theme()
Steven M. Gavaf31eec02002-03-05 00:25:58 +0000607 else:
csabellabac7d332017-06-26 17:46:26 -0400608 self.var_changed_custom_theme()
Steven M. Gava052937f2002-02-11 02:20:53 +0000609
csabellabac7d332017-06-26 17:46:26 -0400610 def var_changed_highlight_target(self, *params):
csabella7eb58832017-07-04 21:30:58 -0400611 "Process selection of new target tag for highlighting."
csabellabac7d332017-06-26 17:46:26 -0400612 self.set_highlight_target()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000613
csabellabac7d332017-06-26 17:46:26 -0400614 def var_changed_keybinding(self, *params):
csabella7eb58832017-07-04 21:30:58 -0400615 "Store change to a keybinding."
csabellabac7d332017-06-26 17:46:26 -0400616 value = self.keybinding.get()
617 key_set = self.custom_keys.get()
618 event = self.list_bindings.get(ANCHOR).split()[0]
Steven M. Gavaa498af22002-02-01 01:33:36 +0000619 if idleConf.IsCoreBinding(event):
terryjreedy349abd92017-07-07 16:00:57 -0400620 changes.add_option('keys', key_set, event, value)
csabella7eb58832017-07-04 21:30:58 -0400621 else: # Event is an extension binding.
csabellabac7d332017-06-26 17:46:26 -0400622 ext_name = idleConf.GetExtnNameForEvent(event)
623 ext_keybind_section = ext_name + '_cfgBindings'
terryjreedy349abd92017-07-07 16:00:57 -0400624 changes.add_option('extensions', ext_keybind_section, event, value)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000625
csabellabac7d332017-06-26 17:46:26 -0400626 def var_changed_builtin_keys(self, *params):
csabella7eb58832017-07-04 21:30:58 -0400627 "Process selection of builtin key set."
csabellabac7d332017-06-26 17:46:26 -0400628 old_keys = (
Terry Jan Reedy9bdb1ed2016-07-10 13:46:34 -0400629 'IDLE Classic Windows',
630 'IDLE Classic Unix',
631 'IDLE Classic Mac',
632 'IDLE Classic OSX',
633 )
csabellabac7d332017-06-26 17:46:26 -0400634 value = self.builtin_keys.get()
635 if value not in old_keys:
636 if idleConf.GetOption('main', 'Keys', 'name') not in old_keys:
terryjreedy349abd92017-07-07 16:00:57 -0400637 changes.add_option('main', 'Keys', 'name', old_keys[0])
638 changes.add_option('main', 'Keys', 'name2', value)
Terry Jan Reedy9bdb1ed2016-07-10 13:46:34 -0400639 self.new_custom_keys.config(text='New key set, see Help',
640 fg='#500000')
641 else:
terryjreedy349abd92017-07-07 16:00:57 -0400642 changes.add_option('main', 'Keys', 'name', value)
643 changes.add_option('main', 'Keys', 'name2', '')
Terry Jan Reedy9bdb1ed2016-07-10 13:46:34 -0400644 self.new_custom_keys.config(text='', fg='black')
csabellabac7d332017-06-26 17:46:26 -0400645 self.load_keys_list(value)
Steven M. Gava052937f2002-02-11 02:20:53 +0000646
csabellabac7d332017-06-26 17:46:26 -0400647 def var_changed_custom_keys(self, *params):
csabella7eb58832017-07-04 21:30:58 -0400648 "Process selection of custom key set."
csabellabac7d332017-06-26 17:46:26 -0400649 value = self.custom_keys.get()
Steven M. Gava49745752002-02-18 01:43:11 +0000650 if value != '- no custom keys -':
terryjreedy349abd92017-07-07 16:00:57 -0400651 changes.add_option('main', 'Keys', 'name', value)
csabellabac7d332017-06-26 17:46:26 -0400652 self.load_keys_list(value)
Steven M. Gava052937f2002-02-11 02:20:53 +0000653
csabellabac7d332017-06-26 17:46:26 -0400654 def var_changed_are_keys_builtin(self, *params):
csabella7eb58832017-07-04 21:30:58 -0400655 "Process toggle between builtin key set and custom key set."
csabellabac7d332017-06-26 17:46:26 -0400656 value = self.are_keys_builtin.get()
terryjreedy349abd92017-07-07 16:00:57 -0400657 changes.add_option('main', 'Keys', 'default', value)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000658 if value:
csabellabac7d332017-06-26 17:46:26 -0400659 self.var_changed_builtin_keys()
Steven M. Gava052937f2002-02-11 02:20:53 +0000660 else:
csabellabac7d332017-06-26 17:46:26 -0400661 self.var_changed_custom_keys()
Steven M. Gava052937f2002-02-11 02:20:53 +0000662
csabellabac7d332017-06-26 17:46:26 -0400663 def var_changed_win_width(self, *params):
csabella7eb58832017-07-04 21:30:58 -0400664 "Store change to window width."
csabellabac7d332017-06-26 17:46:26 -0400665 value = self.win_width.get()
terryjreedy349abd92017-07-07 16:00:57 -0400666 changes.add_option('main', 'EditorWindow', 'width', value)
Steven M. Gavac112cd82002-01-22 05:56:40 +0000667
csabellabac7d332017-06-26 17:46:26 -0400668 def var_changed_win_height(self, *params):
csabella7eb58832017-07-04 21:30:58 -0400669 "Store change to window height."
csabellabac7d332017-06-26 17:46:26 -0400670 value = self.win_height.get()
terryjreedy349abd92017-07-07 16:00:57 -0400671 changes.add_option('main', 'EditorWindow', 'height', value)
Steven M. Gavac112cd82002-01-22 05:56:40 +0000672
csabellabac7d332017-06-26 17:46:26 -0400673 def var_changed_startup_edit(self, *params):
csabella7eb58832017-07-04 21:30:58 -0400674 "Store change to toggle for starting IDLE in the editor or shell."
csabellabac7d332017-06-26 17:46:26 -0400675 value = self.startup_edit.get()
terryjreedy349abd92017-07-07 16:00:57 -0400676 changes.add_option('main', 'General', 'editor-on-startup', value)
Steven M. Gavac112cd82002-01-22 05:56:40 +0000677
csabellabac7d332017-06-26 17:46:26 -0400678 def var_changed_autosave(self, *params):
csabella7eb58832017-07-04 21:30:58 -0400679 "Store change to autosave."
csabellabac7d332017-06-26 17:46:26 -0400680 value = self.autosave.get()
terryjreedy349abd92017-07-07 16:00:57 -0400681 changes.add_option('main', 'General', 'autosave', value)
Kurt B. Kaiser6c638b62003-05-26 06:23:10 +0000682
csabellabac7d332017-06-26 17:46:26 -0400683 def set_theme_type(self):
csabella7eb58832017-07-04 21:30:58 -0400684 "Set available screen options based on builtin or custom theme."
csabellabac7d332017-06-26 17:46:26 -0400685 if self.is_builtin_theme.get():
686 self.opt_menu_theme_builtin.config(state=NORMAL)
687 self.opt_menu_theme_custom.config(state=DISABLED)
688 self.button_delete_custom_theme.config(state=DISABLED)
Steven M. Gava5f28e8f2002-01-21 06:38:21 +0000689 else:
csabellabac7d332017-06-26 17:46:26 -0400690 self.opt_menu_theme_builtin.config(state=DISABLED)
691 self.radio_theme_custom.config(state=NORMAL)
692 self.opt_menu_theme_custom.config(state=NORMAL)
693 self.button_delete_custom_theme.config(state=NORMAL)
Steven M. Gava5f28e8f2002-01-21 06:38:21 +0000694
csabellabac7d332017-06-26 17:46:26 -0400695 def set_keys_type(self):
csabella7eb58832017-07-04 21:30:58 -0400696 "Set available screen options based on builtin or custom key set."
csabellabac7d332017-06-26 17:46:26 -0400697 if self.are_keys_builtin.get():
698 self.opt_menu_keys_builtin.config(state=NORMAL)
699 self.opt_menu_keys_custom.config(state=DISABLED)
700 self.button_delete_custom_keys.config(state=DISABLED)
Steven M. Gava5f28e8f2002-01-21 06:38:21 +0000701 else:
csabellabac7d332017-06-26 17:46:26 -0400702 self.opt_menu_keys_builtin.config(state=DISABLED)
703 self.radio_keys_custom.config(state=NORMAL)
704 self.opt_menu_keys_custom.config(state=NORMAL)
705 self.button_delete_custom_keys.config(state=NORMAL)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000706
csabellabac7d332017-06-26 17:46:26 -0400707 def get_new_keys(self):
csabella7eb58832017-07-04 21:30:58 -0400708 """Handle event to change key binding for selected line.
709
710 A selection of a key/binding in the list of current
711 bindings pops up a dialog to enter a new binding. If
712 the current key set is builtin and a binding has
713 changed, then a name for a custom key set needs to be
714 entered for the change to be applied.
715 """
csabellabac7d332017-06-26 17:46:26 -0400716 list_index = self.list_bindings.index(ANCHOR)
717 binding = self.list_bindings.get(list_index)
csabella7eb58832017-07-04 21:30:58 -0400718 bind_name = binding.split()[0]
csabellabac7d332017-06-26 17:46:26 -0400719 if self.are_keys_builtin.get():
720 current_key_set_name = self.builtin_keys.get()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000721 else:
csabellabac7d332017-06-26 17:46:26 -0400722 current_key_set_name = self.custom_keys.get()
723 current_bindings = idleConf.GetCurrentKeySet()
terryjreedy349abd92017-07-07 16:00:57 -0400724 if current_key_set_name in changes['keys']: # unsaved changes
725 key_set_changes = changes['keys'][current_key_set_name]
csabellabac7d332017-06-26 17:46:26 -0400726 for event in key_set_changes:
727 current_bindings[event] = key_set_changes[event].split()
728 current_key_sequences = list(current_bindings.values())
729 new_keys = GetKeysDialog(self, 'Get New Keys', bind_name,
730 current_key_sequences).result
csabella7eb58832017-07-04 21:30:58 -0400731 if new_keys:
732 if self.are_keys_builtin.get(): # Current key set is a built-in.
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400733 message = ('Your changes will be saved as a new Custom Key Set.'
734 ' Enter a name for your new Custom Key Set below.')
csabellabac7d332017-06-26 17:46:26 -0400735 new_keyset = self.get_new_keys_name(message)
csabella7eb58832017-07-04 21:30:58 -0400736 if not new_keyset: # User cancelled custom key set creation.
csabellabac7d332017-06-26 17:46:26 -0400737 self.list_bindings.select_set(list_index)
738 self.list_bindings.select_anchor(list_index)
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000739 return
csabella7eb58832017-07-04 21:30:58 -0400740 else: # Create new custom key set based on previously active key set.
csabellabac7d332017-06-26 17:46:26 -0400741 self.create_new_key_set(new_keyset)
742 self.list_bindings.delete(list_index)
743 self.list_bindings.insert(list_index, bind_name+' - '+new_keys)
744 self.list_bindings.select_set(list_index)
745 self.list_bindings.select_anchor(list_index)
746 self.keybinding.set(new_keys)
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000747 else:
csabellabac7d332017-06-26 17:46:26 -0400748 self.list_bindings.select_set(list_index)
749 self.list_bindings.select_anchor(list_index)
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000750
csabellabac7d332017-06-26 17:46:26 -0400751 def get_new_keys_name(self, message):
csabella7eb58832017-07-04 21:30:58 -0400752 "Return new key set name from query popup."
csabellabac7d332017-06-26 17:46:26 -0400753 used_names = (idleConf.GetSectionList('user', 'keys') +
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400754 idleConf.GetSectionList('default', 'keys'))
csabellabac7d332017-06-26 17:46:26 -0400755 new_keyset = SectionName(
756 self, 'New Custom Key Set', message, used_names).result
757 return new_keyset
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000758
csabellabac7d332017-06-26 17:46:26 -0400759 def save_as_new_key_set(self):
csabella7eb58832017-07-04 21:30:58 -0400760 "Prompt for name of new key set and save changes using that name."
csabellabac7d332017-06-26 17:46:26 -0400761 new_keys_name = self.get_new_keys_name('New Key Set Name:')
762 if new_keys_name:
763 self.create_new_key_set(new_keys_name)
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000764
csabellabac7d332017-06-26 17:46:26 -0400765 def keybinding_selected(self, event):
csabella7eb58832017-07-04 21:30:58 -0400766 "Activate button to assign new keys to selected action."
csabellabac7d332017-06-26 17:46:26 -0400767 self.button_new_keys.config(state=NORMAL)
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000768
csabellabac7d332017-06-26 17:46:26 -0400769 def create_new_key_set(self, new_key_set_name):
csabella7eb58832017-07-04 21:30:58 -0400770 """Create a new custom key set with the given name.
771
772 Create the new key set based on the previously active set
773 with the current changes applied. Once it is saved, then
774 activate the new key set.
775 """
csabellabac7d332017-06-26 17:46:26 -0400776 if self.are_keys_builtin.get():
777 prev_key_set_name = self.builtin_keys.get()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000778 else:
csabellabac7d332017-06-26 17:46:26 -0400779 prev_key_set_name = self.custom_keys.get()
780 prev_keys = idleConf.GetCoreKeys(prev_key_set_name)
781 new_keys = {}
csabella7eb58832017-07-04 21:30:58 -0400782 for event in prev_keys: # Add key set to changed items.
783 event_name = event[2:-2] # Trim off the angle brackets.
csabellabac7d332017-06-26 17:46:26 -0400784 binding = ' '.join(prev_keys[event])
785 new_keys[event_name] = binding
csabella7eb58832017-07-04 21:30:58 -0400786 # Handle any unsaved changes to prev key set.
terryjreedy349abd92017-07-07 16:00:57 -0400787 if prev_key_set_name in changes['keys']:
788 key_set_changes = changes['keys'][prev_key_set_name]
csabellabac7d332017-06-26 17:46:26 -0400789 for event in key_set_changes:
790 new_keys[event] = key_set_changes[event]
csabella7eb58832017-07-04 21:30:58 -0400791 # Save the new key set.
csabellabac7d332017-06-26 17:46:26 -0400792 self.save_new_key_set(new_key_set_name, new_keys)
csabella7eb58832017-07-04 21:30:58 -0400793 # Change GUI over to the new key set.
csabellabac7d332017-06-26 17:46:26 -0400794 custom_key_list = idleConf.GetSectionList('user', 'keys')
795 custom_key_list.sort()
796 self.opt_menu_keys_custom.SetMenu(custom_key_list, new_key_set_name)
797 self.are_keys_builtin.set(0)
798 self.set_keys_type()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000799
csabellabac7d332017-06-26 17:46:26 -0400800 def load_keys_list(self, keyset_name):
csabella7eb58832017-07-04 21:30:58 -0400801 """Reload the list of action/key binding pairs for the active key set.
802
803 An action/key binding can be selected to change the key binding.
804 """
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400805 reselect = 0
csabellabac7d332017-06-26 17:46:26 -0400806 if self.list_bindings.curselection():
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400807 reselect = 1
csabellabac7d332017-06-26 17:46:26 -0400808 list_index = self.list_bindings.index(ANCHOR)
809 keyset = idleConf.GetKeySet(keyset_name)
810 bind_names = list(keyset.keys())
811 bind_names.sort()
812 self.list_bindings.delete(0, END)
813 for bind_name in bind_names:
csabella7eb58832017-07-04 21:30:58 -0400814 key = ' '.join(keyset[bind_name])
815 bind_name = bind_name[2:-2] # Trim off the angle brackets.
terryjreedy349abd92017-07-07 16:00:57 -0400816 if keyset_name in changes['keys']:
csabella7eb58832017-07-04 21:30:58 -0400817 # Handle any unsaved changes to this key set.
terryjreedy349abd92017-07-07 16:00:57 -0400818 if bind_name in changes['keys'][keyset_name]:
819 key = changes['keys'][keyset_name][bind_name]
csabellabac7d332017-06-26 17:46:26 -0400820 self.list_bindings.insert(END, bind_name+' - '+key)
Steven M. Gava052937f2002-02-11 02:20:53 +0000821 if reselect:
csabellabac7d332017-06-26 17:46:26 -0400822 self.list_bindings.see(list_index)
823 self.list_bindings.select_set(list_index)
824 self.list_bindings.select_anchor(list_index)
Steven M. Gava052937f2002-02-11 02:20:53 +0000825
csabellabac7d332017-06-26 17:46:26 -0400826 def delete_custom_keys(self):
csabella7eb58832017-07-04 21:30:58 -0400827 """Handle event to delete a custom key set.
828
829 Applying the delete deactivates the current configuration and
830 reverts to the default. The custom key set is permanently
831 deleted from the config file.
832 """
csabellabac7d332017-06-26 17:46:26 -0400833 keyset_name=self.custom_keys.get()
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400834 delmsg = 'Are you sure you wish to delete the key set %r ?'
835 if not tkMessageBox.askyesno(
csabellabac7d332017-06-26 17:46:26 -0400836 'Delete Key Set', delmsg % keyset_name, parent=self):
Steven M. Gava49745752002-02-18 01:43:11 +0000837 return
csabellabac7d332017-06-26 17:46:26 -0400838 self.deactivate_current_config()
terryjreedy349abd92017-07-07 16:00:57 -0400839 # Remove key set from changes, config, and file.
840 changes.remove(keyset_name)
csabella7eb58832017-07-04 21:30:58 -0400841 # Reload user key set list.
csabellabac7d332017-06-26 17:46:26 -0400842 item_list = idleConf.GetSectionList('user', 'keys')
843 item_list.sort()
844 if not item_list:
845 self.radio_keys_custom.config(state=DISABLED)
846 self.opt_menu_keys_custom.SetMenu(item_list, '- no custom keys -')
Steven M. Gava49745752002-02-18 01:43:11 +0000847 else:
csabellabac7d332017-06-26 17:46:26 -0400848 self.opt_menu_keys_custom.SetMenu(item_list, item_list[0])
csabella7eb58832017-07-04 21:30:58 -0400849 # Revert to default key set.
csabellabac7d332017-06-26 17:46:26 -0400850 self.are_keys_builtin.set(idleConf.defaultCfg['main']
Terry Jan Reedy9bdb1ed2016-07-10 13:46:34 -0400851 .Get('Keys', 'default'))
csabellabac7d332017-06-26 17:46:26 -0400852 self.builtin_keys.set(idleConf.defaultCfg['main'].Get('Keys', 'name')
Terry Jan Reedy9bdb1ed2016-07-10 13:46:34 -0400853 or idleConf.default_keys())
csabella7eb58832017-07-04 21:30:58 -0400854 # User can't back out of these changes, they must be applied now.
terryjreedy349abd92017-07-07 16:00:57 -0400855 changes.save_all()
856 self.save_all_changed_extensions()
csabellabac7d332017-06-26 17:46:26 -0400857 self.activate_config_changes()
858 self.set_keys_type()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000859
csabellabac7d332017-06-26 17:46:26 -0400860 def delete_custom_theme(self):
csabella7eb58832017-07-04 21:30:58 -0400861 """Handle event to delete custom theme.
862
863 The current theme is deactivated and the default theme is
864 activated. The custom theme is permanently removed from
865 the config file.
866 """
csabellabac7d332017-06-26 17:46:26 -0400867 theme_name = self.custom_theme.get()
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400868 delmsg = 'Are you sure you wish to delete the theme %r ?'
869 if not tkMessageBox.askyesno(
csabellabac7d332017-06-26 17:46:26 -0400870 'Delete Theme', delmsg % theme_name, parent=self):
Steven M. Gava49745752002-02-18 01:43:11 +0000871 return
csabellabac7d332017-06-26 17:46:26 -0400872 self.deactivate_current_config()
terryjreedy349abd92017-07-07 16:00:57 -0400873 # Remove theme from changes, config, and file.
874 changes.delete_section('highlight')
csabella7eb58832017-07-04 21:30:58 -0400875 # Reload user theme list.
csabellabac7d332017-06-26 17:46:26 -0400876 item_list = idleConf.GetSectionList('user', 'highlight')
877 item_list.sort()
878 if not item_list:
879 self.radio_theme_custom.config(state=DISABLED)
880 self.opt_menu_theme_custom.SetMenu(item_list, '- no custom themes -')
Steven M. Gava49745752002-02-18 01:43:11 +0000881 else:
csabellabac7d332017-06-26 17:46:26 -0400882 self.opt_menu_theme_custom.SetMenu(item_list, item_list[0])
csabella7eb58832017-07-04 21:30:58 -0400883 # Revert to default theme.
csabellabac7d332017-06-26 17:46:26 -0400884 self.is_builtin_theme.set(idleConf.defaultCfg['main'].Get('Theme', 'default'))
885 self.builtin_theme.set(idleConf.defaultCfg['main'].Get('Theme', 'name'))
csabella7eb58832017-07-04 21:30:58 -0400886 # User can't back out of these changes, they must be applied now.
terryjreedy349abd92017-07-07 16:00:57 -0400887 changes.save_all()
888 self.save_all_changed_extensions()
csabellabac7d332017-06-26 17:46:26 -0400889 self.activate_config_changes()
890 self.set_theme_type()
Steven M. Gava49745752002-02-18 01:43:11 +0000891
csabellabac7d332017-06-26 17:46:26 -0400892 def get_colour(self):
csabella7eb58832017-07-04 21:30:58 -0400893 """Handle button to select a new color for the target tag.
894
895 If a new color is selected while using a builtin theme, a
896 name must be supplied to create a custom theme.
897 """
csabellabac7d332017-06-26 17:46:26 -0400898 target = self.highlight_target.get()
899 prev_colour = self.frame_colour_set.cget('bg')
900 rgbTuplet, colour_string = tkColorChooser.askcolor(
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400901 parent=self, title='Pick new colour for : '+target,
csabellabac7d332017-06-26 17:46:26 -0400902 initialcolor=prev_colour)
903 if colour_string and (colour_string != prev_colour):
csabella7eb58832017-07-04 21:30:58 -0400904 # User didn't cancel and they chose a new colour.
905 if self.is_builtin_theme.get(): # Current theme is a built-in.
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400906 message = ('Your changes will be saved as a new Custom Theme. '
907 'Enter a name for your new Custom Theme below.')
csabellabac7d332017-06-26 17:46:26 -0400908 new_theme = self.get_new_theme_name(message)
csabella7eb58832017-07-04 21:30:58 -0400909 if not new_theme: # User cancelled custom theme creation.
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000910 return
csabella7eb58832017-07-04 21:30:58 -0400911 else: # Create new custom theme based on previously active theme.
csabellabac7d332017-06-26 17:46:26 -0400912 self.create_new_theme(new_theme)
913 self.colour.set(colour_string)
csabella7eb58832017-07-04 21:30:58 -0400914 else: # Current theme is user defined.
csabellabac7d332017-06-26 17:46:26 -0400915 self.colour.set(colour_string)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000916
csabellabac7d332017-06-26 17:46:26 -0400917 def on_new_colour_set(self):
csabella7eb58832017-07-04 21:30:58 -0400918 "Display sample of new color selection on the dialog."
csabellabac7d332017-06-26 17:46:26 -0400919 new_colour=self.colour.get()
csabella7eb58832017-07-04 21:30:58 -0400920 self.frame_colour_set.config(bg=new_colour) # Set sample.
csabellabac7d332017-06-26 17:46:26 -0400921 plane ='foreground' if self.fg_bg_toggle.get() else 'background'
922 sample_element = self.theme_elements[self.highlight_target.get()][0]
923 self.text_highlight_sample.tag_config(sample_element, **{plane:new_colour})
924 theme = self.custom_theme.get()
925 theme_element = sample_element + '-' + plane
terryjreedy349abd92017-07-07 16:00:57 -0400926 changes.add_option('highlight', theme, theme_element, new_colour)
Steven M. Gava052937f2002-02-11 02:20:53 +0000927
csabellabac7d332017-06-26 17:46:26 -0400928 def get_new_theme_name(self, message):
csabella7eb58832017-07-04 21:30:58 -0400929 "Return name of new theme from query popup."
csabellabac7d332017-06-26 17:46:26 -0400930 used_names = (idleConf.GetSectionList('user', 'highlight') +
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400931 idleConf.GetSectionList('default', 'highlight'))
csabellabac7d332017-06-26 17:46:26 -0400932 new_theme = SectionName(
933 self, 'New Custom Theme', message, used_names).result
934 return new_theme
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000935
csabellabac7d332017-06-26 17:46:26 -0400936 def save_as_new_theme(self):
csabella7eb58832017-07-04 21:30:58 -0400937 "Prompt for new theme name and create the theme."
csabellabac7d332017-06-26 17:46:26 -0400938 new_theme_name = self.get_new_theme_name('New Theme Name:')
939 if new_theme_name:
940 self.create_new_theme(new_theme_name)
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000941
csabellabac7d332017-06-26 17:46:26 -0400942 def create_new_theme(self, new_theme_name):
csabella7eb58832017-07-04 21:30:58 -0400943 """Create a new custom theme with the given name.
944
945 Create the new theme based on the previously active theme
946 with the current changes applied. Once it is saved, then
947 activate the new theme.
948 """
csabellabac7d332017-06-26 17:46:26 -0400949 if self.is_builtin_theme.get():
950 theme_type = 'default'
951 theme_name = self.builtin_theme.get()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000952 else:
csabellabac7d332017-06-26 17:46:26 -0400953 theme_type = 'user'
954 theme_name = self.custom_theme.get()
955 new_theme = idleConf.GetThemeDict(theme_type, theme_name)
csabella7eb58832017-07-04 21:30:58 -0400956 # Apply any of the old theme's unsaved changes to the new theme.
terryjreedy349abd92017-07-07 16:00:57 -0400957 if theme_name in changes['highlight']:
958 theme_changes = changes['highlight'][theme_name]
csabellabac7d332017-06-26 17:46:26 -0400959 for element in theme_changes:
960 new_theme[element] = theme_changes[element]
csabella7eb58832017-07-04 21:30:58 -0400961 # Save the new theme.
csabellabac7d332017-06-26 17:46:26 -0400962 self.save_new_theme(new_theme_name, new_theme)
csabella7eb58832017-07-04 21:30:58 -0400963 # Change GUI over to the new theme.
csabellabac7d332017-06-26 17:46:26 -0400964 custom_theme_list = idleConf.GetSectionList('user', 'highlight')
965 custom_theme_list.sort()
966 self.opt_menu_theme_custom.SetMenu(custom_theme_list, new_theme_name)
967 self.is_builtin_theme.set(0)
968 self.set_theme_type()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000969
Louie Lubb2bae82017-07-10 06:57:18 +0800970 def on_fontlist_select(self, event):
971 """Handle selecting a font from the list.
csabella7eb58832017-07-04 21:30:58 -0400972
Louie Lubb2bae82017-07-10 06:57:18 +0800973 Event can result from either mouse click or Up or Down key.
974 Set font_name and example display to selection.
csabella7eb58832017-07-04 21:30:58 -0400975 """
Louie Lubb2bae82017-07-10 06:57:18 +0800976 font = self.fontlist.get(ANCHOR if event.type == 3 else ACTIVE)
csabellabac7d332017-06-26 17:46:26 -0400977 self.font_name.set(font.lower())
978 self.set_font_sample()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000979
csabellabac7d332017-06-26 17:46:26 -0400980 def set_font_sample(self, event=None):
csabella7eb58832017-07-04 21:30:58 -0400981 "Update the screen samples with the font settings from the dialog."
csabellabac7d332017-06-26 17:46:26 -0400982 font_name = self.font_name.get()
983 font_weight = tkFont.BOLD if self.font_bold.get() else tkFont.NORMAL
984 new_font = (font_name, self.font_size.get(), font_weight)
985 self.font_sample.config(font=new_font)
986 self.text_highlight_sample.configure(font=new_font)
Steven M. Gava5f28e8f2002-01-21 06:38:21 +0000987
csabellabac7d332017-06-26 17:46:26 -0400988 def set_highlight_target(self):
csabella7eb58832017-07-04 21:30:58 -0400989 "Set fg/bg toggle and color based on highlight tag target."
990 if self.highlight_target.get() == 'Cursor': # bg not possible
csabellabac7d332017-06-26 17:46:26 -0400991 self.radio_fg.config(state=DISABLED)
992 self.radio_bg.config(state=DISABLED)
993 self.fg_bg_toggle.set(1)
csabella7eb58832017-07-04 21:30:58 -0400994 else: # Both fg and bg can be set.
csabellabac7d332017-06-26 17:46:26 -0400995 self.radio_fg.config(state=NORMAL)
996 self.radio_bg.config(state=NORMAL)
997 self.fg_bg_toggle.set(1)
998 self.set_colour_sample()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000999
csabellabac7d332017-06-26 17:46:26 -04001000 def set_colour_sample_binding(self, *args):
csabella7eb58832017-07-04 21:30:58 -04001001 "Change color sample based on foreground/background toggle."
csabellabac7d332017-06-26 17:46:26 -04001002 self.set_colour_sample()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001003
csabellabac7d332017-06-26 17:46:26 -04001004 def set_colour_sample(self):
csabella7eb58832017-07-04 21:30:58 -04001005 "Set the color of the frame background to reflect the selected target."
1006 # Set the colour sample area.
csabellabac7d332017-06-26 17:46:26 -04001007 tag = self.theme_elements[self.highlight_target.get()][0]
1008 plane = 'foreground' if self.fg_bg_toggle.get() else 'background'
1009 colour = self.text_highlight_sample.tag_cget(tag, plane)
1010 self.frame_colour_set.config(bg=colour)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001011
csabellabac7d332017-06-26 17:46:26 -04001012 def paint_theme_sample(self):
csabella7eb58832017-07-04 21:30:58 -04001013 "Apply the theme colors to each element tag in the sample text."
1014 if self.is_builtin_theme.get(): # Default theme
csabellabac7d332017-06-26 17:46:26 -04001015 theme = self.builtin_theme.get()
csabella7eb58832017-07-04 21:30:58 -04001016 else: # User theme
csabellabac7d332017-06-26 17:46:26 -04001017 theme = self.custom_theme.get()
1018 for element_title in self.theme_elements:
1019 element = self.theme_elements[element_title][0]
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001020 colours = idleConf.GetHighlight(theme, element)
csabella7eb58832017-07-04 21:30:58 -04001021 if element == 'cursor': # Cursor sample needs special painting.
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001022 colours['background'] = idleConf.GetHighlight(
1023 theme, 'normal', fgBg='bg')
csabella7eb58832017-07-04 21:30:58 -04001024 # Handle any unsaved changes to this theme.
terryjreedy349abd92017-07-07 16:00:57 -04001025 if theme in changes['highlight']:
1026 theme_dict = changes['highlight'][theme]
csabellabac7d332017-06-26 17:46:26 -04001027 if element + '-foreground' in theme_dict:
1028 colours['foreground'] = theme_dict[element + '-foreground']
1029 if element + '-background' in theme_dict:
1030 colours['background'] = theme_dict[element + '-background']
1031 self.text_highlight_sample.tag_config(element, **colours)
1032 self.set_colour_sample()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001033
csabellabac7d332017-06-26 17:46:26 -04001034 def help_source_selected(self, event):
csabella7eb58832017-07-04 21:30:58 -04001035 "Handle event for selecting additional help."
csabellabac7d332017-06-26 17:46:26 -04001036 self.set_helplist_button_states()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001037
csabellabac7d332017-06-26 17:46:26 -04001038 def set_helplist_button_states(self):
csabella7eb58832017-07-04 21:30:58 -04001039 "Toggle the state for the help list buttons based on list entries."
1040 if self.list_help.size() < 1: # No entries in list.
csabellabac7d332017-06-26 17:46:26 -04001041 self.button_helplist_edit.config(state=DISABLED)
1042 self.button_helplist_remove.config(state=DISABLED)
csabella7eb58832017-07-04 21:30:58 -04001043 else: # Some entries.
1044 if self.list_help.curselection(): # There currently is a selection.
csabellabac7d332017-06-26 17:46:26 -04001045 self.button_helplist_edit.config(state=NORMAL)
1046 self.button_helplist_remove.config(state=NORMAL)
csabella7eb58832017-07-04 21:30:58 -04001047 else: # There currently is not a selection.
csabellabac7d332017-06-26 17:46:26 -04001048 self.button_helplist_edit.config(state=DISABLED)
1049 self.button_helplist_remove.config(state=DISABLED)
Steven M. Gava085eb1b2002-02-05 04:52:32 +00001050
csabellabac7d332017-06-26 17:46:26 -04001051 def helplist_item_add(self):
csabella7eb58832017-07-04 21:30:58 -04001052 """Handle add button for the help list.
1053
1054 Query for name and location of new help sources and add
1055 them to the list.
1056 """
csabellabac7d332017-06-26 17:46:26 -04001057 help_source = HelpSource(self, 'New Help Source',
Terry Jan Reedy8b22c0a2016-07-08 00:22:50 -04001058 ).result
csabellabac7d332017-06-26 17:46:26 -04001059 if help_source:
1060 self.user_helplist.append((help_source[0], help_source[1]))
1061 self.list_help.insert(END, help_source[0])
1062 self.update_user_help_changed_items()
1063 self.set_helplist_button_states()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001064
csabellabac7d332017-06-26 17:46:26 -04001065 def helplist_item_edit(self):
csabella7eb58832017-07-04 21:30:58 -04001066 """Handle edit button for the help list.
1067
1068 Query with existing help source information and update
1069 config if the values are changed.
1070 """
csabellabac7d332017-06-26 17:46:26 -04001071 item_index = self.list_help.index(ANCHOR)
1072 help_source = self.user_helplist[item_index]
1073 new_help_source = HelpSource(
Terry Jan Reedy8b22c0a2016-07-08 00:22:50 -04001074 self, 'Edit Help Source',
csabellabac7d332017-06-26 17:46:26 -04001075 menuitem=help_source[0],
1076 filepath=help_source[1],
Terry Jan Reedy8b22c0a2016-07-08 00:22:50 -04001077 ).result
csabellabac7d332017-06-26 17:46:26 -04001078 if new_help_source and new_help_source != help_source:
1079 self.user_helplist[item_index] = new_help_source
1080 self.list_help.delete(item_index)
1081 self.list_help.insert(item_index, new_help_source[0])
1082 self.update_user_help_changed_items()
1083 self.set_helplist_button_states()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001084
csabellabac7d332017-06-26 17:46:26 -04001085 def helplist_item_remove(self):
csabella7eb58832017-07-04 21:30:58 -04001086 """Handle remove button for the help list.
1087
1088 Delete the help list item from config.
1089 """
csabellabac7d332017-06-26 17:46:26 -04001090 item_index = self.list_help.index(ANCHOR)
1091 del(self.user_helplist[item_index])
1092 self.list_help.delete(item_index)
1093 self.update_user_help_changed_items()
1094 self.set_helplist_button_states()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001095
csabellabac7d332017-06-26 17:46:26 -04001096 def update_user_help_changed_items(self):
terryjreedy349abd92017-07-07 16:00:57 -04001097 "Clear and rebuild the HelpFiles section in changes"
1098 changes['main']['HelpFiles'] = {}
csabellabac7d332017-06-26 17:46:26 -04001099 for num in range(1, len(self.user_helplist) + 1):
terryjreedy349abd92017-07-07 16:00:57 -04001100 changes.add_option(
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001101 'main', 'HelpFiles', str(num),
csabellabac7d332017-06-26 17:46:26 -04001102 ';'.join(self.user_helplist[num-1][:2]))
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001103
csabellabac7d332017-06-26 17:46:26 -04001104 def load_font_cfg(self):
csabella7eb58832017-07-04 21:30:58 -04001105 "Load current configuration settings for the font options."
1106 # Set base editor font selection list.
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001107 fonts = list(tkFont.families(self))
Steven M. Gavac11ccf32001-09-24 09:43:17 +00001108 fonts.sort()
1109 for font in fonts:
Louie Lubb2bae82017-07-10 06:57:18 +08001110 self.fontlist.insert(END, font)
csabellabac7d332017-06-26 17:46:26 -04001111 configured_font = idleConf.GetFont(self, 'main', 'EditorWindow')
1112 font_name = configured_font[0].lower()
1113 font_size = configured_font[1]
1114 font_bold = configured_font[2]=='bold'
1115 self.font_name.set(font_name)
Kurt B. Kaiser05391692003-05-26 20:35:53 +00001116 lc_fonts = [s.lower() for s in fonts]
Terry Jan Reedyd87d1682015-08-01 18:57:33 -04001117 try:
csabellabac7d332017-06-26 17:46:26 -04001118 current_font_index = lc_fonts.index(font_name)
Louie Lubb2bae82017-07-10 06:57:18 +08001119 self.fontlist.see(current_font_index)
1120 self.fontlist.select_set(current_font_index)
1121 self.fontlist.select_anchor(current_font_index)
1122 self.fontlist.activate(current_font_index)
Terry Jan Reedyd87d1682015-08-01 18:57:33 -04001123 except ValueError:
1124 pass
csabella7eb58832017-07-04 21:30:58 -04001125 # Set font size dropdown.
csabellabac7d332017-06-26 17:46:26 -04001126 self.opt_menu_font_size.SetMenu(('7', '8', '9', '10', '11', '12', '13',
Terry Jan Reedyda028872016-08-30 20:19:13 -04001127 '14', '16', '18', '20', '22',
csabellabac7d332017-06-26 17:46:26 -04001128 '25', '29', '34', '40'), font_size )
csabella7eb58832017-07-04 21:30:58 -04001129 # Set font weight.
csabellabac7d332017-06-26 17:46:26 -04001130 self.font_bold.set(font_bold)
csabella7eb58832017-07-04 21:30:58 -04001131 # Set font sample.
csabellabac7d332017-06-26 17:46:26 -04001132 self.set_font_sample()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001133
csabellabac7d332017-06-26 17:46:26 -04001134 def load_tab_cfg(self):
csabella7eb58832017-07-04 21:30:58 -04001135 "Load current configuration settings for the tab options."
1136 # Set indent sizes.
csabellabac7d332017-06-26 17:46:26 -04001137 space_num = idleConf.GetOption(
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001138 'main', 'Indent', 'num-spaces', default=4, type='int')
csabellabac7d332017-06-26 17:46:26 -04001139 self.space_num.set(space_num)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001140
csabellabac7d332017-06-26 17:46:26 -04001141 def load_theme_cfg(self):
csabella7eb58832017-07-04 21:30:58 -04001142 "Load current configuration settings for the theme options."
1143 # Set current theme type radiobutton.
csabellabac7d332017-06-26 17:46:26 -04001144 self.is_builtin_theme.set(idleConf.GetOption(
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001145 'main', 'Theme', 'default', type='bool', default=1))
csabella7eb58832017-07-04 21:30:58 -04001146 # Set current theme.
csabellabac7d332017-06-26 17:46:26 -04001147 current_option = idleConf.CurrentTheme()
csabella7eb58832017-07-04 21:30:58 -04001148 # Load available theme option menus.
1149 if self.is_builtin_theme.get(): # Default theme selected.
csabellabac7d332017-06-26 17:46:26 -04001150 item_list = idleConf.GetSectionList('default', 'highlight')
1151 item_list.sort()
1152 self.opt_menu_theme_builtin.SetMenu(item_list, current_option)
1153 item_list = idleConf.GetSectionList('user', 'highlight')
1154 item_list.sort()
1155 if not item_list:
1156 self.radio_theme_custom.config(state=DISABLED)
1157 self.custom_theme.set('- no custom themes -')
Steven M. Gava41a85322001-10-29 08:05:34 +00001158 else:
csabellabac7d332017-06-26 17:46:26 -04001159 self.opt_menu_theme_custom.SetMenu(item_list, item_list[0])
csabella7eb58832017-07-04 21:30:58 -04001160 else: # User theme selected.
csabellabac7d332017-06-26 17:46:26 -04001161 item_list = idleConf.GetSectionList('user', 'highlight')
1162 item_list.sort()
1163 self.opt_menu_theme_custom.SetMenu(item_list, current_option)
1164 item_list = idleConf.GetSectionList('default', 'highlight')
1165 item_list.sort()
1166 self.opt_menu_theme_builtin.SetMenu(item_list, item_list[0])
1167 self.set_theme_type()
csabella7eb58832017-07-04 21:30:58 -04001168 # Load theme element option menu.
csabellabac7d332017-06-26 17:46:26 -04001169 theme_names = list(self.theme_elements.keys())
1170 theme_names.sort(key=lambda x: self.theme_elements[x][1])
1171 self.opt_menu_highlight_target.SetMenu(theme_names, theme_names[0])
1172 self.paint_theme_sample()
1173 self.set_highlight_target()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001174
csabellabac7d332017-06-26 17:46:26 -04001175 def load_key_cfg(self):
csabella7eb58832017-07-04 21:30:58 -04001176 "Load current configuration settings for the keybinding options."
1177 # Set current keys type radiobutton.
csabellabac7d332017-06-26 17:46:26 -04001178 self.are_keys_builtin.set(idleConf.GetOption(
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001179 'main', 'Keys', 'default', type='bool', default=1))
csabella7eb58832017-07-04 21:30:58 -04001180 # Set current keys.
csabellabac7d332017-06-26 17:46:26 -04001181 current_option = idleConf.CurrentKeys()
csabella7eb58832017-07-04 21:30:58 -04001182 # Load available keyset option menus.
1183 if self.are_keys_builtin.get(): # Default theme selected.
csabellabac7d332017-06-26 17:46:26 -04001184 item_list = idleConf.GetSectionList('default', 'keys')
1185 item_list.sort()
1186 self.opt_menu_keys_builtin.SetMenu(item_list, current_option)
1187 item_list = idleConf.GetSectionList('user', 'keys')
1188 item_list.sort()
1189 if not item_list:
1190 self.radio_keys_custom.config(state=DISABLED)
1191 self.custom_keys.set('- no custom keys -')
Steven M. Gava41a85322001-10-29 08:05:34 +00001192 else:
csabellabac7d332017-06-26 17:46:26 -04001193 self.opt_menu_keys_custom.SetMenu(item_list, item_list[0])
csabella7eb58832017-07-04 21:30:58 -04001194 else: # User key set selected.
csabellabac7d332017-06-26 17:46:26 -04001195 item_list = idleConf.GetSectionList('user', 'keys')
1196 item_list.sort()
1197 self.opt_menu_keys_custom.SetMenu(item_list, current_option)
1198 item_list = idleConf.GetSectionList('default', 'keys')
1199 item_list.sort()
1200 self.opt_menu_keys_builtin.SetMenu(item_list, idleConf.default_keys())
1201 self.set_keys_type()
csabella7eb58832017-07-04 21:30:58 -04001202 # Load keyset element list.
csabellabac7d332017-06-26 17:46:26 -04001203 keyset_name = idleConf.CurrentKeys()
1204 self.load_keys_list(keyset_name)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001205
csabellabac7d332017-06-26 17:46:26 -04001206 def load_general_cfg(self):
csabella7eb58832017-07-04 21:30:58 -04001207 "Load current configuration settings for the general options."
1208 # Set startup state.
csabellabac7d332017-06-26 17:46:26 -04001209 self.startup_edit.set(idleConf.GetOption(
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001210 'main', 'General', 'editor-on-startup', default=1, type='bool'))
csabella7eb58832017-07-04 21:30:58 -04001211 # Set autosave state.
csabellabac7d332017-06-26 17:46:26 -04001212 self.autosave.set(idleConf.GetOption(
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001213 'main', 'General', 'autosave', default=0, type='bool'))
csabella7eb58832017-07-04 21:30:58 -04001214 # Set initial window size.
csabellabac7d332017-06-26 17:46:26 -04001215 self.win_width.set(idleConf.GetOption(
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001216 'main', 'EditorWindow', 'width', type='int'))
csabellabac7d332017-06-26 17:46:26 -04001217 self.win_height.set(idleConf.GetOption(
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001218 'main', 'EditorWindow', 'height', type='int'))
csabella7eb58832017-07-04 21:30:58 -04001219 # Set additional help sources.
csabellabac7d332017-06-26 17:46:26 -04001220 self.user_helplist = idleConf.GetAllExtraHelpSourcesList()
1221 for help_item in self.user_helplist:
1222 self.list_help.insert(END, help_item[0])
1223 self.set_helplist_button_states()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001224
csabellabac7d332017-06-26 17:46:26 -04001225 def load_configs(self):
csabella7eb58832017-07-04 21:30:58 -04001226 """Load configuration for each page.
1227
1228 Load configuration from default and user config files and populate
Steven M. Gava429a86af2001-10-23 10:42:12 +00001229 the widgets on the config dialog pages.
1230 """
csabellabac7d332017-06-26 17:46:26 -04001231 self.load_font_cfg()
1232 self.load_tab_cfg()
csabellabac7d332017-06-26 17:46:26 -04001233 self.load_theme_cfg()
csabellabac7d332017-06-26 17:46:26 -04001234 self.load_key_cfg()
csabellabac7d332017-06-26 17:46:26 -04001235 self.load_general_cfg()
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001236 # note: extension page handled separately
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001237
csabellabac7d332017-06-26 17:46:26 -04001238 def save_new_key_set(self, keyset_name, keyset):
csabella7eb58832017-07-04 21:30:58 -04001239 """Save a newly created core key set.
1240
csabellabac7d332017-06-26 17:46:26 -04001241 keyset_name - string, the name of the new key set
1242 keyset - dictionary containing the new key set
Steven M. Gava052937f2002-02-11 02:20:53 +00001243 """
csabellabac7d332017-06-26 17:46:26 -04001244 if not idleConf.userCfg['keys'].has_section(keyset_name):
1245 idleConf.userCfg['keys'].add_section(keyset_name)
1246 for event in keyset:
1247 value = keyset[event]
1248 idleConf.userCfg['keys'].SetOption(keyset_name, event, value)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001249
csabellabac7d332017-06-26 17:46:26 -04001250 def save_new_theme(self, theme_name, theme):
csabella7eb58832017-07-04 21:30:58 -04001251 """Save a newly created theme.
1252
csabellabac7d332017-06-26 17:46:26 -04001253 theme_name - string, the name of the new theme
Steven M. Gava052937f2002-02-11 02:20:53 +00001254 theme - dictionary containing the new theme
1255 """
csabellabac7d332017-06-26 17:46:26 -04001256 if not idleConf.userCfg['highlight'].has_section(theme_name):
1257 idleConf.userCfg['highlight'].add_section(theme_name)
Kurt B. Kaisere0712772007-08-23 05:25:55 +00001258 for element in theme:
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001259 value = theme[element]
csabellabac7d332017-06-26 17:46:26 -04001260 idleConf.userCfg['highlight'].SetOption(theme_name, element, value)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001261
csabellabac7d332017-06-26 17:46:26 -04001262 def deactivate_current_config(self):
csabella7eb58832017-07-04 21:30:58 -04001263 "Remove current key bindings."
1264 # Before a config is saved, some cleanup of current
1265 # config must be done - remove the previous keybindings.
csabellabac7d332017-06-26 17:46:26 -04001266 win_instances = self.parent.instance_dict.keys()
1267 for instance in win_instances:
Kurt B. Kaiserb1754452005-11-18 22:05:48 +00001268 instance.RemoveKeybindings()
1269
csabellabac7d332017-06-26 17:46:26 -04001270 def activate_config_changes(self):
Kurt B. Kaiseracdef852005-01-31 03:34:26 +00001271 "Dynamically apply configuration changes"
csabellabac7d332017-06-26 17:46:26 -04001272 win_instances = self.parent.instance_dict.keys()
1273 for instance in win_instances:
Steven M. Gavab77d3432002-03-02 07:16:21 +00001274 instance.ResetColorizer()
Steven M. Gavab1585412002-03-12 00:21:56 +00001275 instance.ResetFont()
Kurt B. Kaiseracdef852005-01-31 03:34:26 +00001276 instance.set_notabs_indentwidth()
Kurt B. Kaiserb1754452005-11-18 22:05:48 +00001277 instance.ApplyKeybindings()
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +00001278 instance.reset_help_menu_entries()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001279
csabellabac7d332017-06-26 17:46:26 -04001280 def cancel(self):
csabella7eb58832017-07-04 21:30:58 -04001281 "Dismiss config dialog."
Steven M. Gava5f28e8f2002-01-21 06:38:21 +00001282 self.destroy()
1283
csabellabac7d332017-06-26 17:46:26 -04001284 def ok(self):
csabella7eb58832017-07-04 21:30:58 -04001285 "Apply config changes, then dismiss dialog."
csabellabac7d332017-06-26 17:46:26 -04001286 self.apply()
Steven M. Gava5f28e8f2002-01-21 06:38:21 +00001287 self.destroy()
1288
csabellabac7d332017-06-26 17:46:26 -04001289 def apply(self):
csabella7eb58832017-07-04 21:30:58 -04001290 "Apply config changes and leave dialog open."
csabellabac7d332017-06-26 17:46:26 -04001291 self.deactivate_current_config()
terryjreedy349abd92017-07-07 16:00:57 -04001292 changes.save_all()
1293 self.save_all_changed_extensions()
csabellabac7d332017-06-26 17:46:26 -04001294 self.activate_config_changes()
Steven M. Gava5f28e8f2002-01-21 06:38:21 +00001295
csabellabac7d332017-06-26 17:46:26 -04001296 def help(self):
csabella7eb58832017-07-04 21:30:58 -04001297 "Create textview for config dialog help."
csabellabac7d332017-06-26 17:46:26 -04001298 page = self.tab_pages._current_page
Terry Jan Reedyd0cadba2015-10-11 22:07:31 -04001299 view_text(self, title='Help for IDLE preferences',
1300 text=help_common+help_pages.get(page, ''))
1301
csabellabac7d332017-06-26 17:46:26 -04001302 def create_page_extensions(self):
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001303 """Part of the config dialog used for configuring IDLE extensions.
1304
1305 This code is generic - it works for any and all IDLE extensions.
1306
1307 IDLE extensions save their configuration options using idleConf.
Terry Jan Reedyb2f87602015-10-13 22:09:06 -04001308 This code reads the current configuration using idleConf, supplies a
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001309 GUI interface to change the configuration values, and saves the
1310 changes using idleConf.
1311
1312 Not all changes take effect immediately - some may require restarting IDLE.
1313 This depends on each extension's implementation.
1314
1315 All values are treated as text, and it is up to the user to supply
1316 reasonable values. The only exception to this are the 'enable*' options,
Serhiy Storchaka6a7b3a72016-04-17 08:32:47 +03001317 which are boolean, and can be toggled with a True/False button.
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001318 """
1319 parent = self.parent
csabellabac7d332017-06-26 17:46:26 -04001320 frame = self.tab_pages.pages['Extensions'].frame
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001321 self.ext_defaultCfg = idleConf.defaultCfg['extensions']
1322 self.ext_userCfg = idleConf.userCfg['extensions']
1323 self.is_int = self.register(is_int)
1324 self.load_extensions()
csabella7eb58832017-07-04 21:30:58 -04001325 # Create widgets - a listbox shows all available extensions, with the
1326 # controls for the extension selected in the listbox to the right.
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001327 self.extension_names = StringVar(self)
1328 frame.rowconfigure(0, weight=1)
1329 frame.columnconfigure(2, weight=1)
1330 self.extension_list = Listbox(frame, listvariable=self.extension_names,
1331 selectmode='browse')
1332 self.extension_list.bind('<<ListboxSelect>>', self.extension_selected)
1333 scroll = Scrollbar(frame, command=self.extension_list.yview)
1334 self.extension_list.yscrollcommand=scroll.set
1335 self.details_frame = LabelFrame(frame, width=250, height=250)
1336 self.extension_list.grid(column=0, row=0, sticky='nws')
1337 scroll.grid(column=1, row=0, sticky='ns')
1338 self.details_frame.grid(column=2, row=0, sticky='nsew', padx=[10, 0])
1339 frame.configure(padx=10, pady=10)
1340 self.config_frame = {}
1341 self.current_extension = None
1342
1343 self.outerframe = self # TEMPORARY
1344 self.tabbed_page_set = self.extension_list # TEMPORARY
1345
csabella7eb58832017-07-04 21:30:58 -04001346 # Create the frame holding controls for each extension.
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001347 ext_names = ''
1348 for ext_name in sorted(self.extensions):
1349 self.create_extension_frame(ext_name)
1350 ext_names = ext_names + '{' + ext_name + '} '
1351 self.extension_names.set(ext_names)
1352 self.extension_list.selection_set(0)
1353 self.extension_selected(None)
1354
1355 def load_extensions(self):
1356 "Fill self.extensions with data from the default and user configs."
1357 self.extensions = {}
1358 for ext_name in idleConf.GetExtensions(active_only=False):
1359 self.extensions[ext_name] = []
1360
1361 for ext_name in self.extensions:
1362 opt_list = sorted(self.ext_defaultCfg.GetOptionList(ext_name))
1363
csabella7eb58832017-07-04 21:30:58 -04001364 # Bring 'enable' options to the beginning of the list.
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001365 enables = [opt_name for opt_name in opt_list
1366 if opt_name.startswith('enable')]
1367 for opt_name in enables:
1368 opt_list.remove(opt_name)
1369 opt_list = enables + opt_list
1370
1371 for opt_name in opt_list:
1372 def_str = self.ext_defaultCfg.Get(
1373 ext_name, opt_name, raw=True)
1374 try:
1375 def_obj = {'True':True, 'False':False}[def_str]
1376 opt_type = 'bool'
1377 except KeyError:
1378 try:
1379 def_obj = int(def_str)
1380 opt_type = 'int'
1381 except ValueError:
1382 def_obj = def_str
1383 opt_type = None
1384 try:
1385 value = self.ext_userCfg.Get(
1386 ext_name, opt_name, type=opt_type, raw=True,
1387 default=def_obj)
csabella7eb58832017-07-04 21:30:58 -04001388 except ValueError: # Need this until .Get fixed.
1389 value = def_obj # Bad values overwritten by entry.
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001390 var = StringVar(self)
1391 var.set(str(value))
1392
1393 self.extensions[ext_name].append({'name': opt_name,
1394 'type': opt_type,
1395 'default': def_str,
1396 'value': value,
1397 'var': var,
1398 })
1399
1400 def extension_selected(self, event):
csabella7eb58832017-07-04 21:30:58 -04001401 "Handle selection of an extension from the list."
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001402 newsel = self.extension_list.curselection()
1403 if newsel:
1404 newsel = self.extension_list.get(newsel)
1405 if newsel is None or newsel != self.current_extension:
1406 if self.current_extension:
1407 self.details_frame.config(text='')
1408 self.config_frame[self.current_extension].grid_forget()
1409 self.current_extension = None
1410 if newsel:
1411 self.details_frame.config(text=newsel)
1412 self.config_frame[newsel].grid(column=0, row=0, sticky='nsew')
1413 self.current_extension = newsel
1414
1415 def create_extension_frame(self, ext_name):
1416 """Create a frame holding the widgets to configure one extension"""
1417 f = VerticalScrolledFrame(self.details_frame, height=250, width=250)
1418 self.config_frame[ext_name] = f
1419 entry_area = f.interior
csabella7eb58832017-07-04 21:30:58 -04001420 # Create an entry for each configuration option.
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001421 for row, opt in enumerate(self.extensions[ext_name]):
csabella7eb58832017-07-04 21:30:58 -04001422 # Create a row with a label and entry/checkbutton.
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001423 label = Label(entry_area, text=opt['name'])
1424 label.grid(row=row, column=0, sticky=NW)
1425 var = opt['var']
1426 if opt['type'] == 'bool':
1427 Checkbutton(entry_area, textvariable=var, variable=var,
1428 onvalue='True', offvalue='False',
1429 indicatoron=FALSE, selectcolor='', width=8
1430 ).grid(row=row, column=1, sticky=W, padx=7)
1431 elif opt['type'] == 'int':
1432 Entry(entry_area, textvariable=var, validate='key',
1433 validatecommand=(self.is_int, '%P')
1434 ).grid(row=row, column=1, sticky=NSEW, padx=7)
1435
1436 else:
1437 Entry(entry_area, textvariable=var
1438 ).grid(row=row, column=1, sticky=NSEW, padx=7)
1439 return
1440
1441 def set_extension_value(self, section, opt):
csabella7eb58832017-07-04 21:30:58 -04001442 """Return True if the configuration was added or changed.
1443
1444 If the value is the same as the default, then remove it
1445 from user config file.
1446 """
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001447 name = opt['name']
1448 default = opt['default']
1449 value = opt['var'].get().strip() or default
1450 opt['var'].set(value)
1451 # if self.defaultCfg.has_section(section):
csabella7eb58832017-07-04 21:30:58 -04001452 # Currently, always true; if not, indent to return.
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001453 if (value == default):
1454 return self.ext_userCfg.RemoveOption(section, name)
csabella7eb58832017-07-04 21:30:58 -04001455 # Set the option.
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001456 return self.ext_userCfg.SetOption(section, name, value)
1457
1458 def save_all_changed_extensions(self):
1459 """Save configuration changes to the user config file."""
1460 has_changes = False
1461 for ext_name in self.extensions:
1462 options = self.extensions[ext_name]
1463 for opt in options:
1464 if self.set_extension_value(ext_name, opt):
1465 has_changes = True
1466 if has_changes:
1467 self.ext_userCfg.Save()
1468
1469
Terry Jan Reedyd0cadba2015-10-11 22:07:31 -04001470help_common = '''\
1471When you click either the Apply or Ok buttons, settings in this
1472dialog that are different from IDLE's default are saved in
1473a .idlerc directory in your home directory. Except as noted,
Terry Jan Reedyd0c0f002015-11-12 15:02:57 -05001474these changes apply to all versions of IDLE installed on this
Terry Jan Reedyd0cadba2015-10-11 22:07:31 -04001475machine. Some do not take affect until IDLE is restarted.
1476[Cancel] only cancels changes made since the last save.
1477'''
1478help_pages = {
Terry Jan Reedy9bdb1ed2016-07-10 13:46:34 -04001479 'Highlighting': '''
Terry Jan Reedyd0cadba2015-10-11 22:07:31 -04001480Highlighting:
Terry Jan Reedyd0c0f002015-11-12 15:02:57 -05001481The IDLE Dark color theme is new in October 2015. It can only
Terry Jan Reedyd0cadba2015-10-11 22:07:31 -04001482be used with older IDLE releases if it is saved as a custom
1483theme, with a different name.
Terry Jan Reedy9bdb1ed2016-07-10 13:46:34 -04001484''',
1485 'Keys': '''
1486Keys:
1487The IDLE Modern Unix key set is new in June 2016. It can only
1488be used with older IDLE releases if it is saved as a custom
1489key set, with a different name.
1490''',
wohlgangerfae2c352017-06-27 21:36:23 -05001491 'Extensions': '''
1492Extensions:
1493
1494Autocomplete: Popupwait is milleseconds to wait after key char, without
1495cursor movement, before popping up completion box. Key char is '.' after
1496identifier or a '/' (or '\\' on Windows) within a string.
1497
1498FormatParagraph: Max-width is max chars in lines after re-formatting.
1499Use with paragraphs in both strings and comment blocks.
1500
1501ParenMatch: Style indicates what is highlighted when closer is entered:
1502'opener' - opener '({[' corresponding to closer; 'parens' - both chars;
1503'expression' (default) - also everything in between. Flash-delay is how
1504long to highlight if cursor is not moved (0 means forever).
1505'''
Terry Jan Reedyd0cadba2015-10-11 22:07:31 -04001506}
1507
Steven M. Gavac11ccf32001-09-24 09:43:17 +00001508
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001509def is_int(s):
1510 "Return 's is blank or represents an int'"
1511 if not s:
1512 return True
1513 try:
1514 int(s)
1515 return True
1516 except ValueError:
1517 return False
1518
1519
Terry Jan Reedya9421fb2014-10-22 20:15:18 -04001520class VerticalScrolledFrame(Frame):
1521 """A pure Tkinter vertically scrollable frame.
1522
1523 * Use the 'interior' attribute to place widgets inside the scrollable frame
1524 * Construct and pack/place/grid normally
1525 * This frame only allows vertical scrolling
1526 """
1527 def __init__(self, parent, *args, **kw):
1528 Frame.__init__(self, parent, *args, **kw)
1529
csabella7eb58832017-07-04 21:30:58 -04001530 # Create a canvas object and a vertical scrollbar for scrolling it.
Terry Jan Reedya9421fb2014-10-22 20:15:18 -04001531 vscrollbar = Scrollbar(self, orient=VERTICAL)
1532 vscrollbar.pack(fill=Y, side=RIGHT, expand=FALSE)
1533 canvas = Canvas(self, bd=0, highlightthickness=0,
Terry Jan Reedyd0812292015-10-22 03:27:31 -04001534 yscrollcommand=vscrollbar.set, width=240)
Terry Jan Reedya9421fb2014-10-22 20:15:18 -04001535 canvas.pack(side=LEFT, fill=BOTH, expand=TRUE)
1536 vscrollbar.config(command=canvas.yview)
1537
csabella7eb58832017-07-04 21:30:58 -04001538 # Reset the view.
Terry Jan Reedya9421fb2014-10-22 20:15:18 -04001539 canvas.xview_moveto(0)
1540 canvas.yview_moveto(0)
1541
csabella7eb58832017-07-04 21:30:58 -04001542 # Create a frame inside the canvas which will be scrolled with it.
Terry Jan Reedya9421fb2014-10-22 20:15:18 -04001543 self.interior = interior = Frame(canvas)
1544 interior_id = canvas.create_window(0, 0, window=interior, anchor=NW)
1545
csabella7eb58832017-07-04 21:30:58 -04001546 # Track changes to the canvas and frame width and sync them,
1547 # also updating the scrollbar.
Terry Jan Reedya9421fb2014-10-22 20:15:18 -04001548 def _configure_interior(event):
csabella7eb58832017-07-04 21:30:58 -04001549 # Update the scrollbars to match the size of the inner frame.
Terry Jan Reedya9421fb2014-10-22 20:15:18 -04001550 size = (interior.winfo_reqwidth(), interior.winfo_reqheight())
1551 canvas.config(scrollregion="0 0 %s %s" % size)
Terry Jan Reedya9421fb2014-10-22 20:15:18 -04001552 interior.bind('<Configure>', _configure_interior)
1553
1554 def _configure_canvas(event):
1555 if interior.winfo_reqwidth() != canvas.winfo_width():
csabella7eb58832017-07-04 21:30:58 -04001556 # Update the inner frame's width to fill the canvas.
Terry Jan Reedya9421fb2014-10-22 20:15:18 -04001557 canvas.itemconfigure(interior_id, width=canvas.winfo_width())
1558 canvas.bind('<Configure>', _configure_canvas)
1559
1560 return
1561
Terry Jan Reedya9421fb2014-10-22 20:15:18 -04001562
Steven M. Gava44d3d1a2001-07-31 06:59:02 +00001563if __name__ == '__main__':
Terry Jan Reedycfa89502014-07-14 23:07:32 -04001564 import unittest
1565 unittest.main('idlelib.idle_test.test_configdialog',
1566 verbosity=2, exit=False)
Terry Jan Reedy2e8234a2014-05-29 01:46:26 -04001567 from idlelib.idle_test.htest import run
Terry Jan Reedy47304c02015-10-20 02:15:28 -04001568 run(ConfigDialog)