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