blob: 814689c821253315eeb117f3fe4dc475cfb4db78 [file] [log] [blame]
Guido van Rossum57cd21f2003-04-29 10:23:27 +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.
9Refer to comment in EditorWindow autoindent code for details.
10
11"""
12from Tkinter import *
13import tkMessageBox, tkColorChooser, tkFont
14import string, copy
15
16from configHandler import idleConf
17from dynOptionMenuWidget import DynOptionMenu
18from tabpage import TabPageSet
19from keybindingDialog import GetKeysDialog
20from configSectionNameDialog import GetCfgSectionNameDialog
21from configHelpSourceEdit import GetHelpSourceDialog
22
23class ConfigDialog(Toplevel):
24 """
25 configuration dialog for idle
26 """
27 def __init__(self,parent,title):
28 Toplevel.__init__(self, parent)
29 self.configure(borderwidth=5)
30 self.geometry("+%d+%d" % (parent.winfo_rootx()+20,
31 parent.winfo_rooty()+30))
32 #Theme Elements. Each theme element key is it's display name.
33 #The first value of the tuple is the sample area tag name.
34 #The second value is the display name list sort index.
35 self.themeElements={'Normal Text':('normal','00'),
36 'Python Keywords':('keyword','01'),
37 'Python Definitions':('definition','02'),
38 'Python Comments':('comment','03'),
39 'Python Strings':('string','04'),
40 'Selected Text':('hilite','05'),
41 'Found Text':('hit','06'),
42 'Cursor':('cursor','07'),
43 'Error Text':('error','08'),
44 'Shell Normal Text':('console','09'),
45 'Shell Stdout Text':('stdout','10'),
46 'Shell Stderr Text':('stderr','11')}
47 self.ResetChangedItems() #load initial values in changed items dict
48 self.CreateWidgets()
49 self.resizable(height=FALSE,width=FALSE)
50 self.transient(parent)
51 self.grab_set()
52 self.protocol("WM_DELETE_WINDOW", self.Cancel)
53 self.parent = parent
54 self.tabPages.focus_set()
55 #key bindings for this dialog
56 #self.bind('<Escape>',self.Cancel) #dismiss dialog, no save
57 #self.bind('<Alt-a>',self.Apply) #apply changes, save
58 #self.bind('<F1>',self.Help) #context help
59 self.LoadConfigs()
60 self.AttachVarCallbacks() #avoid callbacks during LoadConfigs
61 self.wait_window()
62
63 def CreateWidgets(self):
64 self.tabPages = TabPageSet(self,
65 pageNames=['Fonts/Tabs','Highlighting','Keys','General'])
66 self.tabPages.ChangePage()#activates default (first) page
67 frameActionButtons = Frame(self)
68 #action buttons
69 self.buttonHelp = Button(frameActionButtons,text='Help',
70 command=self.Help,takefocus=FALSE)
71 self.buttonOk = Button(frameActionButtons,text='Ok',
72 command=self.Ok,takefocus=FALSE)
73 self.buttonApply = Button(frameActionButtons,text='Apply',
74 command=self.Apply,takefocus=FALSE)
75 self.buttonCancel = Button(frameActionButtons,text='Cancel',
76 command=self.Cancel,takefocus=FALSE)
77 self.CreatePageFontTab()
78 self.CreatePageHighlight()
79 self.CreatePageKeys()
80 self.CreatePageGeneral()
81 self.buttonHelp.pack(side=RIGHT,padx=5,pady=5)
82 self.buttonOk.pack(side=LEFT,padx=5,pady=5)
83 self.buttonApply.pack(side=LEFT,padx=5,pady=5)
84 self.buttonCancel.pack(side=LEFT,padx=5,pady=5)
85 frameActionButtons.pack(side=BOTTOM)
86 self.tabPages.pack(side=TOP,expand=TRUE,fill=BOTH)
87
88 def CreatePageFontTab(self):
89 #tkVars
90 self.fontSize=StringVar(self)
91 self.fontBold=BooleanVar(self)
92 self.fontName=StringVar(self)
93 self.spaceNum=IntVar(self)
94 #self.tabCols=IntVar(self)
95 self.indentBySpaces=BooleanVar(self)
96 self.editFont=tkFont.Font(self,('courier',12,'normal'))
97 ##widget creation
98 #body frame
99 frame=self.tabPages.pages['Fonts/Tabs']['page']
100 #body section frames
101 frameFont=Frame(frame,borderwidth=2,relief=GROOVE)
102 frameIndent=Frame(frame,borderwidth=2,relief=GROOVE)
103 #frameFont
104 labelFontTitle=Label(frameFont,text='Set Base Editor Font')
105 frameFontName=Frame(frameFont)
106 frameFontParam=Frame(frameFont)
107 labelFontNameTitle=Label(frameFontName,justify=LEFT,
108 text='Font :')
109 self.listFontName=Listbox(frameFontName,height=5,takefocus=FALSE,
110 exportselection=FALSE)
111 self.listFontName.bind('<ButtonRelease-1>',self.OnListFontButtonRelease)
112 scrollFont=Scrollbar(frameFontName)
113 scrollFont.config(command=self.listFontName.yview)
114 self.listFontName.config(yscrollcommand=scrollFont.set)
115 labelFontSizeTitle=Label(frameFontParam,text='Size :')
116 self.optMenuFontSize=DynOptionMenu(frameFontParam,self.fontSize,None,
117 command=self.SetFontSample)
118 checkFontBold=Checkbutton(frameFontParam,variable=self.fontBold,
119 onvalue=1,offvalue=0,text='Bold',command=self.SetFontSample)
120 frameFontSample=Frame(frameFont,relief=SOLID,borderwidth=1)
121 self.labelFontSample=Label(frameFontSample,
122 text='AaBbCcDdEe\nFfGgHhIiJjK\n1234567890\n#:+=(){}[]',
123 justify=LEFT,font=self.editFont)
124 #frameIndent
125 labelIndentTitle=Label(frameIndent,text='Set Indentation Defaults')
126 frameIndentType=Frame(frameIndent)
127 frameIndentSize=Frame(frameIndent)
128 labelIndentTypeTitle=Label(frameIndentType,
129 text='Choose indentation type :')
130 radioUseSpaces=Radiobutton(frameIndentType,variable=self.indentBySpaces,
131 value=1,text='Tab key inserts spaces')
132 radioUseTabs=Radiobutton(frameIndentType,variable=self.indentBySpaces,
133 value=0,text='Tab key inserts tabs')
134 labelIndentSizeTitle=Label(frameIndentSize,
135 text='Choose indentation size :')
136 labelSpaceNumTitle=Label(frameIndentSize,justify=LEFT,
137 text='indent width')
138 self.scaleSpaceNum=Scale(frameIndentSize,variable=self.spaceNum,
139 orient='horizontal',tickinterval=2,from_=2,to=16)
140 #labeltabColsTitle=Label(frameIndentSize,justify=LEFT,
141 # text='when tab key inserts tabs,\ncolumns per tab')
142 #self.scaleTabCols=Scale(frameIndentSize,variable=self.tabCols,
143 # orient='horizontal',tickinterval=2,from_=2,to=8)
144 #widget packing
145 #body
146 frameFont.pack(side=LEFT,padx=5,pady=10,expand=TRUE,fill=BOTH)
147 frameIndent.pack(side=LEFT,padx=5,pady=10,fill=Y)
148 #frameFont
149 labelFontTitle.pack(side=TOP,anchor=W,padx=5,pady=5)
150 frameFontName.pack(side=TOP,padx=5,pady=5,fill=X)
151 frameFontParam.pack(side=TOP,padx=5,pady=5,fill=X)
152 labelFontNameTitle.pack(side=TOP,anchor=W)
153 self.listFontName.pack(side=LEFT,expand=TRUE,fill=X)
154 scrollFont.pack(side=LEFT,fill=Y)
155 labelFontSizeTitle.pack(side=LEFT,anchor=W)
156 self.optMenuFontSize.pack(side=LEFT,anchor=W)
157 checkFontBold.pack(side=LEFT,anchor=W,padx=20)
158 frameFontSample.pack(side=TOP,padx=5,pady=5,expand=TRUE,fill=BOTH)
159 self.labelFontSample.pack(expand=TRUE,fill=BOTH)
160 #frameIndent
161 labelIndentTitle.pack(side=TOP,anchor=W,padx=5,pady=5)
162 frameIndentType.pack(side=TOP,padx=5,fill=X)
163 frameIndentSize.pack(side=TOP,padx=5,pady=5,fill=BOTH)
164 labelIndentTypeTitle.pack(side=TOP,anchor=W,padx=5,pady=5)
165 radioUseSpaces.pack(side=TOP,anchor=W,padx=5)
166 radioUseTabs.pack(side=TOP,anchor=W,padx=5)
167 labelIndentSizeTitle.pack(side=TOP,anchor=W,padx=5,pady=5)
168 labelSpaceNumTitle.pack(side=TOP,anchor=W,padx=5)
169 self.scaleSpaceNum.pack(side=TOP,padx=5,fill=X)
170 #labeltabColsTitle.pack(side=TOP,anchor=W,padx=5)
171 #self.scaleTabCols.pack(side=TOP,padx=5,fill=X)
172 return frame
173
174 def CreatePageHighlight(self):
175 self.builtinTheme=StringVar(self)
176 self.customTheme=StringVar(self)
177 self.fgHilite=BooleanVar(self)
178 self.colour=StringVar(self)
179 self.fontName=StringVar(self)
180 self.themeIsBuiltin=BooleanVar(self)
181 self.highlightTarget=StringVar(self)
182 ##widget creation
183 #body frame
184 frame=self.tabPages.pages['Highlighting']['page']
185 #body section frames
186 frameCustom=Frame(frame,borderwidth=2,relief=GROOVE)
187 frameTheme=Frame(frame,borderwidth=2,relief=GROOVE)
188 #frameCustom
189 self.textHighlightSample=Text(frameCustom,relief=SOLID,borderwidth=1,
190 font=('courier',12,''),cursor='hand2',width=21,height=10,
191 takefocus=FALSE,highlightthickness=0,wrap=NONE)
192 text=self.textHighlightSample
193 text.bind('<Double-Button-1>',lambda e: 'break')
194 text.bind('<B1-Motion>',lambda e: 'break')
195 textAndTags=(('#you can click here','comment'),('\n','normal'),
196 ('#to choose items','comment'),('\n','normal'),('def','keyword'),
197 (' ','normal'),('func','definition'),('(param):','normal'),
198 ('\n ','normal'),('"""string"""','string'),('\n var0 = ','normal'),
199 ("'string'",'string'),('\n var1 = ','normal'),("'selected'",'hilite'),
200 ('\n var2 = ','normal'),("'found'",'hit'),('\n\n','normal'),
201 (' error ','error'),(' ','normal'),('cursor |','cursor'),
202 ('\n ','normal'),('shell','console'),(' ','normal'),('stdout','stdout'),
203 (' ','normal'),('stderr','stderr'),('\n','normal'))
204 for txTa in textAndTags:
205 text.insert(END,txTa[0],txTa[1])
206 for element in self.themeElements.keys():
207 text.tag_bind(self.themeElements[element][0],'<ButtonPress-1>',
208 lambda event,elem=element: event.widget.winfo_toplevel()
209 .highlightTarget.set(elem))
210 text.config(state=DISABLED)
211 self.frameColourSet=Frame(frameCustom,relief=SOLID,borderwidth=1)
212 frameFgBg=Frame(frameCustom)
213 labelCustomTitle=Label(frameCustom,text='Set Custom Highlighting')
214 buttonSetColour=Button(self.frameColourSet,text='Choose Colour for :',
215 command=self.GetColour,highlightthickness=0)
216 self.optMenuHighlightTarget=DynOptionMenu(self.frameColourSet,
217 self.highlightTarget,None,highlightthickness=0)#,command=self.SetHighlightTargetBinding
218 self.radioFg=Radiobutton(frameFgBg,variable=self.fgHilite,
219 value=1,text='Foreground',command=self.SetColourSampleBinding)
220 self.radioBg=Radiobutton(frameFgBg,variable=self.fgHilite,
221 value=0,text='Background',command=self.SetColourSampleBinding)
222 self.fgHilite.set(1)
223 buttonSaveCustomTheme=Button(frameCustom,
224 text='Save as New Custom Theme',command=self.SaveAsNewTheme)
225 #frameTheme
226 labelThemeTitle=Label(frameTheme,text='Select a Highlighting Theme')
227 labelTypeTitle=Label(frameTheme,text='Select : ')
228 self.radioThemeBuiltin=Radiobutton(frameTheme,variable=self.themeIsBuiltin,
229 value=1,command=self.SetThemeType,text='a Built-in Theme')
230 self.radioThemeCustom=Radiobutton(frameTheme,variable=self.themeIsBuiltin,
231 value=0,command=self.SetThemeType,text='a Custom Theme')
232 self.optMenuThemeBuiltin=DynOptionMenu(frameTheme,
233 self.builtinTheme,None,command=None)
234 self.optMenuThemeCustom=DynOptionMenu(frameTheme,
235 self.customTheme,None,command=None)
236 self.buttonDeleteCustomTheme=Button(frameTheme,text='Delete Custom Theme',
237 command=self.DeleteCustomTheme)
238 ##widget packing
239 #body
240 frameCustom.pack(side=LEFT,padx=5,pady=10,expand=TRUE,fill=BOTH)
241 frameTheme.pack(side=LEFT,padx=5,pady=10,fill=Y)
242 #frameCustom
243 labelCustomTitle.pack(side=TOP,anchor=W,padx=5,pady=5)
244 self.frameColourSet.pack(side=TOP,padx=5,pady=5,expand=TRUE,fill=X)
245 frameFgBg.pack(side=TOP,padx=5,pady=0)
246 self.textHighlightSample.pack(side=TOP,padx=5,pady=5,expand=TRUE,
247 fill=BOTH)
248 buttonSetColour.pack(side=TOP,expand=TRUE,fill=X,padx=8,pady=4)
249 self.optMenuHighlightTarget.pack(side=TOP,expand=TRUE,fill=X,padx=8,pady=3)
250 self.radioFg.pack(side=LEFT,anchor=E)
251 self.radioBg.pack(side=RIGHT,anchor=W)
252 buttonSaveCustomTheme.pack(side=BOTTOM,fill=X,padx=5,pady=5)
253 #frameTheme
254 labelThemeTitle.pack(side=TOP,anchor=W,padx=5,pady=5)
255 labelTypeTitle.pack(side=TOP,anchor=W,padx=5,pady=5)
256 self.radioThemeBuiltin.pack(side=TOP,anchor=W,padx=5)
257 self.radioThemeCustom.pack(side=TOP,anchor=W,padx=5,pady=2)
258 self.optMenuThemeBuiltin.pack(side=TOP,fill=X,padx=5,pady=5)
259 self.optMenuThemeCustom.pack(side=TOP,fill=X,anchor=W,padx=5,pady=5)
260 self.buttonDeleteCustomTheme.pack(side=TOP,fill=X,padx=5,pady=5)
261 return frame
262
263 def CreatePageKeys(self):
264 #tkVars
265 self.bindingTarget=StringVar(self)
266 self.builtinKeys=StringVar(self)
267 self.customKeys=StringVar(self)
268 self.keysAreBuiltin=BooleanVar(self)
269 self.keyBinding=StringVar(self)
270 ##widget creation
271 #body frame
272 frame=self.tabPages.pages['Keys']['page']
273 #body section frames
274 frameCustom=Frame(frame,borderwidth=2,relief=GROOVE)
275 frameKeySets=Frame(frame,borderwidth=2,relief=GROOVE)
276 #frameCustom
277 frameTarget=Frame(frameCustom)
278 labelCustomTitle=Label(frameCustom,text='Set Custom Key Bindings')
279 labelTargetTitle=Label(frameTarget,text='Action - Key(s)')
280 scrollTargetY=Scrollbar(frameTarget)
281 scrollTargetX=Scrollbar(frameTarget,orient=HORIZONTAL)
282 self.listBindings=Listbox(frameTarget,takefocus=FALSE,
283 exportselection=FALSE)
284 self.listBindings.bind('<ButtonRelease-1>',self.KeyBindingSelected)
285 scrollTargetY.config(command=self.listBindings.yview)
286 scrollTargetX.config(command=self.listBindings.xview)
287 self.listBindings.config(yscrollcommand=scrollTargetY.set)
288 self.listBindings.config(xscrollcommand=scrollTargetX.set)
289 self.buttonNewKeys=Button(frameCustom,text='Get New Keys for Selection',
290 command=self.GetNewKeys,state=DISABLED)
291 buttonSaveCustomKeys=Button(frameCustom,
292 text='Save as New Custom Key Set',command=self.SaveAsNewKeySet)
293 #frameKeySets
294 labelKeysTitle=Label(frameKeySets,text='Select a Key Set')
295 labelTypeTitle=Label(frameKeySets,text='Select : ')
296 self.radioKeysBuiltin=Radiobutton(frameKeySets,variable=self.keysAreBuiltin,
297 value=1,command=self.SetKeysType,text='a Built-in Key Set')
298 self.radioKeysCustom=Radiobutton(frameKeySets,variable=self.keysAreBuiltin,
299 value=0,command=self.SetKeysType,text='a Custom Key Set')
300 self.optMenuKeysBuiltin=DynOptionMenu(frameKeySets,
301 self.builtinKeys,None,command=None)
302 self.optMenuKeysCustom=DynOptionMenu(frameKeySets,
303 self.customKeys,None,command=None)
304 self.buttonDeleteCustomKeys=Button(frameKeySets,text='Delete Custom Key Set',
305 command=self.DeleteCustomKeys)
306 ##widget packing
307 #body
308 frameCustom.pack(side=LEFT,padx=5,pady=5,expand=TRUE,fill=BOTH)
309 frameKeySets.pack(side=LEFT,padx=5,pady=5,fill=Y)
310 #frameCustom
311 labelCustomTitle.pack(side=TOP,anchor=W,padx=5,pady=5)
312 buttonSaveCustomKeys.pack(side=BOTTOM,fill=X,padx=5,pady=5)
313 self.buttonNewKeys.pack(side=BOTTOM,fill=X,padx=5,pady=5)
314 frameTarget.pack(side=LEFT,padx=5,pady=5,expand=TRUE,fill=BOTH)
315 #frame target
316 frameTarget.columnconfigure(0,weight=1)
317 frameTarget.rowconfigure(1,weight=1)
318 labelTargetTitle.grid(row=0,column=0,columnspan=2,sticky=W)
319 self.listBindings.grid(row=1,column=0,sticky=NSEW)
320 scrollTargetY.grid(row=1,column=1,sticky=NS)
321 scrollTargetX.grid(row=2,column=0,sticky=EW)
322 #frameKeySets
323 labelKeysTitle.pack(side=TOP,anchor=W,padx=5,pady=5)
324 labelTypeTitle.pack(side=TOP,anchor=W,padx=5,pady=5)
325 self.radioKeysBuiltin.pack(side=TOP,anchor=W,padx=5)
326 self.radioKeysCustom.pack(side=TOP,anchor=W,padx=5,pady=2)
327 self.optMenuKeysBuiltin.pack(side=TOP,fill=X,padx=5,pady=5)
328 self.optMenuKeysCustom.pack(side=TOP,fill=X,anchor=W,padx=5,pady=5)
329 self.buttonDeleteCustomKeys.pack(side=TOP,fill=X,padx=5,pady=5)
330 return frame
331
332 def CreatePageGeneral(self):
333 #tkVars
334 self.winWidth=StringVar(self)
335 self.winHeight=StringVar(self)
336 self.startupEdit=IntVar(self)
337 self.userHelpBrowser=BooleanVar(self)
338 self.helpBrowser=StringVar(self)
339 #widget creation
340 #body
341 frame=self.tabPages.pages['General']['page']
342 #body section frames
343 frameRun=Frame(frame,borderwidth=2,relief=GROOVE)
344 frameWinSize=Frame(frame,borderwidth=2,relief=GROOVE)
345 frameHelp=Frame(frame,borderwidth=2,relief=GROOVE)
346 #frameRun
347 labelRunTitle=Label(frameRun,text='Startup Preferences')
348 labelRunChoiceTitle=Label(frameRun,text='On Startup : ')
349 radioStartupEdit=Radiobutton(frameRun,variable=self.startupEdit,
350 value=1,command=self.SetKeysType,text="Open Edit Window")
351 radioStartupShell=Radiobutton(frameRun,variable=self.startupEdit,
352 value=0,command=self.SetKeysType,text='Open Shell Window')
353 #frameWinSize
354 labelWinSizeTitle=Label(frameWinSize,text='Initial Window Size'+
355 ' (in characters)')
356 labelWinWidthTitle=Label(frameWinSize,text='Width')
357 entryWinWidth=Entry(frameWinSize,textvariable=self.winWidth,
358 width=3)
359 labelWinHeightTitle=Label(frameWinSize,text='Height')
360 entryWinHeight=Entry(frameWinSize,textvariable=self.winHeight,
361 width=3)
362 #frameHelp
363 labelHelpTitle=Label(frameHelp,text='Help Options')
364 frameHelpList=Frame(frameHelp)
365 frameHelpListButtons=Frame(frameHelpList)
366 labelHelpListTitle=Label(frameHelpList,text='Additional Help Sources:')
367 scrollHelpList=Scrollbar(frameHelpList)
368 self.listHelp=Listbox(frameHelpList,height=5,takefocus=FALSE,
369 exportselection=FALSE)
370 scrollHelpList.config(command=self.listHelp.yview)
371 self.listHelp.config(yscrollcommand=scrollHelpList.set)
372 self.listHelp.bind('<ButtonRelease-1>',self.HelpSourceSelected)
373 self.buttonHelpListEdit=Button(frameHelpListButtons,text='Edit',
374 state=DISABLED,width=8,command=self.HelpListItemEdit)
375 self.buttonHelpListAdd=Button(frameHelpListButtons,text='Add',
376 width=8,command=self.HelpListItemAdd)
377 self.buttonHelpListRemove=Button(frameHelpListButtons,text='Remove',
378 state=DISABLED,width=8,command=self.HelpListItemRemove)
379 # the following is better handled by the BROWSER environment
380 # variable under unix/linux
381 #checkHelpBrowser=Checkbutton(frameHelp,variable=self.userHelpBrowser,
382 # onvalue=1,offvalue=0,text='user specified (html) help browser:',
383 # command=self.OnCheckUserHelpBrowser)
384 #self.entryHelpBrowser=Entry(frameHelp,textvariable=self.helpBrowser,
385 # width=40)
386 #widget packing
387 #body
388 frameRun.pack(side=TOP,padx=5,pady=5,fill=X)
389 frameWinSize.pack(side=TOP,padx=5,pady=5,fill=X)
390 frameHelp.pack(side=TOP,padx=5,pady=5,expand=TRUE,fill=BOTH)
391 #frameRun
392 labelRunTitle.pack(side=TOP,anchor=W,padx=5,pady=5)
393 labelRunChoiceTitle.pack(side=LEFT,anchor=W,padx=5,pady=5)
394 radioStartupEdit.pack(side=LEFT,anchor=W,padx=5,pady=5)
395 radioStartupShell.pack(side=LEFT,anchor=W,padx=5,pady=5)
396 #frameWinSize
397 labelWinSizeTitle.pack(side=LEFT,anchor=W,padx=5,pady=5)
398 entryWinHeight.pack(side=RIGHT,anchor=E,padx=10,pady=5)
399 labelWinHeightTitle.pack(side=RIGHT,anchor=E,pady=5)
400 entryWinWidth.pack(side=RIGHT,anchor=E,padx=10,pady=5)
401 labelWinWidthTitle.pack(side=RIGHT,anchor=E,pady=5)
402 #frameHelp
403 labelHelpTitle.pack(side=TOP,anchor=W,padx=5,pady=5)
404 frameHelpListButtons.pack(side=RIGHT,padx=5,pady=5,fill=Y)
405 frameHelpList.pack(side=TOP,padx=5,pady=5,expand=TRUE,fill=BOTH)
406 labelHelpListTitle.pack(side=TOP,anchor=W)
407 scrollHelpList.pack(side=RIGHT,anchor=W,fill=Y)
408 self.listHelp.pack(side=LEFT,anchor=E,expand=TRUE,fill=BOTH)
409 self.buttonHelpListEdit.pack(side=TOP,anchor=W,pady=5)
410 self.buttonHelpListAdd.pack(side=TOP,anchor=W)
411 self.buttonHelpListRemove.pack(side=TOP,anchor=W,pady=5)
412 #checkHelpBrowser.pack(side=TOP,anchor=W,padx=5)
413 #self.entryHelpBrowser.pack(side=TOP,anchor=W,padx=5,pady=5)
414 return frame
415
416 def AttachVarCallbacks(self):
417 self.fontSize.trace_variable('w',self.VarChanged_fontSize)
418 self.fontName.trace_variable('w',self.VarChanged_fontName)
419 self.fontBold.trace_variable('w',self.VarChanged_fontBold)
420 self.spaceNum.trace_variable('w',self.VarChanged_spaceNum)
421 #self.tabCols.trace_variable('w',self.VarChanged_tabCols)
422 self.indentBySpaces.trace_variable('w',self.VarChanged_indentBySpaces)
423 self.colour.trace_variable('w',self.VarChanged_colour)
424 self.builtinTheme.trace_variable('w',self.VarChanged_builtinTheme)
425 self.customTheme.trace_variable('w',self.VarChanged_customTheme)
426 self.themeIsBuiltin.trace_variable('w',self.VarChanged_themeIsBuiltin)
427 self.highlightTarget.trace_variable('w',self.VarChanged_highlightTarget)
428 self.keyBinding.trace_variable('w',self.VarChanged_keyBinding)
429 self.builtinKeys.trace_variable('w',self.VarChanged_builtinKeys)
430 self.customKeys.trace_variable('w',self.VarChanged_customKeys)
431 self.keysAreBuiltin.trace_variable('w',self.VarChanged_keysAreBuiltin)
432 self.winWidth.trace_variable('w',self.VarChanged_winWidth)
433 self.winHeight.trace_variable('w',self.VarChanged_winHeight)
434 self.startupEdit.trace_variable('w',self.VarChanged_startupEdit)
435
436 def VarChanged_fontSize(self,*params):
437 value=self.fontSize.get()
438 self.AddChangedItem('main','EditorWindow','font-size',value)
439
440 def VarChanged_fontName(self,*params):
441 value=self.fontName.get()
442 self.AddChangedItem('main','EditorWindow','font',value)
443
444 def VarChanged_fontBold(self,*params):
445 value=self.fontBold.get()
446 self.AddChangedItem('main','EditorWindow','font-bold',value)
447
448 def VarChanged_indentBySpaces(self,*params):
449 value=self.indentBySpaces.get()
450 self.AddChangedItem('main','Indent','use-spaces',value)
451
452 def VarChanged_spaceNum(self,*params):
453 value=self.spaceNum.get()
454 self.AddChangedItem('main','Indent','num-spaces',value)
455
456 #def VarChanged_tabCols(self,*params):
457 # value=self.tabCols.get()
458 # self.AddChangedItem('main','Indent','tab-cols',value)
459
460 def VarChanged_colour(self,*params):
461 self.OnNewColourSet()
462
463 def VarChanged_builtinTheme(self,*params):
464 value=self.builtinTheme.get()
465 self.AddChangedItem('main','Theme','name',value)
466 self.PaintThemeSample()
467
468 def VarChanged_customTheme(self,*params):
469 value=self.customTheme.get()
470 if value != '- no custom themes -':
471 self.AddChangedItem('main','Theme','name',value)
472 self.PaintThemeSample()
473
474 def VarChanged_themeIsBuiltin(self,*params):
475 value=self.themeIsBuiltin.get()
476 self.AddChangedItem('main','Theme','default',value)
477 if value:
478 self.VarChanged_builtinTheme()
479 else:
480 self.VarChanged_customTheme()
481
482 def VarChanged_highlightTarget(self,*params):
483 self.SetHighlightTarget()
484
485 def VarChanged_keyBinding(self,*params):
486 value=self.keyBinding.get()
487 keySet=self.customKeys.get()
488 event=self.listBindings.get(ANCHOR).split()[0]
489 if idleConf.IsCoreBinding(event):
490 #this is a core keybinding
491 self.AddChangedItem('keys',keySet,event,value)
492 else: #this is an extension key binding
493 extName=idleConf.GetExtnNameForEvent(event)
494 extKeybindSection=extName+'_cfgBindings'
495 self.AddChangedItem('extensions',extKeybindSection,event,value)
496
497 def VarChanged_builtinKeys(self,*params):
498 value=self.builtinKeys.get()
499 self.AddChangedItem('main','Keys','name',value)
500 self.LoadKeysList(value)
501
502 def VarChanged_customKeys(self,*params):
503 value=self.customKeys.get()
504 if value != '- no custom keys -':
505 self.AddChangedItem('main','Keys','name',value)
506 self.LoadKeysList(value)
507
508 def VarChanged_keysAreBuiltin(self,*params):
509 value=self.keysAreBuiltin.get()
510 self.AddChangedItem('main','Keys','default',value)
511 if value:
512 self.VarChanged_builtinKeys()
513 else:
514 self.VarChanged_customKeys()
515
516 def VarChanged_winWidth(self,*params):
517 value=self.winWidth.get()
518 self.AddChangedItem('main','EditorWindow','width',value)
519
520 def VarChanged_winHeight(self,*params):
521 value=self.winHeight.get()
522 self.AddChangedItem('main','EditorWindow','height',value)
523
524 def VarChanged_startupEdit(self,*params):
525 value=self.startupEdit.get()
526 self.AddChangedItem('main','General','editor-on-startup',value)
527
528 def ResetChangedItems(self):
529 #When any config item is changed in this dialog, an entry
530 #should be made in the relevant section (config type) of this
531 #dictionary. The key should be the config file section name and the
532 #value a dictionary, whose key:value pairs are item=value pairs for
533 #that config file section.
534 self.changedItems={'main':{},'highlight':{},'keys':{},'extensions':{}}
535
536 def AddChangedItem(self,type,section,item,value):
537 value=str(value) #make sure we use a string
538 if not self.changedItems[type].has_key(section):
539 self.changedItems[type][section]={}
540 self.changedItems[type][section][item]=value
541
542 def GetDefaultItems(self):
543 dItems={'main':{},'highlight':{},'keys':{},'extensions':{}}
544 for configType in dItems.keys():
545 sections=idleConf.GetSectionList('default',configType)
546 for section in sections:
547 dItems[configType][section]={}
548 options=idleConf.defaultCfg[configType].GetOptionList(section)
549 for option in options:
550 dItems[configType][section][option]=(
551 idleConf.defaultCfg[configType].Get(section,option))
552 return dItems
553
554 def SetThemeType(self):
555 if self.themeIsBuiltin.get():
556 self.optMenuThemeBuiltin.config(state=NORMAL)
557 self.optMenuThemeCustom.config(state=DISABLED)
558 self.buttonDeleteCustomTheme.config(state=DISABLED)
559 else:
560 self.optMenuThemeBuiltin.config(state=DISABLED)
561 self.radioThemeCustom.config(state=NORMAL)
562 self.optMenuThemeCustom.config(state=NORMAL)
563 self.buttonDeleteCustomTheme.config(state=NORMAL)
564
565 def SetKeysType(self):
566 if self.keysAreBuiltin.get():
567 self.optMenuKeysBuiltin.config(state=NORMAL)
568 self.optMenuKeysCustom.config(state=DISABLED)
569 self.buttonDeleteCustomKeys.config(state=DISABLED)
570 else:
571 self.optMenuKeysBuiltin.config(state=DISABLED)
572 self.radioKeysCustom.config(state=NORMAL)
573 self.optMenuKeysCustom.config(state=NORMAL)
574 self.buttonDeleteCustomKeys.config(state=NORMAL)
575
576 def GetNewKeys(self):
577 listIndex=self.listBindings.index(ANCHOR)
578 binding=self.listBindings.get(listIndex)
579 bindName=binding.split()[0] #first part, up to first space
580 if self.keysAreBuiltin.get():
581 currentKeySetName=self.builtinKeys.get()
582 else:
583 currentKeySetName=self.customKeys.get()
584 currentBindings=idleConf.GetCurrentKeySet()
585 if currentKeySetName in self.changedItems['keys'].keys(): #unsaved changes
586 keySetChanges=self.changedItems['keys'][currentKeySetName]
587 for event in keySetChanges.keys():
588 currentBindings[event]=keySetChanges[event].split()
589 currentKeySequences=currentBindings.values()
590 newKeys=GetKeysDialog(self,'Get New Keys',bindName,
591 currentKeySequences).result
592 if newKeys: #new keys were specified
593 if self.keysAreBuiltin.get(): #current key set is a built-in
594 message=('Your changes will be saved as a new Custom Key Set. '+
595 'Enter a name for your new Custom Key Set below.')
596 newKeySet=self.GetNewKeysName(message)
597 if not newKeySet: #user cancelled custom key set creation
598 self.listBindings.select_set(listIndex)
599 self.listBindings.select_anchor(listIndex)
600 return
601 else: #create new custom key set based on previously active key set
602 self.CreateNewKeySet(newKeySet)
603 self.listBindings.delete(listIndex)
604 self.listBindings.insert(listIndex,bindName+' - '+newKeys)
605 self.listBindings.select_set(listIndex)
606 self.listBindings.select_anchor(listIndex)
607 self.keyBinding.set(newKeys)
608 else:
609 self.listBindings.select_set(listIndex)
610 self.listBindings.select_anchor(listIndex)
611
612 def GetNewKeysName(self,message):
613 usedNames=(idleConf.GetSectionList('user','keys')+
614 idleConf.GetSectionList('default','keys'))
615 newKeySet=GetCfgSectionNameDialog(self,'New Custom Key Set',
616 message,usedNames).result
617 return newKeySet
618
619 def SaveAsNewKeySet(self):
620 newKeysName=self.GetNewKeysName('New Key Set Name:')
621 if newKeysName:
622 self.CreateNewKeySet(newKeysName)
623
624 def KeyBindingSelected(self,event):
625 self.buttonNewKeys.config(state=NORMAL)
626
627 def CreateNewKeySet(self,newKeySetName):
628 #creates new custom key set based on the previously active key set,
629 #and makes the new key set active
630 if self.keysAreBuiltin.get():
631 prevKeySetName=self.builtinKeys.get()
632 else:
633 prevKeySetName=self.customKeys.get()
634 prevKeys=idleConf.GetCoreKeys(prevKeySetName)
635 newKeys={}
636 for event in prevKeys.keys(): #add key set to changed items
637 eventName=event[2:-2] #trim off the angle brackets
638 binding=string.join(prevKeys[event])
639 newKeys[eventName]=binding
640 #handle any unsaved changes to prev key set
641 if prevKeySetName in self.changedItems['keys'].keys():
642 keySetChanges=self.changedItems['keys'][prevKeySetName]
643 for event in keySetChanges.keys():
644 newKeys[event]=keySetChanges[event]
645 #save the new theme
646 self.SaveNewKeySet(newKeySetName,newKeys)
647 #change gui over to the new key set
648 customKeyList=idleConf.GetSectionList('user','keys')
649 customKeyList.sort()
650 self.optMenuKeysCustom.SetMenu(customKeyList,newKeySetName)
651 self.keysAreBuiltin.set(0)
652 self.SetKeysType()
653
654 def LoadKeysList(self,keySetName):
655 reselect=0
656 newKeySet=0
657 if self.listBindings.curselection():
658 reselect=1
659 listIndex=self.listBindings.index(ANCHOR)
660 keySet=idleConf.GetKeySet(keySetName)
661 bindNames=keySet.keys()
662 bindNames.sort()
663 self.listBindings.delete(0,END)
664 for bindName in bindNames:
665 key=string.join(keySet[bindName]) #make key(s) into a string
666 bindName=bindName[2:-2] #trim off the angle brackets
667 if keySetName in self.changedItems['keys'].keys():
668 #handle any unsaved changes to this key set
669 if bindName in self.changedItems['keys'][keySetName].keys():
670 key=self.changedItems['keys'][keySetName][bindName]
671 self.listBindings.insert(END, bindName+' - '+key)
672 if reselect:
673 self.listBindings.see(listIndex)
674 self.listBindings.select_set(listIndex)
675 self.listBindings.select_anchor(listIndex)
676
677 def DeleteCustomKeys(self):
678 keySetName=self.customKeys.get()
679 if not tkMessageBox.askyesno('Delete Key Set','Are you sure you wish '+
680 'to delete the key set '+`keySetName`+' ?',
681 parent=self):
682 return
683 #remove key set from config
684 idleConf.userCfg['keys'].remove_section(keySetName)
685 if self.changedItems['keys'].has_key(keySetName):
686 del(self.changedItems['keys'][keySetName])
687 #write changes
688 idleConf.userCfg['keys'].Save()
689 #reload user key set list
690 itemList=idleConf.GetSectionList('user','keys')
691 itemList.sort()
692 if not itemList:
693 self.radioKeysCustom.config(state=DISABLED)
694 self.optMenuKeysCustom.SetMenu(itemList,'- no custom keys -')
695 else:
696 self.optMenuKeysCustom.SetMenu(itemList,itemList[0])
697 #revert to default key set
698 self.keysAreBuiltin.set(idleConf.defaultCfg['main'].Get('Keys','default'))
699 self.builtinKeys.set(idleConf.defaultCfg['main'].Get('Keys','name'))
700 #user can't back out of these changes, they must be applied now
701 self.Apply()
702 self.SetKeysType()
703
704 def DeleteCustomTheme(self):
705 themeName=self.customTheme.get()
706 if not tkMessageBox.askyesno('Delete Theme','Are you sure you wish '+
707 'to delete the theme '+`themeName`+' ?',
708 parent=self):
709 return
710 #remove theme from config
711 idleConf.userCfg['highlight'].remove_section(themeName)
712 if self.changedItems['highlight'].has_key(themeName):
713 del(self.changedItems['highlight'][themeName])
714 #write changes
715 idleConf.userCfg['highlight'].Save()
716 #reload user theme list
717 itemList=idleConf.GetSectionList('user','highlight')
718 itemList.sort()
719 if not itemList:
720 self.radioThemeCustom.config(state=DISABLED)
721 self.optMenuThemeCustom.SetMenu(itemList,'- no custom themes -')
722 else:
723 self.optMenuThemeCustom.SetMenu(itemList,itemList[0])
724 #revert to default theme
725 self.themeIsBuiltin.set(idleConf.defaultCfg['main'].Get('Theme','default'))
726 self.builtinTheme.set(idleConf.defaultCfg['main'].Get('Theme','name'))
727 #user can't back out of these changes, they must be applied now
728 self.Apply()
729 self.SetThemeType()
730
731 def GetColour(self):
732 target=self.highlightTarget.get()
733 prevColour=self.frameColourSet.cget('bg')
734 rgbTuplet, colourString = tkColorChooser.askcolor(parent=self,
735 title='Pick new colour for : '+target,initialcolor=prevColour)
736 if colourString and (colourString!=prevColour):
737 #user didn't cancel, and they chose a new colour
738 if self.themeIsBuiltin.get(): #current theme is a built-in
739 message=('Your changes will be saved as a new Custom Theme. '+
740 'Enter a name for your new Custom Theme below.')
741 newTheme=self.GetNewThemeName(message)
742 if not newTheme: #user cancelled custom theme creation
743 return
744 else: #create new custom theme based on previously active theme
745 self.CreateNewTheme(newTheme)
746 self.colour.set(colourString)
747 else: #current theme is user defined
748 self.colour.set(colourString)
749
750 def OnNewColourSet(self):
751 newColour=self.colour.get()
752 self.frameColourSet.config(bg=newColour)#set sample
753 if self.fgHilite.get(): plane='foreground'
754 else: plane='background'
755 sampleElement=self.themeElements[self.highlightTarget.get()][0]
756 apply(self.textHighlightSample.tag_config,
757 (sampleElement,),{plane:newColour})
758 theme=self.customTheme.get()
759 themeElement=sampleElement+'-'+plane
760 self.AddChangedItem('highlight',theme,themeElement,newColour)
761
762 def GetNewThemeName(self,message):
763 usedNames=(idleConf.GetSectionList('user','highlight')+
764 idleConf.GetSectionList('default','highlight'))
765 newTheme=GetCfgSectionNameDialog(self,'New Custom Theme',
766 message,usedNames).result
767 return newTheme
768
769 def SaveAsNewTheme(self):
770 newThemeName=self.GetNewThemeName('New Theme Name:')
771 if newThemeName:
772 self.CreateNewTheme(newThemeName)
773
774 def CreateNewTheme(self,newThemeName):
775 #creates new custom theme based on the previously active theme,
776 #and makes the new theme active
777 if self.themeIsBuiltin.get():
778 themeType='default'
779 themeName=self.builtinTheme.get()
780 else:
781 themeType='user'
782 themeName=self.customTheme.get()
783 newTheme=idleConf.GetThemeDict(themeType,themeName)
784 #apply any of the old theme's unsaved changes to the new theme
785 if themeName in self.changedItems['highlight'].keys():
786 themeChanges=self.changedItems['highlight'][themeName]
787 for element in themeChanges.keys():
788 newTheme[element]=themeChanges[element]
789 #save the new theme
790 self.SaveNewTheme(newThemeName,newTheme)
791 #change gui over to the new theme
792 customThemeList=idleConf.GetSectionList('user','highlight')
793 customThemeList.sort()
794 self.optMenuThemeCustom.SetMenu(customThemeList,newThemeName)
795 self.themeIsBuiltin.set(0)
796 self.SetThemeType()
797
798 def OnListFontButtonRelease(self,event):
799 self.fontName.set(self.listFontName.get(ANCHOR))
800 self.SetFontSample()
801
802 def SetFontSample(self,event=None):
803 fontName=self.fontName.get()
804 if self.fontBold.get():
805 fontWeight=tkFont.BOLD
806 else:
807 fontWeight=tkFont.NORMAL
808 self.editFont.config(size=self.fontSize.get(),
809 weight=fontWeight,family=fontName)
810
811 def SetHighlightTarget(self):
812 if self.highlightTarget.get()=='Cursor': #bg not possible
813 self.radioFg.config(state=DISABLED)
814 self.radioBg.config(state=DISABLED)
815 self.fgHilite.set(1)
816 else: #both fg and bg can be set
817 self.radioFg.config(state=NORMAL)
818 self.radioBg.config(state=NORMAL)
819 self.fgHilite.set(1)
820 self.SetColourSample()
821
822 def SetColourSampleBinding(self,*args):
823 self.SetColourSample()
824
825 def SetColourSample(self):
826 #set the colour smaple area
827 tag=self.themeElements[self.highlightTarget.get()][0]
828 if self.fgHilite.get(): plane='foreground'
829 else: plane='background'
830 colour=self.textHighlightSample.tag_cget(tag,plane)
831 self.frameColourSet.config(bg=colour)
832
833 def PaintThemeSample(self):
834 if self.themeIsBuiltin.get(): #a default theme
835 theme=self.builtinTheme.get()
836 else: #a user theme
837 theme=self.customTheme.get()
838 for elementTitle in self.themeElements.keys():
839 element=self.themeElements[elementTitle][0]
840 colours=idleConf.GetHighlight(theme,element)
841 if element=='cursor': #cursor sample needs special painting
842 colours['background']=idleConf.GetHighlight(theme,
843 'normal', fgBg='bg')
844 #handle any unsaved changes to this theme
845 if theme in self.changedItems['highlight'].keys():
846 themeDict=self.changedItems['highlight'][theme]
847 if themeDict.has_key(element+'-foreground'):
848 colours['foreground']=themeDict[element+'-foreground']
849 if themeDict.has_key(element+'-background'):
850 colours['background']=themeDict[element+'-background']
851 apply(self.textHighlightSample.tag_config,(element,),colours)
852 self.SetColourSample()
853
854## def OnCheckUserHelpBrowser(self):
855## if self.userHelpBrowser.get():
856## self.entryHelpBrowser.config(state=NORMAL)
857## else:
858## self.entryHelpBrowser.config(state=DISABLED)
859
860 def HelpSourceSelected(self,event):
861 self.SetHelpListButtonStates()
862
863 def SetHelpListButtonStates(self):
864 if self.listHelp.size()<1: #no entries in list
865 self.buttonHelpListEdit.config(state=DISABLED)
866 self.buttonHelpListRemove.config(state=DISABLED)
867 else: #there are some entries
868 if self.listHelp.curselection(): #there currently is a selection
869 self.buttonHelpListEdit.config(state=NORMAL)
870 self.buttonHelpListRemove.config(state=NORMAL)
871 else: #there currently is not a selection
872 self.buttonHelpListEdit.config(state=DISABLED)
873 self.buttonHelpListRemove.config(state=DISABLED)
874
875 def HelpListItemAdd(self):
876 helpSource=GetHelpSourceDialog(self,'New Help Source').result
877 if helpSource:
878 self.userHelpList.append( (helpSource[0],helpSource[1]) )
879 self.listHelp.insert(END,helpSource[0])
880 self.UpdateUserHelpChangedItems()
881 self.SetHelpListButtonStates()
882
883 def HelpListItemEdit(self):
884 itemIndex=self.listHelp.index(ANCHOR)
885 helpSource=self.userHelpList[itemIndex]
886 newHelpSource=GetHelpSourceDialog(self,'Edit Help Source',
887 menuItem=helpSource[0],filePath=helpSource[1]).result
888 if (not newHelpSource) or (newHelpSource==helpSource):
889 return #no changes
890 self.userHelpList[itemIndex]=newHelpSource
891 self.listHelp.delete(itemIndex)
892 self.listHelp.insert(itemIndex,newHelpSource[0])
893 self.UpdateUserHelpChangedItems()
894 self.SetHelpListButtonStates()
895
896 def HelpListItemRemove(self):
897 itemIndex=self.listHelp.index(ANCHOR)
898 del(self.userHelpList[itemIndex])
899 self.listHelp.delete(itemIndex)
900 self.UpdateUserHelpChangedItems()
901 self.SetHelpListButtonStates()
902
903 def UpdateUserHelpChangedItems(self):
904 "Clear and rebuild the HelpFiles section in self.changedItems"
905 self.changedItems['main']['HelpFiles'] = {}
906 for num in range(1,len(self.userHelpList)+1):
907 self.AddChangedItem('main','HelpFiles',str(num),
908 string.join(self.userHelpList[num-1][:2],';'))
909
910 def LoadFontCfg(self):
911 ##base editor font selection list
912 fonts=list(tkFont.families(self))
913 fonts.sort()
914 for font in fonts:
915 self.listFontName.insert(END,font)
916 configuredFont=idleConf.GetOption('main','EditorWindow','font',
917 default='courier')
918 self.fontName.set(configuredFont)
919 if configuredFont in fonts:
920 currentFontIndex=fonts.index(configuredFont)
921 self.listFontName.see(currentFontIndex)
922 self.listFontName.select_set(currentFontIndex)
923 self.listFontName.select_anchor(currentFontIndex)
924 ##font size dropdown
925 fontSize=idleConf.GetOption('main','EditorWindow','font-size',
926 default='12')
927 self.optMenuFontSize.SetMenu(('7','8','9','10','11','12','13','14',
928 '16','18','20','22'),fontSize )
929 ##fontWeight
930 self.fontBold.set(idleConf.GetOption('main','EditorWindow',
931 'font-bold',default=0,type='bool'))
932 ##font sample
933 self.SetFontSample()
934
935 def LoadTabCfg(self):
936 ##indent type radiobuttons
937 spaceIndent=idleConf.GetOption('main','Indent','use-spaces',
938 default=1,type='bool')
939 self.indentBySpaces.set(spaceIndent)
940 ##indent sizes
941 spaceNum=idleConf.GetOption('main','Indent','num-spaces',
942 default=4,type='int')
943 #tabCols=idleConf.GetOption('main','Indent','tab-cols',
944 # default=4,type='int')
945 self.spaceNum.set(spaceNum)
946 #self.tabCols.set(tabCols)
947
948 def LoadThemeCfg(self):
949 ##current theme type radiobutton
950 self.themeIsBuiltin.set(idleConf.GetOption('main','Theme','default',
951 type='bool',default=1))
952 ##currently set theme
953 currentOption=idleConf.CurrentTheme()
954 ##load available theme option menus
955 if self.themeIsBuiltin.get(): #default theme selected
956 itemList=idleConf.GetSectionList('default','highlight')
957 itemList.sort()
958 self.optMenuThemeBuiltin.SetMenu(itemList,currentOption)
959 itemList=idleConf.GetSectionList('user','highlight')
960 itemList.sort()
961 if not itemList:
962 self.radioThemeCustom.config(state=DISABLED)
963 self.customTheme.set('- no custom themes -')
964 else:
965 self.optMenuThemeCustom.SetMenu(itemList,itemList[0])
966 else: #user theme selected
967 itemList=idleConf.GetSectionList('user','highlight')
968 itemList.sort()
969 self.optMenuThemeCustom.SetMenu(itemList,currentOption)
970 itemList=idleConf.GetSectionList('default','highlight')
971 itemList.sort()
972 self.optMenuThemeBuiltin.SetMenu(itemList,itemList[0])
973 self.SetThemeType()
974 ##load theme element option menu
975 themeNames=self.themeElements.keys()
976 themeNames.sort(self.__ThemeNameIndexCompare)
977 self.optMenuHighlightTarget.SetMenu(themeNames,themeNames[0])
978 self.PaintThemeSample()
979 self.SetHighlightTarget()
980
981 def __ThemeNameIndexCompare(self,a,b):
982 if self.themeElements[a][1]<self.themeElements[b][1]: return -1
983 elif self.themeElements[a][1]==self.themeElements[b][1]: return 0
984 else: return 1
985
986 def LoadKeyCfg(self):
987 ##current keys type radiobutton
988 self.keysAreBuiltin.set(idleConf.GetOption('main','Keys','default',
989 type='bool',default=1))
990 ##currently set keys
991 currentOption=idleConf.CurrentKeys()
992 ##load available keyset option menus
993 if self.keysAreBuiltin.get(): #default theme selected
994 itemList=idleConf.GetSectionList('default','keys')
995 itemList.sort()
996 self.optMenuKeysBuiltin.SetMenu(itemList,currentOption)
997 itemList=idleConf.GetSectionList('user','keys')
998 itemList.sort()
999 if not itemList:
1000 self.radioKeysCustom.config(state=DISABLED)
1001 self.customKeys.set('- no custom keys -')
1002 else:
1003 self.optMenuKeysCustom.SetMenu(itemList,itemList[0])
1004 else: #user key set selected
1005 itemList=idleConf.GetSectionList('user','keys')
1006 itemList.sort()
1007 self.optMenuKeysCustom.SetMenu(itemList,currentOption)
1008 itemList=idleConf.GetSectionList('default','keys')
1009 itemList.sort()
1010 self.optMenuKeysBuiltin.SetMenu(itemList,itemList[0])
1011 self.SetKeysType()
1012 ##load keyset element list
1013 keySetName=idleConf.CurrentKeys()
1014 self.LoadKeysList(keySetName)
1015
1016 def LoadGeneralCfg(self):
1017 #startup state
1018 self.startupEdit.set(idleConf.GetOption('main','General',
1019 'editor-on-startup',default=1,type='bool'))
1020 #initial window size
1021 self.winWidth.set(idleConf.GetOption('main','EditorWindow','width'))
1022 self.winHeight.set(idleConf.GetOption('main','EditorWindow','height'))
1023 # additional help sources
1024 self.userHelpList = idleConf.GetAllExtraHelpSourcesList()
1025 for helpItem in self.userHelpList:
1026 self.listHelp.insert(END,helpItem[0])
1027 self.SetHelpListButtonStates()
1028 #self.userHelpBrowser.set(idleConf.GetOption('main','General',
1029 # 'user-help-browser',default=0,type='bool'))
1030 #self.helpBrowser.set(idleConf.GetOption('main','General',
1031 # 'user-help-browser-command',default=''))
1032 #self.OnCheckUserHelpBrowser()
1033
1034 def LoadConfigs(self):
1035 """
1036 load configuration from default and user config files and populate
1037 the widgets on the config dialog pages.
1038 """
1039 ### fonts / tabs page
1040 self.LoadFontCfg()
1041 self.LoadTabCfg()
1042 ### highlighting page
1043 self.LoadThemeCfg()
1044 ### keys page
1045 self.LoadKeyCfg()
1046 ### general page
1047 self.LoadGeneralCfg()
1048
1049 def SaveNewKeySet(self,keySetName,keySet):
1050 """
1051 save a newly created core key set.
1052 keySetName - string, the name of the new key set
1053 keySet - dictionary containing the new key set
1054 """
1055 if not idleConf.userCfg['keys'].has_section(keySetName):
1056 idleConf.userCfg['keys'].add_section(keySetName)
1057 for event in keySet.keys():
1058 value=keySet[event]
1059 idleConf.userCfg['keys'].SetOption(keySetName,event,value)
1060
1061 def SaveNewTheme(self,themeName,theme):
1062 """
1063 save a newly created theme.
1064 themeName - string, the name of the new theme
1065 theme - dictionary containing the new theme
1066 """
1067 if not idleConf.userCfg['highlight'].has_section(themeName):
1068 idleConf.userCfg['highlight'].add_section(themeName)
1069 for element in theme.keys():
1070 value=theme[element]
1071 idleConf.userCfg['highlight'].SetOption(themeName,element,value)
1072
1073 def SetUserValue(self,configType,section,item,value):
1074 if idleConf.defaultCfg[configType].has_option(section,item):
1075 if idleConf.defaultCfg[configType].Get(section,item)==value:
1076 #the setting equals a default setting, remove it from user cfg
1077 return idleConf.userCfg[configType].RemoveOption(section,item)
1078 #if we got here set the option
1079 return idleConf.userCfg[configType].SetOption(section,item,value)
1080
1081 def SaveAllChangedConfigs(self):
1082 "Save configuration changes to the user config file."
1083 idleConf.userCfg['main'].Save()
1084 for configType in self.changedItems.keys():
1085 cfgTypeHasChanges = False
1086 for section in self.changedItems[configType].keys():
1087 if section == 'HelpFiles':
1088 #this section gets completely replaced
1089 idleConf.userCfg['main'].remove_section('HelpFiles')
1090 cfgTypeHasChanges = True
1091 for item in self.changedItems[configType][section].keys():
1092 value = self.changedItems[configType][section][item]
1093 if self.SetUserValue(configType,section,item,value):
1094 cfgTypeHasChanges = True
1095 if cfgTypeHasChanges:
1096 idleConf.userCfg[configType].Save()
1097 self.ResetChangedItems() #clear the changed items dict
1098
1099 def ActivateConfigChanges(self):
1100 #things that need to be done to make
1101 #applied config changes dynamic:
1102 #update editor/shell font and repaint
1103 #dynamically update indentation setttings
1104 #update theme and repaint
1105 #update keybindings and re-bind
1106 #update user help sources menu
1107 winInstances=self.parent.instanceDict.keys()
1108 for instance in winInstances:
1109 instance.ResetColorizer()
1110 instance.ResetFont()
1111 instance.ResetKeybindings()
1112 instance.reset_help_menu_entries()
1113
1114 def Cancel(self):
1115 self.destroy()
1116
1117 def Ok(self):
1118 self.Apply()
1119 self.destroy()
1120
1121 def Apply(self):
1122 self.SaveAllChangedConfigs()
1123 self.ActivateConfigChanges()
1124
1125 def Help(self):
1126 pass
1127
1128if __name__ == '__main__':
1129 #test the dialog
1130 root=Tk()
1131 Button(root,text='Dialog',
1132 command=lambda:ConfigDialog(root,'Settings')).pack()
1133 root.instanceDict={}
1134 root.mainloop()