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