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