blob: d19749a07e358627eff212c04e241788b314c88c [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"""
Georg Brandl14fc4272008-05-17 18:39:55 +000012from tkinter import *
Terry Jan Reedy01e35752016-06-10 18:19:21 -040013from tkinter.ttk import Scrollbar
Georg Brandl14fc4272008-05-17 18:39:55 +000014import tkinter.messagebox as tkMessageBox
15import tkinter.colorchooser as tkColorChooser
16import tkinter.font as tkFont
Steven M. Gava44d3d1a2001-07-31 06:59:02 +000017
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -040018from idlelib.config import idleConf
19from idlelib.dynoption import DynOptionMenu
20from idlelib.config_key import GetKeysDialog
21from idlelib.config_sec import GetCfgSectionNameDialog
22from idlelib.config_help import GetHelpSourceDialog
Terry Jan Reedya9421fb2014-10-22 20:15:18 -040023from idlelib.tabbedpages import TabbedPageSet
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -040024from idlelib.textview import view_text
25from idlelib import macosx
Terry Jan Reedyd0cadba2015-10-11 22:07:31 -040026
Steven M. Gava44d3d1a2001-07-31 06:59:02 +000027class ConfigDialog(Toplevel):
Kurt B. Kaiseracdef852005-01-31 03:34:26 +000028
Terry Jan Reedycd567362014-10-17 01:31:35 -040029 def __init__(self, parent, title='', _htest=False, _utest=False):
Terry Jan Reedy2e8234a2014-05-29 01:46:26 -040030 """
31 _htest - bool, change box location when running htest
Terry Jan Reedycfa89502014-07-14 23:07:32 -040032 _utest - bool, don't wait_window when running unittest
Terry Jan Reedy2e8234a2014-05-29 01:46:26 -040033 """
Steven M. Gavad721c482001-07-31 10:46:53 +000034 Toplevel.__init__(self, parent)
Terry Jan Reedy22405332014-07-30 19:24:32 -040035 self.parent = parent
Terry Jan Reedy4036d872014-08-03 23:02:58 -040036 if _htest:
37 parent.instance_dict = {}
Guido van Rossum8ce8a782007-11-01 19:42:39 +000038 self.wm_withdraw()
39
Steven M. Gavad721c482001-07-31 10:46:53 +000040 self.configure(borderwidth=5)
Terry Jan Reedycd567362014-10-17 01:31:35 -040041 self.title(title or 'IDLE Preferences')
Terry Jan Reedy4036d872014-08-03 23:02:58 -040042 self.geometry(
43 "+%d+%d" % (parent.winfo_rootx() + 20,
44 parent.winfo_rooty() + (30 if not _htest else 150)))
Georg Brandl7eb4b7d2005-07-22 21:49:32 +000045 #Theme Elements. Each theme element key is its display name.
Steven M. Gava9dd16b32001-11-03 14:54:25 +000046 #The first value of the tuple is the sample area tag name.
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000047 #The second value is the display name list sort index.
Terry Jan Reedy4036d872014-08-03 23:02:58 -040048 self.themeElements={
Terry Jan Reedya8aa4d52015-10-02 22:12:17 -040049 'Normal Text': ('normal', '00'),
50 'Python Keywords': ('keyword', '01'),
51 'Python Definitions': ('definition', '02'),
52 'Python Builtins': ('builtin', '03'),
53 'Python Comments': ('comment', '04'),
54 'Python Strings': ('string', '05'),
55 'Selected Text': ('hilite', '06'),
56 'Found Text': ('hit', '07'),
57 'Cursor': ('cursor', '08'),
58 'Editor Breakpoint': ('break', '09'),
59 'Shell Normal Text': ('console', '10'),
60 'Shell Error Text': ('error', '11'),
61 'Shell Stdout Text': ('stdout', '12'),
62 'Shell Stderr Text': ('stderr', '13'),
Kurt B. Kaiser73360a32004-03-08 18:15:31 +000063 }
Steven M. Gavaa498af22002-02-01 01:33:36 +000064 self.ResetChangedItems() #load initial values in changed items dict
Steven M. Gavad721c482001-07-31 10:46:53 +000065 self.CreateWidgets()
Terry Jan Reedy4036d872014-08-03 23:02:58 -040066 self.resizable(height=FALSE, width=FALSE)
Steven M. Gavad721c482001-07-31 10:46:53 +000067 self.transient(parent)
68 self.grab_set()
69 self.protocol("WM_DELETE_WINDOW", self.Cancel)
Steven M. Gava2d4e03b2001-12-05 07:54:07 +000070 self.tabPages.focus_set()
Steven M. Gavad721c482001-07-31 10:46:53 +000071 #key bindings for this dialog
Terry Jan Reedy4036d872014-08-03 23:02:58 -040072 #self.bind('<Escape>', self.Cancel) #dismiss dialog, no save
73 #self.bind('<Alt-a>', self.Apply) #apply changes, save
74 #self.bind('<F1>', self.Help) #context help
Steven M. Gava429a86af2001-10-23 10:42:12 +000075 self.LoadConfigs()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000076 self.AttachVarCallbacks() #avoid callbacks during LoadConfigs
Guido van Rossum8ce8a782007-11-01 19:42:39 +000077
Terry Jan Reedycfa89502014-07-14 23:07:32 -040078 if not _utest:
79 self.wm_deiconify()
80 self.wait_window()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000081
Steven M. Gavad721c482001-07-31 10:46:53 +000082 def CreateWidgets(self):
Guido van Rossum8ce8a782007-11-01 19:42:39 +000083 self.tabPages = TabbedPageSet(self,
Terry Jan Reedy93f35422015-10-13 22:03:51 -040084 page_names=['Fonts/Tabs', 'Highlighting', 'Keys', 'General',
85 'Extensions'])
Terry Jan Reedy92cb0a32014-10-08 20:29:13 -040086 self.tabPages.pack(side=TOP, expand=TRUE, fill=BOTH)
87 self.CreatePageFontTab()
88 self.CreatePageHighlight()
89 self.CreatePageKeys()
90 self.CreatePageGeneral()
Terry Jan Reedy93f35422015-10-13 22:03:51 -040091 self.CreatePageExtensions()
Terry Jan Reedy92cb0a32014-10-08 20:29:13 -040092 self.create_action_buttons().pack(side=BOTTOM)
Terry Jan Reedyd0cadba2015-10-11 22:07:31 -040093
Terry Jan Reedy92cb0a32014-10-08 20:29:13 -040094 def create_action_buttons(self):
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -040095 if macosx.isAquaTk():
Terry Jan Reedye3416e62014-07-26 19:40:16 -040096 # Changing the default padding on OSX results in unreadable
97 # text in the buttons
Terry Jan Reedy4036d872014-08-03 23:02:58 -040098 paddingArgs = {}
Ronald Oussoren9e350042009-02-12 16:02:11 +000099 else:
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400100 paddingArgs = {'padx':6, 'pady':3}
Terry Jan Reedya9421fb2014-10-22 20:15:18 -0400101 outer = Frame(self, pady=2)
102 buttons = Frame(outer, pady=2)
Terry Jan Reedyd0cadba2015-10-11 22:07:31 -0400103 for txt, cmd in (
104 ('Ok', self.Ok),
105 ('Apply', self.Apply),
106 ('Cancel', self.Cancel),
107 ('Help', self.Help)):
108 Button(buttons, text=txt, command=cmd, takefocus=FALSE,
109 **paddingArgs).pack(side=LEFT, padx=5)
Terry Jan Reedya9421fb2014-10-22 20:15:18 -0400110 # add space above buttons
111 Frame(outer, height=2, borderwidth=0).pack(side=TOP)
112 buttons.pack(side=BOTTOM)
113 return outer
Terry Jan Reedyd0cadba2015-10-11 22:07:31 -0400114
Steven M. Gava60fc7072001-08-04 13:58:22 +0000115 def CreatePageFontTab(self):
Terry Jan Reedy22405332014-07-30 19:24:32 -0400116 parent = self.parent
117 self.fontSize = StringVar(parent)
118 self.fontBold = BooleanVar(parent)
119 self.fontName = StringVar(parent)
120 self.spaceNum = IntVar(parent)
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400121 self.editFont = tkFont.Font(parent, ('courier', 10, 'normal'))
Terry Jan Reedy22405332014-07-30 19:24:32 -0400122
Steven M. Gavaf213ccb2001-08-05 08:00:28 +0000123 ##widget creation
124 #body frame
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400125 frame = self.tabPages.pages['Fonts/Tabs'].frame
Steven M. Gavaf213ccb2001-08-05 08:00:28 +0000126 #body section frames
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400127 frameFont = LabelFrame(
128 frame, borderwidth=2, relief=GROOVE, text=' Base Editor Font ')
129 frameIndent = LabelFrame(
130 frame, borderwidth=2, relief=GROOVE, text=' Indentation Width ')
Steven M. Gavaf213ccb2001-08-05 08:00:28 +0000131 #frameFont
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400132 frameFontName = Frame(frameFont)
133 frameFontParam = Frame(frameFont)
134 labelFontNameTitle = Label(
135 frameFontName, justify=LEFT, text='Font Face :')
136 self.listFontName = Listbox(
137 frameFontName, height=5, takefocus=FALSE, exportselection=FALSE)
138 self.listFontName.bind(
139 '<ButtonRelease-1>', self.OnListFontButtonRelease)
140 scrollFont = Scrollbar(frameFontName)
Steven M. Gavac01e30f2001-08-11 15:48:13 +0000141 scrollFont.config(command=self.listFontName.yview)
142 self.listFontName.config(yscrollcommand=scrollFont.set)
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400143 labelFontSizeTitle = Label(frameFontParam, text='Size :')
144 self.optMenuFontSize = DynOptionMenu(
145 frameFontParam, self.fontSize, None, command=self.SetFontSample)
146 checkFontBold = Checkbutton(
147 frameFontParam, variable=self.fontBold, onvalue=1,
148 offvalue=0, text='Bold', command=self.SetFontSample)
149 frameFontSample = Frame(frameFont, relief=SOLID, borderwidth=1)
150 self.labelFontSample = Label(
151 frameFontSample, justify=LEFT, font=self.editFont,
152 text='AaBbCcDdEe\nFfGgHhIiJjK\n1234567890\n#:+=(){}[]')
Steven M. Gavaf213ccb2001-08-05 08:00:28 +0000153 #frameIndent
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400154 frameIndentSize = Frame(frameIndent)
155 labelSpaceNumTitle = Label(
156 frameIndentSize, justify=LEFT,
157 text='Python Standard: 4 Spaces!')
158 self.scaleSpaceNum = Scale(
159 frameIndentSize, variable=self.spaceNum,
160 orient='horizontal', tickinterval=2, from_=2, to=16)
Terry Jan Reedy22405332014-07-30 19:24:32 -0400161
Steven M. Gavaf213ccb2001-08-05 08:00:28 +0000162 #widget packing
163 #body
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400164 frameFont.pack(side=LEFT, padx=5, pady=5, expand=TRUE, fill=BOTH)
165 frameIndent.pack(side=LEFT, padx=5, pady=5, fill=Y)
Steven M. Gavaf213ccb2001-08-05 08:00:28 +0000166 #frameFont
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400167 frameFontName.pack(side=TOP, padx=5, pady=5, fill=X)
168 frameFontParam.pack(side=TOP, padx=5, pady=5, fill=X)
169 labelFontNameTitle.pack(side=TOP, anchor=W)
170 self.listFontName.pack(side=LEFT, expand=TRUE, fill=X)
171 scrollFont.pack(side=LEFT, fill=Y)
172 labelFontSizeTitle.pack(side=LEFT, anchor=W)
173 self.optMenuFontSize.pack(side=LEFT, anchor=W)
174 checkFontBold.pack(side=LEFT, anchor=W, padx=20)
175 frameFontSample.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH)
176 self.labelFontSample.pack(expand=TRUE, fill=BOTH)
Steven M. Gavaf213ccb2001-08-05 08:00:28 +0000177 #frameIndent
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400178 frameIndentSize.pack(side=TOP, fill=X)
179 labelSpaceNumTitle.pack(side=TOP, anchor=W, padx=5)
180 self.scaleSpaceNum.pack(side=TOP, padx=5, fill=X)
Steven M. Gava952d0a52001-08-03 04:43:44 +0000181 return frame
182
183 def CreatePageHighlight(self):
Terry Jan Reedy22405332014-07-30 19:24:32 -0400184 parent = self.parent
185 self.builtinTheme = StringVar(parent)
186 self.customTheme = StringVar(parent)
187 self.fgHilite = BooleanVar(parent)
188 self.colour = StringVar(parent)
189 self.fontName = StringVar(parent)
190 self.themeIsBuiltin = BooleanVar(parent)
191 self.highlightTarget = StringVar(parent)
192
Steven M. Gava952d0a52001-08-03 04:43:44 +0000193 ##widget creation
194 #body frame
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400195 frame = self.tabPages.pages['Highlighting'].frame
Steven M. Gava952d0a52001-08-03 04:43:44 +0000196 #body section frames
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400197 frameCustom = LabelFrame(frame, borderwidth=2, relief=GROOVE,
198 text=' Custom Highlighting ')
199 frameTheme = LabelFrame(frame, borderwidth=2, relief=GROOVE,
200 text=' Highlighting Theme ')
Steven M. Gava952d0a52001-08-03 04:43:44 +0000201 #frameCustom
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400202 self.textHighlightSample=Text(
203 frameCustom, relief=SOLID, borderwidth=1,
204 font=('courier', 12, ''), cursor='hand2', width=21, height=11,
205 takefocus=FALSE, highlightthickness=0, wrap=NONE)
Steven M. Gavae16d94b2001-11-03 05:07:28 +0000206 text=self.textHighlightSample
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400207 text.bind('<Double-Button-1>', lambda e: 'break')
208 text.bind('<B1-Motion>', lambda e: 'break')
209 textAndTags=(
210 ('#you can click here', 'comment'), ('\n', 'normal'),
211 ('#to choose items', 'comment'), ('\n', 'normal'),
212 ('def', 'keyword'), (' ', 'normal'),
213 ('func', 'definition'), ('(param):\n ', 'normal'),
214 ('"""string"""', 'string'), ('\n var0 = ', 'normal'),
215 ("'string'", 'string'), ('\n var1 = ', 'normal'),
216 ("'selected'", 'hilite'), ('\n var2 = ', 'normal'),
217 ("'found'", 'hit'), ('\n var3 = ', 'normal'),
218 ('list', 'builtin'), ('(', 'normal'),
Terry Jan Reedya8aa4d52015-10-02 22:12:17 -0400219 ('None', 'keyword'), (')\n', 'normal'),
220 (' breakpoint("line")', 'break'), ('\n\n', 'normal'),
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400221 (' error ', 'error'), (' ', 'normal'),
222 ('cursor |', 'cursor'), ('\n ', 'normal'),
223 ('shell', 'console'), (' ', 'normal'),
224 ('stdout', 'stdout'), (' ', 'normal'),
225 ('stderr', 'stderr'), ('\n', 'normal'))
Steven M. Gava9dd16b32001-11-03 14:54:25 +0000226 for txTa in textAndTags:
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400227 text.insert(END, txTa[0], txTa[1])
Kurt B. Kaisere0712772007-08-23 05:25:55 +0000228 for element in self.themeElements:
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400229 def tem(event, elem=element):
230 event.widget.winfo_toplevel().highlightTarget.set(elem)
231 text.tag_bind(
232 self.themeElements[element][0], '<ButtonPress-1>', tem)
Steven M. Gavae16d94b2001-11-03 05:07:28 +0000233 text.config(state=DISABLED)
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400234 self.frameColourSet = Frame(frameCustom, relief=SOLID, borderwidth=1)
235 frameFgBg = Frame(frameCustom)
236 buttonSetColour = Button(
237 self.frameColourSet, text='Choose Colour for :',
238 command=self.GetColour, highlightthickness=0)
239 self.optMenuHighlightTarget = DynOptionMenu(
240 self.frameColourSet, self.highlightTarget, None,
241 highlightthickness=0) #, command=self.SetHighlightTargetBinding
242 self.radioFg = Radiobutton(
243 frameFgBg, variable=self.fgHilite, value=1,
244 text='Foreground', command=self.SetColourSampleBinding)
245 self.radioBg=Radiobutton(
246 frameFgBg, variable=self.fgHilite, value=0,
247 text='Background', command=self.SetColourSampleBinding)
Steven M. Gava7c017862001-10-29 11:19:46 +0000248 self.fgHilite.set(1)
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400249 buttonSaveCustomTheme = Button(
250 frameCustom, text='Save as New Custom Theme',
251 command=self.SaveAsNewTheme)
Steven M. Gava952d0a52001-08-03 04:43:44 +0000252 #frameTheme
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400253 labelTypeTitle = Label(frameTheme, text='Select : ')
254 self.radioThemeBuiltin = Radiobutton(
255 frameTheme, variable=self.themeIsBuiltin, value=1,
256 command=self.SetThemeType, text='a Built-in Theme')
257 self.radioThemeCustom = Radiobutton(
258 frameTheme, variable=self.themeIsBuiltin, value=0,
259 command=self.SetThemeType, text='a Custom Theme')
260 self.optMenuThemeBuiltin = DynOptionMenu(
261 frameTheme, self.builtinTheme, None, command=None)
262 self.optMenuThemeCustom=DynOptionMenu(
263 frameTheme, self.customTheme, None, command=None)
264 self.buttonDeleteCustomTheme=Button(
265 frameTheme, text='Delete Custom Theme',
Steven M. Gava49745752002-02-18 01:43:11 +0000266 command=self.DeleteCustomTheme)
Terry Jan Reedyd0c0f002015-11-12 15:02:57 -0500267 self.new_custom_theme = Label(frameTheme, bd=2)
Terry Jan Reedy22405332014-07-30 19:24:32 -0400268
Steven M. Gava952d0a52001-08-03 04:43:44 +0000269 ##widget packing
270 #body
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400271 frameCustom.pack(side=LEFT, padx=5, pady=5, expand=TRUE, fill=BOTH)
272 frameTheme.pack(side=LEFT, padx=5, pady=5, fill=Y)
Steven M. Gava952d0a52001-08-03 04:43:44 +0000273 #frameCustom
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400274 self.frameColourSet.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=X)
275 frameFgBg.pack(side=TOP, padx=5, pady=0)
276 self.textHighlightSample.pack(
277 side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH)
278 buttonSetColour.pack(side=TOP, expand=TRUE, fill=X, padx=8, pady=4)
279 self.optMenuHighlightTarget.pack(
280 side=TOP, expand=TRUE, fill=X, padx=8, pady=3)
281 self.radioFg.pack(side=LEFT, anchor=E)
282 self.radioBg.pack(side=RIGHT, anchor=W)
283 buttonSaveCustomTheme.pack(side=BOTTOM, fill=X, padx=5, pady=5)
Steven M. Gava952d0a52001-08-03 04:43:44 +0000284 #frameTheme
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400285 labelTypeTitle.pack(side=TOP, anchor=W, padx=5, pady=5)
286 self.radioThemeBuiltin.pack(side=TOP, anchor=W, padx=5)
287 self.radioThemeCustom.pack(side=TOP, anchor=W, padx=5, pady=2)
288 self.optMenuThemeBuiltin.pack(side=TOP, fill=X, padx=5, pady=5)
289 self.optMenuThemeCustom.pack(side=TOP, fill=X, anchor=W, padx=5, pady=5)
290 self.buttonDeleteCustomTheme.pack(side=TOP, fill=X, padx=5, pady=5)
Terry Jan Reedyd0c0f002015-11-12 15:02:57 -0500291 self.new_custom_theme.pack(side=TOP, fill=X, pady=5)
Steven M. Gava952d0a52001-08-03 04:43:44 +0000292 return frame
293
294 def CreatePageKeys(self):
Terry Jan Reedy22405332014-07-30 19:24:32 -0400295 parent = self.parent
296 self.bindingTarget = StringVar(parent)
297 self.builtinKeys = StringVar(parent)
298 self.customKeys = StringVar(parent)
299 self.keysAreBuiltin = BooleanVar(parent)
300 self.keyBinding = StringVar(parent)
301
Steven M. Gava60fc7072001-08-04 13:58:22 +0000302 ##widget creation
303 #body frame
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400304 frame = self.tabPages.pages['Keys'].frame
Steven M. Gava60fc7072001-08-04 13:58:22 +0000305 #body section frames
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400306 frameCustom = LabelFrame(
307 frame, borderwidth=2, relief=GROOVE,
308 text=' Custom Key Bindings ')
309 frameKeySets = LabelFrame(
310 frame, borderwidth=2, relief=GROOVE, text=' Key Set ')
Steven M. Gava60fc7072001-08-04 13:58:22 +0000311 #frameCustom
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400312 frameTarget = Frame(frameCustom)
313 labelTargetTitle = Label(frameTarget, text='Action - Key(s)')
314 scrollTargetY = Scrollbar(frameTarget)
315 scrollTargetX = Scrollbar(frameTarget, orient=HORIZONTAL)
316 self.listBindings = Listbox(
317 frameTarget, takefocus=FALSE, exportselection=FALSE)
318 self.listBindings.bind('<ButtonRelease-1>', self.KeyBindingSelected)
Steven M. Gavafacfc092002-01-19 00:29:54 +0000319 scrollTargetY.config(command=self.listBindings.yview)
320 scrollTargetX.config(command=self.listBindings.xview)
321 self.listBindings.config(yscrollcommand=scrollTargetY.set)
322 self.listBindings.config(xscrollcommand=scrollTargetX.set)
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400323 self.buttonNewKeys = Button(
324 frameCustom, text='Get New Keys for Selection',
325 command=self.GetNewKeys, state=DISABLED)
Steven M. Gava60fc7072001-08-04 13:58:22 +0000326 #frameKeySets
Christian Heimes9a371592007-12-28 14:08:13 +0000327 frames = [Frame(frameKeySets, padx=2, pady=2, borderwidth=0)
328 for i in range(2)]
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400329 self.radioKeysBuiltin = Radiobutton(
330 frames[0], variable=self.keysAreBuiltin, value=1,
331 command=self.SetKeysType, text='Use a Built-in Key Set')
332 self.radioKeysCustom = Radiobutton(
333 frames[0], variable=self.keysAreBuiltin, value=0,
334 command=self.SetKeysType, text='Use a Custom Key Set')
335 self.optMenuKeysBuiltin = DynOptionMenu(
336 frames[0], self.builtinKeys, None, command=None)
337 self.optMenuKeysCustom = DynOptionMenu(
338 frames[0], self.customKeys, None, command=None)
339 self.buttonDeleteCustomKeys = Button(
340 frames[1], text='Delete Custom Key Set',
Steven M. Gava49745752002-02-18 01:43:11 +0000341 command=self.DeleteCustomKeys)
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400342 buttonSaveCustomKeys = Button(
343 frames[1], text='Save as New Custom Key Set',
344 command=self.SaveAsNewKeySet)
Terry Jan Reedy22405332014-07-30 19:24:32 -0400345
Steven M. Gava60fc7072001-08-04 13:58:22 +0000346 ##widget packing
347 #body
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400348 frameCustom.pack(side=BOTTOM, padx=5, pady=5, expand=TRUE, fill=BOTH)
349 frameKeySets.pack(side=BOTTOM, padx=5, pady=5, fill=BOTH)
Steven M. Gava60fc7072001-08-04 13:58:22 +0000350 #frameCustom
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400351 self.buttonNewKeys.pack(side=BOTTOM, fill=X, padx=5, pady=5)
352 frameTarget.pack(side=LEFT, padx=5, pady=5, expand=TRUE, fill=BOTH)
Steven M. Gavafacfc092002-01-19 00:29:54 +0000353 #frame target
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400354 frameTarget.columnconfigure(0, weight=1)
355 frameTarget.rowconfigure(1, weight=1)
356 labelTargetTitle.grid(row=0, column=0, columnspan=2, sticky=W)
357 self.listBindings.grid(row=1, column=0, sticky=NSEW)
358 scrollTargetY.grid(row=1, column=1, sticky=NS)
359 scrollTargetX.grid(row=2, column=0, sticky=EW)
Steven M. Gava60fc7072001-08-04 13:58:22 +0000360 #frameKeySets
Christian Heimes9a371592007-12-28 14:08:13 +0000361 self.radioKeysBuiltin.grid(row=0, column=0, sticky=W+NS)
362 self.radioKeysCustom.grid(row=1, column=0, sticky=W+NS)
363 self.optMenuKeysBuiltin.grid(row=0, column=1, sticky=NSEW)
364 self.optMenuKeysCustom.grid(row=1, column=1, sticky=NSEW)
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400365 self.buttonDeleteCustomKeys.pack(side=LEFT, fill=X, expand=True, padx=2)
366 buttonSaveCustomKeys.pack(side=LEFT, fill=X, expand=True, padx=2)
Christian Heimes9a371592007-12-28 14:08:13 +0000367 frames[0].pack(side=TOP, fill=BOTH, expand=True)
368 frames[1].pack(side=TOP, fill=X, expand=True, pady=2)
Steven M. Gava952d0a52001-08-03 04:43:44 +0000369 return frame
370
371 def CreatePageGeneral(self):
Terry Jan Reedy22405332014-07-30 19:24:32 -0400372 parent = self.parent
373 self.winWidth = StringVar(parent)
374 self.winHeight = StringVar(parent)
Terry Jan Reedy22405332014-07-30 19:24:32 -0400375 self.startupEdit = IntVar(parent)
376 self.autoSave = IntVar(parent)
377 self.encoding = StringVar(parent)
378 self.userHelpBrowser = BooleanVar(parent)
379 self.helpBrowser = StringVar(parent)
380
Steven M. Gava230e5782001-08-07 03:28:25 +0000381 #widget creation
382 #body
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400383 frame = self.tabPages.pages['General'].frame
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000384 #body section frames
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400385 frameRun = LabelFrame(frame, borderwidth=2, relief=GROOVE,
386 text=' Startup Preferences ')
387 frameSave = LabelFrame(frame, borderwidth=2, relief=GROOVE,
388 text=' Autosave Preferences ')
389 frameWinSize = Frame(frame, borderwidth=2, relief=GROOVE)
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400390 frameHelp = LabelFrame(frame, borderwidth=2, relief=GROOVE,
391 text=' Additional Help Sources ')
Steven M. Gava230e5782001-08-07 03:28:25 +0000392 #frameRun
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400393 labelRunChoiceTitle = Label(frameRun, text='At Startup')
394 radioStartupEdit = Radiobutton(
395 frameRun, variable=self.startupEdit, value=1,
396 command=self.SetKeysType, text="Open Edit Window")
397 radioStartupShell = Radiobutton(
398 frameRun, variable=self.startupEdit, value=0,
399 command=self.SetKeysType, text='Open Shell Window')
Kurt B. Kaiser6c638b62003-05-26 06:23:10 +0000400 #frameSave
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400401 labelRunSaveTitle = Label(frameSave, text='At Start of Run (F5) ')
402 radioSaveAsk = Radiobutton(
403 frameSave, variable=self.autoSave, value=0,
404 command=self.SetKeysType, text="Prompt to Save")
405 radioSaveAuto = Radiobutton(
406 frameSave, variable=self.autoSave, value=1,
407 command=self.SetKeysType, text='No Prompt')
Steven M. Gava230e5782001-08-07 03:28:25 +0000408 #frameWinSize
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400409 labelWinSizeTitle = Label(
410 frameWinSize, text='Initial Window Size (in characters)')
411 labelWinWidthTitle = Label(frameWinSize, text='Width')
412 entryWinWidth = Entry(
413 frameWinSize, textvariable=self.winWidth, width=3)
414 labelWinHeightTitle = Label(frameWinSize, text='Height')
415 entryWinHeight = Entry(
416 frameWinSize, textvariable=self.winHeight, width=3)
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000417 #frameHelp
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400418 frameHelpList = Frame(frameHelp)
419 frameHelpListButtons = Frame(frameHelpList)
420 scrollHelpList = Scrollbar(frameHelpList)
421 self.listHelp = Listbox(
422 frameHelpList, height=5, takefocus=FALSE,
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000423 exportselection=FALSE)
424 scrollHelpList.config(command=self.listHelp.yview)
425 self.listHelp.config(yscrollcommand=scrollHelpList.set)
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400426 self.listHelp.bind('<ButtonRelease-1>', self.HelpSourceSelected)
427 self.buttonHelpListEdit = Button(
428 frameHelpListButtons, text='Edit', state=DISABLED,
429 width=8, command=self.HelpListItemEdit)
430 self.buttonHelpListAdd = Button(
431 frameHelpListButtons, text='Add',
432 width=8, command=self.HelpListItemAdd)
433 self.buttonHelpListRemove = Button(
434 frameHelpListButtons, text='Remove', state=DISABLED,
435 width=8, command=self.HelpListItemRemove)
Terry Jan Reedy22405332014-07-30 19:24:32 -0400436
Steven M. Gava230e5782001-08-07 03:28:25 +0000437 #widget packing
438 #body
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400439 frameRun.pack(side=TOP, padx=5, pady=5, fill=X)
440 frameSave.pack(side=TOP, padx=5, pady=5, fill=X)
441 frameWinSize.pack(side=TOP, padx=5, pady=5, fill=X)
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400442 frameHelp.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH)
Steven M. Gava230e5782001-08-07 03:28:25 +0000443 #frameRun
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400444 labelRunChoiceTitle.pack(side=LEFT, anchor=W, padx=5, pady=5)
445 radioStartupShell.pack(side=RIGHT, anchor=W, padx=5, pady=5)
446 radioStartupEdit.pack(side=RIGHT, anchor=W, padx=5, pady=5)
Kurt B. Kaiser6c638b62003-05-26 06:23:10 +0000447 #frameSave
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400448 labelRunSaveTitle.pack(side=LEFT, anchor=W, padx=5, pady=5)
449 radioSaveAuto.pack(side=RIGHT, anchor=W, padx=5, pady=5)
450 radioSaveAsk.pack(side=RIGHT, anchor=W, padx=5, pady=5)
Steven M. Gava230e5782001-08-07 03:28:25 +0000451 #frameWinSize
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400452 labelWinSizeTitle.pack(side=LEFT, anchor=W, padx=5, pady=5)
453 entryWinHeight.pack(side=RIGHT, anchor=E, padx=10, pady=5)
454 labelWinHeightTitle.pack(side=RIGHT, anchor=E, pady=5)
455 entryWinWidth.pack(side=RIGHT, anchor=E, padx=10, pady=5)
456 labelWinWidthTitle.pack(side=RIGHT, anchor=E, pady=5)
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000457 #frameHelp
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400458 frameHelpListButtons.pack(side=RIGHT, padx=5, pady=5, fill=Y)
459 frameHelpList.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH)
460 scrollHelpList.pack(side=RIGHT, anchor=W, fill=Y)
461 self.listHelp.pack(side=LEFT, anchor=E, expand=TRUE, fill=BOTH)
462 self.buttonHelpListEdit.pack(side=TOP, anchor=W, pady=5)
463 self.buttonHelpListAdd.pack(side=TOP, anchor=W)
464 self.buttonHelpListRemove.pack(side=TOP, anchor=W, pady=5)
Steven M. Gava952d0a52001-08-03 04:43:44 +0000465 return frame
466
Steven M. Gavac112cd82002-01-22 05:56:40 +0000467 def AttachVarCallbacks(self):
Terry Jan Reedyd87d1682015-08-01 18:57:33 -0400468 self.fontSize.trace_variable('w', self.VarChanged_font)
469 self.fontName.trace_variable('w', self.VarChanged_font)
470 self.fontBold.trace_variable('w', self.VarChanged_font)
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400471 self.spaceNum.trace_variable('w', self.VarChanged_spaceNum)
472 self.colour.trace_variable('w', self.VarChanged_colour)
473 self.builtinTheme.trace_variable('w', self.VarChanged_builtinTheme)
474 self.customTheme.trace_variable('w', self.VarChanged_customTheme)
475 self.themeIsBuiltin.trace_variable('w', self.VarChanged_themeIsBuiltin)
476 self.highlightTarget.trace_variable('w', self.VarChanged_highlightTarget)
477 self.keyBinding.trace_variable('w', self.VarChanged_keyBinding)
478 self.builtinKeys.trace_variable('w', self.VarChanged_builtinKeys)
479 self.customKeys.trace_variable('w', self.VarChanged_customKeys)
480 self.keysAreBuiltin.trace_variable('w', self.VarChanged_keysAreBuiltin)
481 self.winWidth.trace_variable('w', self.VarChanged_winWidth)
482 self.winHeight.trace_variable('w', self.VarChanged_winHeight)
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400483 self.startupEdit.trace_variable('w', self.VarChanged_startupEdit)
484 self.autoSave.trace_variable('w', self.VarChanged_autoSave)
485 self.encoding.trace_variable('w', self.VarChanged_encoding)
Steven M. Gava052937f2002-02-11 02:20:53 +0000486
Terry Jan Reedy6b98ce22016-05-16 22:27:28 -0400487 def remove_var_callbacks(self):
488 "Remove callbacks to prevent memory leaks."
489 for var in (
490 self.fontSize, self.fontName, self.fontBold,
491 self.spaceNum, self.colour, self.builtinTheme,
492 self.customTheme, self.themeIsBuiltin, self.highlightTarget,
493 self.keyBinding, self.builtinKeys, self.customKeys,
494 self.keysAreBuiltin, self.winWidth, self.winHeight,
495 self.startupEdit, self.autoSave, self.encoding,):
496 var.trace_vdelete('w', var.trace_vinfo()[0][1])
497
Terry Jan Reedyd87d1682015-08-01 18:57:33 -0400498 def VarChanged_font(self, *params):
499 '''When one font attribute changes, save them all, as they are
500 not independent from each other. In particular, when we are
501 overriding the default font, we need to write out everything.
502 '''
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400503 value = self.fontName.get()
504 self.AddChangedItem('main', 'EditorWindow', 'font', value)
Terry Jan Reedyd87d1682015-08-01 18:57:33 -0400505 value = self.fontSize.get()
506 self.AddChangedItem('main', 'EditorWindow', 'font-size', value)
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400507 value = self.fontBold.get()
508 self.AddChangedItem('main', 'EditorWindow', 'font-bold', value)
Steven M. Gavac112cd82002-01-22 05:56:40 +0000509
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400510 def VarChanged_spaceNum(self, *params):
511 value = self.spaceNum.get()
512 self.AddChangedItem('main', 'Indent', 'num-spaces', value)
Steven M. Gavac112cd82002-01-22 05:56:40 +0000513
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400514 def VarChanged_colour(self, *params):
Steven M. Gava052937f2002-02-11 02:20:53 +0000515 self.OnNewColourSet()
516
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400517 def VarChanged_builtinTheme(self, *params):
518 value = self.builtinTheme.get()
Terry Jan Reedybe5b7a22015-10-04 00:31:05 -0400519 if value == 'IDLE Dark':
Terry Jan Reedyd0c0f002015-11-12 15:02:57 -0500520 if idleConf.GetOption('main', 'Theme', 'name') != 'IDLE New':
521 self.AddChangedItem('main', 'Theme', 'name', 'IDLE Classic')
522 self.AddChangedItem('main', 'Theme', 'name2', value)
523 self.new_custom_theme.config(text='New theme, see Help',
524 fg='#500000')
525 else:
526 self.AddChangedItem('main', 'Theme', 'name', value)
527 self.AddChangedItem('main', 'Theme', 'name2', '')
528 self.new_custom_theme.config(text='', fg='black')
Steven M. Gava052937f2002-02-11 02:20:53 +0000529 self.PaintThemeSample()
530
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400531 def VarChanged_customTheme(self, *params):
532 value = self.customTheme.get()
Steven M. Gava49745752002-02-18 01:43:11 +0000533 if value != '- no custom themes -':
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400534 self.AddChangedItem('main', 'Theme', 'name', value)
Steven M. Gava49745752002-02-18 01:43:11 +0000535 self.PaintThemeSample()
Steven M. Gava052937f2002-02-11 02:20:53 +0000536
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400537 def VarChanged_themeIsBuiltin(self, *params):
538 value = self.themeIsBuiltin.get()
539 self.AddChangedItem('main', 'Theme', 'default', value)
Steven M. Gavaf31eec02002-03-05 00:25:58 +0000540 if value:
541 self.VarChanged_builtinTheme()
542 else:
543 self.VarChanged_customTheme()
Steven M. Gava052937f2002-02-11 02:20:53 +0000544
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400545 def VarChanged_highlightTarget(self, *params):
Steven M. Gava052937f2002-02-11 02:20:53 +0000546 self.SetHighlightTarget()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000547
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400548 def VarChanged_keyBinding(self, *params):
549 value = self.keyBinding.get()
550 keySet = self.customKeys.get()
551 event = self.listBindings.get(ANCHOR).split()[0]
Steven M. Gavaa498af22002-02-01 01:33:36 +0000552 if idleConf.IsCoreBinding(event):
553 #this is a core keybinding
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400554 self.AddChangedItem('keys', keySet, event, value)
Steven M. Gavaa498af22002-02-01 01:33:36 +0000555 else: #this is an extension key binding
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400556 extName = idleConf.GetExtnNameForEvent(event)
557 extKeybindSection = extName + '_cfgBindings'
558 self.AddChangedItem('extensions', extKeybindSection, event, value)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000559
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400560 def VarChanged_builtinKeys(self, *params):
561 value = self.builtinKeys.get()
562 self.AddChangedItem('main', 'Keys', 'name', value)
Steven M. Gava052937f2002-02-11 02:20:53 +0000563 self.LoadKeysList(value)
564
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400565 def VarChanged_customKeys(self, *params):
566 value = self.customKeys.get()
Steven M. Gava49745752002-02-18 01:43:11 +0000567 if value != '- no custom keys -':
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400568 self.AddChangedItem('main', 'Keys', 'name', value)
Steven M. Gava49745752002-02-18 01:43:11 +0000569 self.LoadKeysList(value)
Steven M. Gava052937f2002-02-11 02:20:53 +0000570
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400571 def VarChanged_keysAreBuiltin(self, *params):
572 value = self.keysAreBuiltin.get()
573 self.AddChangedItem('main', 'Keys', 'default', value)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000574 if value:
Steven M. Gavaf31eec02002-03-05 00:25:58 +0000575 self.VarChanged_builtinKeys()
Steven M. Gava052937f2002-02-11 02:20:53 +0000576 else:
Steven M. Gavaf31eec02002-03-05 00:25:58 +0000577 self.VarChanged_customKeys()
Steven M. Gava052937f2002-02-11 02:20:53 +0000578
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400579 def VarChanged_winWidth(self, *params):
580 value = self.winWidth.get()
581 self.AddChangedItem('main', 'EditorWindow', 'width', value)
Steven M. Gavac112cd82002-01-22 05:56:40 +0000582
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400583 def VarChanged_winHeight(self, *params):
584 value = self.winHeight.get()
585 self.AddChangedItem('main', 'EditorWindow', 'height', value)
Steven M. Gavac112cd82002-01-22 05:56:40 +0000586
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400587 def VarChanged_startupEdit(self, *params):
588 value = self.startupEdit.get()
589 self.AddChangedItem('main', 'General', 'editor-on-startup', value)
Steven M. Gavac112cd82002-01-22 05:56:40 +0000590
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400591 def VarChanged_autoSave(self, *params):
592 value = self.autoSave.get()
593 self.AddChangedItem('main', 'General', 'autosave', value)
Kurt B. Kaiser6c638b62003-05-26 06:23:10 +0000594
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400595 def VarChanged_encoding(self, *params):
596 value = self.encoding.get()
597 self.AddChangedItem('main', 'EditorWindow', 'encoding', value)
Kurt B. Kaisera053f332003-05-10 00:49:56 +0000598
Steven M. Gavaa498af22002-02-01 01:33:36 +0000599 def ResetChangedItems(self):
Steven M. Gavab77d3432002-03-02 07:16:21 +0000600 #When any config item is changed in this dialog, an entry
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000601 #should be made in the relevant section (config type) of this
602 #dictionary. The key should be the config file section name and the
Steven M. Gavaa498af22002-02-01 01:33:36 +0000603 #value a dictionary, whose key:value pairs are item=value pairs for
604 #that config file section.
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400605 self.changedItems = {'main':{}, 'highlight':{}, 'keys':{},
606 'extensions':{}}
Steven M. Gavaa498af22002-02-01 01:33:36 +0000607
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400608 def AddChangedItem(self, typ, section, item, value):
609 value = str(value) #make sure we use a string
610 if section not in self.changedItems[typ]:
611 self.changedItems[typ][section] = {}
612 self.changedItems[typ][section][item] = value
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000613
Steven M. Gava5f28e8f2002-01-21 06:38:21 +0000614 def GetDefaultItems(self):
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400615 dItems={'main':{}, 'highlight':{}, 'keys':{}, 'extensions':{}}
Kurt B. Kaisere0712772007-08-23 05:25:55 +0000616 for configType in dItems:
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400617 sections = idleConf.GetSectionList('default', configType)
Steven M. Gava5f28e8f2002-01-21 06:38:21 +0000618 for section in sections:
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400619 dItems[configType][section] = {}
620 options = idleConf.defaultCfg[configType].GetOptionList(section)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000621 for option in options:
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400622 dItems[configType][section][option] = (
623 idleConf.defaultCfg[configType].Get(section, option))
Steven M. Gava5f28e8f2002-01-21 06:38:21 +0000624 return dItems
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000625
Steven M. Gava5f28e8f2002-01-21 06:38:21 +0000626 def SetThemeType(self):
627 if self.themeIsBuiltin.get():
628 self.optMenuThemeBuiltin.config(state=NORMAL)
629 self.optMenuThemeCustom.config(state=DISABLED)
630 self.buttonDeleteCustomTheme.config(state=DISABLED)
631 else:
632 self.optMenuThemeBuiltin.config(state=DISABLED)
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000633 self.radioThemeCustom.config(state=NORMAL)
Steven M. Gava5f28e8f2002-01-21 06:38:21 +0000634 self.optMenuThemeCustom.config(state=NORMAL)
635 self.buttonDeleteCustomTheme.config(state=NORMAL)
636
637 def SetKeysType(self):
Steven M. Gava052937f2002-02-11 02:20:53 +0000638 if self.keysAreBuiltin.get():
Steven M. Gava5f28e8f2002-01-21 06:38:21 +0000639 self.optMenuKeysBuiltin.config(state=NORMAL)
640 self.optMenuKeysCustom.config(state=DISABLED)
641 self.buttonDeleteCustomKeys.config(state=DISABLED)
642 else:
643 self.optMenuKeysBuiltin.config(state=DISABLED)
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000644 self.radioKeysCustom.config(state=NORMAL)
Steven M. Gava5f28e8f2002-01-21 06:38:21 +0000645 self.optMenuKeysCustom.config(state=NORMAL)
646 self.buttonDeleteCustomKeys.config(state=NORMAL)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000647
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000648 def GetNewKeys(self):
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400649 listIndex = self.listBindings.index(ANCHOR)
650 binding = self.listBindings.get(listIndex)
651 bindName = binding.split()[0] #first part, up to first space
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000652 if self.keysAreBuiltin.get():
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400653 currentKeySetName = self.builtinKeys.get()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000654 else:
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400655 currentKeySetName = self.customKeys.get()
656 currentBindings = idleConf.GetCurrentKeySet()
Kurt B. Kaisere0712772007-08-23 05:25:55 +0000657 if currentKeySetName in self.changedItems['keys']: #unsaved changes
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400658 keySetChanges = self.changedItems['keys'][currentKeySetName]
Kurt B. Kaisere0712772007-08-23 05:25:55 +0000659 for event in keySetChanges:
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400660 currentBindings[event] = keySetChanges[event].split()
Kurt B. Kaisere0712772007-08-23 05:25:55 +0000661 currentKeySequences = list(currentBindings.values())
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400662 newKeys = GetKeysDialog(self, 'Get New Keys', bindName,
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000663 currentKeySequences).result
664 if newKeys: #new keys were specified
Steven M. Gava052937f2002-02-11 02:20:53 +0000665 if self.keysAreBuiltin.get(): #current key set is a built-in
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400666 message = ('Your changes will be saved as a new Custom Key Set.'
667 ' Enter a name for your new Custom Key Set below.')
668 newKeySet = self.GetNewKeysName(message)
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000669 if not newKeySet: #user cancelled custom key set creation
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000670 self.listBindings.select_set(listIndex)
671 self.listBindings.select_anchor(listIndex)
672 return
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000673 else: #create new custom key set based on previously active key set
674 self.CreateNewKeySet(newKeySet)
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000675 self.listBindings.delete(listIndex)
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400676 self.listBindings.insert(listIndex, bindName+' - '+newKeys)
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000677 self.listBindings.select_set(listIndex)
678 self.listBindings.select_anchor(listIndex)
Steven M. Gava052937f2002-02-11 02:20:53 +0000679 self.keyBinding.set(newKeys)
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000680 else:
681 self.listBindings.select_set(listIndex)
682 self.listBindings.select_anchor(listIndex)
683
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400684 def GetNewKeysName(self, message):
685 usedNames = (idleConf.GetSectionList('user', 'keys') +
686 idleConf.GetSectionList('default', 'keys'))
687 newKeySet = GetCfgSectionNameDialog(
688 self, 'New Custom Key Set', message, usedNames).result
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000689 return newKeySet
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000690
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000691 def SaveAsNewKeySet(self):
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400692 newKeysName = self.GetNewKeysName('New Key Set Name:')
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000693 if newKeysName:
694 self.CreateNewKeySet(newKeysName)
695
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400696 def KeyBindingSelected(self, event):
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000697 self.buttonNewKeys.config(state=NORMAL)
698
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400699 def CreateNewKeySet(self, newKeySetName):
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000700 #creates new custom key set based on the previously active key set,
701 #and makes the new key set active
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000702 if self.keysAreBuiltin.get():
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400703 prevKeySetName = self.builtinKeys.get()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000704 else:
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400705 prevKeySetName = self.customKeys.get()
706 prevKeys = idleConf.GetCoreKeys(prevKeySetName)
707 newKeys = {}
Kurt B. Kaisere0712772007-08-23 05:25:55 +0000708 for event in prevKeys: #add key set to changed items
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400709 eventName = event[2:-2] #trim off the angle brackets
710 binding = ' '.join(prevKeys[event])
711 newKeys[eventName] = binding
Steven M. Gava052937f2002-02-11 02:20:53 +0000712 #handle any unsaved changes to prev key set
Kurt B. Kaisere0712772007-08-23 05:25:55 +0000713 if prevKeySetName in self.changedItems['keys']:
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400714 keySetChanges = self.changedItems['keys'][prevKeySetName]
Kurt B. Kaisere0712772007-08-23 05:25:55 +0000715 for event in keySetChanges:
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400716 newKeys[event] = keySetChanges[event]
Steven M. Gava052937f2002-02-11 02:20:53 +0000717 #save the new theme
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400718 self.SaveNewKeySet(newKeySetName, newKeys)
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000719 #change gui over to the new key set
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400720 customKeyList = idleConf.GetSectionList('user', 'keys')
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000721 customKeyList.sort()
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400722 self.optMenuKeysCustom.SetMenu(customKeyList, newKeySetName)
Steven M. Gava052937f2002-02-11 02:20:53 +0000723 self.keysAreBuiltin.set(0)
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000724 self.SetKeysType()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000725
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400726 def LoadKeysList(self, keySetName):
727 reselect = 0
728 newKeySet = 0
Steven M. Gava052937f2002-02-11 02:20:53 +0000729 if self.listBindings.curselection():
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400730 reselect = 1
731 listIndex = self.listBindings.index(ANCHOR)
732 keySet = idleConf.GetKeySet(keySetName)
Kurt B. Kaisere0712772007-08-23 05:25:55 +0000733 bindNames = list(keySet.keys())
Steven M. Gava052937f2002-02-11 02:20:53 +0000734 bindNames.sort()
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400735 self.listBindings.delete(0, END)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000736 for bindName in bindNames:
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400737 key = ' '.join(keySet[bindName]) #make key(s) into a string
738 bindName = bindName[2:-2] #trim off the angle brackets
Kurt B. Kaisere0712772007-08-23 05:25:55 +0000739 if keySetName in self.changedItems['keys']:
Steven M. Gava052937f2002-02-11 02:20:53 +0000740 #handle any unsaved changes to this key set
Kurt B. Kaisere0712772007-08-23 05:25:55 +0000741 if bindName in self.changedItems['keys'][keySetName]:
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400742 key = self.changedItems['keys'][keySetName][bindName]
Steven M. Gava052937f2002-02-11 02:20:53 +0000743 self.listBindings.insert(END, bindName+' - '+key)
Steven M. Gava052937f2002-02-11 02:20:53 +0000744 if reselect:
745 self.listBindings.see(listIndex)
746 self.listBindings.select_set(listIndex)
747 self.listBindings.select_anchor(listIndex)
748
Steven M. Gava49745752002-02-18 01:43:11 +0000749 def DeleteCustomKeys(self):
750 keySetName=self.customKeys.get()
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400751 delmsg = 'Are you sure you wish to delete the key set %r ?'
752 if not tkMessageBox.askyesno(
753 'Delete Key Set', delmsg % keySetName, parent=self):
Steven M. Gava49745752002-02-18 01:43:11 +0000754 return
Terry Jan Reedyec844172016-06-14 00:53:25 -0400755 self.DeactivateCurrentConfig()
Steven M. Gava49745752002-02-18 01:43:11 +0000756 #remove key set from config
757 idleConf.userCfg['keys'].remove_section(keySetName)
Guido van Rossum811c4e02006-08-22 15:45:46 +0000758 if keySetName in self.changedItems['keys']:
Steven M. Gava49745752002-02-18 01:43:11 +0000759 del(self.changedItems['keys'][keySetName])
760 #write changes
761 idleConf.userCfg['keys'].Save()
762 #reload user key set list
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400763 itemList = idleConf.GetSectionList('user', 'keys')
Steven M. Gava49745752002-02-18 01:43:11 +0000764 itemList.sort()
765 if not itemList:
766 self.radioKeysCustom.config(state=DISABLED)
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400767 self.optMenuKeysCustom.SetMenu(itemList, '- no custom keys -')
Steven M. Gava49745752002-02-18 01:43:11 +0000768 else:
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400769 self.optMenuKeysCustom.SetMenu(itemList, itemList[0])
Steven M. Gava49745752002-02-18 01:43:11 +0000770 #revert to default key set
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400771 self.keysAreBuiltin.set(idleConf.defaultCfg['main'].Get('Keys', 'default'))
772 self.builtinKeys.set(idleConf.defaultCfg['main'].Get('Keys', 'name'))
Steven M. Gava49745752002-02-18 01:43:11 +0000773 #user can't back out of these changes, they must be applied now
Terry Jan Reedyec844172016-06-14 00:53:25 -0400774 self.SaveAllChangedConfigs()
775 self.ActivateConfigChanges()
Steven M. Gava49745752002-02-18 01:43:11 +0000776 self.SetKeysType()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000777
Steven M. Gava49745752002-02-18 01:43:11 +0000778 def DeleteCustomTheme(self):
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400779 themeName = self.customTheme.get()
780 delmsg = 'Are you sure you wish to delete the theme %r ?'
781 if not tkMessageBox.askyesno(
782 'Delete Theme', delmsg % themeName, parent=self):
Steven M. Gava49745752002-02-18 01:43:11 +0000783 return
Terry Jan Reedyec844172016-06-14 00:53:25 -0400784 self.DeactivateCurrentConfig()
Steven M. Gava49745752002-02-18 01:43:11 +0000785 #remove theme from config
786 idleConf.userCfg['highlight'].remove_section(themeName)
Guido van Rossum811c4e02006-08-22 15:45:46 +0000787 if themeName in self.changedItems['highlight']:
Steven M. Gava49745752002-02-18 01:43:11 +0000788 del(self.changedItems['highlight'][themeName])
789 #write changes
790 idleConf.userCfg['highlight'].Save()
791 #reload user theme list
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400792 itemList = idleConf.GetSectionList('user', 'highlight')
Steven M. Gava49745752002-02-18 01:43:11 +0000793 itemList.sort()
794 if not itemList:
795 self.radioThemeCustom.config(state=DISABLED)
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400796 self.optMenuThemeCustom.SetMenu(itemList, '- no custom themes -')
Steven M. Gava49745752002-02-18 01:43:11 +0000797 else:
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400798 self.optMenuThemeCustom.SetMenu(itemList, itemList[0])
Steven M. Gava49745752002-02-18 01:43:11 +0000799 #revert to default theme
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400800 self.themeIsBuiltin.set(idleConf.defaultCfg['main'].Get('Theme', 'default'))
801 self.builtinTheme.set(idleConf.defaultCfg['main'].Get('Theme', 'name'))
Steven M. Gava49745752002-02-18 01:43:11 +0000802 #user can't back out of these changes, they must be applied now
Terry Jan Reedyec844172016-06-14 00:53:25 -0400803 self.SaveAllChangedConfigs()
804 self.ActivateConfigChanges()
Steven M. Gava49745752002-02-18 01:43:11 +0000805 self.SetThemeType()
806
Steven M. Gava5f28e8f2002-01-21 06:38:21 +0000807 def GetColour(self):
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400808 target = self.highlightTarget.get()
809 prevColour = self.frameColourSet.cget('bg')
810 rgbTuplet, colourString = tkColorChooser.askcolor(
811 parent=self, title='Pick new colour for : '+target,
812 initialcolor=prevColour)
813 if colourString and (colourString != prevColour):
Steven M. Gava052937f2002-02-11 02:20:53 +0000814 #user didn't cancel, and they chose a new colour
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400815 if self.themeIsBuiltin.get(): #current theme is a built-in
816 message = ('Your changes will be saved as a new Custom Theme. '
817 'Enter a name for your new Custom Theme below.')
818 newTheme = self.GetNewThemeName(message)
819 if not newTheme: #user cancelled custom theme creation
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000820 return
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400821 else: #create new custom theme based on previously active theme
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000822 self.CreateNewTheme(newTheme)
Steven M. Gava052937f2002-02-11 02:20:53 +0000823 self.colour.set(colourString)
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400824 else: #current theme is user defined
Steven M. Gava052937f2002-02-11 02:20:53 +0000825 self.colour.set(colourString)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000826
Steven M. Gava052937f2002-02-11 02:20:53 +0000827 def OnNewColourSet(self):
828 newColour=self.colour.get()
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400829 self.frameColourSet.config(bg=newColour) #set sample
830 plane ='foreground' if self.fgHilite.get() else 'background'
831 sampleElement = self.themeElements[self.highlightTarget.get()][0]
Raymond Hettinger931237e2003-07-09 18:48:24 +0000832 self.textHighlightSample.tag_config(sampleElement, **{plane:newColour})
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400833 theme = self.customTheme.get()
834 themeElement = sampleElement + '-' + plane
835 self.AddChangedItem('highlight', theme, themeElement, newColour)
Steven M. Gava052937f2002-02-11 02:20:53 +0000836
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400837 def GetNewThemeName(self, message):
838 usedNames = (idleConf.GetSectionList('user', 'highlight') +
839 idleConf.GetSectionList('default', 'highlight'))
840 newTheme = GetCfgSectionNameDialog(
841 self, 'New Custom Theme', message, usedNames).result
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000842 return newTheme
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000843
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000844 def SaveAsNewTheme(self):
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400845 newThemeName = self.GetNewThemeName('New Theme Name:')
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000846 if newThemeName:
847 self.CreateNewTheme(newThemeName)
848
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400849 def CreateNewTheme(self, newThemeName):
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000850 #creates new custom theme based on the previously active theme,
851 #and makes the new theme active
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000852 if self.themeIsBuiltin.get():
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400853 themeType = 'default'
854 themeName = self.builtinTheme.get()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000855 else:
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400856 themeType = 'user'
857 themeName = self.customTheme.get()
858 newTheme = idleConf.GetThemeDict(themeType, themeName)
Steven M. Gava052937f2002-02-11 02:20:53 +0000859 #apply any of the old theme's unsaved changes to the new theme
Kurt B. Kaisere0712772007-08-23 05:25:55 +0000860 if themeName in self.changedItems['highlight']:
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400861 themeChanges = self.changedItems['highlight'][themeName]
Kurt B. Kaisere0712772007-08-23 05:25:55 +0000862 for element in themeChanges:
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400863 newTheme[element] = themeChanges[element]
Steven M. Gava052937f2002-02-11 02:20:53 +0000864 #save the new theme
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400865 self.SaveNewTheme(newThemeName, newTheme)
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000866 #change gui over to the new theme
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400867 customThemeList = idleConf.GetSectionList('user', 'highlight')
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000868 customThemeList.sort()
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400869 self.optMenuThemeCustom.SetMenu(customThemeList, newThemeName)
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000870 self.themeIsBuiltin.set(0)
871 self.SetThemeType()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000872
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400873 def OnListFontButtonRelease(self, event):
Kurt B. Kaiser05391692003-05-26 20:35:53 +0000874 font = self.listFontName.get(ANCHOR)
875 self.fontName.set(font.lower())
Steven M. Gava5f28e8f2002-01-21 06:38:21 +0000876 self.SetFontSample()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000877
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400878 def SetFontSample(self, event=None):
879 fontName = self.fontName.get()
880 fontWeight = tkFont.BOLD if self.fontBold.get() else tkFont.NORMAL
Ned Deilyfdf0f272012-10-22 15:14:31 -0700881 newFont = (fontName, self.fontSize.get(), fontWeight)
882 self.labelFontSample.config(font=newFont)
883 self.textHighlightSample.configure(font=newFont)
Steven M. Gava5f28e8f2002-01-21 06:38:21 +0000884
Steven M. Gava5f28e8f2002-01-21 06:38:21 +0000885 def SetHighlightTarget(self):
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400886 if self.highlightTarget.get() == 'Cursor': #bg not possible
Steven M. Gava5f28e8f2002-01-21 06:38:21 +0000887 self.radioFg.config(state=DISABLED)
888 self.radioBg.config(state=DISABLED)
889 self.fgHilite.set(1)
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400890 else: #both fg and bg can be set
Steven M. Gava5f28e8f2002-01-21 06:38:21 +0000891 self.radioFg.config(state=NORMAL)
892 self.radioBg.config(state=NORMAL)
893 self.fgHilite.set(1)
894 self.SetColourSample()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000895
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400896 def SetColourSampleBinding(self, *args):
Steven M. Gava5f28e8f2002-01-21 06:38:21 +0000897 self.SetColourSample()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000898
Steven M. Gava5f28e8f2002-01-21 06:38:21 +0000899 def SetColourSample(self):
900 #set the colour smaple area
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400901 tag = self.themeElements[self.highlightTarget.get()][0]
902 plane = 'foreground' if self.fgHilite.get() else 'background'
903 colour = self.textHighlightSample.tag_cget(tag, plane)
Steven M. Gava5f28e8f2002-01-21 06:38:21 +0000904 self.frameColourSet.config(bg=colour)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000905
Steven M. Gava9dd16b32001-11-03 14:54:25 +0000906 def PaintThemeSample(self):
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400907 if self.themeIsBuiltin.get(): #a default theme
908 theme = self.builtinTheme.get()
909 else: #a user theme
910 theme = self.customTheme.get()
Kurt B. Kaisere0712772007-08-23 05:25:55 +0000911 for elementTitle in self.themeElements:
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400912 element = self.themeElements[elementTitle][0]
913 colours = idleConf.GetHighlight(theme, element)
914 if element == 'cursor': #cursor sample needs special painting
915 colours['background'] = idleConf.GetHighlight(
916 theme, 'normal', fgBg='bg')
Steven M. Gava052937f2002-02-11 02:20:53 +0000917 #handle any unsaved changes to this theme
Kurt B. Kaisere0712772007-08-23 05:25:55 +0000918 if theme in self.changedItems['highlight']:
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400919 themeDict = self.changedItems['highlight'][theme]
920 if element + '-foreground' in themeDict:
921 colours['foreground'] = themeDict[element + '-foreground']
922 if element + '-background' in themeDict:
923 colours['background'] = themeDict[element + '-background']
Raymond Hettinger931237e2003-07-09 18:48:24 +0000924 self.textHighlightSample.tag_config(element, **colours)
Steven M. Gava052937f2002-02-11 02:20:53 +0000925 self.SetColourSample()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000926
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400927 def HelpSourceSelected(self, event):
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000928 self.SetHelpListButtonStates()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000929
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000930 def SetHelpListButtonStates(self):
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400931 if self.listHelp.size() < 1: #no entries in list
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000932 self.buttonHelpListEdit.config(state=DISABLED)
933 self.buttonHelpListRemove.config(state=DISABLED)
934 else: #there are some entries
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400935 if self.listHelp.curselection(): #there currently is a selection
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000936 self.buttonHelpListEdit.config(state=NORMAL)
937 self.buttonHelpListRemove.config(state=NORMAL)
938 else: #there currently is not a selection
939 self.buttonHelpListEdit.config(state=DISABLED)
940 self.buttonHelpListRemove.config(state=DISABLED)
941
942 def HelpListItemAdd(self):
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400943 helpSource = GetHelpSourceDialog(self, 'New Help Source').result
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000944 if helpSource:
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400945 self.userHelpList.append((helpSource[0], helpSource[1]))
946 self.listHelp.insert(END, helpSource[0])
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000947 self.UpdateUserHelpChangedItems()
948 self.SetHelpListButtonStates()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000949
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000950 def HelpListItemEdit(self):
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400951 itemIndex = self.listHelp.index(ANCHOR)
952 helpSource = self.userHelpList[itemIndex]
953 newHelpSource = GetHelpSourceDialog(
954 self, 'Edit Help Source', menuItem=helpSource[0],
955 filePath=helpSource[1]).result
956 if (not newHelpSource) or (newHelpSource == helpSource):
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000957 return #no changes
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400958 self.userHelpList[itemIndex] = newHelpSource
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000959 self.listHelp.delete(itemIndex)
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400960 self.listHelp.insert(itemIndex, newHelpSource[0])
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000961 self.UpdateUserHelpChangedItems()
962 self.SetHelpListButtonStates()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000963
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000964 def HelpListItemRemove(self):
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400965 itemIndex = self.listHelp.index(ANCHOR)
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000966 del(self.userHelpList[itemIndex])
967 self.listHelp.delete(itemIndex)
968 self.UpdateUserHelpChangedItems()
969 self.SetHelpListButtonStates()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000970
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000971 def UpdateUserHelpChangedItems(self):
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +0000972 "Clear and rebuild the HelpFiles section in self.changedItems"
973 self.changedItems['main']['HelpFiles'] = {}
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400974 for num in range(1, len(self.userHelpList) + 1):
975 self.AddChangedItem(
976 'main', 'HelpFiles', str(num),
Neal Norwitz9d72bb42007-04-17 08:48:32 +0000977 ';'.join(self.userHelpList[num-1][:2]))
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000978
Steven M. Gava429a86af2001-10-23 10:42:12 +0000979 def LoadFontCfg(self):
980 ##base editor font selection list
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400981 fonts = list(tkFont.families(self))
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000982 fonts.sort()
983 for font in fonts:
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400984 self.listFontName.insert(END, font)
Terry Jan Reedyd87d1682015-08-01 18:57:33 -0400985 configuredFont = idleConf.GetFont(self, 'main', 'EditorWindow')
986 fontName = configuredFont[0].lower()
987 fontSize = configuredFont[1]
988 fontBold = configuredFont[2]=='bold'
989 self.fontName.set(fontName)
Kurt B. Kaiser05391692003-05-26 20:35:53 +0000990 lc_fonts = [s.lower() for s in fonts]
Terry Jan Reedyd87d1682015-08-01 18:57:33 -0400991 try:
992 currentFontIndex = lc_fonts.index(fontName)
Steven M. Gava429a86af2001-10-23 10:42:12 +0000993 self.listFontName.see(currentFontIndex)
994 self.listFontName.select_set(currentFontIndex)
Steven M. Gavac112cd82002-01-22 05:56:40 +0000995 self.listFontName.select_anchor(currentFontIndex)
Terry Jan Reedyd87d1682015-08-01 18:57:33 -0400996 except ValueError:
997 pass
Steven M. Gava429a86af2001-10-23 10:42:12 +0000998 ##font size dropdown
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400999 self.optMenuFontSize.SetMenu(('7', '8', '9', '10', '11', '12', '13',
1000 '14', '16', '18', '20', '22'), fontSize )
Steven M. Gavac112cd82002-01-22 05:56:40 +00001001 ##fontWeight
Terry Jan Reedyd87d1682015-08-01 18:57:33 -04001002 self.fontBold.set(fontBold)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001003 ##font sample
Steven M. Gava429a86af2001-10-23 10:42:12 +00001004 self.SetFontSample()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001005
Steven M. Gava429a86af2001-10-23 10:42:12 +00001006 def LoadTabCfg(self):
Steven M. Gava429a86af2001-10-23 10:42:12 +00001007 ##indent sizes
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001008 spaceNum = idleConf.GetOption(
1009 'main', 'Indent', 'num-spaces', default=4, type='int')
Steven M. Gava429a86af2001-10-23 10:42:12 +00001010 self.spaceNum.set(spaceNum)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001011
Steven M. Gava9dd16b32001-11-03 14:54:25 +00001012 def LoadThemeCfg(self):
Steven M. Gava41a85322001-10-29 08:05:34 +00001013 ##current theme type radiobutton
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001014 self.themeIsBuiltin.set(idleConf.GetOption(
1015 'main', 'Theme', 'default', type='bool', default=1))
Steven M. Gava41a85322001-10-29 08:05:34 +00001016 ##currently set theme
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001017 currentOption = idleConf.CurrentTheme()
Steven M. Gava7c017862001-10-29 11:19:46 +00001018 ##load available theme option menus
Steven M. Gavad0342cd2001-11-04 11:53:10 +00001019 if self.themeIsBuiltin.get(): #default theme selected
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001020 itemList = idleConf.GetSectionList('default', 'highlight')
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +00001021 itemList.sort()
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001022 self.optMenuThemeBuiltin.SetMenu(itemList, currentOption)
1023 itemList = idleConf.GetSectionList('user', 'highlight')
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +00001024 itemList.sort()
Steven M. Gava41a85322001-10-29 08:05:34 +00001025 if not itemList:
1026 self.radioThemeCustom.config(state=DISABLED)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001027 self.customTheme.set('- no custom themes -')
Steven M. Gava41a85322001-10-29 08:05:34 +00001028 else:
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001029 self.optMenuThemeCustom.SetMenu(itemList, itemList[0])
Steven M. Gava9dd16b32001-11-03 14:54:25 +00001030 else: #user theme selected
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001031 itemList = idleConf.GetSectionList('user', 'highlight')
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +00001032 itemList.sort()
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001033 self.optMenuThemeCustom.SetMenu(itemList, currentOption)
1034 itemList = idleConf.GetSectionList('default', 'highlight')
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +00001035 itemList.sort()
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001036 self.optMenuThemeBuiltin.SetMenu(itemList, itemList[0])
Steven M. Gava7c017862001-10-29 11:19:46 +00001037 self.SetThemeType()
1038 ##load theme element option menu
Kurt B. Kaisere0712772007-08-23 05:25:55 +00001039 themeNames = list(self.themeElements.keys())
Kurt B. Kaiser4718bf82008-02-12 21:34:12 +00001040 themeNames.sort(key=lambda x: self.themeElements[x][1])
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001041 self.optMenuHighlightTarget.SetMenu(themeNames, themeNames[0])
Steven M. Gava9dd16b32001-11-03 14:54:25 +00001042 self.PaintThemeSample()
Steven M. Gava0cae01c2002-01-04 07:53:06 +00001043 self.SetHighlightTarget()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001044
Steven M. Gava9dd16b32001-11-03 14:54:25 +00001045 def LoadKeyCfg(self):
Steven M. Gava41a85322001-10-29 08:05:34 +00001046 ##current keys type radiobutton
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001047 self.keysAreBuiltin.set(idleConf.GetOption(
1048 'main', 'Keys', 'default', type='bool', default=1))
Steven M. Gava41a85322001-10-29 08:05:34 +00001049 ##currently set keys
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001050 currentOption = idleConf.CurrentKeys()
Steven M. Gava7c017862001-10-29 11:19:46 +00001051 ##load available keyset option menus
Steven M. Gava052937f2002-02-11 02:20:53 +00001052 if self.keysAreBuiltin.get(): #default theme selected
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001053 itemList = idleConf.GetSectionList('default', 'keys')
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +00001054 itemList.sort()
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001055 self.optMenuKeysBuiltin.SetMenu(itemList, currentOption)
1056 itemList = idleConf.GetSectionList('user', 'keys')
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +00001057 itemList.sort()
Steven M. Gava41a85322001-10-29 08:05:34 +00001058 if not itemList:
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001059 self.radioKeysCustom.config(state=DISABLED)
1060 self.customKeys.set('- no custom keys -')
Steven M. Gava41a85322001-10-29 08:05:34 +00001061 else:
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001062 self.optMenuKeysCustom.SetMenu(itemList, itemList[0])
Steven M. Gavaa498af22002-02-01 01:33:36 +00001063 else: #user key set selected
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001064 itemList = idleConf.GetSectionList('user', 'keys')
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +00001065 itemList.sort()
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001066 self.optMenuKeysCustom.SetMenu(itemList, currentOption)
1067 itemList = idleConf.GetSectionList('default', 'keys')
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +00001068 itemList.sort()
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001069 self.optMenuKeysBuiltin.SetMenu(itemList, itemList[0])
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001070 self.SetKeysType()
Steven M. Gavafacfc092002-01-19 00:29:54 +00001071 ##load keyset element list
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001072 keySetName = idleConf.CurrentKeys()
Steven M. Gava052937f2002-02-11 02:20:53 +00001073 self.LoadKeysList(keySetName)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001074
Steven M. Gavafacfc092002-01-19 00:29:54 +00001075 def LoadGeneralCfg(self):
Steven M. Gava5f28e8f2002-01-21 06:38:21 +00001076 #startup state
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001077 self.startupEdit.set(idleConf.GetOption(
1078 'main', 'General', 'editor-on-startup', default=1, type='bool'))
Kurt B. Kaiser6c638b62003-05-26 06:23:10 +00001079 #autosave state
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001080 self.autoSave.set(idleConf.GetOption(
1081 'main', 'General', 'autosave', default=0, type='bool'))
Steven M. Gavafacfc092002-01-19 00:29:54 +00001082 #initial window size
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001083 self.winWidth.set(idleConf.GetOption(
1084 'main', 'EditorWindow', 'width', type='int'))
1085 self.winHeight.set(idleConf.GetOption(
1086 'main', 'EditorWindow', 'height', type='int'))
Kurt B. Kaisera053f332003-05-10 00:49:56 +00001087 # default source encoding
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001088 self.encoding.set(idleConf.GetOption(
1089 'main', 'EditorWindow', 'encoding', default='none'))
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +00001090 # additional help sources
1091 self.userHelpList = idleConf.GetAllExtraHelpSourcesList()
Steven M. Gava085eb1b2002-02-05 04:52:32 +00001092 for helpItem in self.userHelpList:
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001093 self.listHelp.insert(END, helpItem[0])
Steven M. Gava085eb1b2002-02-05 04:52:32 +00001094 self.SetHelpListButtonStates()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001095
Steven M. Gava429a86af2001-10-23 10:42:12 +00001096 def LoadConfigs(self):
1097 """
1098 load configuration from default and user config files and populate
1099 the widgets on the config dialog pages.
1100 """
1101 ### fonts / tabs page
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001102 self.LoadFontCfg()
1103 self.LoadTabCfg()
Steven M. Gava429a86af2001-10-23 10:42:12 +00001104 ### highlighting page
Steven M. Gava9dd16b32001-11-03 14:54:25 +00001105 self.LoadThemeCfg()
Steven M. Gava429a86af2001-10-23 10:42:12 +00001106 ### keys page
Steven M. Gava9dd16b32001-11-03 14:54:25 +00001107 self.LoadKeyCfg()
Steven M. Gava429a86af2001-10-23 10:42:12 +00001108 ### general page
Steven M. Gavafacfc092002-01-19 00:29:54 +00001109 self.LoadGeneralCfg()
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001110 # note: extension page handled separately
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001111
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001112 def SaveNewKeySet(self, keySetName, keySet):
Steven M. Gava052937f2002-02-11 02:20:53 +00001113 """
1114 save a newly created core key set.
1115 keySetName - string, the name of the new key set
1116 keySet - dictionary containing the new key set
1117 """
1118 if not idleConf.userCfg['keys'].has_section(keySetName):
1119 idleConf.userCfg['keys'].add_section(keySetName)
Kurt B. Kaisere0712772007-08-23 05:25:55 +00001120 for event in keySet:
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001121 value = keySet[event]
1122 idleConf.userCfg['keys'].SetOption(keySetName, event, value)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001123
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001124 def SaveNewTheme(self, themeName, theme):
Steven M. Gava052937f2002-02-11 02:20:53 +00001125 """
1126 save a newly created theme.
1127 themeName - string, the name of the new theme
1128 theme - dictionary containing the new theme
1129 """
1130 if not idleConf.userCfg['highlight'].has_section(themeName):
1131 idleConf.userCfg['highlight'].add_section(themeName)
Kurt B. Kaisere0712772007-08-23 05:25:55 +00001132 for element in theme:
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001133 value = theme[element]
1134 idleConf.userCfg['highlight'].SetOption(themeName, element, value)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001135
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001136 def SetUserValue(self, configType, section, item, value):
1137 if idleConf.defaultCfg[configType].has_option(section, item):
1138 if idleConf.defaultCfg[configType].Get(section, item) == value:
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +00001139 #the setting equals a default setting, remove it from user cfg
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001140 return idleConf.userCfg[configType].RemoveOption(section, item)
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +00001141 #if we got here set the option
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001142 return idleConf.userCfg[configType].SetOption(section, item, value)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001143
Steven M. Gava052937f2002-02-11 02:20:53 +00001144 def SaveAllChangedConfigs(self):
Kurt B. Kaisere7a161e2003-01-10 20:13:57 +00001145 "Save configuration changes to the user config file."
Steven M. Gava0c5bc8c2002-03-27 02:25:44 +00001146 idleConf.userCfg['main'].Save()
Kurt B. Kaisere0712772007-08-23 05:25:55 +00001147 for configType in self.changedItems:
Kurt B. Kaisere7a161e2003-01-10 20:13:57 +00001148 cfgTypeHasChanges = False
Kurt B. Kaisere0712772007-08-23 05:25:55 +00001149 for section in self.changedItems[configType]:
Kurt B. Kaisere7a161e2003-01-10 20:13:57 +00001150 if section == 'HelpFiles':
1151 #this section gets completely replaced
1152 idleConf.userCfg['main'].remove_section('HelpFiles')
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +00001153 cfgTypeHasChanges = True
Kurt B. Kaisere0712772007-08-23 05:25:55 +00001154 for item in self.changedItems[configType][section]:
Kurt B. Kaisere7a161e2003-01-10 20:13:57 +00001155 value = self.changedItems[configType][section][item]
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001156 if self.SetUserValue(configType, section, item, value):
Kurt B. Kaisere7a161e2003-01-10 20:13:57 +00001157 cfgTypeHasChanges = True
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001158 if cfgTypeHasChanges:
1159 idleConf.userCfg[configType].Save()
Kurt B. Kaiser5acdf932004-11-16 21:28:36 +00001160 for configType in ['keys', 'highlight']:
1161 # save these even if unchanged!
1162 idleConf.userCfg[configType].Save()
Steven M. Gavaa498af22002-02-01 01:33:36 +00001163 self.ResetChangedItems() #clear the changed items dict
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001164 self.save_all_changed_extensions() # uses a different mechanism
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001165
Kurt B. Kaiserb1754452005-11-18 22:05:48 +00001166 def DeactivateCurrentConfig(self):
1167 #Before a config is saved, some cleanup of current
1168 #config must be done - remove the previous keybindings
Kurt B. Kaisere0712772007-08-23 05:25:55 +00001169 winInstances = self.parent.instance_dict.keys()
Kurt B. Kaiserb1754452005-11-18 22:05:48 +00001170 for instance in winInstances:
1171 instance.RemoveKeybindings()
1172
Steven M. Gava49745752002-02-18 01:43:11 +00001173 def ActivateConfigChanges(self):
Kurt B. Kaiseracdef852005-01-31 03:34:26 +00001174 "Dynamically apply configuration changes"
Kurt B. Kaisere0712772007-08-23 05:25:55 +00001175 winInstances = self.parent.instance_dict.keys()
Steven M. Gavab77d3432002-03-02 07:16:21 +00001176 for instance in winInstances:
1177 instance.ResetColorizer()
Steven M. Gavab1585412002-03-12 00:21:56 +00001178 instance.ResetFont()
Kurt B. Kaiseracdef852005-01-31 03:34:26 +00001179 instance.set_notabs_indentwidth()
Kurt B. Kaiserb1754452005-11-18 22:05:48 +00001180 instance.ApplyKeybindings()
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +00001181 instance.reset_help_menu_entries()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001182
Steven M. Gava5f28e8f2002-01-21 06:38:21 +00001183 def Cancel(self):
1184 self.destroy()
1185
1186 def Ok(self):
1187 self.Apply()
1188 self.destroy()
1189
1190 def Apply(self):
Kurt B. Kaiserb1754452005-11-18 22:05:48 +00001191 self.DeactivateCurrentConfig()
Steven M. Gava052937f2002-02-11 02:20:53 +00001192 self.SaveAllChangedConfigs()
Steven M. Gava49745752002-02-18 01:43:11 +00001193 self.ActivateConfigChanges()
Steven M. Gava5f28e8f2002-01-21 06:38:21 +00001194
1195 def Help(self):
Terry Jan Reedyd0cadba2015-10-11 22:07:31 -04001196 page = self.tabPages._current_page
1197 view_text(self, title='Help for IDLE preferences',
1198 text=help_common+help_pages.get(page, ''))
1199
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001200 def CreatePageExtensions(self):
1201 """Part of the config dialog used for configuring IDLE extensions.
1202
1203 This code is generic - it works for any and all IDLE extensions.
1204
1205 IDLE extensions save their configuration options using idleConf.
Terry Jan Reedyb2f87602015-10-13 22:09:06 -04001206 This code reads the current configuration using idleConf, supplies a
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001207 GUI interface to change the configuration values, and saves the
1208 changes using idleConf.
1209
1210 Not all changes take effect immediately - some may require restarting IDLE.
1211 This depends on each extension's implementation.
1212
1213 All values are treated as text, and it is up to the user to supply
1214 reasonable values. The only exception to this are the 'enable*' options,
Serhiy Storchaka6a7b3a72016-04-17 08:32:47 +03001215 which are boolean, and can be toggled with a True/False button.
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001216 """
1217 parent = self.parent
1218 frame = self.tabPages.pages['Extensions'].frame
1219 self.ext_defaultCfg = idleConf.defaultCfg['extensions']
1220 self.ext_userCfg = idleConf.userCfg['extensions']
1221 self.is_int = self.register(is_int)
1222 self.load_extensions()
1223 # create widgets - a listbox shows all available extensions, with the
1224 # controls for the extension selected in the listbox to the right
1225 self.extension_names = StringVar(self)
1226 frame.rowconfigure(0, weight=1)
1227 frame.columnconfigure(2, weight=1)
1228 self.extension_list = Listbox(frame, listvariable=self.extension_names,
1229 selectmode='browse')
1230 self.extension_list.bind('<<ListboxSelect>>', self.extension_selected)
1231 scroll = Scrollbar(frame, command=self.extension_list.yview)
1232 self.extension_list.yscrollcommand=scroll.set
1233 self.details_frame = LabelFrame(frame, width=250, height=250)
1234 self.extension_list.grid(column=0, row=0, sticky='nws')
1235 scroll.grid(column=1, row=0, sticky='ns')
1236 self.details_frame.grid(column=2, row=0, sticky='nsew', padx=[10, 0])
1237 frame.configure(padx=10, pady=10)
1238 self.config_frame = {}
1239 self.current_extension = None
1240
1241 self.outerframe = self # TEMPORARY
1242 self.tabbed_page_set = self.extension_list # TEMPORARY
1243
1244 # create the frame holding controls for each extension
1245 ext_names = ''
1246 for ext_name in sorted(self.extensions):
1247 self.create_extension_frame(ext_name)
1248 ext_names = ext_names + '{' + ext_name + '} '
1249 self.extension_names.set(ext_names)
1250 self.extension_list.selection_set(0)
1251 self.extension_selected(None)
1252
1253 def load_extensions(self):
1254 "Fill self.extensions with data from the default and user configs."
1255 self.extensions = {}
1256 for ext_name in idleConf.GetExtensions(active_only=False):
1257 self.extensions[ext_name] = []
1258
1259 for ext_name in self.extensions:
1260 opt_list = sorted(self.ext_defaultCfg.GetOptionList(ext_name))
1261
1262 # bring 'enable' options to the beginning of the list
1263 enables = [opt_name for opt_name in opt_list
1264 if opt_name.startswith('enable')]
1265 for opt_name in enables:
1266 opt_list.remove(opt_name)
1267 opt_list = enables + opt_list
1268
1269 for opt_name in opt_list:
1270 def_str = self.ext_defaultCfg.Get(
1271 ext_name, opt_name, raw=True)
1272 try:
1273 def_obj = {'True':True, 'False':False}[def_str]
1274 opt_type = 'bool'
1275 except KeyError:
1276 try:
1277 def_obj = int(def_str)
1278 opt_type = 'int'
1279 except ValueError:
1280 def_obj = def_str
1281 opt_type = None
1282 try:
1283 value = self.ext_userCfg.Get(
1284 ext_name, opt_name, type=opt_type, raw=True,
1285 default=def_obj)
1286 except ValueError: # Need this until .Get fixed
1287 value = def_obj # bad values overwritten by entry
1288 var = StringVar(self)
1289 var.set(str(value))
1290
1291 self.extensions[ext_name].append({'name': opt_name,
1292 'type': opt_type,
1293 'default': def_str,
1294 'value': value,
1295 'var': var,
1296 })
1297
1298 def extension_selected(self, event):
1299 newsel = self.extension_list.curselection()
1300 if newsel:
1301 newsel = self.extension_list.get(newsel)
1302 if newsel is None or newsel != self.current_extension:
1303 if self.current_extension:
1304 self.details_frame.config(text='')
1305 self.config_frame[self.current_extension].grid_forget()
1306 self.current_extension = None
1307 if newsel:
1308 self.details_frame.config(text=newsel)
1309 self.config_frame[newsel].grid(column=0, row=0, sticky='nsew')
1310 self.current_extension = newsel
1311
1312 def create_extension_frame(self, ext_name):
1313 """Create a frame holding the widgets to configure one extension"""
1314 f = VerticalScrolledFrame(self.details_frame, height=250, width=250)
1315 self.config_frame[ext_name] = f
1316 entry_area = f.interior
1317 # create an entry for each configuration option
1318 for row, opt in enumerate(self.extensions[ext_name]):
1319 # create a row with a label and entry/checkbutton
1320 label = Label(entry_area, text=opt['name'])
1321 label.grid(row=row, column=0, sticky=NW)
1322 var = opt['var']
1323 if opt['type'] == 'bool':
1324 Checkbutton(entry_area, textvariable=var, variable=var,
1325 onvalue='True', offvalue='False',
1326 indicatoron=FALSE, selectcolor='', width=8
1327 ).grid(row=row, column=1, sticky=W, padx=7)
1328 elif opt['type'] == 'int':
1329 Entry(entry_area, textvariable=var, validate='key',
1330 validatecommand=(self.is_int, '%P')
1331 ).grid(row=row, column=1, sticky=NSEW, padx=7)
1332
1333 else:
1334 Entry(entry_area, textvariable=var
1335 ).grid(row=row, column=1, sticky=NSEW, padx=7)
1336 return
1337
1338 def set_extension_value(self, section, opt):
1339 name = opt['name']
1340 default = opt['default']
1341 value = opt['var'].get().strip() or default
1342 opt['var'].set(value)
1343 # if self.defaultCfg.has_section(section):
1344 # Currently, always true; if not, indent to return
1345 if (value == default):
1346 return self.ext_userCfg.RemoveOption(section, name)
1347 # set the option
1348 return self.ext_userCfg.SetOption(section, name, value)
1349
1350 def save_all_changed_extensions(self):
1351 """Save configuration changes to the user config file."""
1352 has_changes = False
1353 for ext_name in self.extensions:
1354 options = self.extensions[ext_name]
1355 for opt in options:
1356 if self.set_extension_value(ext_name, opt):
1357 has_changes = True
1358 if has_changes:
1359 self.ext_userCfg.Save()
1360
1361
Terry Jan Reedyd0cadba2015-10-11 22:07:31 -04001362help_common = '''\
1363When you click either the Apply or Ok buttons, settings in this
1364dialog that are different from IDLE's default are saved in
1365a .idlerc directory in your home directory. Except as noted,
Terry Jan Reedyd0c0f002015-11-12 15:02:57 -05001366these changes apply to all versions of IDLE installed on this
Terry Jan Reedyd0cadba2015-10-11 22:07:31 -04001367machine. Some do not take affect until IDLE is restarted.
1368[Cancel] only cancels changes made since the last save.
1369'''
1370help_pages = {
1371 'Highlighting':'''
1372Highlighting:
Terry Jan Reedyd0c0f002015-11-12 15:02:57 -05001373The IDLE Dark color theme is new in October 2015. It can only
Terry Jan Reedyd0cadba2015-10-11 22:07:31 -04001374be used with older IDLE releases if it is saved as a custom
1375theme, with a different name.
1376'''
1377}
1378
Steven M. Gavac11ccf32001-09-24 09:43:17 +00001379
Terry Jan Reedy93f35422015-10-13 22:03:51 -04001380def is_int(s):
1381 "Return 's is blank or represents an int'"
1382 if not s:
1383 return True
1384 try:
1385 int(s)
1386 return True
1387 except ValueError:
1388 return False
1389
1390
Terry Jan Reedya9421fb2014-10-22 20:15:18 -04001391class VerticalScrolledFrame(Frame):
1392 """A pure Tkinter vertically scrollable frame.
1393
1394 * Use the 'interior' attribute to place widgets inside the scrollable frame
1395 * Construct and pack/place/grid normally
1396 * This frame only allows vertical scrolling
1397 """
1398 def __init__(self, parent, *args, **kw):
1399 Frame.__init__(self, parent, *args, **kw)
1400
1401 # create a canvas object and a vertical scrollbar for scrolling it
1402 vscrollbar = Scrollbar(self, orient=VERTICAL)
1403 vscrollbar.pack(fill=Y, side=RIGHT, expand=FALSE)
1404 canvas = Canvas(self, bd=0, highlightthickness=0,
Terry Jan Reedyd0812292015-10-22 03:27:31 -04001405 yscrollcommand=vscrollbar.set, width=240)
Terry Jan Reedya9421fb2014-10-22 20:15:18 -04001406 canvas.pack(side=LEFT, fill=BOTH, expand=TRUE)
1407 vscrollbar.config(command=canvas.yview)
1408
1409 # reset the view
1410 canvas.xview_moveto(0)
1411 canvas.yview_moveto(0)
1412
1413 # create a frame inside the canvas which will be scrolled with it
1414 self.interior = interior = Frame(canvas)
1415 interior_id = canvas.create_window(0, 0, window=interior, anchor=NW)
1416
1417 # track changes to the canvas and frame width and sync them,
1418 # also updating the scrollbar
1419 def _configure_interior(event):
1420 # update the scrollbars to match the size of the inner frame
1421 size = (interior.winfo_reqwidth(), interior.winfo_reqheight())
1422 canvas.config(scrollregion="0 0 %s %s" % size)
Terry Jan Reedya9421fb2014-10-22 20:15:18 -04001423 interior.bind('<Configure>', _configure_interior)
1424
1425 def _configure_canvas(event):
1426 if interior.winfo_reqwidth() != canvas.winfo_width():
1427 # update the inner frame's width to fill the canvas
1428 canvas.itemconfigure(interior_id, width=canvas.winfo_width())
1429 canvas.bind('<Configure>', _configure_canvas)
1430
1431 return
1432
Terry Jan Reedya9421fb2014-10-22 20:15:18 -04001433
Steven M. Gava44d3d1a2001-07-31 06:59:02 +00001434if __name__ == '__main__':
Terry Jan Reedycfa89502014-07-14 23:07:32 -04001435 import unittest
1436 unittest.main('idlelib.idle_test.test_configdialog',
1437 verbosity=2, exit=False)
Terry Jan Reedy2e8234a2014-05-29 01:46:26 -04001438 from idlelib.idle_test.htest import run
Terry Jan Reedy47304c02015-10-20 02:15:28 -04001439 run(ConfigDialog)