blob: b70cb60f41d0b90904f84d2543f56fbc28b17412 [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 *
13import tkinter.messagebox as tkMessageBox
14import tkinter.colorchooser as tkColorChooser
15import tkinter.font as tkFont
Steven M. Gava44d3d1a2001-07-31 06:59:02 +000016
Kurt B. Kaiser2d7f6a02007-08-22 23:01:33 +000017from idlelib.configHandler import idleConf
18from idlelib.dynOptionMenuWidget import DynOptionMenu
Kurt B. Kaiser2d7f6a02007-08-22 23:01:33 +000019from idlelib.keybindingDialog import GetKeysDialog
20from idlelib.configSectionNameDialog import GetCfgSectionNameDialog
21from idlelib.configHelpSourceEdit import GetHelpSourceDialog
Terry Jan Reedya9421fb2014-10-22 20:15:18 -040022from idlelib.tabbedpages import TabbedPageSet
Ronald Oussoren9e350042009-02-12 16:02:11 +000023from idlelib import macosxSupport
Steven M. Gava44d3d1a2001-07-31 06:59:02 +000024class ConfigDialog(Toplevel):
Kurt B. Kaiseracdef852005-01-31 03:34:26 +000025
Terry Jan Reedycd567362014-10-17 01:31:35 -040026 def __init__(self, parent, title='', _htest=False, _utest=False):
Terry Jan Reedy2e8234a2014-05-29 01:46:26 -040027 """
28 _htest - bool, change box location when running htest
Terry Jan Reedycfa89502014-07-14 23:07:32 -040029 _utest - bool, don't wait_window when running unittest
Terry Jan Reedy2e8234a2014-05-29 01:46:26 -040030 """
Steven M. Gavad721c482001-07-31 10:46:53 +000031 Toplevel.__init__(self, parent)
Terry Jan Reedy22405332014-07-30 19:24:32 -040032 self.parent = parent
Terry Jan Reedy4036d872014-08-03 23:02:58 -040033 if _htest:
34 parent.instance_dict = {}
Guido van Rossum8ce8a782007-11-01 19:42:39 +000035 self.wm_withdraw()
36
Steven M. Gavad721c482001-07-31 10:46:53 +000037 self.configure(borderwidth=5)
Terry Jan Reedycd567362014-10-17 01:31:35 -040038 self.title(title or 'IDLE Preferences')
Terry Jan Reedy4036d872014-08-03 23:02:58 -040039 self.geometry(
40 "+%d+%d" % (parent.winfo_rootx() + 20,
41 parent.winfo_rooty() + (30 if not _htest else 150)))
Georg Brandl7eb4b7d2005-07-22 21:49:32 +000042 #Theme Elements. Each theme element key is its display name.
Steven M. Gava9dd16b32001-11-03 14:54:25 +000043 #The first value of the tuple is the sample area tag name.
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000044 #The second value is the display name list sort index.
Terry Jan Reedy4036d872014-08-03 23:02:58 -040045 self.themeElements={
46 'Normal Text':('normal', '00'),
47 'Python Keywords':('keyword', '01'),
48 'Python Definitions':('definition', '02'),
Kurt B. Kaiser73360a32004-03-08 18:15:31 +000049 'Python Builtins':('builtin', '03'),
Terry Jan Reedy4036d872014-08-03 23:02:58 -040050 'Python Comments':('comment', '04'),
51 'Python Strings':('string', '05'),
52 'Selected Text':('hilite', '06'),
53 'Found Text':('hit', '07'),
54 'Cursor':('cursor', '08'),
55 'Error Text':('error', '09'),
56 'Shell Normal Text':('console', '10'),
57 'Shell Stdout Text':('stdout', '11'),
58 'Shell Stderr Text':('stderr', '12'),
Kurt B. Kaiser73360a32004-03-08 18:15:31 +000059 }
Steven M. Gavaa498af22002-02-01 01:33:36 +000060 self.ResetChangedItems() #load initial values in changed items dict
Steven M. Gavad721c482001-07-31 10:46:53 +000061 self.CreateWidgets()
Terry Jan Reedy4036d872014-08-03 23:02:58 -040062 self.resizable(height=FALSE, width=FALSE)
Steven M. Gavad721c482001-07-31 10:46:53 +000063 self.transient(parent)
64 self.grab_set()
65 self.protocol("WM_DELETE_WINDOW", self.Cancel)
Steven M. Gava2d4e03b2001-12-05 07:54:07 +000066 self.tabPages.focus_set()
Steven M. Gavad721c482001-07-31 10:46:53 +000067 #key bindings for this dialog
Terry Jan Reedy4036d872014-08-03 23:02:58 -040068 #self.bind('<Escape>', self.Cancel) #dismiss dialog, no save
69 #self.bind('<Alt-a>', self.Apply) #apply changes, save
70 #self.bind('<F1>', self.Help) #context help
Steven M. Gava429a86af2001-10-23 10:42:12 +000071 self.LoadConfigs()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000072 self.AttachVarCallbacks() #avoid callbacks during LoadConfigs
Guido van Rossum8ce8a782007-11-01 19:42:39 +000073
Terry Jan Reedycfa89502014-07-14 23:07:32 -040074 if not _utest:
75 self.wm_deiconify()
76 self.wait_window()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000077
Steven M. Gavad721c482001-07-31 10:46:53 +000078 def CreateWidgets(self):
Guido van Rossum8ce8a782007-11-01 19:42:39 +000079 self.tabPages = TabbedPageSet(self,
Terry Jan Reedy4036d872014-08-03 23:02:58 -040080 page_names=['Fonts/Tabs', 'Highlighting', 'Keys', 'General'])
Terry Jan Reedy92cb0a32014-10-08 20:29:13 -040081 self.tabPages.pack(side=TOP, expand=TRUE, fill=BOTH)
82 self.CreatePageFontTab()
83 self.CreatePageHighlight()
84 self.CreatePageKeys()
85 self.CreatePageGeneral()
86 self.create_action_buttons().pack(side=BOTTOM)
Terry Jan Reedy92cb0a32014-10-08 20:29:13 -040087 def create_action_buttons(self):
Ned Deilyb7601672014-03-27 20:49:14 -070088 if macosxSupport.isAquaTk():
Terry Jan Reedye3416e62014-07-26 19:40:16 -040089 # Changing the default padding on OSX results in unreadable
90 # text in the buttons
Terry Jan Reedy4036d872014-08-03 23:02:58 -040091 paddingArgs = {}
Ronald Oussoren9e350042009-02-12 16:02:11 +000092 else:
Terry Jan Reedy4036d872014-08-03 23:02:58 -040093 paddingArgs = {'padx':6, 'pady':3}
Terry Jan Reedya9421fb2014-10-22 20:15:18 -040094 outer = Frame(self, pady=2)
95 buttons = Frame(outer, pady=2)
Terry Jan Reedy4036d872014-08-03 23:02:58 -040096 self.buttonOk = Button(
Terry Jan Reedya9421fb2014-10-22 20:15:18 -040097 buttons, text='Ok', command=self.Ok,
Terry Jan Reedy92cb0a32014-10-08 20:29:13 -040098 takefocus=FALSE, **paddingArgs)
Terry Jan Reedy4036d872014-08-03 23:02:58 -040099 self.buttonApply = Button(
Terry Jan Reedya9421fb2014-10-22 20:15:18 -0400100 buttons, text='Apply', command=self.Apply,
Terry Jan Reedy92cb0a32014-10-08 20:29:13 -0400101 takefocus=FALSE, **paddingArgs)
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400102 self.buttonCancel = Button(
Terry Jan Reedya9421fb2014-10-22 20:15:18 -0400103 buttons, text='Cancel', command=self.Cancel,
Terry Jan Reedy92cb0a32014-10-08 20:29:13 -0400104 takefocus=FALSE, **paddingArgs)
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400105 self.buttonOk.pack(side=LEFT, padx=5)
106 self.buttonApply.pack(side=LEFT, padx=5)
107 self.buttonCancel.pack(side=LEFT, padx=5)
Terry Jan Reedya9421fb2014-10-22 20:15:18 -0400108# Comment out Help button creation and packing until implement self.Help
109## self.buttonHelp = Button(
110## buttons, text='Help', command=self.Help,
111## takefocus=FALSE, **paddingArgs)
112## self.buttonHelp.pack(side=RIGHT, padx=5)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000113
Terry Jan Reedya9421fb2014-10-22 20:15:18 -0400114 # add space above buttons
115 Frame(outer, height=2, borderwidth=0).pack(side=TOP)
116 buttons.pack(side=BOTTOM)
117 return outer
Steven M. Gava60fc7072001-08-04 13:58:22 +0000118 def CreatePageFontTab(self):
Terry Jan Reedy22405332014-07-30 19:24:32 -0400119 parent = self.parent
120 self.fontSize = StringVar(parent)
121 self.fontBold = BooleanVar(parent)
122 self.fontName = StringVar(parent)
123 self.spaceNum = IntVar(parent)
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400124 self.editFont = tkFont.Font(parent, ('courier', 10, 'normal'))
Terry Jan Reedy22405332014-07-30 19:24:32 -0400125
Steven M. Gavaf213ccb2001-08-05 08:00:28 +0000126 ##widget creation
127 #body frame
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400128 frame = self.tabPages.pages['Fonts/Tabs'].frame
Steven M. Gavaf213ccb2001-08-05 08:00:28 +0000129 #body section frames
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400130 frameFont = LabelFrame(
131 frame, borderwidth=2, relief=GROOVE, text=' Base Editor Font ')
132 frameIndent = LabelFrame(
133 frame, borderwidth=2, relief=GROOVE, text=' Indentation Width ')
Steven M. Gavaf213ccb2001-08-05 08:00:28 +0000134 #frameFont
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400135 frameFontName = Frame(frameFont)
136 frameFontParam = Frame(frameFont)
137 labelFontNameTitle = Label(
138 frameFontName, justify=LEFT, text='Font Face :')
139 self.listFontName = Listbox(
140 frameFontName, height=5, takefocus=FALSE, exportselection=FALSE)
141 self.listFontName.bind(
142 '<ButtonRelease-1>', self.OnListFontButtonRelease)
143 scrollFont = Scrollbar(frameFontName)
Steven M. Gavac01e30f2001-08-11 15:48:13 +0000144 scrollFont.config(command=self.listFontName.yview)
145 self.listFontName.config(yscrollcommand=scrollFont.set)
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400146 labelFontSizeTitle = Label(frameFontParam, text='Size :')
147 self.optMenuFontSize = DynOptionMenu(
148 frameFontParam, self.fontSize, None, command=self.SetFontSample)
149 checkFontBold = Checkbutton(
150 frameFontParam, variable=self.fontBold, onvalue=1,
151 offvalue=0, text='Bold', command=self.SetFontSample)
152 frameFontSample = Frame(frameFont, relief=SOLID, borderwidth=1)
153 self.labelFontSample = Label(
154 frameFontSample, justify=LEFT, font=self.editFont,
155 text='AaBbCcDdEe\nFfGgHhIiJjK\n1234567890\n#:+=(){}[]')
Steven M. Gavaf213ccb2001-08-05 08:00:28 +0000156 #frameIndent
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400157 frameIndentSize = Frame(frameIndent)
158 labelSpaceNumTitle = Label(
159 frameIndentSize, justify=LEFT,
160 text='Python Standard: 4 Spaces!')
161 self.scaleSpaceNum = Scale(
162 frameIndentSize, variable=self.spaceNum,
163 orient='horizontal', tickinterval=2, from_=2, to=16)
Terry Jan Reedy22405332014-07-30 19:24:32 -0400164
Steven M. Gavaf213ccb2001-08-05 08:00:28 +0000165 #widget packing
166 #body
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400167 frameFont.pack(side=LEFT, padx=5, pady=5, expand=TRUE, fill=BOTH)
168 frameIndent.pack(side=LEFT, padx=5, pady=5, fill=Y)
Steven M. Gavaf213ccb2001-08-05 08:00:28 +0000169 #frameFont
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400170 frameFontName.pack(side=TOP, padx=5, pady=5, fill=X)
171 frameFontParam.pack(side=TOP, padx=5, pady=5, fill=X)
172 labelFontNameTitle.pack(side=TOP, anchor=W)
173 self.listFontName.pack(side=LEFT, expand=TRUE, fill=X)
174 scrollFont.pack(side=LEFT, fill=Y)
175 labelFontSizeTitle.pack(side=LEFT, anchor=W)
176 self.optMenuFontSize.pack(side=LEFT, anchor=W)
177 checkFontBold.pack(side=LEFT, anchor=W, padx=20)
178 frameFontSample.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH)
179 self.labelFontSample.pack(expand=TRUE, fill=BOTH)
Steven M. Gavaf213ccb2001-08-05 08:00:28 +0000180 #frameIndent
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400181 frameIndentSize.pack(side=TOP, fill=X)
182 labelSpaceNumTitle.pack(side=TOP, anchor=W, padx=5)
183 self.scaleSpaceNum.pack(side=TOP, padx=5, fill=X)
Steven M. Gava952d0a52001-08-03 04:43:44 +0000184 return frame
185
186 def CreatePageHighlight(self):
Terry Jan Reedy22405332014-07-30 19:24:32 -0400187 parent = self.parent
188 self.builtinTheme = StringVar(parent)
189 self.customTheme = StringVar(parent)
190 self.fgHilite = BooleanVar(parent)
191 self.colour = StringVar(parent)
192 self.fontName = StringVar(parent)
193 self.themeIsBuiltin = BooleanVar(parent)
194 self.highlightTarget = StringVar(parent)
195
Steven M. Gava952d0a52001-08-03 04:43:44 +0000196 ##widget creation
197 #body frame
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400198 frame = self.tabPages.pages['Highlighting'].frame
Steven M. Gava952d0a52001-08-03 04:43:44 +0000199 #body section frames
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400200 frameCustom = LabelFrame(frame, borderwidth=2, relief=GROOVE,
201 text=' Custom Highlighting ')
202 frameTheme = LabelFrame(frame, borderwidth=2, relief=GROOVE,
203 text=' Highlighting Theme ')
Steven M. Gava952d0a52001-08-03 04:43:44 +0000204 #frameCustom
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400205 self.textHighlightSample=Text(
206 frameCustom, relief=SOLID, borderwidth=1,
207 font=('courier', 12, ''), cursor='hand2', width=21, height=11,
208 takefocus=FALSE, highlightthickness=0, wrap=NONE)
Steven M. Gavae16d94b2001-11-03 05:07:28 +0000209 text=self.textHighlightSample
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400210 text.bind('<Double-Button-1>', lambda e: 'break')
211 text.bind('<B1-Motion>', lambda e: 'break')
212 textAndTags=(
213 ('#you can click here', 'comment'), ('\n', 'normal'),
214 ('#to choose items', 'comment'), ('\n', 'normal'),
215 ('def', 'keyword'), (' ', 'normal'),
216 ('func', 'definition'), ('(param):\n ', 'normal'),
217 ('"""string"""', 'string'), ('\n var0 = ', 'normal'),
218 ("'string'", 'string'), ('\n var1 = ', 'normal'),
219 ("'selected'", 'hilite'), ('\n var2 = ', 'normal'),
220 ("'found'", 'hit'), ('\n var3 = ', 'normal'),
221 ('list', 'builtin'), ('(', 'normal'),
222 ('None', 'keyword'), (')\n\n', 'normal'),
223 (' error ', 'error'), (' ', 'normal'),
224 ('cursor |', 'cursor'), ('\n ', 'normal'),
225 ('shell', 'console'), (' ', 'normal'),
226 ('stdout', 'stdout'), (' ', 'normal'),
227 ('stderr', 'stderr'), ('\n', 'normal'))
Steven M. Gava9dd16b32001-11-03 14:54:25 +0000228 for txTa in textAndTags:
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400229 text.insert(END, txTa[0], txTa[1])
Kurt B. Kaisere0712772007-08-23 05:25:55 +0000230 for element in self.themeElements:
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400231 def tem(event, elem=element):
232 event.widget.winfo_toplevel().highlightTarget.set(elem)
233 text.tag_bind(
234 self.themeElements[element][0], '<ButtonPress-1>', tem)
Steven M. Gavae16d94b2001-11-03 05:07:28 +0000235 text.config(state=DISABLED)
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400236 self.frameColourSet = Frame(frameCustom, relief=SOLID, borderwidth=1)
237 frameFgBg = Frame(frameCustom)
238 buttonSetColour = Button(
239 self.frameColourSet, text='Choose Colour for :',
240 command=self.GetColour, highlightthickness=0)
241 self.optMenuHighlightTarget = DynOptionMenu(
242 self.frameColourSet, self.highlightTarget, None,
243 highlightthickness=0) #, command=self.SetHighlightTargetBinding
244 self.radioFg = Radiobutton(
245 frameFgBg, variable=self.fgHilite, value=1,
246 text='Foreground', command=self.SetColourSampleBinding)
247 self.radioBg=Radiobutton(
248 frameFgBg, variable=self.fgHilite, value=0,
249 text='Background', command=self.SetColourSampleBinding)
Steven M. Gava7c017862001-10-29 11:19:46 +0000250 self.fgHilite.set(1)
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400251 buttonSaveCustomTheme = Button(
252 frameCustom, text='Save as New Custom Theme',
253 command=self.SaveAsNewTheme)
Steven M. Gava952d0a52001-08-03 04:43:44 +0000254 #frameTheme
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400255 labelTypeTitle = Label(frameTheme, text='Select : ')
256 self.radioThemeBuiltin = Radiobutton(
257 frameTheme, variable=self.themeIsBuiltin, value=1,
258 command=self.SetThemeType, text='a Built-in Theme')
259 self.radioThemeCustom = Radiobutton(
260 frameTheme, variable=self.themeIsBuiltin, value=0,
261 command=self.SetThemeType, text='a Custom Theme')
262 self.optMenuThemeBuiltin = DynOptionMenu(
263 frameTheme, self.builtinTheme, None, command=None)
264 self.optMenuThemeCustom=DynOptionMenu(
265 frameTheme, self.customTheme, None, command=None)
266 self.buttonDeleteCustomTheme=Button(
267 frameTheme, text='Delete Custom Theme',
Steven M. Gava49745752002-02-18 01:43:11 +0000268 command=self.DeleteCustomTheme)
Terry Jan Reedy22405332014-07-30 19:24:32 -0400269
Steven M. Gava952d0a52001-08-03 04:43:44 +0000270 ##widget packing
271 #body
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400272 frameCustom.pack(side=LEFT, padx=5, pady=5, expand=TRUE, fill=BOTH)
273 frameTheme.pack(side=LEFT, padx=5, pady=5, fill=Y)
Steven M. Gava952d0a52001-08-03 04:43:44 +0000274 #frameCustom
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400275 self.frameColourSet.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=X)
276 frameFgBg.pack(side=TOP, padx=5, pady=0)
277 self.textHighlightSample.pack(
278 side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH)
279 buttonSetColour.pack(side=TOP, expand=TRUE, fill=X, padx=8, pady=4)
280 self.optMenuHighlightTarget.pack(
281 side=TOP, expand=TRUE, fill=X, padx=8, pady=3)
282 self.radioFg.pack(side=LEFT, anchor=E)
283 self.radioBg.pack(side=RIGHT, anchor=W)
284 buttonSaveCustomTheme.pack(side=BOTTOM, fill=X, padx=5, pady=5)
Steven M. Gava952d0a52001-08-03 04:43:44 +0000285 #frameTheme
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400286 labelTypeTitle.pack(side=TOP, anchor=W, padx=5, pady=5)
287 self.radioThemeBuiltin.pack(side=TOP, anchor=W, padx=5)
288 self.radioThemeCustom.pack(side=TOP, anchor=W, padx=5, pady=2)
289 self.optMenuThemeBuiltin.pack(side=TOP, fill=X, padx=5, pady=5)
290 self.optMenuThemeCustom.pack(side=TOP, fill=X, anchor=W, padx=5, pady=5)
291 self.buttonDeleteCustomTheme.pack(side=TOP, fill=X, padx=5, 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 Reedyd87d1682015-08-01 18:57:33 -0400487 def VarChanged_font(self, *params):
488 '''When one font attribute changes, save them all, as they are
489 not independent from each other. In particular, when we are
490 overriding the default font, we need to write out everything.
491 '''
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400492 value = self.fontName.get()
493 self.AddChangedItem('main', 'EditorWindow', 'font', value)
Terry Jan Reedyd87d1682015-08-01 18:57:33 -0400494 value = self.fontSize.get()
495 self.AddChangedItem('main', 'EditorWindow', 'font-size', value)
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400496 value = self.fontBold.get()
497 self.AddChangedItem('main', 'EditorWindow', 'font-bold', value)
Steven M. Gavac112cd82002-01-22 05:56:40 +0000498
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400499 def VarChanged_spaceNum(self, *params):
500 value = self.spaceNum.get()
501 self.AddChangedItem('main', 'Indent', 'num-spaces', value)
Steven M. Gavac112cd82002-01-22 05:56:40 +0000502
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400503 def VarChanged_colour(self, *params):
Steven M. Gava052937f2002-02-11 02:20:53 +0000504 self.OnNewColourSet()
505
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400506 def VarChanged_builtinTheme(self, *params):
507 value = self.builtinTheme.get()
508 self.AddChangedItem('main', 'Theme', 'name', value)
Steven M. Gava052937f2002-02-11 02:20:53 +0000509 self.PaintThemeSample()
510
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400511 def VarChanged_customTheme(self, *params):
512 value = self.customTheme.get()
Steven M. Gava49745752002-02-18 01:43:11 +0000513 if value != '- no custom themes -':
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400514 self.AddChangedItem('main', 'Theme', 'name', value)
Steven M. Gava49745752002-02-18 01:43:11 +0000515 self.PaintThemeSample()
Steven M. Gava052937f2002-02-11 02:20:53 +0000516
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400517 def VarChanged_themeIsBuiltin(self, *params):
518 value = self.themeIsBuiltin.get()
519 self.AddChangedItem('main', 'Theme', 'default', value)
Steven M. Gavaf31eec02002-03-05 00:25:58 +0000520 if value:
521 self.VarChanged_builtinTheme()
522 else:
523 self.VarChanged_customTheme()
Steven M. Gava052937f2002-02-11 02:20:53 +0000524
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400525 def VarChanged_highlightTarget(self, *params):
Steven M. Gava052937f2002-02-11 02:20:53 +0000526 self.SetHighlightTarget()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000527
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400528 def VarChanged_keyBinding(self, *params):
529 value = self.keyBinding.get()
530 keySet = self.customKeys.get()
531 event = self.listBindings.get(ANCHOR).split()[0]
Steven M. Gavaa498af22002-02-01 01:33:36 +0000532 if idleConf.IsCoreBinding(event):
533 #this is a core keybinding
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400534 self.AddChangedItem('keys', keySet, event, value)
Steven M. Gavaa498af22002-02-01 01:33:36 +0000535 else: #this is an extension key binding
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400536 extName = idleConf.GetExtnNameForEvent(event)
537 extKeybindSection = extName + '_cfgBindings'
538 self.AddChangedItem('extensions', extKeybindSection, event, value)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000539
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400540 def VarChanged_builtinKeys(self, *params):
541 value = self.builtinKeys.get()
542 self.AddChangedItem('main', 'Keys', 'name', value)
Steven M. Gava052937f2002-02-11 02:20:53 +0000543 self.LoadKeysList(value)
544
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400545 def VarChanged_customKeys(self, *params):
546 value = self.customKeys.get()
Steven M. Gava49745752002-02-18 01:43:11 +0000547 if value != '- no custom keys -':
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400548 self.AddChangedItem('main', 'Keys', 'name', value)
Steven M. Gava49745752002-02-18 01:43:11 +0000549 self.LoadKeysList(value)
Steven M. Gava052937f2002-02-11 02:20:53 +0000550
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400551 def VarChanged_keysAreBuiltin(self, *params):
552 value = self.keysAreBuiltin.get()
553 self.AddChangedItem('main', 'Keys', 'default', value)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000554 if value:
Steven M. Gavaf31eec02002-03-05 00:25:58 +0000555 self.VarChanged_builtinKeys()
Steven M. Gava052937f2002-02-11 02:20:53 +0000556 else:
Steven M. Gavaf31eec02002-03-05 00:25:58 +0000557 self.VarChanged_customKeys()
Steven M. Gava052937f2002-02-11 02:20:53 +0000558
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400559 def VarChanged_winWidth(self, *params):
560 value = self.winWidth.get()
561 self.AddChangedItem('main', 'EditorWindow', 'width', value)
Steven M. Gavac112cd82002-01-22 05:56:40 +0000562
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400563 def VarChanged_winHeight(self, *params):
564 value = self.winHeight.get()
565 self.AddChangedItem('main', 'EditorWindow', 'height', value)
Steven M. Gavac112cd82002-01-22 05:56:40 +0000566
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400567 def VarChanged_startupEdit(self, *params):
568 value = self.startupEdit.get()
569 self.AddChangedItem('main', 'General', 'editor-on-startup', value)
Steven M. Gavac112cd82002-01-22 05:56:40 +0000570
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400571 def VarChanged_autoSave(self, *params):
572 value = self.autoSave.get()
573 self.AddChangedItem('main', 'General', 'autosave', value)
Kurt B. Kaiser6c638b62003-05-26 06:23:10 +0000574
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400575 def VarChanged_encoding(self, *params):
576 value = self.encoding.get()
577 self.AddChangedItem('main', 'EditorWindow', 'encoding', value)
Kurt B. Kaisera053f332003-05-10 00:49:56 +0000578
Steven M. Gavaa498af22002-02-01 01:33:36 +0000579 def ResetChangedItems(self):
Steven M. Gavab77d3432002-03-02 07:16:21 +0000580 #When any config item is changed in this dialog, an entry
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000581 #should be made in the relevant section (config type) of this
582 #dictionary. The key should be the config file section name and the
Steven M. Gavaa498af22002-02-01 01:33:36 +0000583 #value a dictionary, whose key:value pairs are item=value pairs for
584 #that config file section.
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400585 self.changedItems = {'main':{}, 'highlight':{}, 'keys':{},
586 'extensions':{}}
Steven M. Gavaa498af22002-02-01 01:33:36 +0000587
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400588 def AddChangedItem(self, typ, section, item, value):
589 value = str(value) #make sure we use a string
590 if section not in self.changedItems[typ]:
591 self.changedItems[typ][section] = {}
592 self.changedItems[typ][section][item] = value
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000593
Steven M. Gava5f28e8f2002-01-21 06:38:21 +0000594 def GetDefaultItems(self):
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400595 dItems={'main':{}, 'highlight':{}, 'keys':{}, 'extensions':{}}
Kurt B. Kaisere0712772007-08-23 05:25:55 +0000596 for configType in dItems:
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400597 sections = idleConf.GetSectionList('default', configType)
Steven M. Gava5f28e8f2002-01-21 06:38:21 +0000598 for section in sections:
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400599 dItems[configType][section] = {}
600 options = idleConf.defaultCfg[configType].GetOptionList(section)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000601 for option in options:
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400602 dItems[configType][section][option] = (
603 idleConf.defaultCfg[configType].Get(section, option))
Steven M. Gava5f28e8f2002-01-21 06:38:21 +0000604 return dItems
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000605
Steven M. Gava5f28e8f2002-01-21 06:38:21 +0000606 def SetThemeType(self):
607 if self.themeIsBuiltin.get():
608 self.optMenuThemeBuiltin.config(state=NORMAL)
609 self.optMenuThemeCustom.config(state=DISABLED)
610 self.buttonDeleteCustomTheme.config(state=DISABLED)
611 else:
612 self.optMenuThemeBuiltin.config(state=DISABLED)
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000613 self.radioThemeCustom.config(state=NORMAL)
Steven M. Gava5f28e8f2002-01-21 06:38:21 +0000614 self.optMenuThemeCustom.config(state=NORMAL)
615 self.buttonDeleteCustomTheme.config(state=NORMAL)
616
617 def SetKeysType(self):
Steven M. Gava052937f2002-02-11 02:20:53 +0000618 if self.keysAreBuiltin.get():
Steven M. Gava5f28e8f2002-01-21 06:38:21 +0000619 self.optMenuKeysBuiltin.config(state=NORMAL)
620 self.optMenuKeysCustom.config(state=DISABLED)
621 self.buttonDeleteCustomKeys.config(state=DISABLED)
622 else:
623 self.optMenuKeysBuiltin.config(state=DISABLED)
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000624 self.radioKeysCustom.config(state=NORMAL)
Steven M. Gava5f28e8f2002-01-21 06:38:21 +0000625 self.optMenuKeysCustom.config(state=NORMAL)
626 self.buttonDeleteCustomKeys.config(state=NORMAL)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000627
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000628 def GetNewKeys(self):
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400629 listIndex = self.listBindings.index(ANCHOR)
630 binding = self.listBindings.get(listIndex)
631 bindName = binding.split()[0] #first part, up to first space
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000632 if self.keysAreBuiltin.get():
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400633 currentKeySetName = self.builtinKeys.get()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000634 else:
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400635 currentKeySetName = self.customKeys.get()
636 currentBindings = idleConf.GetCurrentKeySet()
Kurt B. Kaisere0712772007-08-23 05:25:55 +0000637 if currentKeySetName in self.changedItems['keys']: #unsaved changes
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400638 keySetChanges = self.changedItems['keys'][currentKeySetName]
Kurt B. Kaisere0712772007-08-23 05:25:55 +0000639 for event in keySetChanges:
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400640 currentBindings[event] = keySetChanges[event].split()
Kurt B. Kaisere0712772007-08-23 05:25:55 +0000641 currentKeySequences = list(currentBindings.values())
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400642 newKeys = GetKeysDialog(self, 'Get New Keys', bindName,
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000643 currentKeySequences).result
644 if newKeys: #new keys were specified
Steven M. Gava052937f2002-02-11 02:20:53 +0000645 if self.keysAreBuiltin.get(): #current key set is a built-in
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400646 message = ('Your changes will be saved as a new Custom Key Set.'
647 ' Enter a name for your new Custom Key Set below.')
648 newKeySet = self.GetNewKeysName(message)
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000649 if not newKeySet: #user cancelled custom key set creation
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000650 self.listBindings.select_set(listIndex)
651 self.listBindings.select_anchor(listIndex)
652 return
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000653 else: #create new custom key set based on previously active key set
654 self.CreateNewKeySet(newKeySet)
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000655 self.listBindings.delete(listIndex)
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400656 self.listBindings.insert(listIndex, bindName+' - '+newKeys)
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000657 self.listBindings.select_set(listIndex)
658 self.listBindings.select_anchor(listIndex)
Steven M. Gava052937f2002-02-11 02:20:53 +0000659 self.keyBinding.set(newKeys)
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000660 else:
661 self.listBindings.select_set(listIndex)
662 self.listBindings.select_anchor(listIndex)
663
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400664 def GetNewKeysName(self, message):
665 usedNames = (idleConf.GetSectionList('user', 'keys') +
666 idleConf.GetSectionList('default', 'keys'))
667 newKeySet = GetCfgSectionNameDialog(
668 self, 'New Custom Key Set', message, usedNames).result
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000669 return newKeySet
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000670
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000671 def SaveAsNewKeySet(self):
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400672 newKeysName = self.GetNewKeysName('New Key Set Name:')
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000673 if newKeysName:
674 self.CreateNewKeySet(newKeysName)
675
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400676 def KeyBindingSelected(self, event):
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000677 self.buttonNewKeys.config(state=NORMAL)
678
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400679 def CreateNewKeySet(self, newKeySetName):
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000680 #creates new custom key set based on the previously active key set,
681 #and makes the new key set active
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000682 if self.keysAreBuiltin.get():
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400683 prevKeySetName = self.builtinKeys.get()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000684 else:
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400685 prevKeySetName = self.customKeys.get()
686 prevKeys = idleConf.GetCoreKeys(prevKeySetName)
687 newKeys = {}
Kurt B. Kaisere0712772007-08-23 05:25:55 +0000688 for event in prevKeys: #add key set to changed items
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400689 eventName = event[2:-2] #trim off the angle brackets
690 binding = ' '.join(prevKeys[event])
691 newKeys[eventName] = binding
Steven M. Gava052937f2002-02-11 02:20:53 +0000692 #handle any unsaved changes to prev key set
Kurt B. Kaisere0712772007-08-23 05:25:55 +0000693 if prevKeySetName in self.changedItems['keys']:
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400694 keySetChanges = self.changedItems['keys'][prevKeySetName]
Kurt B. Kaisere0712772007-08-23 05:25:55 +0000695 for event in keySetChanges:
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400696 newKeys[event] = keySetChanges[event]
Steven M. Gava052937f2002-02-11 02:20:53 +0000697 #save the new theme
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400698 self.SaveNewKeySet(newKeySetName, newKeys)
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000699 #change gui over to the new key set
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400700 customKeyList = idleConf.GetSectionList('user', 'keys')
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000701 customKeyList.sort()
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400702 self.optMenuKeysCustom.SetMenu(customKeyList, newKeySetName)
Steven M. Gava052937f2002-02-11 02:20:53 +0000703 self.keysAreBuiltin.set(0)
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000704 self.SetKeysType()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000705
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400706 def LoadKeysList(self, keySetName):
707 reselect = 0
708 newKeySet = 0
Steven M. Gava052937f2002-02-11 02:20:53 +0000709 if self.listBindings.curselection():
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400710 reselect = 1
711 listIndex = self.listBindings.index(ANCHOR)
712 keySet = idleConf.GetKeySet(keySetName)
Kurt B. Kaisere0712772007-08-23 05:25:55 +0000713 bindNames = list(keySet.keys())
Steven M. Gava052937f2002-02-11 02:20:53 +0000714 bindNames.sort()
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400715 self.listBindings.delete(0, END)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000716 for bindName in bindNames:
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400717 key = ' '.join(keySet[bindName]) #make key(s) into a string
718 bindName = bindName[2:-2] #trim off the angle brackets
Kurt B. Kaisere0712772007-08-23 05:25:55 +0000719 if keySetName in self.changedItems['keys']:
Steven M. Gava052937f2002-02-11 02:20:53 +0000720 #handle any unsaved changes to this key set
Kurt B. Kaisere0712772007-08-23 05:25:55 +0000721 if bindName in self.changedItems['keys'][keySetName]:
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400722 key = self.changedItems['keys'][keySetName][bindName]
Steven M. Gava052937f2002-02-11 02:20:53 +0000723 self.listBindings.insert(END, bindName+' - '+key)
Steven M. Gava052937f2002-02-11 02:20:53 +0000724 if reselect:
725 self.listBindings.see(listIndex)
726 self.listBindings.select_set(listIndex)
727 self.listBindings.select_anchor(listIndex)
728
Steven M. Gava49745752002-02-18 01:43:11 +0000729 def DeleteCustomKeys(self):
730 keySetName=self.customKeys.get()
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400731 delmsg = 'Are you sure you wish to delete the key set %r ?'
732 if not tkMessageBox.askyesno(
733 'Delete Key Set', delmsg % keySetName, parent=self):
Steven M. Gava49745752002-02-18 01:43:11 +0000734 return
735 #remove key set from config
736 idleConf.userCfg['keys'].remove_section(keySetName)
Guido van Rossum811c4e02006-08-22 15:45:46 +0000737 if keySetName in self.changedItems['keys']:
Steven M. Gava49745752002-02-18 01:43:11 +0000738 del(self.changedItems['keys'][keySetName])
739 #write changes
740 idleConf.userCfg['keys'].Save()
741 #reload user key set list
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400742 itemList = idleConf.GetSectionList('user', 'keys')
Steven M. Gava49745752002-02-18 01:43:11 +0000743 itemList.sort()
744 if not itemList:
745 self.radioKeysCustom.config(state=DISABLED)
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400746 self.optMenuKeysCustom.SetMenu(itemList, '- no custom keys -')
Steven M. Gava49745752002-02-18 01:43:11 +0000747 else:
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400748 self.optMenuKeysCustom.SetMenu(itemList, itemList[0])
Steven M. Gava49745752002-02-18 01:43:11 +0000749 #revert to default key set
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400750 self.keysAreBuiltin.set(idleConf.defaultCfg['main'].Get('Keys', 'default'))
751 self.builtinKeys.set(idleConf.defaultCfg['main'].Get('Keys', 'name'))
Steven M. Gava49745752002-02-18 01:43:11 +0000752 #user can't back out of these changes, they must be applied now
753 self.Apply()
754 self.SetKeysType()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000755
Steven M. Gava49745752002-02-18 01:43:11 +0000756 def DeleteCustomTheme(self):
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400757 themeName = self.customTheme.get()
758 delmsg = 'Are you sure you wish to delete the theme %r ?'
759 if not tkMessageBox.askyesno(
760 'Delete Theme', delmsg % themeName, parent=self):
Steven M. Gava49745752002-02-18 01:43:11 +0000761 return
762 #remove theme from config
763 idleConf.userCfg['highlight'].remove_section(themeName)
Guido van Rossum811c4e02006-08-22 15:45:46 +0000764 if themeName in self.changedItems['highlight']:
Steven M. Gava49745752002-02-18 01:43:11 +0000765 del(self.changedItems['highlight'][themeName])
766 #write changes
767 idleConf.userCfg['highlight'].Save()
768 #reload user theme list
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400769 itemList = idleConf.GetSectionList('user', 'highlight')
Steven M. Gava49745752002-02-18 01:43:11 +0000770 itemList.sort()
771 if not itemList:
772 self.radioThemeCustom.config(state=DISABLED)
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400773 self.optMenuThemeCustom.SetMenu(itemList, '- no custom themes -')
Steven M. Gava49745752002-02-18 01:43:11 +0000774 else:
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400775 self.optMenuThemeCustom.SetMenu(itemList, itemList[0])
Steven M. Gava49745752002-02-18 01:43:11 +0000776 #revert to default theme
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400777 self.themeIsBuiltin.set(idleConf.defaultCfg['main'].Get('Theme', 'default'))
778 self.builtinTheme.set(idleConf.defaultCfg['main'].Get('Theme', 'name'))
Steven M. Gava49745752002-02-18 01:43:11 +0000779 #user can't back out of these changes, they must be applied now
780 self.Apply()
781 self.SetThemeType()
782
Steven M. Gava5f28e8f2002-01-21 06:38:21 +0000783 def GetColour(self):
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400784 target = self.highlightTarget.get()
785 prevColour = self.frameColourSet.cget('bg')
786 rgbTuplet, colourString = tkColorChooser.askcolor(
787 parent=self, title='Pick new colour for : '+target,
788 initialcolor=prevColour)
789 if colourString and (colourString != prevColour):
Steven M. Gava052937f2002-02-11 02:20:53 +0000790 #user didn't cancel, and they chose a new colour
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400791 if self.themeIsBuiltin.get(): #current theme is a built-in
792 message = ('Your changes will be saved as a new Custom Theme. '
793 'Enter a name for your new Custom Theme below.')
794 newTheme = self.GetNewThemeName(message)
795 if not newTheme: #user cancelled custom theme creation
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000796 return
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400797 else: #create new custom theme based on previously active theme
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000798 self.CreateNewTheme(newTheme)
Steven M. Gava052937f2002-02-11 02:20:53 +0000799 self.colour.set(colourString)
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400800 else: #current theme is user defined
Steven M. Gava052937f2002-02-11 02:20:53 +0000801 self.colour.set(colourString)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000802
Steven M. Gava052937f2002-02-11 02:20:53 +0000803 def OnNewColourSet(self):
804 newColour=self.colour.get()
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400805 self.frameColourSet.config(bg=newColour) #set sample
806 plane ='foreground' if self.fgHilite.get() else 'background'
807 sampleElement = self.themeElements[self.highlightTarget.get()][0]
Raymond Hettinger931237e2003-07-09 18:48:24 +0000808 self.textHighlightSample.tag_config(sampleElement, **{plane:newColour})
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400809 theme = self.customTheme.get()
810 themeElement = sampleElement + '-' + plane
811 self.AddChangedItem('highlight', theme, themeElement, newColour)
Steven M. Gava052937f2002-02-11 02:20:53 +0000812
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400813 def GetNewThemeName(self, message):
814 usedNames = (idleConf.GetSectionList('user', 'highlight') +
815 idleConf.GetSectionList('default', 'highlight'))
816 newTheme = GetCfgSectionNameDialog(
817 self, 'New Custom Theme', message, usedNames).result
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000818 return newTheme
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000819
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000820 def SaveAsNewTheme(self):
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400821 newThemeName = self.GetNewThemeName('New Theme Name:')
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000822 if newThemeName:
823 self.CreateNewTheme(newThemeName)
824
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400825 def CreateNewTheme(self, newThemeName):
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000826 #creates new custom theme based on the previously active theme,
827 #and makes the new theme active
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000828 if self.themeIsBuiltin.get():
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400829 themeType = 'default'
830 themeName = self.builtinTheme.get()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000831 else:
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400832 themeType = 'user'
833 themeName = self.customTheme.get()
834 newTheme = idleConf.GetThemeDict(themeType, themeName)
Steven M. Gava052937f2002-02-11 02:20:53 +0000835 #apply any of the old theme's unsaved changes to the new theme
Kurt B. Kaisere0712772007-08-23 05:25:55 +0000836 if themeName in self.changedItems['highlight']:
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400837 themeChanges = self.changedItems['highlight'][themeName]
Kurt B. Kaisere0712772007-08-23 05:25:55 +0000838 for element in themeChanges:
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400839 newTheme[element] = themeChanges[element]
Steven M. Gava052937f2002-02-11 02:20:53 +0000840 #save the new theme
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400841 self.SaveNewTheme(newThemeName, newTheme)
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000842 #change gui over to the new theme
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400843 customThemeList = idleConf.GetSectionList('user', 'highlight')
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000844 customThemeList.sort()
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400845 self.optMenuThemeCustom.SetMenu(customThemeList, newThemeName)
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000846 self.themeIsBuiltin.set(0)
847 self.SetThemeType()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000848
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400849 def OnListFontButtonRelease(self, event):
Kurt B. Kaiser05391692003-05-26 20:35:53 +0000850 font = self.listFontName.get(ANCHOR)
851 self.fontName.set(font.lower())
Steven M. Gava5f28e8f2002-01-21 06:38:21 +0000852 self.SetFontSample()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000853
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400854 def SetFontSample(self, event=None):
855 fontName = self.fontName.get()
856 fontWeight = tkFont.BOLD if self.fontBold.get() else tkFont.NORMAL
Ned Deilyfdf0f272012-10-22 15:14:31 -0700857 newFont = (fontName, self.fontSize.get(), fontWeight)
858 self.labelFontSample.config(font=newFont)
859 self.textHighlightSample.configure(font=newFont)
Steven M. Gava5f28e8f2002-01-21 06:38:21 +0000860
Steven M. Gava5f28e8f2002-01-21 06:38:21 +0000861 def SetHighlightTarget(self):
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400862 if self.highlightTarget.get() == 'Cursor': #bg not possible
Steven M. Gava5f28e8f2002-01-21 06:38:21 +0000863 self.radioFg.config(state=DISABLED)
864 self.radioBg.config(state=DISABLED)
865 self.fgHilite.set(1)
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400866 else: #both fg and bg can be set
Steven M. Gava5f28e8f2002-01-21 06:38:21 +0000867 self.radioFg.config(state=NORMAL)
868 self.radioBg.config(state=NORMAL)
869 self.fgHilite.set(1)
870 self.SetColourSample()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000871
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400872 def SetColourSampleBinding(self, *args):
Steven M. Gava5f28e8f2002-01-21 06:38:21 +0000873 self.SetColourSample()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000874
Steven M. Gava5f28e8f2002-01-21 06:38:21 +0000875 def SetColourSample(self):
876 #set the colour smaple area
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400877 tag = self.themeElements[self.highlightTarget.get()][0]
878 plane = 'foreground' if self.fgHilite.get() else 'background'
879 colour = self.textHighlightSample.tag_cget(tag, plane)
Steven M. Gava5f28e8f2002-01-21 06:38:21 +0000880 self.frameColourSet.config(bg=colour)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000881
Steven M. Gava9dd16b32001-11-03 14:54:25 +0000882 def PaintThemeSample(self):
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400883 if self.themeIsBuiltin.get(): #a default theme
884 theme = self.builtinTheme.get()
885 else: #a user theme
886 theme = self.customTheme.get()
Kurt B. Kaisere0712772007-08-23 05:25:55 +0000887 for elementTitle in self.themeElements:
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400888 element = self.themeElements[elementTitle][0]
889 colours = idleConf.GetHighlight(theme, element)
890 if element == 'cursor': #cursor sample needs special painting
891 colours['background'] = idleConf.GetHighlight(
892 theme, 'normal', fgBg='bg')
Steven M. Gava052937f2002-02-11 02:20:53 +0000893 #handle any unsaved changes to this theme
Kurt B. Kaisere0712772007-08-23 05:25:55 +0000894 if theme in self.changedItems['highlight']:
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400895 themeDict = self.changedItems['highlight'][theme]
896 if element + '-foreground' in themeDict:
897 colours['foreground'] = themeDict[element + '-foreground']
898 if element + '-background' in themeDict:
899 colours['background'] = themeDict[element + '-background']
Raymond Hettinger931237e2003-07-09 18:48:24 +0000900 self.textHighlightSample.tag_config(element, **colours)
Steven M. Gava052937f2002-02-11 02:20:53 +0000901 self.SetColourSample()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000902
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400903 def HelpSourceSelected(self, event):
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000904 self.SetHelpListButtonStates()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000905
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000906 def SetHelpListButtonStates(self):
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400907 if self.listHelp.size() < 1: #no entries in list
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000908 self.buttonHelpListEdit.config(state=DISABLED)
909 self.buttonHelpListRemove.config(state=DISABLED)
910 else: #there are some entries
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400911 if self.listHelp.curselection(): #there currently is a selection
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000912 self.buttonHelpListEdit.config(state=NORMAL)
913 self.buttonHelpListRemove.config(state=NORMAL)
914 else: #there currently is not a selection
915 self.buttonHelpListEdit.config(state=DISABLED)
916 self.buttonHelpListRemove.config(state=DISABLED)
917
918 def HelpListItemAdd(self):
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400919 helpSource = GetHelpSourceDialog(self, 'New Help Source').result
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000920 if helpSource:
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400921 self.userHelpList.append((helpSource[0], helpSource[1]))
922 self.listHelp.insert(END, helpSource[0])
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000923 self.UpdateUserHelpChangedItems()
924 self.SetHelpListButtonStates()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000925
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000926 def HelpListItemEdit(self):
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400927 itemIndex = self.listHelp.index(ANCHOR)
928 helpSource = self.userHelpList[itemIndex]
929 newHelpSource = GetHelpSourceDialog(
930 self, 'Edit Help Source', menuItem=helpSource[0],
931 filePath=helpSource[1]).result
932 if (not newHelpSource) or (newHelpSource == helpSource):
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000933 return #no changes
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400934 self.userHelpList[itemIndex] = newHelpSource
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000935 self.listHelp.delete(itemIndex)
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400936 self.listHelp.insert(itemIndex, newHelpSource[0])
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000937 self.UpdateUserHelpChangedItems()
938 self.SetHelpListButtonStates()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000939
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000940 def HelpListItemRemove(self):
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400941 itemIndex = self.listHelp.index(ANCHOR)
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000942 del(self.userHelpList[itemIndex])
943 self.listHelp.delete(itemIndex)
944 self.UpdateUserHelpChangedItems()
945 self.SetHelpListButtonStates()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000946
Steven M. Gava085eb1b2002-02-05 04:52:32 +0000947 def UpdateUserHelpChangedItems(self):
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +0000948 "Clear and rebuild the HelpFiles section in self.changedItems"
949 self.changedItems['main']['HelpFiles'] = {}
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400950 for num in range(1, len(self.userHelpList) + 1):
951 self.AddChangedItem(
952 'main', 'HelpFiles', str(num),
Neal Norwitz9d72bb42007-04-17 08:48:32 +0000953 ';'.join(self.userHelpList[num-1][:2]))
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000954
Steven M. Gava429a86af2001-10-23 10:42:12 +0000955 def LoadFontCfg(self):
956 ##base editor font selection list
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400957 fonts = list(tkFont.families(self))
Steven M. Gavac11ccf32001-09-24 09:43:17 +0000958 fonts.sort()
959 for font in fonts:
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400960 self.listFontName.insert(END, font)
Terry Jan Reedyd87d1682015-08-01 18:57:33 -0400961 configuredFont = idleConf.GetFont(self, 'main', 'EditorWindow')
962 fontName = configuredFont[0].lower()
963 fontSize = configuredFont[1]
964 fontBold = configuredFont[2]=='bold'
965 self.fontName.set(fontName)
Kurt B. Kaiser05391692003-05-26 20:35:53 +0000966 lc_fonts = [s.lower() for s in fonts]
Terry Jan Reedyd87d1682015-08-01 18:57:33 -0400967 try:
968 currentFontIndex = lc_fonts.index(fontName)
Steven M. Gava429a86af2001-10-23 10:42:12 +0000969 self.listFontName.see(currentFontIndex)
970 self.listFontName.select_set(currentFontIndex)
Steven M. Gavac112cd82002-01-22 05:56:40 +0000971 self.listFontName.select_anchor(currentFontIndex)
Terry Jan Reedyd87d1682015-08-01 18:57:33 -0400972 except ValueError:
973 pass
Steven M. Gava429a86af2001-10-23 10:42:12 +0000974 ##font size dropdown
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400975 self.optMenuFontSize.SetMenu(('7', '8', '9', '10', '11', '12', '13',
976 '14', '16', '18', '20', '22'), fontSize )
Steven M. Gavac112cd82002-01-22 05:56:40 +0000977 ##fontWeight
Terry Jan Reedyd87d1682015-08-01 18:57:33 -0400978 self.fontBold.set(fontBold)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000979 ##font sample
Steven M. Gava429a86af2001-10-23 10:42:12 +0000980 self.SetFontSample()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000981
Steven M. Gava429a86af2001-10-23 10:42:12 +0000982 def LoadTabCfg(self):
Steven M. Gava429a86af2001-10-23 10:42:12 +0000983 ##indent sizes
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400984 spaceNum = idleConf.GetOption(
985 'main', 'Indent', 'num-spaces', default=4, type='int')
Steven M. Gava429a86af2001-10-23 10:42:12 +0000986 self.spaceNum.set(spaceNum)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000987
Steven M. Gava9dd16b32001-11-03 14:54:25 +0000988 def LoadThemeCfg(self):
Steven M. Gava41a85322001-10-29 08:05:34 +0000989 ##current theme type radiobutton
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400990 self.themeIsBuiltin.set(idleConf.GetOption(
991 'main', 'Theme', 'default', type='bool', default=1))
Steven M. Gava41a85322001-10-29 08:05:34 +0000992 ##currently set theme
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400993 currentOption = idleConf.CurrentTheme()
Steven M. Gava7c017862001-10-29 11:19:46 +0000994 ##load available theme option menus
Steven M. Gavad0342cd2001-11-04 11:53:10 +0000995 if self.themeIsBuiltin.get(): #default theme selected
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400996 itemList = idleConf.GetSectionList('default', 'highlight')
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +0000997 itemList.sort()
Terry Jan Reedy4036d872014-08-03 23:02:58 -0400998 self.optMenuThemeBuiltin.SetMenu(itemList, currentOption)
999 itemList = idleConf.GetSectionList('user', 'highlight')
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +00001000 itemList.sort()
Steven M. Gava41a85322001-10-29 08:05:34 +00001001 if not itemList:
1002 self.radioThemeCustom.config(state=DISABLED)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001003 self.customTheme.set('- no custom themes -')
Steven M. Gava41a85322001-10-29 08:05:34 +00001004 else:
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001005 self.optMenuThemeCustom.SetMenu(itemList, itemList[0])
Steven M. Gava9dd16b32001-11-03 14:54:25 +00001006 else: #user theme selected
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001007 itemList = idleConf.GetSectionList('user', 'highlight')
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +00001008 itemList.sort()
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001009 self.optMenuThemeCustom.SetMenu(itemList, currentOption)
1010 itemList = idleConf.GetSectionList('default', 'highlight')
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +00001011 itemList.sort()
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001012 self.optMenuThemeBuiltin.SetMenu(itemList, itemList[0])
Steven M. Gava7c017862001-10-29 11:19:46 +00001013 self.SetThemeType()
1014 ##load theme element option menu
Kurt B. Kaisere0712772007-08-23 05:25:55 +00001015 themeNames = list(self.themeElements.keys())
Kurt B. Kaiser4718bf82008-02-12 21:34:12 +00001016 themeNames.sort(key=lambda x: self.themeElements[x][1])
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001017 self.optMenuHighlightTarget.SetMenu(themeNames, themeNames[0])
Steven M. Gava9dd16b32001-11-03 14:54:25 +00001018 self.PaintThemeSample()
Steven M. Gava0cae01c2002-01-04 07:53:06 +00001019 self.SetHighlightTarget()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001020
Steven M. Gava9dd16b32001-11-03 14:54:25 +00001021 def LoadKeyCfg(self):
Steven M. Gava41a85322001-10-29 08:05:34 +00001022 ##current keys type radiobutton
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001023 self.keysAreBuiltin.set(idleConf.GetOption(
1024 'main', 'Keys', 'default', type='bool', default=1))
Steven M. Gava41a85322001-10-29 08:05:34 +00001025 ##currently set keys
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001026 currentOption = idleConf.CurrentKeys()
Steven M. Gava7c017862001-10-29 11:19:46 +00001027 ##load available keyset option menus
Steven M. Gava052937f2002-02-11 02:20:53 +00001028 if self.keysAreBuiltin.get(): #default theme selected
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001029 itemList = idleConf.GetSectionList('default', 'keys')
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +00001030 itemList.sort()
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001031 self.optMenuKeysBuiltin.SetMenu(itemList, currentOption)
1032 itemList = idleConf.GetSectionList('user', 'keys')
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +00001033 itemList.sort()
Steven M. Gava41a85322001-10-29 08:05:34 +00001034 if not itemList:
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001035 self.radioKeysCustom.config(state=DISABLED)
1036 self.customKeys.set('- no custom keys -')
Steven M. Gava41a85322001-10-29 08:05:34 +00001037 else:
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001038 self.optMenuKeysCustom.SetMenu(itemList, itemList[0])
Steven M. Gavaa498af22002-02-01 01:33:36 +00001039 else: #user key set selected
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001040 itemList = idleConf.GetSectionList('user', 'keys')
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +00001041 itemList.sort()
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001042 self.optMenuKeysCustom.SetMenu(itemList, currentOption)
1043 itemList = idleConf.GetSectionList('default', 'keys')
Steven M. Gavaf9bb90e2002-01-24 06:02:50 +00001044 itemList.sort()
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001045 self.optMenuKeysBuiltin.SetMenu(itemList, itemList[0])
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001046 self.SetKeysType()
Steven M. Gavafacfc092002-01-19 00:29:54 +00001047 ##load keyset element list
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001048 keySetName = idleConf.CurrentKeys()
Steven M. Gava052937f2002-02-11 02:20:53 +00001049 self.LoadKeysList(keySetName)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001050
Steven M. Gavafacfc092002-01-19 00:29:54 +00001051 def LoadGeneralCfg(self):
Steven M. Gava5f28e8f2002-01-21 06:38:21 +00001052 #startup state
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001053 self.startupEdit.set(idleConf.GetOption(
1054 'main', 'General', 'editor-on-startup', default=1, type='bool'))
Kurt B. Kaiser6c638b62003-05-26 06:23:10 +00001055 #autosave state
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001056 self.autoSave.set(idleConf.GetOption(
1057 'main', 'General', 'autosave', default=0, type='bool'))
Steven M. Gavafacfc092002-01-19 00:29:54 +00001058 #initial window size
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001059 self.winWidth.set(idleConf.GetOption(
1060 'main', 'EditorWindow', 'width', type='int'))
1061 self.winHeight.set(idleConf.GetOption(
1062 'main', 'EditorWindow', 'height', type='int'))
Kurt B. Kaisera053f332003-05-10 00:49:56 +00001063 # default source encoding
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001064 self.encoding.set(idleConf.GetOption(
1065 'main', 'EditorWindow', 'encoding', default='none'))
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +00001066 # additional help sources
1067 self.userHelpList = idleConf.GetAllExtraHelpSourcesList()
Steven M. Gava085eb1b2002-02-05 04:52:32 +00001068 for helpItem in self.userHelpList:
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001069 self.listHelp.insert(END, helpItem[0])
Steven M. Gava085eb1b2002-02-05 04:52:32 +00001070 self.SetHelpListButtonStates()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001071
Steven M. Gava429a86af2001-10-23 10:42:12 +00001072 def LoadConfigs(self):
1073 """
1074 load configuration from default and user config files and populate
1075 the widgets on the config dialog pages.
1076 """
1077 ### fonts / tabs page
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001078 self.LoadFontCfg()
1079 self.LoadTabCfg()
Steven M. Gava429a86af2001-10-23 10:42:12 +00001080 ### highlighting page
Steven M. Gava9dd16b32001-11-03 14:54:25 +00001081 self.LoadThemeCfg()
Steven M. Gava429a86af2001-10-23 10:42:12 +00001082 ### keys page
Steven M. Gava9dd16b32001-11-03 14:54:25 +00001083 self.LoadKeyCfg()
Steven M. Gava429a86af2001-10-23 10:42:12 +00001084 ### general page
Steven M. Gavafacfc092002-01-19 00:29:54 +00001085 self.LoadGeneralCfg()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001086
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001087 def SaveNewKeySet(self, keySetName, keySet):
Steven M. Gava052937f2002-02-11 02:20:53 +00001088 """
1089 save a newly created core key set.
1090 keySetName - string, the name of the new key set
1091 keySet - dictionary containing the new key set
1092 """
1093 if not idleConf.userCfg['keys'].has_section(keySetName):
1094 idleConf.userCfg['keys'].add_section(keySetName)
Kurt B. Kaisere0712772007-08-23 05:25:55 +00001095 for event in keySet:
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001096 value = keySet[event]
1097 idleConf.userCfg['keys'].SetOption(keySetName, event, value)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001098
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001099 def SaveNewTheme(self, themeName, theme):
Steven M. Gava052937f2002-02-11 02:20:53 +00001100 """
1101 save a newly created theme.
1102 themeName - string, the name of the new theme
1103 theme - dictionary containing the new theme
1104 """
1105 if not idleConf.userCfg['highlight'].has_section(themeName):
1106 idleConf.userCfg['highlight'].add_section(themeName)
Kurt B. Kaisere0712772007-08-23 05:25:55 +00001107 for element in theme:
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001108 value = theme[element]
1109 idleConf.userCfg['highlight'].SetOption(themeName, element, value)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001110
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001111 def SetUserValue(self, configType, section, item, value):
1112 if idleConf.defaultCfg[configType].has_option(section, item):
1113 if idleConf.defaultCfg[configType].Get(section, item) == value:
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +00001114 #the setting equals a default setting, remove it from user cfg
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001115 return idleConf.userCfg[configType].RemoveOption(section, item)
Steven M. Gava2d7bb3f2002-01-29 08:35:29 +00001116 #if we got here set the option
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001117 return idleConf.userCfg[configType].SetOption(section, item, value)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001118
Steven M. Gava052937f2002-02-11 02:20:53 +00001119 def SaveAllChangedConfigs(self):
Kurt B. Kaisere7a161e2003-01-10 20:13:57 +00001120 "Save configuration changes to the user config file."
Steven M. Gava0c5bc8c2002-03-27 02:25:44 +00001121 idleConf.userCfg['main'].Save()
Kurt B. Kaisere0712772007-08-23 05:25:55 +00001122 for configType in self.changedItems:
Kurt B. Kaisere7a161e2003-01-10 20:13:57 +00001123 cfgTypeHasChanges = False
Kurt B. Kaisere0712772007-08-23 05:25:55 +00001124 for section in self.changedItems[configType]:
Kurt B. Kaisere7a161e2003-01-10 20:13:57 +00001125 if section == 'HelpFiles':
1126 #this section gets completely replaced
1127 idleConf.userCfg['main'].remove_section('HelpFiles')
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +00001128 cfgTypeHasChanges = True
Kurt B. Kaisere0712772007-08-23 05:25:55 +00001129 for item in self.changedItems[configType][section]:
Kurt B. Kaisere7a161e2003-01-10 20:13:57 +00001130 value = self.changedItems[configType][section][item]
Terry Jan Reedy4036d872014-08-03 23:02:58 -04001131 if self.SetUserValue(configType, section, item, value):
Kurt B. Kaisere7a161e2003-01-10 20:13:57 +00001132 cfgTypeHasChanges = True
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001133 if cfgTypeHasChanges:
1134 idleConf.userCfg[configType].Save()
Kurt B. Kaiser5acdf932004-11-16 21:28:36 +00001135 for configType in ['keys', 'highlight']:
1136 # save these even if unchanged!
1137 idleConf.userCfg[configType].Save()
Steven M. Gavaa498af22002-02-01 01:33:36 +00001138 self.ResetChangedItems() #clear the changed items dict
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001139
Kurt B. Kaiserb1754452005-11-18 22:05:48 +00001140 def DeactivateCurrentConfig(self):
1141 #Before a config is saved, some cleanup of current
1142 #config must be done - remove the previous keybindings
Kurt B. Kaisere0712772007-08-23 05:25:55 +00001143 winInstances = self.parent.instance_dict.keys()
Kurt B. Kaiserb1754452005-11-18 22:05:48 +00001144 for instance in winInstances:
1145 instance.RemoveKeybindings()
1146
Steven M. Gava49745752002-02-18 01:43:11 +00001147 def ActivateConfigChanges(self):
Kurt B. Kaiseracdef852005-01-31 03:34:26 +00001148 "Dynamically apply configuration changes"
Kurt B. Kaisere0712772007-08-23 05:25:55 +00001149 winInstances = self.parent.instance_dict.keys()
Steven M. Gavab77d3432002-03-02 07:16:21 +00001150 for instance in winInstances:
1151 instance.ResetColorizer()
Steven M. Gavab1585412002-03-12 00:21:56 +00001152 instance.ResetFont()
Kurt B. Kaiseracdef852005-01-31 03:34:26 +00001153 instance.set_notabs_indentwidth()
Kurt B. Kaiserb1754452005-11-18 22:05:48 +00001154 instance.ApplyKeybindings()
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +00001155 instance.reset_help_menu_entries()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001156
Steven M. Gava5f28e8f2002-01-21 06:38:21 +00001157 def Cancel(self):
1158 self.destroy()
1159
1160 def Ok(self):
1161 self.Apply()
1162 self.destroy()
1163
1164 def Apply(self):
Kurt B. Kaiserb1754452005-11-18 22:05:48 +00001165 self.DeactivateCurrentConfig()
Steven M. Gava052937f2002-02-11 02:20:53 +00001166 self.SaveAllChangedConfigs()
Steven M. Gava49745752002-02-18 01:43:11 +00001167 self.ActivateConfigChanges()
Steven M. Gava5f28e8f2002-01-21 06:38:21 +00001168
1169 def Help(self):
Steven M. Gavac11ccf32001-09-24 09:43:17 +00001170 pass
1171
Terry Jan Reedya9421fb2014-10-22 20:15:18 -04001172class VerticalScrolledFrame(Frame):
1173 """A pure Tkinter vertically scrollable frame.
1174
1175 * Use the 'interior' attribute to place widgets inside the scrollable frame
1176 * Construct and pack/place/grid normally
1177 * This frame only allows vertical scrolling
1178 """
1179 def __init__(self, parent, *args, **kw):
1180 Frame.__init__(self, parent, *args, **kw)
1181
1182 # create a canvas object and a vertical scrollbar for scrolling it
1183 vscrollbar = Scrollbar(self, orient=VERTICAL)
1184 vscrollbar.pack(fill=Y, side=RIGHT, expand=FALSE)
1185 canvas = Canvas(self, bd=0, highlightthickness=0,
1186 yscrollcommand=vscrollbar.set)
1187 canvas.pack(side=LEFT, fill=BOTH, expand=TRUE)
1188 vscrollbar.config(command=canvas.yview)
1189
1190 # reset the view
1191 canvas.xview_moveto(0)
1192 canvas.yview_moveto(0)
1193
1194 # create a frame inside the canvas which will be scrolled with it
1195 self.interior = interior = Frame(canvas)
1196 interior_id = canvas.create_window(0, 0, window=interior, anchor=NW)
1197
1198 # track changes to the canvas and frame width and sync them,
1199 # also updating the scrollbar
1200 def _configure_interior(event):
1201 # update the scrollbars to match the size of the inner frame
1202 size = (interior.winfo_reqwidth(), interior.winfo_reqheight())
1203 canvas.config(scrollregion="0 0 %s %s" % size)
Terry Jan Reedya9421fb2014-10-22 20:15:18 -04001204 interior.bind('<Configure>', _configure_interior)
1205
1206 def _configure_canvas(event):
1207 if interior.winfo_reqwidth() != canvas.winfo_width():
1208 # update the inner frame's width to fill the canvas
1209 canvas.itemconfigure(interior_id, width=canvas.winfo_width())
1210 canvas.bind('<Configure>', _configure_canvas)
1211
1212 return
1213
1214def is_int(s):
1215 "Return 's is blank or represents an int'"
1216 if not s:
1217 return True
1218 try:
1219 int(s)
1220 return True
1221 except ValueError:
1222 return False
1223
1224# TODO:
1225# * Revert to default(s)? Per option or per extension?
1226# * List options in their original order (possible??)
1227class ConfigExtensionsDialog(Toplevel):
1228 """A dialog for configuring IDLE extensions.
1229
1230 This dialog is generic - it works for any and all IDLE extensions.
1231
1232 IDLE extensions save their configuration options using idleConf.
1233 ConfigExtensionsDialog reads the current configuration using idleConf,
1234 supplies a GUI interface to change the configuration values, and saves the
1235 changes using idleConf.
1236
1237 Not all changes take effect immediately - some may require restarting IDLE.
1238 This depends on each extension's implementation.
1239
1240 All values are treated as text, and it is up to the user to supply
1241 reasonable values. The only exception to this are the 'enable*' options,
1242 which are boolean, and can be toggled with an True/False button.
1243 """
1244 def __init__(self, parent, title=None, _htest=False):
1245 Toplevel.__init__(self, parent)
1246 self.wm_withdraw()
1247
1248 self.configure(borderwidth=5)
1249 self.geometry(
1250 "+%d+%d" % (parent.winfo_rootx() + 20,
1251 parent.winfo_rooty() + (30 if not _htest else 150)))
1252 self.wm_title(title or 'IDLE Extensions Configuration')
1253
1254 self.defaultCfg = idleConf.defaultCfg['extensions']
1255 self.userCfg = idleConf.userCfg['extensions']
1256 self.is_int = self.register(is_int)
1257 self.load_extensions()
1258 self.create_widgets()
1259
1260 self.resizable(height=FALSE, width=FALSE) # don't allow resizing yet
1261 self.transient(parent)
1262 self.protocol("WM_DELETE_WINDOW", self.Cancel)
1263 self.tabbed_page_set.focus_set()
1264 # wait for window to be generated
1265 self.update()
1266 # set current width as the minimum width
1267 self.wm_minsize(self.winfo_width(), 1)
1268 # now allow resizing
1269 self.resizable(height=TRUE, width=TRUE)
1270
1271 self.wm_deiconify()
1272 if not _htest:
1273 self.grab_set()
1274 self.wait_window()
1275
1276 def load_extensions(self):
1277 "Fill self.extensions with data from the default and user configs."
1278 self.extensions = {}
1279 for ext_name in idleConf.GetExtensions(active_only=False):
1280 self.extensions[ext_name] = []
1281
1282 for ext_name in self.extensions:
1283 opt_list = sorted(self.defaultCfg.GetOptionList(ext_name))
1284
1285 # bring 'enable' options to the beginning of the list
1286 enables = [opt_name for opt_name in opt_list
1287 if opt_name.startswith('enable')]
1288 for opt_name in enables:
1289 opt_list.remove(opt_name)
1290 opt_list = enables + opt_list
1291
1292 for opt_name in opt_list:
1293 def_str = self.defaultCfg.Get(
1294 ext_name, opt_name, raw=True)
1295 try:
1296 def_obj = {'True':True, 'False':False}[def_str]
1297 opt_type = 'bool'
1298 except KeyError:
1299 try:
1300 def_obj = int(def_str)
1301 opt_type = 'int'
1302 except ValueError:
1303 def_obj = def_str
1304 opt_type = None
1305 try:
1306 value = self.userCfg.Get(
1307 ext_name, opt_name, type=opt_type, raw=True,
1308 default=def_obj)
1309 except ValueError: # Need this until .Get fixed
1310 value = def_obj # bad values overwritten by entry
1311 var = StringVar(self)
1312 var.set(str(value))
1313
1314 self.extensions[ext_name].append({'name': opt_name,
1315 'type': opt_type,
1316 'default': def_str,
1317 'value': value,
1318 'var': var,
1319 })
1320
1321 def create_widgets(self):
1322 """Create the dialog's widgets."""
Terry Jan Reedy41a27e02015-08-26 23:13:22 -04001323 self.extension_names = StringVar(self)
Terry Jan Reedya9421fb2014-10-22 20:15:18 -04001324 self.rowconfigure(0, weight=1)
Terry Jan Reedy41a27e02015-08-26 23:13:22 -04001325 self.columnconfigure(2, weight=1)
1326 self.extension_list = Listbox(self, listvariable=self.extension_names,
1327 selectmode='browse')
1328 self.extension_list.bind('<<ListboxSelect>>', self.extension_selected)
1329 scroll = Scrollbar(self, command=self.extension_list.yview)
1330 self.extension_list.yscrollcommand=scroll.set
1331 self.details_frame = LabelFrame(self, width=250, height=250)
1332 self.extension_list.grid(column=0, row=0, sticky='nws')
1333 scroll.grid(column=1, row=0, sticky='ns')
1334 self.details_frame.grid(column=2, row=0, sticky='nsew', padx=[10, 0])
1335 self.configure(padx=10, pady=10)
1336 self.config_frame = {}
1337 self.current_extension = None
Terry Jan Reedya9421fb2014-10-22 20:15:18 -04001338
Terry Jan Reedy41a27e02015-08-26 23:13:22 -04001339 self.outerframe = self # TEMPORARY
1340 self.tabbed_page_set = self.extension_list # TEMPORARY
Terry Jan Reedya9421fb2014-10-22 20:15:18 -04001341
Terry Jan Reedy41a27e02015-08-26 23:13:22 -04001342 # create the individual pages
1343 ext_names = ''
1344 for ext_name in sorted(self.extensions):
1345 self.create_extension_frame(ext_name)
1346 ext_names = ext_names + '{' + ext_name + '} '
1347 self.extension_names.set(ext_names)
1348 self.extension_list.selection_set(0)
1349 self.extension_selected(None)
1350 self.create_action_buttons().grid(row=1, columnspan=3)
1351
1352 def extension_selected(self, event):
1353 newsel = self.extension_list.curselection()
1354 if newsel:
1355 newsel = self.extension_list.get(newsel)
1356 if newsel is None or newsel != self.current_extension:
1357 if self.current_extension:
1358 self.details_frame.config(text='')
1359 self.config_frame[self.current_extension].grid_forget()
1360 self.current_extension = None
1361 if newsel:
1362 self.details_frame.config(text=newsel)
1363 self.config_frame[newsel].grid(column=0, row=0, sticky='nsew')
1364 self.current_extension = newsel
Terry Jan Reedya9421fb2014-10-22 20:15:18 -04001365
1366 create_action_buttons = ConfigDialog.create_action_buttons
1367
Terry Jan Reedy41a27e02015-08-26 23:13:22 -04001368 def create_extension_frame(self, ext_name):
1369 """Create a frame holding the widgets to configure one extension"""
1370 f = VerticalScrolledFrame(self.details_frame, height=250, width=250)
1371 self.config_frame[ext_name] = f
1372 entry_area = f.interior
Terry Jan Reedya9421fb2014-10-22 20:15:18 -04001373 # create an entry for each configuration option
1374 for row, opt in enumerate(self.extensions[ext_name]):
1375 # create a row with a label and entry/checkbutton
1376 label = Label(entry_area, text=opt['name'])
1377 label.grid(row=row, column=0, sticky=NW)
1378 var = opt['var']
1379 if opt['type'] == 'bool':
1380 Checkbutton(entry_area, textvariable=var, variable=var,
1381 onvalue='True', offvalue='False',
1382 indicatoron=FALSE, selectcolor='', width=8
Terry Jan Reedy41a27e02015-08-26 23:13:22 -04001383 ).grid(row=row, column=1, sticky=W, padx=7)
Terry Jan Reedya9421fb2014-10-22 20:15:18 -04001384 elif opt['type'] == 'int':
1385 Entry(entry_area, textvariable=var, validate='key',
Terry Jan Reedy41a27e02015-08-26 23:13:22 -04001386 validatecommand=(self.is_int, '%P')
1387 ).grid(row=row, column=1, sticky=NSEW, padx=7)
Terry Jan Reedya9421fb2014-10-22 20:15:18 -04001388
1389 else:
1390 Entry(entry_area, textvariable=var
Terry Jan Reedy41a27e02015-08-26 23:13:22 -04001391 ).grid(row=row, column=1, sticky=NSEW, padx=7)
Terry Jan Reedya9421fb2014-10-22 20:15:18 -04001392 return
1393
1394
1395 Ok = ConfigDialog.Ok
1396
1397 def Apply(self):
1398 self.save_all_changed_configs()
1399 pass
1400
1401 Cancel = ConfigDialog.Cancel
1402
1403 def Help(self):
1404 pass
1405
1406 def set_user_value(self, section, opt):
1407 name = opt['name']
1408 default = opt['default']
1409 value = opt['var'].get().strip() or default
1410 opt['var'].set(value)
1411 # if self.defaultCfg.has_section(section):
1412 # Currently, always true; if not, indent to return
1413 if (value == default):
1414 return self.userCfg.RemoveOption(section, name)
1415 # set the option
1416 return self.userCfg.SetOption(section, name, value)
1417
1418 def save_all_changed_configs(self):
1419 """Save configuration changes to the user config file."""
1420 has_changes = False
1421 for ext_name in self.extensions:
1422 options = self.extensions[ext_name]
1423 for opt in options:
1424 if self.set_user_value(ext_name, opt):
1425 has_changes = True
1426 if has_changes:
1427 self.userCfg.Save()
1428
1429
Steven M. Gava44d3d1a2001-07-31 06:59:02 +00001430if __name__ == '__main__':
Terry Jan Reedycfa89502014-07-14 23:07:32 -04001431 import unittest
1432 unittest.main('idlelib.idle_test.test_configdialog',
1433 verbosity=2, exit=False)
Terry Jan Reedy2e8234a2014-05-29 01:46:26 -04001434 from idlelib.idle_test.htest import run
Terry Jan Reedya9421fb2014-10-22 20:15:18 -04001435 run(ConfigDialog, ConfigExtensionsDialog)